@eggjs/koa 3.1.0-beta.35 → 3.1.0-beta.36

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/dist/response.js CHANGED
@@ -1,468 +1,384 @@
1
- import assert from 'node:assert';
2
- import { extname } from 'node:path';
3
- import Stream from 'node:stream';
4
- import util from 'node:util';
5
- import { getType } from 'cache-content-type';
6
- import contentDisposition, {} from 'content-disposition';
7
- import destroy from 'destroy';
8
- import encodeUrl from 'encodeurl';
9
- import escape from 'escape-html';
10
- import onFinish from 'on-finished';
11
- import statuses from 'statuses';
12
- import { is as typeis } from 'type-is';
13
- import vary from 'vary';
14
- export class Response {
15
- app;
16
- req;
17
- res;
18
- ctx;
19
- request;
20
- constructor(app, ctx, req, res) {
21
- this.app = app;
22
- this.req = req;
23
- this.res = res;
24
- this.ctx = ctx;
25
- // Set up custom inspect
26
- this[util.inspect.custom] = this.inspect.bind(this);
27
- }
28
- /**
29
- * Return the request socket.
30
- */
31
- get socket() {
32
- return this.res.socket;
33
- }
34
- /**
35
- * Return response header.
36
- */
37
- get header() {
38
- // res.getHeaders will return null if not set
39
- return this.res.getHeaders() ?? {};
40
- }
41
- /**
42
- * Return response header, alias as response.header
43
- */
44
- get headers() {
45
- return this.header;
46
- }
47
- _explicitStatus;
48
- /**
49
- * Get response status code.
50
- */
51
- get status() {
52
- return this.res.statusCode;
53
- }
54
- /**
55
- * Set response status code.
56
- */
57
- set status(code) {
58
- if (this.headerSent)
59
- return;
60
- assert.ok(Number.isInteger(code), 'status code must be a number');
61
- assert.ok(code >= 100 && code <= 999, `invalid status code: ${code}`);
62
- this._explicitStatus = true;
63
- this.res.statusCode = code;
64
- if (this.req.httpVersionMajor < 2 && statuses.message[code]) {
65
- this.res.statusMessage = statuses.message[code];
66
- }
67
- if (this.body && statuses.empty[code]) {
68
- this.body = null;
69
- }
70
- }
71
- /**
72
- * Get response status message
73
- */
74
- get message() {
75
- return this.res.statusMessage ?? statuses.message[this.status];
76
- }
77
- /**
78
- * Set response status message
79
- */
80
- set message(msg) {
81
- this.res.statusMessage = msg;
82
- }
83
- // oxlint-disable-next-line typescript/no-explicit-any
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 || val === undefined) {
100
- if (!statuses.empty[this.status]) {
101
- this.status = 204;
102
- }
103
- if (val === null) {
104
- this._explicitNullBody = true;
105
- }
106
- this.remove('Content-Type');
107
- this.remove('Content-Length');
108
- this.remove('Transfer-Encoding');
109
- return;
110
- }
111
- // set the status
112
- if (!this._explicitStatus)
113
- this.status = 200;
114
- // set the content-type only if not yet set
115
- const setType = !this.has('Content-Type');
116
- // string
117
- if (typeof val === 'string') {
118
- if (setType)
119
- this.type = /^\s*?</.test(val) ? 'html' : 'text';
120
- this.length = Buffer.byteLength(val);
121
- return;
122
- }
123
- // buffer
124
- if (Buffer.isBuffer(val)) {
125
- if (setType)
126
- this.type = 'bin';
127
- this.length = val.length;
128
- return;
129
- }
130
- // stream
131
- if (val instanceof Stream) {
132
- onFinish(this.res, destroy.bind(null, val));
133
- // oxlint-disable-next-line eqeqeq
134
- if (original != val) {
135
- val.once('error', (err) => this.ctx.onerror(err));
136
- // overwriting
137
- if (original !== null && original !== undefined) {
138
- this.remove('Content-Length');
139
- }
140
- }
141
- if (setType) {
142
- this.type = 'bin';
143
- }
144
- return;
145
- }
146
- // json
147
- this.remove('Content-Length');
148
- this.type = 'json';
149
- }
150
- /**
151
- * Set Content-Length field to `n`.
152
- */
153
- set length(n) {
154
- if (n === undefined)
155
- return;
156
- if (!this.has('Transfer-Encoding')) {
157
- this.set('Content-Length', n);
158
- }
159
- }
160
- /**
161
- * Return parsed response Content-Length when present.
162
- *
163
- * When Content-Length is not defined it will return `undefined`.
164
- */
165
- get length() {
166
- if (this.has('Content-Length')) {
167
- return Number.parseInt(this.get('Content-Length')) || 0;
168
- }
169
- const body = this.body;
170
- if (!body || body instanceof Stream) {
171
- return undefined;
172
- }
173
- if (typeof body === 'string') {
174
- return Buffer.byteLength(body);
175
- }
176
- if (Buffer.isBuffer(body)) {
177
- return body.length;
178
- }
179
- return Buffer.byteLength(JSON.stringify(body));
180
- }
181
- /**
182
- * Check if a header has been written to the socket.
183
- */
184
- get headerSent() {
185
- return this.res.headersSent;
186
- }
187
- /**
188
- * Vary on `field`.
189
- */
190
- vary(field) {
191
- if (this.headerSent)
192
- return;
193
- vary(this.res, field);
194
- }
195
- _getBackReferrer() {
196
- const referrer = this.ctx.get('Referrer');
197
- if (referrer) {
198
- // referrer is an absolute URL, check if it's the same origin
199
- const url = new URL(referrer, this.ctx.href);
200
- if (url.host === this.ctx.host) {
201
- return referrer;
202
- }
203
- }
204
- }
205
- /**
206
- * Perform a 302 redirect to `url`.
207
- *
208
- * The string "back" is special-cased
209
- * to provide Referrer support, when Referrer
210
- * is not present `alt` or "/" is used.
211
- *
212
- * Examples:
213
- *
214
- * this.redirect('back');
215
- * this.redirect('back', '/index.html');
216
- * this.redirect('/login');
217
- * this.redirect('http://google.com'); // will format to 'http://google.com/'
218
- */
219
- redirect(url, alt) {
220
- // location
221
- if (url === 'back') {
222
- url = this._getBackReferrer() || alt || '/';
223
- }
224
- if (url.startsWith('https://') || url.startsWith('http://')) {
225
- // formatting url again avoid security escapes
226
- url = new URL(url).toString();
227
- }
228
- this.set('Location', encodeUrl(url));
229
- // status
230
- if (!statuses.redirect[this.status])
231
- this.status = 302;
232
- // html
233
- if (this.ctx.accepts('html')) {
234
- url = escape(url);
235
- this.type = 'text/html; charset=utf-8';
236
- this.body = `Redirecting to ${url}.`;
237
- return;
238
- }
239
- // text
240
- this.type = 'text/plain; charset=utf-8';
241
- this.body = `Redirecting to ${url}.`;
242
- }
243
- /**
244
- * Set Content-Disposition header to "attachment" with optional `filename`.
245
- */
246
- attachment(filename, options) {
247
- if (filename)
248
- this.type = extname(filename);
249
- this.set('Content-Disposition', contentDisposition(filename, options));
250
- }
251
- /**
252
- * Set Content-Type response header with `type` through `mime.lookup()`
253
- * when it does not contain a charset.
254
- *
255
- * Examples:
256
- *
257
- * this.type = '.html';
258
- * this.type = 'html';
259
- * this.type = 'json';
260
- * this.type = 'application/json';
261
- * this.type = 'png';
262
- */
263
- set type(type) {
264
- if (!type) {
265
- this.remove('Content-Type');
266
- return;
267
- }
268
- const mimeType = getType(type);
269
- if (mimeType) {
270
- this.set('Content-Type', mimeType);
271
- }
272
- }
273
- /**
274
- * Return the response mime type void of
275
- * parameters such as "charset".
276
- */
277
- get type() {
278
- const type = this.get('Content-Type');
279
- if (!type)
280
- return '';
281
- return type.split(';', 1)[0];
282
- }
283
- /**
284
- * Check whether the response is one of the listed types.
285
- * Pretty much the same as `this.request.is()`.
286
- *
287
- * this.response.is('html')
288
- * this.response.is('html', 'json')
289
- */
290
- is(type, ...types) {
291
- let testTypes = [];
292
- if (type) {
293
- testTypes = Array.isArray(type) ? type : [type];
294
- }
295
- return typeis(this.type, [...testTypes, ...types]);
296
- }
297
- /**
298
- * Set the Last-Modified date using a string or a Date.
299
- *
300
- * this.response.lastModified = new Date();
301
- * this.response.lastModified = '2013-09-13';
302
- */
303
- set lastModified(val) {
304
- if (typeof val === 'string')
305
- val = new Date(val);
306
- if (val) {
307
- this.set('Last-Modified', val.toUTCString());
308
- }
309
- }
310
- /**
311
- * Get the Last-Modified date in Date form, if it exists.
312
- */
313
- get lastModified() {
314
- const date = this.get('last-modified');
315
- if (date)
316
- return new Date(date);
317
- }
318
- /**
319
- * Set the ETag of a response.
320
- * This will normalize the quotes if necessary.
321
- *
322
- * this.response.etag = 'md5-hash-sum';
323
- * this.response.etag = '"md5-hash-sum"';
324
- * this.response.etag = 'W/"123456789"';
325
- */
326
- set etag(val) {
327
- if (!/^(W\/)?"/.test(val))
328
- val = `"${val}"`;
329
- this.set('ETag', val);
330
- }
331
- /**
332
- * Get the ETag of a response.
333
- */
334
- get etag() {
335
- return this.get('ETag');
336
- }
337
- /**
338
- * Return response header.
339
- *
340
- * Examples:
341
- *
342
- * this.get('Content-Type');
343
- * // => "text/plain"
344
- *
345
- * this.get('content-type');
346
- * // => "text/plain"
347
- */
348
- get(field) {
349
- return (this.header[field.toLowerCase()] || '');
350
- }
351
- /**
352
- * Returns true if the header identified by name is currently set in the outgoing headers.
353
- * The header name matching is case-insensitive.
354
- *
355
- * Examples:
356
- *
357
- * this.has('Content-Type');
358
- * // => true
359
- *
360
- * this.get('content-type');
361
- * // => true
362
- */
363
- has(field) {
364
- return this.res.hasHeader(field);
365
- }
366
- /**
367
- * Set header `field` to `val` or pass
368
- * an object of header fields.
369
- *
370
- * Examples:
371
- *
372
- * this.set('Foo', ['bar', 'baz']);
373
- * this.set('Accept', 'application/json');
374
- * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
375
- */
376
- set(field, val) {
377
- if (this.headerSent)
378
- return;
379
- if (typeof field === 'string') {
380
- let value = val;
381
- if (Array.isArray(val)) {
382
- value = val.map((v) => (typeof v === 'string' ? v : String(v)));
383
- }
384
- else if (typeof val !== 'string') {
385
- value = String(val);
386
- }
387
- this.res.setHeader(field, value);
388
- }
389
- else {
390
- for (const key in field) {
391
- this.set(key, field[key]);
392
- }
393
- }
394
- }
395
- /**
396
- * Append additional header `field` with value `val`.
397
- *
398
- * Examples:
399
- *
400
- * ```
401
- * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
402
- * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
403
- * this.append('Warning', '199 Miscellaneous warning');
404
- */
405
- append(field, val) {
406
- const prev = this.get(field);
407
- let value = val;
408
- if (prev) {
409
- value = Array.isArray(prev) ? prev.concat(value) : [prev].concat(val);
410
- }
411
- return this.set(field, value);
412
- }
413
- /**
414
- * Remove header `field`.
415
- */
416
- remove(field) {
417
- if (this.headerSent)
418
- return;
419
- this.res.removeHeader(field);
420
- }
421
- /**
422
- * Checks if the request is writable.
423
- * Tests for the existence of the socket
424
- * as node sometimes does not set it.
425
- */
426
- get writable() {
427
- // can't write any more after response finished
428
- // response.writableEnded is available since Node > 12.9
429
- // https://nodejs.org/api/http.html#http_response_writableended
430
- // response.finished is undocumented feature of previous Node versions
431
- // https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
432
- if (this.res.writableEnded || this.res.finished)
433
- return false;
434
- const socket = this.res.socket;
435
- // There are already pending outgoing res, but still writable
436
- // https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
437
- if (!socket)
438
- return true;
439
- return socket.writable;
440
- }
441
- /**
442
- * Inspect implementation.
443
- */
444
- inspect() {
445
- if (!this.res)
446
- return;
447
- const o = this.toJSON();
448
- Reflect.set(o, 'body', this.body);
449
- return o;
450
- }
451
- /**
452
- * Return JSON representation.
453
- */
454
- toJSON() {
455
- return {
456
- status: this.status,
457
- message: this.message,
458
- header: this.header,
459
- };
460
- }
461
- /**
462
- * Flush any set headers and begin the body
463
- */
464
- flushHeaders() {
465
- this.res.flushHeaders();
466
- }
467
- }
468
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzcG9uc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxNQUFNLE1BQU0sYUFBYSxDQUFDO0FBRWpDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDcEMsT0FBTyxNQUFNLE1BQU0sYUFBYSxDQUFDO0FBQ2pDLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUU3QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDN0MsT0FBTyxrQkFBa0IsRUFBRSxFQUE2QyxNQUFNLHFCQUFxQixDQUFDO0FBQ3BHLE9BQU8sT0FBTyxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLFNBQVMsTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxNQUFNLE1BQU0sYUFBYSxDQUFDO0FBQ2pDLE9BQU8sUUFBUSxNQUFNLGFBQWEsQ0FBQztBQUNuQyxPQUFPLFFBQVEsTUFBTSxVQUFVLENBQUM7QUFDaEMsT0FBTyxFQUFFLEVBQUUsSUFBSSxNQUFNLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDdkMsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBTXhCLE1BQU0sT0FBTyxRQUFRO0lBRW5CLEdBQUcsQ0FBYztJQUNqQixHQUFHLENBQWtCO0lBQ3JCLEdBQUcsQ0FBaUI7SUFDcEIsR0FBRyxDQUFVO0lBQ2IsT0FBTyxDQUFVO0lBRWpCLFlBQVksR0FBZ0IsRUFBRSxHQUFZLEVBQUUsR0FBb0IsRUFBRSxHQUFtQjtRQUNuRixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO1FBQ2YsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLHdCQUF3QjtRQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8s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
1
+ import Stream from "node:stream";
2
+ import util from "node:util";
3
+ import onFinished from "on-finished";
4
+ import statuses from "statuses";
5
+ import { is } from "type-is";
6
+ import assert from "node:assert";
7
+ import { extname } from "node:path";
8
+ import { getType } from "cache-content-type";
9
+ import contentDisposition from "content-disposition";
10
+ import destroy from "destroy";
11
+ import encodeUrl from "encodeurl";
12
+ import escape from "escape-html";
13
+ import vary from "vary";
14
+
15
+ //#region src/response.ts
16
+ var Response = class {
17
+ app;
18
+ req;
19
+ res;
20
+ ctx;
21
+ request;
22
+ constructor(app, ctx, req, res) {
23
+ this.app = app;
24
+ this.req = req;
25
+ this.res = res;
26
+ this.ctx = ctx;
27
+ this[util.inspect.custom] = this.inspect.bind(this);
28
+ }
29
+ /**
30
+ * Return the request socket.
31
+ */
32
+ get socket() {
33
+ return this.res.socket;
34
+ }
35
+ /**
36
+ * Return response header.
37
+ */
38
+ get header() {
39
+ return this.res.getHeaders() ?? {};
40
+ }
41
+ /**
42
+ * Return response header, alias as response.header
43
+ */
44
+ get headers() {
45
+ return this.header;
46
+ }
47
+ _explicitStatus;
48
+ /**
49
+ * Get response status code.
50
+ */
51
+ get status() {
52
+ return this.res.statusCode;
53
+ }
54
+ /**
55
+ * Set response status code.
56
+ */
57
+ set status(code) {
58
+ if (this.headerSent) return;
59
+ assert.ok(Number.isInteger(code), "status code must be a number");
60
+ assert.ok(code >= 100 && code <= 999, `invalid status code: ${code}`);
61
+ this._explicitStatus = true;
62
+ this.res.statusCode = code;
63
+ if (this.req.httpVersionMajor < 2 && statuses.message[code]) this.res.statusMessage = statuses.message[code];
64
+ if (this.body && statuses.empty[code]) this.body = null;
65
+ }
66
+ /**
67
+ * Get response status message
68
+ */
69
+ get message() {
70
+ return this.res.statusMessage ?? statuses.message[this.status];
71
+ }
72
+ /**
73
+ * Set response status message
74
+ */
75
+ set message(msg) {
76
+ this.res.statusMessage = msg;
77
+ }
78
+ _body;
79
+ _explicitNullBody;
80
+ /**
81
+ * Get response body.
82
+ */
83
+ get body() {
84
+ return this._body;
85
+ }
86
+ /**
87
+ * Set response body.
88
+ */
89
+ set body(val) {
90
+ const original = this._body;
91
+ this._body = val;
92
+ if (val === null || val === void 0) {
93
+ if (!statuses.empty[this.status]) this.status = 204;
94
+ if (val === null) this._explicitNullBody = true;
95
+ this.remove("Content-Type");
96
+ this.remove("Content-Length");
97
+ this.remove("Transfer-Encoding");
98
+ return;
99
+ }
100
+ if (!this._explicitStatus) this.status = 200;
101
+ const setType = !this.has("Content-Type");
102
+ if (typeof val === "string") {
103
+ if (setType) this.type = /^\s*?</.test(val) ? "html" : "text";
104
+ this.length = Buffer.byteLength(val);
105
+ return;
106
+ }
107
+ if (Buffer.isBuffer(val)) {
108
+ if (setType) this.type = "bin";
109
+ this.length = val.length;
110
+ return;
111
+ }
112
+ if (val instanceof Stream) {
113
+ onFinished(this.res, destroy.bind(null, val));
114
+ if (original != val) {
115
+ val.once("error", (err) => this.ctx.onerror(err));
116
+ if (original !== null && original !== void 0) this.remove("Content-Length");
117
+ }
118
+ if (setType) this.type = "bin";
119
+ return;
120
+ }
121
+ this.remove("Content-Length");
122
+ this.type = "json";
123
+ }
124
+ /**
125
+ * Set Content-Length field to `n`.
126
+ */
127
+ set length(n) {
128
+ if (n === void 0) return;
129
+ if (!this.has("Transfer-Encoding")) this.set("Content-Length", n);
130
+ }
131
+ /**
132
+ * Return parsed response Content-Length when present.
133
+ *
134
+ * When Content-Length is not defined it will return `undefined`.
135
+ */
136
+ get length() {
137
+ if (this.has("Content-Length")) return Number.parseInt(this.get("Content-Length")) || 0;
138
+ const body = this.body;
139
+ if (!body || body instanceof Stream) return;
140
+ if (typeof body === "string") return Buffer.byteLength(body);
141
+ if (Buffer.isBuffer(body)) return body.length;
142
+ return Buffer.byteLength(JSON.stringify(body));
143
+ }
144
+ /**
145
+ * Check if a header has been written to the socket.
146
+ */
147
+ get headerSent() {
148
+ return this.res.headersSent;
149
+ }
150
+ /**
151
+ * Vary on `field`.
152
+ */
153
+ vary(field) {
154
+ if (this.headerSent) return;
155
+ vary(this.res, field);
156
+ }
157
+ _getBackReferrer() {
158
+ const referrer = this.ctx.get("Referrer");
159
+ if (referrer) {
160
+ if (new URL(referrer, this.ctx.href).host === this.ctx.host) return referrer;
161
+ }
162
+ }
163
+ /**
164
+ * Perform a 302 redirect to `url`.
165
+ *
166
+ * The string "back" is special-cased
167
+ * to provide Referrer support, when Referrer
168
+ * is not present `alt` or "/" is used.
169
+ *
170
+ * Examples:
171
+ *
172
+ * this.redirect('back');
173
+ * this.redirect('back', '/index.html');
174
+ * this.redirect('/login');
175
+ * this.redirect('http://google.com'); // will format to 'http://google.com/'
176
+ */
177
+ redirect(url, alt) {
178
+ if (url === "back") url = this._getBackReferrer() || alt || "/";
179
+ if (url.startsWith("https://") || url.startsWith("http://")) url = new URL(url).toString();
180
+ this.set("Location", encodeUrl(url));
181
+ if (!statuses.redirect[this.status]) this.status = 302;
182
+ if (this.ctx.accepts("html")) {
183
+ url = escape(url);
184
+ this.type = "text/html; charset=utf-8";
185
+ this.body = `Redirecting to ${url}.`;
186
+ return;
187
+ }
188
+ this.type = "text/plain; charset=utf-8";
189
+ this.body = `Redirecting to ${url}.`;
190
+ }
191
+ /**
192
+ * Set Content-Disposition header to "attachment" with optional `filename`.
193
+ */
194
+ attachment(filename, options) {
195
+ if (filename) this.type = extname(filename);
196
+ this.set("Content-Disposition", contentDisposition(filename, options));
197
+ }
198
+ /**
199
+ * Set Content-Type response header with `type` through `mime.lookup()`
200
+ * when it does not contain a charset.
201
+ *
202
+ * Examples:
203
+ *
204
+ * this.type = '.html';
205
+ * this.type = 'html';
206
+ * this.type = 'json';
207
+ * this.type = 'application/json';
208
+ * this.type = 'png';
209
+ */
210
+ set type(type) {
211
+ if (!type) {
212
+ this.remove("Content-Type");
213
+ return;
214
+ }
215
+ const mimeType = getType(type);
216
+ if (mimeType) this.set("Content-Type", mimeType);
217
+ }
218
+ /**
219
+ * Return the response mime type void of
220
+ * parameters such as "charset".
221
+ */
222
+ get type() {
223
+ const type = this.get("Content-Type");
224
+ if (!type) return "";
225
+ return type.split(";", 1)[0];
226
+ }
227
+ /**
228
+ * Check whether the response is one of the listed types.
229
+ * Pretty much the same as `this.request.is()`.
230
+ *
231
+ * this.response.is('html')
232
+ * this.response.is('html', 'json')
233
+ */
234
+ is(type, ...types) {
235
+ let testTypes = [];
236
+ if (type) testTypes = Array.isArray(type) ? type : [type];
237
+ return is(this.type, [...testTypes, ...types]);
238
+ }
239
+ /**
240
+ * Set the Last-Modified date using a string or a Date.
241
+ *
242
+ * this.response.lastModified = new Date();
243
+ * this.response.lastModified = '2013-09-13';
244
+ */
245
+ set lastModified(val) {
246
+ if (typeof val === "string") val = new Date(val);
247
+ if (val) this.set("Last-Modified", val.toUTCString());
248
+ }
249
+ /**
250
+ * Get the Last-Modified date in Date form, if it exists.
251
+ */
252
+ get lastModified() {
253
+ const date = this.get("last-modified");
254
+ if (date) return new Date(date);
255
+ }
256
+ /**
257
+ * Set the ETag of a response.
258
+ * This will normalize the quotes if necessary.
259
+ *
260
+ * this.response.etag = 'md5-hash-sum';
261
+ * this.response.etag = '"md5-hash-sum"';
262
+ * this.response.etag = 'W/"123456789"';
263
+ */
264
+ set etag(val) {
265
+ if (!/^(W\/)?"/.test(val)) val = `"${val}"`;
266
+ this.set("ETag", val);
267
+ }
268
+ /**
269
+ * Get the ETag of a response.
270
+ */
271
+ get etag() {
272
+ return this.get("ETag");
273
+ }
274
+ /**
275
+ * Return response header.
276
+ *
277
+ * Examples:
278
+ *
279
+ * this.get('Content-Type');
280
+ * // => "text/plain"
281
+ *
282
+ * this.get('content-type');
283
+ * // => "text/plain"
284
+ */
285
+ get(field) {
286
+ return this.header[field.toLowerCase()] || "";
287
+ }
288
+ /**
289
+ * Returns true if the header identified by name is currently set in the outgoing headers.
290
+ * The header name matching is case-insensitive.
291
+ *
292
+ * Examples:
293
+ *
294
+ * this.has('Content-Type');
295
+ * // => true
296
+ *
297
+ * this.get('content-type');
298
+ * // => true
299
+ */
300
+ has(field) {
301
+ return this.res.hasHeader(field);
302
+ }
303
+ /**
304
+ * Set header `field` to `val` or pass
305
+ * an object of header fields.
306
+ *
307
+ * Examples:
308
+ *
309
+ * this.set('Foo', ['bar', 'baz']);
310
+ * this.set('Accept', 'application/json');
311
+ * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
312
+ */
313
+ set(field, val) {
314
+ if (this.headerSent) return;
315
+ if (typeof field === "string") {
316
+ let value = val;
317
+ if (Array.isArray(val)) value = val.map((v) => typeof v === "string" ? v : String(v));
318
+ else if (typeof val !== "string") value = String(val);
319
+ this.res.setHeader(field, value);
320
+ } else for (const key in field) this.set(key, field[key]);
321
+ }
322
+ /**
323
+ * Append additional header `field` with value `val`.
324
+ *
325
+ * Examples:
326
+ *
327
+ * ```
328
+ * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
329
+ * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
330
+ * this.append('Warning', '199 Miscellaneous warning');
331
+ */
332
+ append(field, val) {
333
+ const prev = this.get(field);
334
+ let value = val;
335
+ if (prev) value = Array.isArray(prev) ? prev.concat(value) : [prev].concat(val);
336
+ return this.set(field, value);
337
+ }
338
+ /**
339
+ * Remove header `field`.
340
+ */
341
+ remove(field) {
342
+ if (this.headerSent) return;
343
+ this.res.removeHeader(field);
344
+ }
345
+ /**
346
+ * Checks if the request is writable.
347
+ * Tests for the existence of the socket
348
+ * as node sometimes does not set it.
349
+ */
350
+ get writable() {
351
+ if (this.res.writableEnded || this.res.finished) return false;
352
+ const socket = this.res.socket;
353
+ if (!socket) return true;
354
+ return socket.writable;
355
+ }
356
+ /**
357
+ * Inspect implementation.
358
+ */
359
+ inspect() {
360
+ if (!this.res) return;
361
+ const o = this.toJSON();
362
+ Reflect.set(o, "body", this.body);
363
+ return o;
364
+ }
365
+ /**
366
+ * Return JSON representation.
367
+ */
368
+ toJSON() {
369
+ return {
370
+ status: this.status,
371
+ message: this.message,
372
+ header: this.header
373
+ };
374
+ }
375
+ /**
376
+ * Flush any set headers and begin the body
377
+ */
378
+ flushHeaders() {
379
+ this.res.flushHeaders();
380
+ }
381
+ };
382
+
383
+ //#endregion
384
+ export { Response };