@eggjs/koa 2.14.2 → 2.15.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/lib/response.js CHANGED
@@ -1,588 +1,443 @@
1
-
2
- 'use strict';
3
-
4
- /**
5
- * Module dependencies.
6
- */
7
-
8
- const contentDisposition = require('content-disposition');
9
- const getType = require('cache-content-type');
10
- const onFinish = require('on-finished');
11
- const escape = require('escape-html');
12
- const typeis = require('type-is').is;
13
- const statuses = require('statuses');
14
- const destroy = require('destroy');
15
- const assert = require('assert');
16
- const extname = require('path').extname;
17
- const vary = require('vary');
18
- const only = require('only');
19
- const util = require('util');
20
- const encodeUrl = require('encodeurl');
21
- const Stream = require('stream');
22
-
23
- /**
24
- * Prototype.
25
- */
26
-
27
- module.exports = {
28
-
29
- /**
30
- * Return the request socket.
31
- *
32
- * @return {Connection}
33
- * @api public
34
- */
35
-
36
- get socket() {
37
- return this.res.socket;
38
- },
39
-
40
- /**
41
- * Return response header.
42
- *
43
- * @return {Object}
44
- * @api public
45
- */
46
-
47
- get header() {
48
- const { res } = this;
49
- return typeof res.getHeaders === 'function'
50
- ? res.getHeaders()
51
- : res._headers || {}; // Node < 7.7
52
- },
53
-
54
- /**
55
- * Return response header, alias as response.header
56
- *
57
- * @return {Object}
58
- * @api public
59
- */
60
-
61
- get headers() {
62
- return this.header;
63
- },
64
-
65
- /**
66
- * Get response status code.
67
- *
68
- * @return {Number}
69
- * @api public
70
- */
71
-
72
- get status() {
73
- return this.res.statusCode;
74
- },
75
-
76
- /**
77
- * Set response status code.
78
- *
79
- * @param {Number} code
80
- * @api public
81
- */
82
-
83
- set status(code) {
84
- if (this.headerSent) return;
85
-
86
- assert(Number.isInteger(code), 'status code must be a number');
87
- assert(code >= 100 && code <= 999, `invalid status code: ${code}`);
88
- this._explicitStatus = true;
89
- this.res.statusCode = code;
90
- if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code];
91
- if (this.body && statuses.empty[code]) this.body = null;
92
- },
93
-
94
- /**
95
- * Get response status message
96
- *
97
- * @return {String}
98
- * @api public
99
- */
100
-
101
- get message() {
102
- return this.res.statusMessage || statuses[this.status];
103
- },
104
-
105
- /**
106
- * Set response status message
107
- *
108
- * @param {String} msg
109
- * @api public
110
- */
111
-
112
- set message(msg) {
113
- this.res.statusMessage = msg;
114
- },
115
-
116
- /**
117
- * Get response body.
118
- *
119
- * @return {Mixed}
120
- * @api public
121
- */
122
-
123
- get body() {
124
- return this._body;
125
- },
126
-
127
- /**
128
- * Set response body.
129
- *
130
- * @param {String|Buffer|Object|Stream} val
131
- * @api public
132
- */
133
-
134
- set body(val) {
135
- const original = this._body;
136
- this._body = val;
137
-
138
- // no content
139
- if (null == val) {
140
- if (!statuses.empty[this.status]) this.status = 204;
141
- if (val === null) this._explicitNullBody = true;
142
- this.remove('Content-Type');
143
- this.remove('Content-Length');
144
- this.remove('Transfer-Encoding');
145
- return;
146
- }
147
-
148
- // set the status
149
- if (!this._explicitStatus) this.status = 200;
150
-
151
- // set the content-type only if not yet set
152
- const setType = !this.has('Content-Type');
153
-
154
- // string
155
- if ('string' === typeof val) {
156
- if (setType) this.type = /^\s*</.test(val) ? 'html' : 'text';
157
- this.length = Buffer.byteLength(val);
158
- return;
159
- }
160
-
161
- // buffer
162
- if (Buffer.isBuffer(val)) {
163
- if (setType) this.type = 'bin';
164
- this.length = val.length;
165
- return;
166
- }
167
-
168
- // stream
169
- if (val instanceof Stream) {
170
- onFinish(this.res, destroy.bind(null, val));
171
- if (original != val) {
172
- val.once('error', err => this.ctx.onerror(err));
173
- // overwriting
174
- if (null != original) this.remove('Content-Length');
175
- }
176
-
177
- if (setType) this.type = 'bin';
178
- return;
179
- }
180
-
181
- // json
182
- this.remove('Content-Length');
183
- this.type = 'json';
184
- },
185
-
186
- /**
187
- * Set Content-Length field to `n`.
188
- *
189
- * @param {Number} n
190
- * @api public
191
- */
192
-
193
- set length(n) {
194
- if (!this.has('Transfer-Encoding')) {
195
- this.set('Content-Length', n);
196
- }
197
- },
198
-
199
- /**
200
- * Return parsed response Content-Length when present.
201
- *
202
- * @return {Number}
203
- * @api public
204
- */
205
-
206
- get length() {
207
- if (this.has('Content-Length')) {
208
- return parseInt(this.get('Content-Length'), 10) || 0;
209
- }
210
-
211
- const { body } = this;
212
- if (!body || body instanceof Stream) return undefined;
213
- if ('string' === typeof body) return Buffer.byteLength(body);
214
- if (Buffer.isBuffer(body)) return body.length;
215
- return Buffer.byteLength(JSON.stringify(body));
216
- },
217
-
218
- /**
219
- * Check if a header has been written to the socket.
220
- *
221
- * @return {Boolean}
222
- * @api public
223
- */
224
-
225
- get headerSent() {
226
- return this.res.headersSent;
227
- },
228
-
229
- /**
230
- * Vary on `field`.
231
- *
232
- * @param {String} field
233
- * @api public
234
- */
235
-
236
- vary(field) {
237
- if (this.headerSent) return;
238
-
239
- vary(this.res, field);
240
- },
241
-
242
- /**
243
- * Perform a 302 redirect to `url`.
244
- *
245
- * The string "back" is special-cased
246
- * to provide Referrer support, when Referrer
247
- * is not present `alt` or "/" is used.
248
- *
249
- * Examples:
250
- *
251
- * this.redirect('back');
252
- * this.redirect('back', '/index.html');
253
- * this.redirect('/login');
254
- * this.redirect('http://google.com');
255
- *
256
- * @param {String} url
257
- * @param {String} [alt]
258
- * @api public
259
- */
260
-
261
- redirect(url, alt) {
262
- // location
263
- if ('back' === url) url = this.ctx.get('Referrer') || alt || '/';
264
- this.set('Location', encodeUrl(url));
265
-
266
- // status
267
- if (!statuses.redirect[this.status]) this.status = 302;
268
-
269
- // html
270
- if (this.ctx.accepts('html')) {
271
- url = escape(url);
272
- this.type = 'text/html; charset=utf-8';
273
- this.body = `Redirecting to <a href="${url}">${url}</a>.`;
274
- return;
275
- }
276
-
277
- // text
278
- this.type = 'text/plain; charset=utf-8';
279
- this.body = `Redirecting to ${url}.`;
280
- },
281
-
282
- /**
283
- * Set Content-Disposition header to "attachment" with optional `filename`.
284
- *
285
- * @param {String} filename
286
- * @api public
287
- */
288
-
289
- attachment(filename, options) {
290
- if (filename) this.type = extname(filename);
291
- this.set('Content-Disposition', contentDisposition(filename, options));
292
- },
293
-
294
- /**
295
- * Set Content-Type response header with `type` through `mime.lookup()`
296
- * when it does not contain a charset.
297
- *
298
- * Examples:
299
- *
300
- * this.type = '.html';
301
- * this.type = 'html';
302
- * this.type = 'json';
303
- * this.type = 'application/json';
304
- * this.type = 'png';
305
- *
306
- * @param {String} type
307
- * @api public
308
- */
309
-
310
- set type(type) {
311
- type = getType(type);
312
- if (type) {
313
- this.set('Content-Type', type);
314
- } else {
315
- this.remove('Content-Type');
316
- }
317
- },
318
-
319
- /**
320
- * Set the Last-Modified date using a string or a Date.
321
- *
322
- * this.response.lastModified = new Date();
323
- * this.response.lastModified = '2013-09-13';
324
- *
325
- * @param {String|Date} type
326
- * @api public
327
- */
328
-
329
- set lastModified(val) {
330
- if ('string' === typeof val) val = new Date(val);
331
- this.set('Last-Modified', val.toUTCString());
332
- },
333
-
334
- /**
335
- * Get the Last-Modified date in Date form, if it exists.
336
- *
337
- * @return {Date}
338
- * @api public
339
- */
340
-
341
- get lastModified() {
342
- const date = this.get('last-modified');
343
- if (date) return new Date(date);
344
- },
345
-
346
- /**
347
- * Set the ETag of a response.
348
- * This will normalize the quotes if necessary.
349
- *
350
- * this.response.etag = 'md5hashsum';
351
- * this.response.etag = '"md5hashsum"';
352
- * this.response.etag = 'W/"123456789"';
353
- *
354
- * @param {String} etag
355
- * @api public
356
- */
357
-
358
- set etag(val) {
359
- if (!/^(W\/)?"/.test(val)) val = `"${val}"`;
360
- this.set('ETag', val);
361
- },
362
-
363
- /**
364
- * Get the ETag of a response.
365
- *
366
- * @return {String}
367
- * @api public
368
- */
369
-
370
- get etag() {
371
- return this.get('ETag');
372
- },
373
-
374
- /**
375
- * Return the response mime type void of
376
- * parameters such as "charset".
377
- *
378
- * @return {String}
379
- * @api public
380
- */
381
-
382
- get type() {
383
- const type = this.get('Content-Type');
384
- if (!type) return '';
385
- return type.split(';', 1)[0];
386
- },
387
-
388
- /**
389
- * Check whether the response is one of the listed types.
390
- * Pretty much the same as `this.request.is()`.
391
- *
392
- * @param {String|String[]} [type]
393
- * @param {String[]} [types]
394
- * @return {String|false}
395
- * @api public
396
- */
397
-
398
- is(type, ...types) {
399
- return typeis(this.type, type, ...types);
400
- },
401
-
402
- /**
403
- * Return response header.
404
- *
405
- * Examples:
406
- *
407
- * this.get('Content-Type');
408
- * // => "text/plain"
409
- *
410
- * this.get('content-type');
411
- * // => "text/plain"
412
- *
413
- * @param {String} field
414
- * @return {String}
415
- * @api public
416
- */
417
-
418
- get(field) {
419
- return this.header[field.toLowerCase()] || '';
420
- },
421
-
422
- /**
423
- * Returns true if the header identified by name is currently set in the outgoing headers.
424
- * The header name matching is case-insensitive.
425
- *
426
- * Examples:
427
- *
428
- * this.has('Content-Type');
429
- * // => true
430
- *
431
- * this.get('content-type');
432
- * // => true
433
- *
434
- * @param {String} field
435
- * @return {boolean}
436
- * @api public
437
- */
438
-
439
- has(field) {
440
- return typeof this.res.hasHeader === 'function'
441
- ? this.res.hasHeader(field)
442
- // Node < 7.7
443
- : field.toLowerCase() in this.headers;
444
- },
445
-
446
- /**
447
- * Set header `field` to `val` or pass
448
- * an object of header fields.
449
- *
450
- * Examples:
451
- *
452
- * this.set('Foo', ['bar', 'baz']);
453
- * this.set('Accept', 'application/json');
454
- * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
455
- *
456
- * @param {String|Object|Array} field
457
- * @param {String} val
458
- * @api public
459
- */
460
-
461
- set(field, val) {
462
- if (this.headerSent) return;
463
-
464
- if (2 === arguments.length) {
465
- if (Array.isArray(val)) val = val.map(v => typeof v === 'string' ? v : String(v));
466
- else if (typeof val !== 'string') val = String(val);
467
- this.res.setHeader(field, val);
468
- } else {
469
- for (const key in field) {
470
- this.set(key, field[key]);
471
- }
472
- }
473
- },
474
-
475
- /**
476
- * Append additional header `field` with value `val`.
477
- *
478
- * Examples:
479
- *
480
- * ```
481
- * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
482
- * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
483
- * this.append('Warning', '199 Miscellaneous warning');
484
- * ```
485
- *
486
- * @param {String} field
487
- * @param {String|Array} val
488
- * @api public
489
- */
490
-
491
- append(field, val) {
492
- const prev = this.get(field);
493
-
494
- if (prev) {
495
- val = Array.isArray(prev)
496
- ? prev.concat(val)
497
- : [prev].concat(val);
498
- }
499
-
500
- return this.set(field, val);
501
- },
502
-
503
- /**
504
- * Remove header `field`.
505
- *
506
- * @param {String} name
507
- * @api public
508
- */
509
-
510
- remove(field) {
511
- if (this.headerSent) return;
512
-
513
- this.res.removeHeader(field);
514
- },
515
-
516
- /**
517
- * Checks if the request is writable.
518
- * Tests for the existence of the socket
519
- * as node sometimes does not set it.
520
- *
521
- * @return {Boolean}
522
- * @api private
523
- */
524
-
525
- get writable() {
526
- // can't write any more after response finished
527
- // response.writableEnded is available since Node > 12.9
528
- // https://nodejs.org/api/http.html#http_response_writableended
529
- // response.finished is undocumented feature of previous Node versions
530
- // https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
531
- if (this.res.writableEnded || this.res.finished) return false;
532
-
533
- const socket = this.res.socket;
534
- // There are already pending outgoing res, but still writable
535
- // https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
536
- if (!socket) return true;
537
- return socket.writable;
538
- },
539
-
540
- /**
541
- * Inspect implementation.
542
- *
543
- * @return {Object}
544
- * @api public
545
- */
546
-
547
- inspect() {
548
- if (!this.res) return;
549
- const o = this.toJSON();
550
- o.body = this.body;
551
- return o;
552
- },
553
-
554
- /**
555
- * Return JSON representation.
556
- *
557
- * @return {Object}
558
- * @api public
559
- */
560
-
561
- toJSON() {
562
- return only(this, [
563
- 'status',
564
- 'message',
565
- 'header'
566
- ]);
567
- },
568
-
569
- /**
570
- * Flush any set headers and begin the body
571
- */
572
-
573
- flushHeaders() {
574
- this.res.flushHeaders();
575
- }
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
576
4
  };
577
-
578
- /**
579
- * Custom inspection implementation for node 6+.
580
- *
581
- * @return {Object}
582
- * @api public
583
- */
584
-
585
- /* istanbul ignore else */
586
- if (util.inspect.custom) {
587
- module.exports[util.inspect.custom] = module.exports.inspect;
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_assert_1 = __importDefault(require("node:assert"));
7
+ const node_path_1 = require("node:path");
8
+ const node_util_1 = __importDefault(require("node:util"));
9
+ const node_stream_1 = __importDefault(require("node:stream"));
10
+ const content_disposition_1 = __importDefault(require("content-disposition"));
11
+ const cache_content_type_1 = __importDefault(require("cache-content-type"));
12
+ const on_finished_1 = __importDefault(require("on-finished"));
13
+ const escape_html_1 = __importDefault(require("escape-html"));
14
+ const type_is_1 = require("type-is");
15
+ const statuses_1 = __importDefault(require("statuses"));
16
+ const destroy_1 = __importDefault(require("destroy"));
17
+ const vary_1 = __importDefault(require("vary"));
18
+ const only_1 = __importDefault(require("only"));
19
+ const encodeurl_1 = __importDefault(require("encodeurl"));
20
+ class Response {
21
+ app;
22
+ req;
23
+ res;
24
+ ctx;
25
+ request;
26
+ constructor(app, ctx, req, res) {
27
+ this.app = app;
28
+ this.req = req;
29
+ this.res = res;
30
+ this.ctx = ctx;
31
+ }
32
+ /**
33
+ * Return the request socket.
34
+ */
35
+ get socket() {
36
+ return this.res.socket;
37
+ }
38
+ /**
39
+ * Return response header.
40
+ */
41
+ get header() {
42
+ return this.res.getHeaders() || {};
43
+ }
44
+ /**
45
+ * Return response header, alias as response.header
46
+ */
47
+ get headers() {
48
+ return this.header;
49
+ }
50
+ _explicitStatus;
51
+ /**
52
+ * Get response status code.
53
+ */
54
+ get status() {
55
+ return this.res.statusCode;
56
+ }
57
+ /**
58
+ * Set response status code.
59
+ */
60
+ set status(code) {
61
+ if (this.headerSent)
62
+ return;
63
+ (0, node_assert_1.default)(Number.isInteger(code), 'status code must be a number');
64
+ (0, node_assert_1.default)(code >= 100 && code <= 999, `invalid status code: ${code}`);
65
+ this._explicitStatus = true;
66
+ this.res.statusCode = code;
67
+ if (this.req.httpVersionMajor < 2)
68
+ this.res.statusMessage = statuses_1.default[code];
69
+ if (this.body && statuses_1.default.empty[code])
70
+ this.body = null;
71
+ }
72
+ /**
73
+ * Get response status message
74
+ */
75
+ get message() {
76
+ return this.res.statusMessage || statuses_1.default[this.status];
77
+ }
78
+ /**
79
+ * Set response status message
80
+ */
81
+ set message(msg) {
82
+ this.res.statusMessage = msg;
83
+ }
84
+ _body;
85
+ _explicitNullBody;
86
+ /**
87
+ * Get response body.
88
+ */
89
+ get body() {
90
+ return this._body;
91
+ }
92
+ /**
93
+ * Set response body.
94
+ */
95
+ set body(val) {
96
+ const original = this._body;
97
+ this._body = val;
98
+ // no content
99
+ if (val == null) {
100
+ if (!statuses_1.default.empty[this.status])
101
+ this.status = 204;
102
+ if (val === null)
103
+ this._explicitNullBody = true;
104
+ this.remove('Content-Type');
105
+ this.remove('Content-Length');
106
+ this.remove('Transfer-Encoding');
107
+ return;
108
+ }
109
+ // set the status
110
+ if (!this._explicitStatus)
111
+ this.status = 200;
112
+ // set the content-type only if not yet set
113
+ const setType = !this.has('Content-Type');
114
+ // string
115
+ if (typeof val === 'string') {
116
+ if (setType)
117
+ this.type = /^\s*</.test(val) ? 'html' : 'text';
118
+ this.length = Buffer.byteLength(val);
119
+ return;
120
+ }
121
+ // buffer
122
+ if (Buffer.isBuffer(val)) {
123
+ if (setType)
124
+ this.type = 'bin';
125
+ this.length = val.length;
126
+ return;
127
+ }
128
+ // stream
129
+ if (val instanceof node_stream_1.default) {
130
+ (0, on_finished_1.default)(this.res, destroy_1.default.bind(null, val));
131
+ // eslint-disable-next-line eqeqeq
132
+ if (original != val) {
133
+ val.once('error', err => this.ctx.onerror(err));
134
+ // overwriting
135
+ if (original != null)
136
+ this.remove('Content-Length');
137
+ }
138
+ if (setType)
139
+ this.type = 'bin';
140
+ return;
141
+ }
142
+ // json
143
+ this.remove('Content-Length');
144
+ this.type = 'json';
145
+ }
146
+ /**
147
+ * Set Content-Length field to `n`.
148
+ */
149
+ set length(n) {
150
+ if (n === undefined)
151
+ return;
152
+ if (!this.has('Transfer-Encoding')) {
153
+ this.set('Content-Length', n);
154
+ }
155
+ }
156
+ /**
157
+ * Return parsed response Content-Length when present.
158
+ */
159
+ get length() {
160
+ if (this.has('Content-Length')) {
161
+ return parseInt(this.get('Content-Length'), 10) || 0;
162
+ }
163
+ const { body } = this;
164
+ if (!body || body instanceof node_stream_1.default)
165
+ return undefined;
166
+ if (typeof body === 'string')
167
+ return Buffer.byteLength(body);
168
+ if (Buffer.isBuffer(body))
169
+ return body.length;
170
+ return Buffer.byteLength(JSON.stringify(body));
171
+ }
172
+ /**
173
+ * Check if a header has been written to the socket.
174
+ */
175
+ get headerSent() {
176
+ return this.res.headersSent;
177
+ }
178
+ /**
179
+ * Vary on `field`.
180
+ */
181
+ vary(field) {
182
+ if (this.headerSent)
183
+ return;
184
+ (0, vary_1.default)(this.res, field);
185
+ }
186
+ /**
187
+ * Perform a 302 redirect to `url`.
188
+ *
189
+ * The string "back" is special-cased
190
+ * to provide Referrer support, when Referrer
191
+ * is not present `alt` or "/" is used.
192
+ *
193
+ * Examples:
194
+ *
195
+ * this.redirect('back');
196
+ * this.redirect('back', '/index.html');
197
+ * this.redirect('/login');
198
+ * this.redirect('http://google.com');
199
+ */
200
+ redirect(url, alt) {
201
+ // location
202
+ if (url === 'back')
203
+ url = this.ctx.get('Referrer') || alt || '/';
204
+ this.set('Location', (0, encodeurl_1.default)(url));
205
+ // status
206
+ if (!statuses_1.default.redirect[this.status])
207
+ this.status = 302;
208
+ // html
209
+ if (this.ctx.accepts('html')) {
210
+ url = (0, escape_html_1.default)(url);
211
+ this.type = 'text/html; charset=utf-8';
212
+ this.body = `Redirecting to <a href="${url}">${url}</a>.`;
213
+ return;
214
+ }
215
+ // text
216
+ this.type = 'text/plain; charset=utf-8';
217
+ this.body = `Redirecting to ${url}.`;
218
+ }
219
+ /**
220
+ * Set Content-Disposition header to "attachment" with optional `filename`.
221
+ */
222
+ attachment(filename, options) {
223
+ if (filename)
224
+ this.type = (0, node_path_1.extname)(filename);
225
+ this.set('Content-Disposition', (0, content_disposition_1.default)(filename, options));
226
+ }
227
+ /**
228
+ * Set Content-Type response header with `type` through `mime.lookup()`
229
+ * when it does not contain a charset.
230
+ *
231
+ * Examples:
232
+ *
233
+ * this.type = '.html';
234
+ * this.type = 'html';
235
+ * this.type = 'json';
236
+ * this.type = 'application/json';
237
+ * this.type = 'png';
238
+ */
239
+ set type(type) {
240
+ type = (0, cache_content_type_1.default)(type);
241
+ if (type) {
242
+ this.set('Content-Type', type);
243
+ }
244
+ else {
245
+ this.remove('Content-Type');
246
+ }
247
+ }
248
+ /**
249
+ * Return the response mime type void of
250
+ * parameters such as "charset".
251
+ */
252
+ get type() {
253
+ const type = this.get('Content-Type');
254
+ if (!type)
255
+ return '';
256
+ return type.split(';', 1)[0];
257
+ }
258
+ /**
259
+ * Check whether the response is one of the listed types.
260
+ * Pretty much the same as `this.request.is()`.
261
+ */
262
+ is(type, ...types) {
263
+ return (0, type_is_1.is)(this.type, type, ...types);
264
+ }
265
+ /**
266
+ * Set the Last-Modified date using a string or a Date.
267
+ *
268
+ * this.response.lastModified = new Date();
269
+ * this.response.lastModified = '2013-09-13';
270
+ */
271
+ set lastModified(val) {
272
+ if (typeof val === 'string')
273
+ val = new Date(val);
274
+ if (val) {
275
+ this.set('Last-Modified', val.toUTCString());
276
+ }
277
+ }
278
+ /**
279
+ * Get the Last-Modified date in Date form, if it exists.
280
+ */
281
+ get lastModified() {
282
+ const date = this.get('last-modified');
283
+ if (date)
284
+ return new Date(date);
285
+ }
286
+ /**
287
+ * Set the ETag of a response.
288
+ * This will normalize the quotes if necessary.
289
+ *
290
+ * this.response.etag = 'md5hashsum';
291
+ * this.response.etag = '"md5hashsum"';
292
+ * this.response.etag = 'W/"123456789"';
293
+ */
294
+ set etag(val) {
295
+ if (!/^(W\/)?"/.test(val))
296
+ val = `"${val}"`;
297
+ this.set('ETag', val);
298
+ }
299
+ /**
300
+ * Get the ETag of a response.
301
+ */
302
+ get etag() {
303
+ return this.get('ETag');
304
+ }
305
+ /**
306
+ * Return response header.
307
+ *
308
+ * Examples:
309
+ *
310
+ * this.get('Content-Type');
311
+ * // => "text/plain"
312
+ *
313
+ * this.get('content-type');
314
+ * // => "text/plain"
315
+ */
316
+ get(field) {
317
+ return (this.header[field.toLowerCase()] || '');
318
+ }
319
+ /**
320
+ * Returns true if the header identified by name is currently set in the outgoing headers.
321
+ * The header name matching is case-insensitive.
322
+ *
323
+ * Examples:
324
+ *
325
+ * this.has('Content-Type');
326
+ * // => true
327
+ *
328
+ * this.get('content-type');
329
+ * // => true
330
+ */
331
+ has(field) {
332
+ return this.res.hasHeader(field);
333
+ }
334
+ /**
335
+ * Set header `field` to `val` or pass
336
+ * an object of header fields.
337
+ *
338
+ * Examples:
339
+ *
340
+ * this.set('Foo', ['bar', 'baz']);
341
+ * this.set('Accept', 'application/json');
342
+ * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
343
+ */
344
+ set(field, val) {
345
+ if (this.headerSent)
346
+ return;
347
+ if (typeof field === 'string') {
348
+ if (Array.isArray(val)) {
349
+ val = val.map(v => {
350
+ return typeof v === 'string' ? v : String(v);
351
+ });
352
+ }
353
+ else if (typeof val !== 'string') {
354
+ val = String(val);
355
+ }
356
+ this.res.setHeader(field, val);
357
+ }
358
+ else {
359
+ for (const key in field) {
360
+ this.set(key, field[key]);
361
+ }
362
+ }
363
+ }
364
+ /**
365
+ * Append additional header `field` with value `val`.
366
+ *
367
+ * Examples:
368
+ *
369
+ * ```
370
+ * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
371
+ * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
372
+ * this.append('Warning', '199 Miscellaneous warning');
373
+ */
374
+ append(field, val) {
375
+ const prev = this.get(field);
376
+ let value = val;
377
+ if (prev) {
378
+ value = Array.isArray(prev)
379
+ ? prev.concat(value)
380
+ : [prev].concat(val);
381
+ }
382
+ return this.set(field, value);
383
+ }
384
+ /**
385
+ * Remove header `field`.
386
+ */
387
+ remove(field) {
388
+ if (this.headerSent)
389
+ return;
390
+ this.res.removeHeader(field);
391
+ }
392
+ /**
393
+ * Checks if the request is writable.
394
+ * Tests for the existence of the socket
395
+ * as node sometimes does not set it.
396
+ */
397
+ get writable() {
398
+ // can't write any more after response finished
399
+ // response.writableEnded is available since Node > 12.9
400
+ // https://nodejs.org/api/http.html#http_response_writableended
401
+ // response.finished is undocumented feature of previous Node versions
402
+ // https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
403
+ if (this.res.writableEnded || this.res.finished)
404
+ return false;
405
+ const socket = this.res.socket;
406
+ // There are already pending outgoing res, but still writable
407
+ // https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
408
+ if (!socket)
409
+ return true;
410
+ return socket.writable;
411
+ }
412
+ /**
413
+ * Inspect implementation.
414
+ */
415
+ inspect() {
416
+ if (!this.res)
417
+ return;
418
+ const o = this.toJSON();
419
+ o.body = this.body;
420
+ return o;
421
+ }
422
+ [node_util_1.default.inspect.custom]() {
423
+ return this.inspect();
424
+ }
425
+ /**
426
+ * Return JSON representation.
427
+ */
428
+ toJSON() {
429
+ return (0, only_1.default)(this, [
430
+ 'status',
431
+ 'message',
432
+ 'header',
433
+ ]);
434
+ }
435
+ /**
436
+ * Flush any set headers and begin the body
437
+ */
438
+ flushHeaders() {
439
+ this.res.flushHeaders();
440
+ }
588
441
  }
442
+ exports.default = Response;
443
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzcG9uc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw4REFBaUM7QUFDakMseUNBQW9DO0FBQ3BDLDBEQUE2QjtBQUM3Qiw4REFBaUM7QUFFakMsOEVBQXFEO0FBQ3JELDRFQUF5QztBQUN6Qyw4REFBbUM7QUFDbkMsOERBQWlDO0FBQ2pDLHFDQUF1QztBQUN2Qyx3REFBZ0M7QUFDaEMsc0RBQThCO0FBQzlCLGdEQUF3QjtBQUN4QixnREFBd0I7QUFDeEIsMERBQWtDO0FBS2xDLE1BQXFCLFFBQVE7SUFDM0IsR0FBRyxDQUFjO0lBQ2pCLEdBQUcsQ0FBa0I7SUFDckIsR0FBRyxDQUFpQjtJQUNwQixHQUFHLENBQW9CO0lBQ3ZCLE9BQU8sQ0FBVTtJQUVqQixZQUFZLEdBQWdCLEVBQUUsR0FBc0IsRUFBRSxHQUFvQixFQUFFLEdBQW1CO1FBQzdGLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO1FBQ2YsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksTUFBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFJLE9BQU87UUFDVCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDckIsQ0FBQztJQUVELGVBQWUsQ0FBVTtJQUV6Qjs7T0FFRztJQUNILElBQUksTUFBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FB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