koa-files 4.1.7 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,17 +9,38 @@
9
9
  > ![Node Version][node-image]
10
10
  > [![License][license-image]][license-url]
11
11
 
12
- ## Installation
12
+ ## Install
13
13
 
14
14
  ```bash
15
15
  $ npm install koa-files
16
16
  ```
17
17
 
18
+ ## Quick start
19
+
20
+ ```ts
21
+ import Koa from 'koa';
22
+ import { server } from 'koa-files';
23
+
24
+ const app = new Koa();
25
+
26
+ app.use(
27
+ server('public', {
28
+ headers: {
29
+ 'Cache-Control': 'public, max-age=31536000, immutable'
30
+ }
31
+ })
32
+ );
33
+
34
+ app.listen(3000);
35
+ ```
36
+
37
+ Then visit `http://127.0.0.1:3000/app.js` to serve `public/app.js`.
38
+
18
39
  ## API
19
40
 
20
41
  ```ts
21
42
  import { Middleware } from 'koa';
22
- import fs, { Stats } from 'node:fs';
43
+ import { PathLike, Stats } from 'node:fs';
23
44
 
24
45
  interface Headers {
25
46
  [key: string]: string | string[];
@@ -38,10 +59,21 @@ interface HeadersFunction {
38
59
  }
39
60
 
40
61
  export interface FileSystem {
41
- stat: typeof fs.stat;
42
- open: typeof fs.open;
43
- read: typeof fs.read;
44
- close: typeof fs.close;
62
+ close(fd: number, callback?: (error?: Error | null) => void): void;
63
+ read<T extends ArrayBufferView>(
64
+ fd: number,
65
+ buffer: T,
66
+ offset: number,
67
+ length: number,
68
+ position: number | bigint | null,
69
+ callback: (error: Error | null | undefined, bytesRead: number, buffer: T) => void
70
+ ): void;
71
+ stat(path: PathLike, callback: (error: Error | null | undefined, stats: Stats) => void): void;
72
+ open(
73
+ path: PathLike,
74
+ flags: string | number | undefined,
75
+ callback: (error: Error | null | undefined, fd: number) => void
76
+ ): void;
45
77
  }
46
78
 
47
79
  export interface Options {
@@ -62,6 +94,7 @@ export function server(root: string, options?: Options): Middleware;
62
94
 
63
95
  - Root directory string.
64
96
  - Nothing above this root directory can be served.
97
+ - `root` is resolved with `path.resolve`, so relative paths are based on the process working directory.
65
98
 
66
99
  ### Options
67
100
 
@@ -75,6 +108,7 @@ export function server(root: string, options?: Options): Middleware;
75
108
  - Defaults to `false`.
76
109
  - If true, serves after `await next()`.
77
110
  - Allowing any downstream middleware to respond first.
111
+ - Useful when you want route handlers to take priority over static files.
78
112
 
79
113
  ##### `etag`
80
114
 
@@ -101,17 +135,28 @@ export function server(root: string, options?: Options): Middleware;
101
135
 
102
136
  - Defaults to `65536` (64 KiB).
103
137
  - Set the high water mark for the read stream.
138
+ - Supports function form to customize by file path and stats.
104
139
 
105
140
  ##### `ignore`
106
141
 
107
142
  - Defaults to `undefined`.
108
143
  - Function that determines if a file should be ignored.
144
+ - Return `true` to skip static serving for the current file.
109
145
 
110
146
  ##### `headers`
111
147
 
112
148
  - Defaults to `undefined`.
113
149
  - Set headers to be sent.
114
150
  - See docs: [Headers in MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
151
+ - Supports function form to compute headers dynamically.
152
+
153
+ ### Behavior notes
154
+
155
+ - Only `GET` and `HEAD` requests are handled.
156
+ - `Range` requests are supported (including multipart range responses).
157
+ - Conditional requests are supported via `ETag` and `Last-Modified`.
158
+ - Requests containing invalid paths (e.g. null bytes) respond with `400`.
159
+ - Files outside `root` are never served.
115
160
 
116
161
  ## Example
117
162
 
@@ -157,9 +202,48 @@ app.listen(port, () => {
157
202
  });
158
203
  ```
159
204
 
205
+ ## More examples
206
+
207
+ ### Prefer application routes first (`defer: true`)
208
+
209
+ ```ts
210
+ app.use(async (ctx, next) => {
211
+ if (ctx.path === '/healthz') {
212
+ ctx.body = 'ok';
213
+ return;
214
+ }
215
+
216
+ await next();
217
+ });
218
+
219
+ app.use(server('public', { defer: true }));
220
+ ```
221
+
222
+ ### Dynamic ignore rule
223
+
224
+ ```ts
225
+ app.use(
226
+ server('public', {
227
+ ignore: path => path.endsWith('.map')
228
+ })
229
+ );
230
+ ```
231
+
232
+ ### Dynamic stream buffer
233
+
234
+ ```ts
235
+ app.use(
236
+ server('public', {
237
+ highWaterMark: (path, stats) => (stats.size > 10 * 1024 * 1024 ? 256 * 1024 : 64 * 1024)
238
+ })
239
+ );
240
+ ```
241
+
160
242
  ## Features
161
243
 
162
- Support multipart range and download resumption.
244
+ - Static file middleware for Koa.
245
+ - Supports multipart range and download resumption.
246
+ - Supports conditional requests (`ETag`, `Last-Modified`).
163
247
 
164
248
  ## License
165
249
 
@@ -1,7 +1,8 @@
1
1
  /**
2
+ * @module koa-files
2
3
  * @package koa-files
3
4
  * @license MIT
4
- * @version 4.1.7
5
+ * @version 5.0.1
5
6
  * @author nuintun <nuintun@qq.com>
6
7
  * @description A static files serving middleware for koa.
7
8
  * @see https://github.com/nuintun/koa-files#readme
@@ -9,14 +10,22 @@
9
10
 
10
11
  'use strict';
11
12
 
12
- const buffer = require('buffer');
13
- const stream = require('stream');
13
+ const node_buffer = require('node:buffer');
14
+ const node_stream = require('node:stream');
14
15
 
15
16
  /**
16
17
  * @module ReadStream
17
18
  */
18
19
  const DISPOSE_EVENT = Symbol('dispose');
19
- class ReadStream extends stream.Readable {
20
+ class ReadStream extends node_stream.Readable {
21
+ #path;
22
+ #fs;
23
+ #ranges;
24
+ #bytesRead = 0;
25
+ #fd = null;
26
+ #reading = false;
27
+ #currentRangeIndex = 0;
28
+ #readState = 0 /* ReadState.PREFIX */;
20
29
  /**
21
30
  * @constructor
22
31
  * @param path The file path.
@@ -26,14 +35,9 @@ class ReadStream extends stream.Readable {
26
35
  constructor(path, ranges, options) {
27
36
  const { fs, highWaterMark } = options;
28
37
  super({ highWaterMark });
29
- this.bytesRead = 0;
30
- this.fd = null;
31
- this.reading = false;
32
- this.currentRangeIndex = 0;
33
- this.readState = 0 /* ReadState.PREFIX */;
34
- this.fs = fs;
35
- this.path = path;
36
- this.ranges = ranges;
38
+ this.#fs = fs;
39
+ this.#path = path;
40
+ this.#ranges = ranges;
37
41
  }
38
42
  /**
39
43
  * @override
@@ -41,27 +45,27 @@ class ReadStream extends stream.Readable {
41
45
  * @param callback The callback.
42
46
  */
43
47
  _construct(callback) {
44
- this.fs.open(this.path, 'r', (openError, fd) => {
48
+ this.#fs.open(this.#path, 'r', (openError, fd) => {
45
49
  if (openError == null) {
46
- this.fd = fd;
50
+ this.#fd = fd;
47
51
  }
48
52
  callback(openError);
49
53
  });
50
54
  }
51
55
  /**
52
56
  * @private
53
- * @method getRange
57
+ * @method #getRange
54
58
  */
55
- getRange() {
56
- return this.ranges[this.currentRangeIndex];
59
+ #getRange() {
60
+ return this.#ranges[this.#currentRangeIndex];
57
61
  }
58
62
  /**
59
63
  * @private
60
- * @method getPadding
64
+ * @method #getPadding
61
65
  * @param range The current range.
62
66
  */
63
- getPadding(range) {
64
- switch (this.readState) {
67
+ #getPadding(range) {
68
+ switch (this.#readState) {
65
69
  case 0 /* ReadState.PREFIX */:
66
70
  return range.prefix;
67
71
  case 2 /* ReadState.SUFFIX */:
@@ -70,42 +74,42 @@ class ReadStream extends stream.Readable {
70
74
  }
71
75
  /**
72
76
  * @private
73
- * @method readPadding
77
+ * @method #readPadding
74
78
  * @param fd The file descriptor.
75
79
  * @param range The current range.
76
80
  * @param size The number of bytes to read.
77
81
  */
78
- readPadding(fd, range, size) {
82
+ #readPadding(fd, range, size) {
79
83
  let bytesRead = 0;
80
- const padding = this.getPadding(range);
84
+ const padding = this.#getPadding(range);
81
85
  // If padding exists.
82
86
  if (padding != null) {
83
87
  const { length } = padding;
84
- const begin = this.bytesRead;
88
+ const begin = this.#bytesRead;
85
89
  if (begin < length) {
86
90
  bytesRead = Math.min(size, length - begin);
87
91
  const end = begin + bytesRead;
88
92
  this.push(padding.subarray(begin, end));
89
- this.bytesRead = end;
93
+ this.#bytesRead = end;
90
94
  }
91
95
  }
92
96
  // If no padding or read completed.
93
97
  if (bytesRead < size) {
94
- this.bytesRead = 0;
98
+ this.#bytesRead = 0;
95
99
  // Change read state.
96
- switch (this.readState) {
100
+ switch (this.#readState) {
97
101
  case 0 /* ReadState.PREFIX */:
98
- this.readState = 1 /* ReadState.RANGE */;
99
- this.readFileRange(fd, range, size - bytesRead);
102
+ this.#readState = 1 /* ReadState.RANGE */;
103
+ this.#readFileRange(fd, range, size - bytesRead);
100
104
  break;
101
105
  case 2 /* ReadState.SUFFIX */:
102
- this.currentRangeIndex++;
103
- this.readState = 0 /* ReadState.PREFIX */;
104
- const nextRange = this.getRange();
106
+ this.#currentRangeIndex++;
107
+ this.#readState = 0 /* ReadState.PREFIX */;
108
+ const nextRange = this.#getRange();
105
109
  if (nextRange == null) {
106
110
  this.push(null);
107
111
  } else {
108
- this.readPadding(fd, nextRange, size - bytesRead);
112
+ this.#readPadding(fd, nextRange, size - bytesRead);
109
113
  }
110
114
  break;
111
115
  }
@@ -113,23 +117,23 @@ class ReadStream extends stream.Readable {
113
117
  }
114
118
  /**
115
119
  * @private
116
- * @method readFileRange
120
+ * @method #readFileRange
117
121
  * @param fd The file descriptor.
118
122
  * @param range The current range.
119
123
  * @param size The number of bytes to read.
120
124
  */
121
- readFileRange(fd, range, size) {
125
+ #readFileRange(fd, range, size) {
122
126
  const { length } = range;
123
- const { bytesRead } = this;
127
+ const bytesRead = this.#bytesRead;
124
128
  // File range not finished.
125
129
  if (bytesRead < length) {
126
- this.reading = true;
130
+ this.#reading = true;
127
131
  const bytesToRead = Math.min(size, length - bytesRead);
128
- const buffer$1 = buffer.Buffer.allocUnsafeSlow(bytesToRead);
132
+ const buffer = node_buffer.Buffer.allocUnsafeSlow(bytesToRead);
129
133
  const position = range.offset + bytesRead;
130
134
  // Read file range.
131
- this.fs.read(fd, buffer$1, 0, bytesToRead, position, (readError, bytesRead, buffer) => {
132
- this.reading = false;
135
+ this.#fs.read(fd, buffer, 0, bytesToRead, position, (readError, bytesRead, buffer) => {
136
+ this.#reading = false;
133
137
  // Tell ._destroy() that it's safe to close the fd now.
134
138
  if (this.destroyed) {
135
139
  this.emit(DISPOSE_EVENT, readError);
@@ -139,19 +143,19 @@ class ReadStream extends stream.Readable {
139
143
  this.destroy(new RangeError('invalid read operation'));
140
144
  } else {
141
145
  this.push(buffer);
142
- this.bytesRead += bytesRead;
146
+ this.#bytesRead += bytesRead;
143
147
  // file range read completed.
144
148
  if (bytesRead < size) {
145
- this.bytesRead = 0;
146
- this.readState = 2 /* ReadState.SUFFIX */;
147
- this.readPadding(fd, range, size - bytesRead);
149
+ this.#bytesRead = 0;
150
+ this.#readState = 2 /* ReadState.SUFFIX */;
151
+ this.#readPadding(fd, range, size - bytesRead);
148
152
  }
149
153
  }
150
154
  });
151
155
  } else {
152
- this.bytesRead = 0;
153
- this.readState = 2 /* ReadState.SUFFIX */;
154
- this.readPadding(fd, range, size);
156
+ this.#bytesRead = 0;
157
+ this.#readState = 2 /* ReadState.SUFFIX */;
158
+ this.#readPadding(fd, range, size);
155
159
  }
156
160
  }
157
161
  /**
@@ -160,21 +164,21 @@ class ReadStream extends stream.Readable {
160
164
  * @param size The number of bytes to read.
161
165
  */
162
166
  _read(size) {
163
- if (!this.reading) {
164
- const { fd } = this;
165
- const range = this.getRange();
167
+ if (!this.#reading) {
168
+ const fd = this.#fd;
169
+ const range = this.#getRange();
166
170
  // If fd or range is null, finish stream.
167
171
  if (fd == null || range == null) {
168
172
  this.push(null);
169
173
  } else {
170
174
  // Read bytes from range.
171
- switch (this.readState) {
175
+ switch (this.#readState) {
172
176
  case 0 /* ReadState.PREFIX */:
173
177
  case 2 /* ReadState.SUFFIX */:
174
- this.readPadding(fd, range, size);
178
+ this.#readPadding(fd, range, size);
175
179
  break;
176
180
  case 1 /* ReadState.RANGE */:
177
- this.readFileRange(fd, range, size);
181
+ this.#readFileRange(fd, range, size);
178
182
  break;
179
183
  }
180
184
  }
@@ -182,17 +186,17 @@ class ReadStream extends stream.Readable {
182
186
  }
183
187
  /**
184
188
  * @private
185
- * @method dispose
189
+ * @method #dispose
186
190
  * @param error The error.
187
191
  * @param callback The callback.
188
192
  */
189
- dispose(error, callback) {
190
- const { fd } = this;
193
+ #dispose(error, callback) {
194
+ const fd = this.#fd;
191
195
  if (fd != null) {
192
- this.fd = null;
196
+ this.#fd = null;
193
197
  // Close the fd.
194
- this.fs.close(fd, closeError => {
195
- callback(error !== null && error !== void 0 ? error : closeError);
198
+ this.#fs.close(fd, closeError => {
199
+ callback(error ?? closeError);
196
200
  });
197
201
  } else {
198
202
  callback(error);
@@ -206,12 +210,12 @@ class ReadStream extends stream.Readable {
206
210
  */
207
211
  _destroy(error, callback) {
208
212
  // Wait I/O completion.
209
- if (this.reading) {
213
+ if (this.#reading) {
210
214
  this.once(DISPOSE_EVENT, disposeError => {
211
- this.dispose(error !== null && error !== void 0 ? error : disposeError, callback);
215
+ this.#dispose(error ?? disposeError, callback);
212
216
  });
213
217
  } else {
214
- this.dispose(error, callback);
218
+ this.#dispose(error, callback);
215
219
  }
216
220
  }
217
221
  }
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * @module ReadStream
3
3
  */
4
- import { PathLike } from 'fs';
5
- import { Readable } from 'stream';
6
4
  import { Range } from './utils/http.cjs';
5
+ import { Readable } from 'node:stream';
7
6
  import { FileSystem } from './utils/fs.cjs';
8
7
  export interface Options {
9
8
  fs: FileSystem;
@@ -13,67 +12,26 @@ interface Callback {
13
12
  (error?: Error | null): void;
14
13
  }
15
14
  export declare class ReadStream extends Readable {
16
- private readonly fs;
17
- private readonly path;
18
- private readonly ranges;
19
- private bytesRead;
20
- private fd;
21
- private reading;
22
- private currentRangeIndex;
23
- private readState;
15
+ #private;
24
16
  /**
25
17
  * @constructor
26
18
  * @param path The file path.
27
19
  * @param ranges The ranges to read.
28
20
  * @param options The stream options.
29
21
  */
30
- constructor(path: PathLike, ranges: Range[], options: Options);
22
+ constructor(path: string, ranges: Range[], options: Options);
31
23
  /**
32
24
  * @override
33
25
  * @method _construct
34
26
  * @param callback The callback.
35
27
  */
36
28
  _construct(callback: Callback): void;
37
- /**
38
- * @private
39
- * @method getRange
40
- */
41
- private getRange;
42
- /**
43
- * @private
44
- * @method getPadding
45
- * @param range The current range.
46
- */
47
- private getPadding;
48
- /**
49
- * @private
50
- * @method readPadding
51
- * @param fd The file descriptor.
52
- * @param range The current range.
53
- * @param size The number of bytes to read.
54
- */
55
- private readPadding;
56
- /**
57
- * @private
58
- * @method readFileRange
59
- * @param fd The file descriptor.
60
- * @param range The current range.
61
- * @param size The number of bytes to read.
62
- */
63
- private readFileRange;
64
29
  /**
65
30
  * @override
66
31
  * @method _read
67
32
  * @param size The number of bytes to read.
68
33
  */
69
34
  _read(size: number): void;
70
- /**
71
- * @private
72
- * @method dispose
73
- * @param error The error.
74
- * @param callback The callback.
75
- */
76
- private dispose;
77
35
  /**
78
36
  * @override
79
37
  * @method _destroy
package/cjs/Service.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
+ * @module koa-files
2
3
  * @package koa-files
3
4
  * @license MIT
4
- * @version 4.1.7
5
+ * @version 5.0.1
5
6
  * @author nuintun <nuintun@qq.com>
6
7
  * @description A static files serving middleware for koa.
7
8
  * @see https://github.com/nuintun/koa-files#readme
@@ -10,11 +11,11 @@
10
11
  'use strict';
11
12
 
12
13
  const createETag = require('etag');
13
- const fs = require('fs');
14
+ const fs = require('node:fs');
14
15
  const ReadStream = require('./ReadStream.cjs');
15
16
  const type = require('./utils/type.cjs');
16
17
  const fs$1 = require('./utils/fs.cjs');
17
- const path$1 = require('path');
18
+ const node_path = require('node:path');
18
19
  const path = require('./utils/path.cjs');
19
20
  const http = require('./utils/http.cjs');
20
21
 
@@ -32,6 +33,8 @@ const fs__default = /*#__PURE__*/ _interopDefault(fs);
32
33
  * @class Service
33
34
  */
34
35
  class Service {
36
+ #root;
37
+ #options;
35
38
  /**
36
39
  * @constructor
37
40
  * @description Create file service.
@@ -39,30 +42,30 @@ class Service {
39
42
  * @param options The file service options.
40
43
  */
41
44
  constructor(root, options = {}) {
42
- var _a;
43
- this.root = path.unixify(path$1.resolve(root));
45
+ this.#root = path.unixify(node_path.resolve(root));
44
46
  const { ignore, headers, highWaterMark = 65536 } = options;
45
- this.options = Object.assign(Object.assign({}, options), {
46
- fs: (_a = options.fs) !== null && _a !== void 0 ? _a : fs__default.default,
47
+ this.#options = {
48
+ ...options,
49
+ fs: options.fs ?? fs__default.default,
47
50
  ignore: type.isFunction(ignore) ? ignore : () => false,
48
51
  headers: type.isFunction(headers) ? headers : () => headers,
49
52
  highWaterMark: type.isFunction(highWaterMark) ? highWaterMark : () => highWaterMark
50
- });
53
+ };
51
54
  }
52
55
  /**
53
56
  * @private
54
- * @method setupHeaders
57
+ * @method #setupHeaders
55
58
  * @description Setup headers.
56
59
  * @param context The koa context.
57
60
  * @param path The file path.
58
61
  * @param stats The file stats.
59
62
  */
60
- async setupHeaders({ response }, path, stats) {
61
- const { options } = this;
63
+ async #setupHeaders({ response }, path, stats) {
64
+ const options = this.#options;
62
65
  // Set status.
63
66
  response.status = 200;
64
67
  // Set Content-Type.
65
- response.type = path$1.extname(path);
68
+ response.type = node_path.extname(path);
66
69
  // Get headers.
67
70
  const headers = await options.headers(path, stats);
68
71
  // If headers not empty, set headers.
@@ -114,21 +117,21 @@ class Service {
114
117
  return context.throw(400);
115
118
  }
116
119
  // Get service root.
117
- const { root } = this;
120
+ const root = this.#root;
118
121
  // Get file path.
119
- const path$2 = path.unixify(path$1.join(root, pathname));
122
+ const path$1 = path.unixify(node_path.join(root, pathname));
120
123
  // Malicious path (403).
121
- if (path.isOutRoot(path$2, root)) {
124
+ if (path.isOutRoot(path$1, root)) {
122
125
  return false;
123
126
  }
124
127
  // Get options.
125
- const { options } = this;
128
+ const options = this.#options;
126
129
  // Is ignore path or file (403).
127
- if (await options.ignore(path$2)) {
130
+ if (await options.ignore(path$1)) {
128
131
  return false;
129
132
  }
130
133
  // File stats.
131
- const stats = await fs$1.stat(options.fs, path$2);
134
+ const stats = await fs$1.stat(options.fs, path$1);
132
135
  // Check file stats.
133
136
  if (
134
137
  // File not exist (404 | 500).
@@ -136,14 +139,14 @@ class Service {
136
139
  // Is directory (403).
137
140
  stats.isDirectory() ||
138
141
  // Not a directory but has trailing slash (404).
139
- path.hasTrailingSlash(path$2)
142
+ path.hasTrailingSlash(path$1)
140
143
  ) {
141
144
  return false;
142
145
  }
143
146
  // Koa response.
144
147
  const { response } = context;
145
148
  // Setup headers.
146
- await this.setupHeaders(context, path$2, stats);
149
+ await this.#setupHeaders(context, path$1, stats);
147
150
  // Conditional get support.
148
151
  if (http.isConditionalGET(context)) {
149
152
  // Request precondition failed.
@@ -183,9 +186,9 @@ class Service {
183
186
  return context.throw(400);
184
187
  }
185
188
  // Set response body.
186
- response.body = new ReadStream.ReadStream(path$2, ranges, {
189
+ response.body = new ReadStream.ReadStream(path$1, ranges, {
187
190
  fs: options.fs,
188
- highWaterMark: await options.highWaterMark(path$2, stats)
191
+ highWaterMark: await options.highWaterMark(path$1, stats)
189
192
  });
190
193
  // File found.
191
194
  return true;
package/cjs/Service.d.cts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @module Service
3
3
  */
4
4
  import { Context } from 'koa';
5
- import { Stats } from 'fs';
5
+ import { Stats } from 'node:fs';
6
6
  import { FileSystem } from './utils/fs.cjs';
7
7
  interface Headers {
8
8
  [key: string]: string | string[];
@@ -29,8 +29,7 @@ export interface Options {
29
29
  * @class Service
30
30
  */
31
31
  export declare class Service {
32
- private readonly root;
33
- private readonly options;
32
+ #private;
34
33
  /**
35
34
  * @constructor
36
35
  * @description Create file service.
@@ -38,15 +37,6 @@ export declare class Service {
38
37
  * @param options The file service options.
39
38
  */
40
39
  constructor(root: string, options?: Options);
41
- /**
42
- * @private
43
- * @method setupHeaders
44
- * @description Setup headers.
45
- * @param context The koa context.
46
- * @param path The file path.
47
- * @param stats The file stats.
48
- */
49
- private setupHeaders;
50
40
  /**
51
41
  * @public
52
42
  * @method respond
package/cjs/index.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
+ * @module koa-files
2
3
  * @package koa-files
3
4
  * @license MIT
4
- * @version 4.1.7
5
+ * @version 5.0.1
5
6
  * @author nuintun <nuintun@qq.com>
6
7
  * @description A static files serving middleware for koa.
7
8
  * @see https://github.com/nuintun/koa-files#readme
@@ -22,7 +23,7 @@ const Service = require('./Service.cjs');
22
23
  */
23
24
  function server(root, options) {
24
25
  const service = new Service.Service(root, options);
25
- if (options === null || options === void 0 ? void 0 : options.defer) {
26
+ if (options?.defer) {
26
27
  return async (context, next) => {
27
28
  await next();
28
29
  await service.respond(context);