@eggjs/koa 3.1.0-beta.20 → 3.1.0-beta.22

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/index.js CHANGED
@@ -1,1369 +1,7 @@
1
- import util, { debuglog } from "node:util";
2
- import Emitter from "node:events";
3
- import Stream from "node:stream";
4
- import http from "node:http";
5
- import { getAsyncLocalStorage } from "gals";
6
- import { isGeneratorFunction } from "is-type-of";
7
- import onFinished from "on-finished";
8
- import statuses from "statuses";
9
- import compose from "koa-compose";
10
- import createError, { HttpError } from "http-errors";
11
- import Cookies from "cookies";
12
- import net from "node:net";
13
- import { format } from "node:url";
14
- import qs from "node:querystring";
15
- import accepts from "accepts";
16
- import contentType from "content-type";
17
- import parse from "parseurl";
18
- import typeis, { is } from "type-is";
19
- import fresh from "fresh";
20
- import assert from "node:assert";
21
- import { extname } from "node:path";
22
- import contentDisposition from "content-disposition";
23
- import { getType } from "cache-content-type";
24
- import escape from "escape-html";
25
- import destroy from "destroy";
26
- import vary from "vary";
27
- import encodeUrl from "encodeurl";
28
-
29
- //#region src/context.ts
30
- var Context = class {
31
- app;
32
- req;
33
- res;
34
- request;
35
- response;
36
- originalUrl;
37
- respond;
38
- #state = {};
39
- constructor(app, req, res) {
40
- this.app = app;
41
- this.req = req;
42
- this.res = res;
43
- this.request = new app.RequestClass(app, this, req, res);
44
- this.response = new app.ResponseClass(app, this, req, res);
45
- this.request.response = this.response;
46
- this.response.request = this.request;
47
- this.originalUrl = req.url ?? "/";
48
- }
49
- /**
50
- * util.inspect() implementation, which
51
- * just returns the JSON output.
52
- */
53
- inspect() {
54
- return this.toJSON();
55
- }
56
- /**
57
- * Custom inspection implementation for newer Node.js versions.
58
- */
59
- [util.inspect.custom]() {
60
- return this.inspect();
61
- }
62
- /**
63
- * Return JSON representation.
64
- *
65
- * Here we explicitly invoke .toJSON() on each
66
- * object, as iteration will otherwise fail due
67
- * to the getters and cause utilities such as
68
- * clone() to fail.
69
- */
70
- toJSON() {
71
- return {
72
- request: this.request.toJSON(),
73
- response: this.response.toJSON(),
74
- app: this.app.toJSON(),
75
- originalUrl: this.originalUrl,
76
- req: "<original node req>",
77
- res: "<original node res>",
78
- socket: "<original node socket>"
79
- };
80
- }
81
- assert(value, status, errorMessageOrProps, errorProps) {
82
- if (value) return;
83
- status = status ?? 500;
84
- if (typeof errorMessageOrProps === "string") throw createError(status, errorMessageOrProps, errorProps ?? {});
85
- throw createError(status, errorMessageOrProps ?? {});
86
- }
87
- throw(arg1, arg2, errorProps) {
88
- const args = [];
89
- if (typeof arg2 === "number") {
90
- args.push(arg2);
91
- args.push(arg1);
92
- } else {
93
- args.push(arg1);
94
- if (arg2) args.push(arg2);
95
- }
96
- if (errorProps) args.push(errorProps);
97
- throw createError(...args);
98
- }
99
- /**
100
- * Default error handling.
101
- * @private
102
- */
103
- onerror(err) {
104
- if (err === null || err === void 0) return;
105
- if (!(err instanceof Error || Object.prototype.toString.call(err) === "[object Error]")) err = new Error(util.format("non-error thrown: %j", err));
106
- let headerSent = false;
107
- if (this.response.headerSent || !this.response.writable) {
108
- headerSent = true;
109
- err.headerSent = true;
110
- }
111
- this.app.emit("error", err, this);
112
- if (headerSent) return;
113
- const res = this.res;
114
- for (const name of res.getHeaderNames()) res.removeHeader(name);
115
- if (err.headers) this.response.set(err.headers);
116
- this.response.type = "text";
117
- let statusCode = err.status || err.statusCode;
118
- if (err.code === "ENOENT") statusCode = 404;
119
- if (typeof statusCode !== "number" || !statuses.message[statusCode]) statusCode = 500;
120
- const statusMessage = statuses.message[statusCode];
121
- const msg = err.expose ? err.message : statusMessage;
122
- err.status = statusCode;
123
- this.response.status = statusCode;
124
- this.response.length = Buffer.byteLength(msg);
125
- res.end(msg);
126
- }
127
- _cookies;
128
- get cookies() {
129
- if (!this._cookies) this._cookies = new Cookies(this.req, this.res, {
130
- keys: this.app.keys,
131
- secure: this.request.secure
132
- });
133
- return this._cookies;
134
- }
135
- set cookies(cookies) {
136
- this._cookies = cookies;
137
- }
138
- get state() {
139
- return this.#state;
140
- }
141
- acceptsLanguages(languages, ...others) {
142
- return this.request.acceptsLanguages(languages, ...others);
143
- }
144
- acceptsEncodings(encodings, ...others) {
145
- return this.request.acceptsEncodings(encodings, ...others);
146
- }
147
- acceptsCharsets(charsets, ...others) {
148
- return this.request.acceptsCharsets(charsets, ...others);
149
- }
150
- accepts(args, ...others) {
151
- return this.request.accepts(args, ...others);
152
- }
153
- get(field) {
154
- return this.request.get(field);
155
- }
156
- is(type, ...types) {
157
- return this.request.is(type, ...types);
158
- }
159
- get querystring() {
160
- return this.request.querystring;
161
- }
162
- set querystring(str) {
163
- this.request.querystring = str;
164
- }
165
- get idempotent() {
166
- return this.request.idempotent;
167
- }
168
- get socket() {
169
- return this.request.socket;
170
- }
171
- get search() {
172
- return this.request.search;
173
- }
174
- set search(str) {
175
- this.request.search = str;
176
- }
177
- get method() {
178
- return this.request.method;
179
- }
180
- set method(method) {
181
- this.request.method = method;
182
- }
183
- get query() {
184
- return this.request.query;
185
- }
186
- set query(obj) {
187
- this.request.query = obj;
188
- }
189
- get path() {
190
- return this.request.path;
191
- }
192
- set path(path) {
193
- this.request.path = path;
194
- }
195
- get url() {
196
- return this.request.url;
197
- }
198
- set url(url) {
199
- this.request.url = url;
200
- }
201
- get accept() {
202
- return this.request.accept;
203
- }
204
- set accept(accept) {
205
- this.request.accept = accept;
206
- }
207
- get origin() {
208
- return this.request.origin;
209
- }
210
- get href() {
211
- return this.request.href;
212
- }
213
- get subdomains() {
214
- return this.request.subdomains;
215
- }
216
- get protocol() {
217
- return this.request.protocol;
218
- }
219
- get host() {
220
- return this.request.host;
221
- }
222
- get hostname() {
223
- return this.request.hostname;
224
- }
225
- get URL() {
226
- return this.request.URL;
227
- }
228
- get header() {
229
- return this.request.header;
230
- }
231
- get headers() {
232
- return this.request.headers;
233
- }
234
- get secure() {
235
- return this.request.secure;
236
- }
237
- get stale() {
238
- return this.request.stale;
239
- }
240
- get fresh() {
241
- return this.request.fresh;
242
- }
243
- get ips() {
244
- return this.request.ips;
245
- }
246
- get ip() {
247
- return this.request.ip;
248
- }
249
- /**
250
- * Response delegation.
251
- */
252
- attachment(...args) {
253
- return this.response.attachment(...args);
254
- }
255
- redirect(...args) {
256
- return this.response.redirect(...args);
257
- }
258
- remove(...args) {
259
- return this.response.remove(...args);
260
- }
261
- vary(...args) {
262
- return this.response.vary(...args);
263
- }
264
- has(...args) {
265
- return this.response.has(...args);
266
- }
267
- set(...args) {
268
- return this.response.set(...args);
269
- }
270
- append(...args) {
271
- return this.response.append(...args);
272
- }
273
- flushHeaders(...args) {
274
- return this.response.flushHeaders(...args);
275
- }
276
- get status() {
277
- return this.response.status;
278
- }
279
- set status(status) {
280
- this.response.status = status;
281
- }
282
- get message() {
283
- return this.response.message;
284
- }
285
- set message(msg) {
286
- this.response.message = msg;
287
- }
288
- get body() {
289
- return this.response.body;
290
- }
291
- set body(val) {
292
- this.response.body = val;
293
- }
294
- get length() {
295
- return this.response.length;
296
- }
297
- set length(n) {
298
- this.response.length = n;
299
- }
300
- get type() {
301
- return this.response.type;
302
- }
303
- set type(type) {
304
- this.response.type = type;
305
- }
306
- get lastModified() {
307
- return this.response.lastModified;
308
- }
309
- set lastModified(val) {
310
- this.response.lastModified = val;
311
- }
312
- get etag() {
313
- return this.response.etag;
314
- }
315
- set etag(val) {
316
- this.response.etag = val;
317
- }
318
- get headerSent() {
319
- return this.response.headerSent;
320
- }
321
- get writable() {
322
- return this.response.writable;
323
- }
324
- };
325
-
326
- //#endregion
327
- //#region src/request.ts
328
- var Request = class {
329
- app;
330
- req;
331
- res;
332
- ctx;
333
- response;
334
- originalUrl;
335
- constructor(app, ctx, req, res) {
336
- this.app = app;
337
- this.req = req;
338
- this.res = res;
339
- this.ctx = ctx;
340
- this.originalUrl = req.url ?? "/";
341
- }
342
- /**
343
- * Return request header.
344
- */
345
- get header() {
346
- return this.req.headers;
347
- }
348
- /**
349
- * Set request header.
350
- */
351
- set header(val) {
352
- this.req.headers = val;
353
- }
354
- /**
355
- * Return request header, alias as request.header
356
- */
357
- get headers() {
358
- return this.req.headers;
359
- }
360
- /**
361
- * Set request header, alias as request.header
362
- */
363
- set headers(val) {
364
- this.req.headers = val;
365
- }
366
- /**
367
- * Get request URL.
368
- */
369
- get url() {
370
- return this.req.url ?? "/";
371
- }
372
- /**
373
- * Set request URL.
374
- */
375
- set url(val) {
376
- this.req.url = val;
377
- }
378
- /**
379
- * Get origin of URL.
380
- */
381
- get origin() {
382
- return `${this.protocol}://${this.host}`;
383
- }
384
- /**
385
- * Get full request URL.
386
- */
387
- get href() {
388
- if (/^https?:\/\//i.test(this.originalUrl)) return this.originalUrl;
389
- return this.origin + this.originalUrl;
390
- }
391
- /**
392
- * Get request method.
393
- */
394
- get method() {
395
- return this.req.method ?? "GET";
396
- }
397
- /**
398
- * Set request method.
399
- */
400
- set method(val) {
401
- this.req.method = val;
402
- }
403
- /**
404
- * Get request pathname.
405
- */
406
- get path() {
407
- return parse(this.req)?.pathname ?? "";
408
- }
409
- /**
410
- * Set pathname, retaining the query string when present.
411
- */
412
- set path(pathname) {
413
- const url = parse(this.req);
414
- if (!url) return;
415
- if (url.pathname === pathname) return;
416
- url.pathname = pathname;
417
- url.path = null;
418
- this.url = format(url);
419
- }
420
- _parsedUrlQueryCache;
421
- /**
422
- * Get parsed query string.
423
- */
424
- get query() {
425
- const str = this.querystring;
426
- if (!this._parsedUrlQueryCache) this._parsedUrlQueryCache = {};
427
- let parsedUrlQuery = this._parsedUrlQueryCache[str];
428
- if (!parsedUrlQuery) {
429
- parsedUrlQuery = qs.parse(str);
430
- this._parsedUrlQueryCache[str] = parsedUrlQuery;
431
- }
432
- return parsedUrlQuery;
433
- }
434
- /**
435
- * Set query string as an object.
436
- */
437
- set query(obj) {
438
- this.querystring = qs.stringify(obj);
439
- }
440
- /**
441
- * Get query string.
442
- */
443
- get querystring() {
444
- if (!this.req) return "";
445
- return parse(this.req)?.query ?? "";
446
- }
447
- /**
448
- * Set query string.
449
- */
450
- set querystring(str) {
451
- const url = parse(this.req);
452
- if (!url) return;
453
- if (url.search === `?${str}`) return;
454
- url.search = str;
455
- url.path = null;
456
- this.url = format(url);
457
- }
458
- /**
459
- * Get the search string. Same as the query string
460
- * except it includes the leading ?.
461
- */
462
- get search() {
463
- const querystring = this.querystring;
464
- if (!querystring) return "";
465
- return `?${querystring}`;
466
- }
467
- /**
468
- * Set the search string. Same as
469
- * request.querystring= but included for ubiquity.
470
- */
471
- set search(str) {
472
- this.querystring = str;
473
- }
474
- /**
475
- * Parse the "Host" header field host
476
- * and support X-Forwarded-Host when a
477
- * proxy is enabled.
478
- * return `hostname:port` format
479
- */
480
- get host() {
481
- let host = this.app.proxy ? this.get("X-Forwarded-Host") : "";
482
- if (host) host = splitCommaSeparatedValues(host, 1)[0];
483
- if (!host) {
484
- if (this.req.httpVersionMajor >= 2) host = this.get(":authority");
485
- if (!host) host = this.get("Host");
486
- }
487
- return host;
488
- }
489
- /**
490
- * Parse the "Host" header field hostname
491
- * and support X-Forwarded-Host when a
492
- * proxy is enabled.
493
- */
494
- get hostname() {
495
- const host = this.host;
496
- if (!host) return "";
497
- if (host[0] === "[") return this.URL.hostname || "";
498
- return host.split(":", 1)[0];
499
- }
500
- _memoizedURL;
501
- /**
502
- * Get WHATWG parsed URL.
503
- * Lazily memoized.
504
- */
505
- get URL() {
506
- if (!this._memoizedURL) {
507
- const originalUrl = this.originalUrl || "";
508
- try {
509
- this._memoizedURL = new URL(`${this.origin}${originalUrl}`);
510
- } catch {
511
- this._memoizedURL = Object.create(null);
512
- }
513
- }
514
- return this._memoizedURL;
515
- }
516
- /**
517
- * Check if the request is fresh, aka
518
- * Last-Modified and/or the ETag
519
- * still match.
520
- */
521
- get fresh() {
522
- const method = this.method;
523
- const status = this.response.status;
524
- if (method !== "GET" && method !== "HEAD") return false;
525
- if (status >= 200 && status < 300 || status === 304) return fresh(this.header, this.response.header);
526
- return false;
527
- }
528
- /**
529
- * Check if the request is stale, aka
530
- * "Last-Modified" and / or the "ETag" for the
531
- * resource has changed.
532
- */
533
- get stale() {
534
- return !this.fresh;
535
- }
536
- /**
537
- * Check if the request is idempotent.
538
- */
539
- get idempotent() {
540
- return [
541
- "GET",
542
- "HEAD",
543
- "PUT",
544
- "DELETE",
545
- "OPTIONS",
546
- "TRACE"
547
- ].includes(this.method);
548
- }
549
- /**
550
- * Return the request socket.
551
- */
552
- get socket() {
553
- return this.req.socket;
554
- }
555
- /**
556
- * Get the charset when present or undefined.
557
- */
558
- get charset() {
559
- try {
560
- const { parameters } = contentType.parse(this.req);
561
- return parameters.charset || "";
562
- } catch {
563
- return "";
564
- }
565
- }
566
- /**
567
- * Return parsed Content-Length when present.
568
- */
569
- get length() {
570
- const len = this.get("Content-Length");
571
- if (len === "") return;
572
- return Number.parseInt(len);
573
- }
574
- /**
575
- * Return the protocol string "http" or "https"
576
- * when requested with TLS. When the proxy setting
577
- * is enabled the "X-Forwarded-Proto" header
578
- * field will be trusted. If you're running behind
579
- * a reverse proxy that supplies https for you this
580
- * may be enabled.
581
- */
582
- get protocol() {
583
- if (this.socket.encrypted) return "https";
584
- if (!this.app.proxy) return "http";
585
- let proto = this.get("X-Forwarded-Proto");
586
- if (proto) proto = splitCommaSeparatedValues(proto, 1)[0];
587
- return proto || "http";
588
- }
589
- /**
590
- * Shorthand for:
591
- *
592
- * this.protocol == 'https'
593
- */
594
- get secure() {
595
- return this.protocol === "https";
596
- }
597
- /**
598
- * When `app.proxy` is `true`, parse
599
- * the "X-Forwarded-For" ip address list.
600
- *
601
- * For example if the value was "client, proxy1, proxy2"
602
- * you would receive the array `["client", "proxy1", "proxy2"]`
603
- * where "proxy2" is the furthest down-stream.
604
- */
605
- get ips() {
606
- const proxy = this.app.proxy;
607
- const val = this.get(this.app.proxyIpHeader);
608
- let ips = proxy && val ? splitCommaSeparatedValues(val) : [];
609
- if (this.app.maxIpsCount > 0) ips = ips.slice(-this.app.maxIpsCount);
610
- return ips;
611
- }
612
- _ip;
613
- /**
614
- * Return request's remote address
615
- * When `app.proxy` is `true`, parse
616
- * the "X-Forwarded-For" ip address list and return the first one
617
- */
618
- get ip() {
619
- if (!this._ip) this._ip = this.ips[0] || this.socket.remoteAddress || "";
620
- return this._ip;
621
- }
622
- set ip(ip) {
623
- this._ip = ip;
624
- }
625
- /**
626
- * Return subdomains as an array.
627
- *
628
- * Subdomains are the dot-separated parts of the host before the main domain
629
- * of the app. By default, the domain of the app is assumed to be the last two
630
- * parts of the host. This can be changed by setting `app.subdomainOffset`.
631
- *
632
- * For example, if the domain is "tobi.ferrets.example.com":
633
- * If `app.subdomainOffset` is not set, this.subdomains is
634
- * `["ferrets", "tobi"]`.
635
- * If `app.subdomainOffset` is 3, this.subdomains is `["tobi"]`.
636
- */
637
- get subdomains() {
638
- const offset = this.app.subdomainOffset;
639
- const hostname = this.hostname;
640
- if (net.isIP(hostname)) return [];
641
- return hostname.split(".").reverse().slice(offset);
642
- }
643
- _accept;
644
- /**
645
- * Get accept object.
646
- * Lazily memoized.
647
- */
648
- get accept() {
649
- return this._accept || (this._accept = accepts(this.req));
650
- }
651
- /**
652
- * Set accept object.
653
- */
654
- set accept(obj) {
655
- this._accept = obj;
656
- }
657
- accepts(args, ...others) {
658
- return this.accept.types(args, ...others);
659
- }
660
- acceptsEncodings(encodings, ...others) {
661
- if (!encodings) return this.accept.encodings();
662
- if (Array.isArray(encodings)) encodings = [...encodings, ...others];
663
- else encodings = [encodings, ...others];
664
- return this.accept.encodings(...encodings);
665
- }
666
- acceptsCharsets(charsets, ...others) {
667
- if (!charsets) return this.accept.charsets();
668
- if (Array.isArray(charsets)) charsets = [...charsets, ...others];
669
- else charsets = [charsets, ...others];
670
- return this.accept.charsets(...charsets);
671
- }
672
- acceptsLanguages(languages, ...others) {
673
- if (!languages) return this.accept.languages();
674
- if (Array.isArray(languages)) languages = [...languages, ...others];
675
- else languages = [languages, ...others];
676
- return this.accept.languages(...languages);
677
- }
678
- /**
679
- * Check if the incoming request contains the "Content-Type"
680
- * header field and if it contains any of the given mime `type`s.
681
- * If there is no request body, `null` is returned.
682
- * If there is no content type, `false` is returned.
683
- * Otherwise, it returns the first `type` that matches.
684
- *
685
- * Examples:
686
- *
687
- * // With Content-Type: text/html; charset=utf-8
688
- * this.is('html'); // => 'html'
689
- * this.is('text/html'); // => 'text/html'
690
- * this.is('text/*', 'application/json'); // => 'text/html'
691
- *
692
- * // When Content-Type is application/json
693
- * this.is('json', 'urlencoded'); // => 'json'
694
- * this.is('application/json'); // => 'application/json'
695
- * this.is('html', 'application/*'); // => 'application/json'
696
- *
697
- * this.is('html'); // => false
698
- */
699
- is(type, ...types) {
700
- let testTypes = [];
701
- if (type) testTypes = Array.isArray(type) ? type : [type];
702
- return typeis(this.req, [...testTypes, ...types]);
703
- }
704
- /**
705
- * Return the request mime type void of
706
- * parameters such as "charset".
707
- */
708
- get type() {
709
- const type = this.get("Content-Type");
710
- if (!type) return "";
711
- return type.split(";")[0];
712
- }
713
- /**
714
- * Return request header.
715
- *
716
- * The `Referrer` header field is special-cased,
717
- * both `Referrer` and `Referer` are interchangeable.
718
- *
719
- * Examples:
720
- *
721
- * this.get('Content-Type');
722
- * // => "text/plain"
723
- *
724
- * this.get('content-type');
725
- * // => "text/plain"
726
- *
727
- * this.get('Something');
728
- * // => ''
729
- */
730
- get(field) {
731
- const req = this.req;
732
- switch (field = field.toLowerCase()) {
733
- case "referer":
734
- case "referrer": return req.headers.referrer || req.headers.referer || "";
735
- default: return req.headers[field] || "";
736
- }
737
- }
738
- /**
739
- * Inspect implementation.
740
- */
741
- inspect() {
742
- if (!this.req) return;
743
- return this.toJSON();
744
- }
745
- /**
746
- * Custom inspection implementation for newer Node.js versions.
747
- */
748
- [util.inspect.custom]() {
749
- return this.inspect();
750
- }
751
- /**
752
- * Return JSON representation.
753
- */
754
- toJSON() {
755
- return {
756
- method: this.method,
757
- url: this.url,
758
- header: this.header
759
- };
760
- }
761
- };
762
- /**
763
- * Split a comma-separated value string into an array of values, with an optional limit.
764
- * All the values are trimmed of whitespace and filtered out empty values.
765
- *
766
- * @param {string} value - The comma-separated value string to split.
767
- * @param {number} [limit] - The maximum number of values to return.
768
- * @returns {string[]} An array of values from the comma-separated string.
769
- */
770
- function splitCommaSeparatedValues(value, limit) {
771
- return value.split(",", limit).map((v) => v.trim()).filter((v) => v.length > 0);
772
- }
773
-
774
- //#endregion
775
- //#region src/response.ts
776
- var Response = class {
777
- app;
778
- req;
779
- res;
780
- ctx;
781
- request;
782
- constructor(app, ctx, req, res) {
783
- this.app = app;
784
- this.req = req;
785
- this.res = res;
786
- this.ctx = ctx;
787
- }
788
- /**
789
- * Return the request socket.
790
- */
791
- get socket() {
792
- return this.res.socket;
793
- }
794
- /**
795
- * Return response header.
796
- */
797
- get header() {
798
- return this.res.getHeaders() || {};
799
- }
800
- /**
801
- * Return response header, alias as response.header
802
- */
803
- get headers() {
804
- return this.header;
805
- }
806
- _explicitStatus;
807
- /**
808
- * Get response status code.
809
- */
810
- get status() {
811
- return this.res.statusCode;
812
- }
813
- /**
814
- * Set response status code.
815
- */
816
- set status(code) {
817
- if (this.headerSent) return;
818
- assert.ok(Number.isInteger(code), "status code must be a number");
819
- assert.ok(code >= 100 && code <= 999, `invalid status code: ${code}`);
820
- this._explicitStatus = true;
821
- this.res.statusCode = code;
822
- if (this.req.httpVersionMajor < 2 && statuses.message[code]) this.res.statusMessage = statuses.message[code];
823
- if (this.body && statuses.empty[code]) this.body = null;
824
- }
825
- /**
826
- * Get response status message
827
- */
828
- get message() {
829
- return this.res.statusMessage ?? statuses.message[this.status];
830
- }
831
- /**
832
- * Set response status message
833
- */
834
- set message(msg) {
835
- this.res.statusMessage = msg;
836
- }
837
- _body;
838
- _explicitNullBody;
839
- /**
840
- * Get response body.
841
- */
842
- get body() {
843
- return this._body;
844
- }
845
- /**
846
- * Set response body.
847
- */
848
- set body(val) {
849
- const original = this._body;
850
- this._body = val;
851
- if (val === null || val === void 0) {
852
- if (!statuses.empty[this.status]) this.status = 204;
853
- if (val === null) this._explicitNullBody = true;
854
- this.remove("Content-Type");
855
- this.remove("Content-Length");
856
- this.remove("Transfer-Encoding");
857
- return;
858
- }
859
- if (!this._explicitStatus) this.status = 200;
860
- const setType = !this.has("Content-Type");
861
- if (typeof val === "string") {
862
- if (setType) this.type = /^\s*?</.test(val) ? "html" : "text";
863
- this.length = Buffer.byteLength(val);
864
- return;
865
- }
866
- if (Buffer.isBuffer(val)) {
867
- if (setType) this.type = "bin";
868
- this.length = val.length;
869
- return;
870
- }
871
- if (val instanceof Stream) {
872
- onFinished(this.res, destroy.bind(null, val));
873
- if (original != val) {
874
- val.once("error", (err) => this.ctx.onerror(err));
875
- if (original !== null && original !== void 0) this.remove("Content-Length");
876
- }
877
- if (setType) this.type = "bin";
878
- return;
879
- }
880
- this.remove("Content-Length");
881
- this.type = "json";
882
- }
883
- /**
884
- * Set Content-Length field to `n`.
885
- */
886
- set length(n) {
887
- if (n === void 0) return;
888
- if (!this.has("Transfer-Encoding")) this.set("Content-Length", n);
889
- }
890
- /**
891
- * Return parsed response Content-Length when present.
892
- *
893
- * When Content-Length is not defined it will return `undefined`.
894
- */
895
- get length() {
896
- if (this.has("Content-Length")) return Number.parseInt(this.get("Content-Length")) || 0;
897
- const body = this.body;
898
- if (!body || body instanceof Stream) return;
899
- if (typeof body === "string") return Buffer.byteLength(body);
900
- if (Buffer.isBuffer(body)) return body.length;
901
- return Buffer.byteLength(JSON.stringify(body));
902
- }
903
- /**
904
- * Check if a header has been written to the socket.
905
- */
906
- get headerSent() {
907
- return this.res.headersSent;
908
- }
909
- /**
910
- * Vary on `field`.
911
- */
912
- vary(field) {
913
- if (this.headerSent) return;
914
- vary(this.res, field);
915
- }
916
- _getBackReferrer() {
917
- const referrer = this.ctx.get("Referrer");
918
- if (referrer) {
919
- if (referrer.startsWith("/")) return referrer;
920
- if (new URL(referrer, this.ctx.href).host === this.ctx.host) return referrer;
921
- }
922
- }
923
- /**
924
- * Perform a 302 redirect to `url`.
925
- *
926
- * The string "back" is special-cased
927
- * to provide Referrer support, when Referrer
928
- * is not present `alt` or "/" is used.
929
- *
930
- * Examples:
931
- *
932
- * this.redirect('back');
933
- * this.redirect('back', '/index.html');
934
- * this.redirect('/login');
935
- * this.redirect('http://google.com'); // will format to 'http://google.com/'
936
- */
937
- redirect(url, alt) {
938
- if (url === "back") url = this._getBackReferrer() || alt || "/";
939
- if (url.startsWith("https://") || url.startsWith("http://")) url = new URL(url).toString();
940
- this.set("Location", encodeUrl(url));
941
- if (!statuses.redirect[this.status]) this.status = 302;
942
- if (this.ctx.accepts("html")) {
943
- url = escape(url);
944
- this.type = "text/html; charset=utf-8";
945
- this.body = `Redirecting to ${url}.`;
946
- return;
947
- }
948
- this.type = "text/plain; charset=utf-8";
949
- this.body = `Redirecting to ${url}.`;
950
- }
951
- /**
952
- * Set Content-Disposition header to "attachment" with optional `filename`.
953
- */
954
- attachment(filename, options) {
955
- if (filename) this.type = extname(filename);
956
- this.set("Content-Disposition", contentDisposition(filename, options));
957
- }
958
- /**
959
- * Set Content-Type response header with `type` through `mime.lookup()`
960
- * when it does not contain a charset.
961
- *
962
- * Examples:
963
- *
964
- * this.type = '.html';
965
- * this.type = 'html';
966
- * this.type = 'json';
967
- * this.type = 'application/json';
968
- * this.type = 'png';
969
- */
970
- set type(type) {
971
- if (!type) {
972
- this.remove("Content-Type");
973
- return;
974
- }
975
- const mimeType = getType(type);
976
- if (mimeType) this.set("Content-Type", mimeType);
977
- }
978
- /**
979
- * Return the response mime type void of
980
- * parameters such as "charset".
981
- */
982
- get type() {
983
- const type = this.get("Content-Type");
984
- if (!type) return "";
985
- return type.split(";", 1)[0];
986
- }
987
- /**
988
- * Check whether the response is one of the listed types.
989
- * Pretty much the same as `this.request.is()`.
990
- *
991
- * this.response.is('html')
992
- * this.response.is('html', 'json')
993
- */
994
- is(type, ...types) {
995
- let testTypes = [];
996
- if (type) testTypes = Array.isArray(type) ? type : [type];
997
- return is(this.type, [...testTypes, ...types]);
998
- }
999
- /**
1000
- * Set the Last-Modified date using a string or a Date.
1001
- *
1002
- * this.response.lastModified = new Date();
1003
- * this.response.lastModified = '2013-09-13';
1004
- */
1005
- set lastModified(val) {
1006
- if (typeof val === "string") val = new Date(val);
1007
- if (val) this.set("Last-Modified", val.toUTCString());
1008
- }
1009
- /**
1010
- * Get the Last-Modified date in Date form, if it exists.
1011
- */
1012
- get lastModified() {
1013
- const date = this.get("last-modified");
1014
- if (date) return new Date(date);
1015
- }
1016
- /**
1017
- * Set the ETag of a response.
1018
- * This will normalize the quotes if necessary.
1019
- *
1020
- * this.response.etag = 'md5-hash-sum';
1021
- * this.response.etag = '"md5-hash-sum"';
1022
- * this.response.etag = 'W/"123456789"';
1023
- */
1024
- set etag(val) {
1025
- if (!/^(W\/)?"/.test(val)) val = `"${val}"`;
1026
- this.set("ETag", val);
1027
- }
1028
- /**
1029
- * Get the ETag of a response.
1030
- */
1031
- get etag() {
1032
- return this.get("ETag");
1033
- }
1034
- /**
1035
- * Return response header.
1036
- *
1037
- * Examples:
1038
- *
1039
- * this.get('Content-Type');
1040
- * // => "text/plain"
1041
- *
1042
- * this.get('content-type');
1043
- * // => "text/plain"
1044
- */
1045
- get(field) {
1046
- return this.header[field.toLowerCase()] || "";
1047
- }
1048
- /**
1049
- * Returns true if the header identified by name is currently set in the outgoing headers.
1050
- * The header name matching is case-insensitive.
1051
- *
1052
- * Examples:
1053
- *
1054
- * this.has('Content-Type');
1055
- * // => true
1056
- *
1057
- * this.get('content-type');
1058
- * // => true
1059
- */
1060
- has(field) {
1061
- return this.res.hasHeader(field);
1062
- }
1063
- /**
1064
- * Set header `field` to `val` or pass
1065
- * an object of header fields.
1066
- *
1067
- * Examples:
1068
- *
1069
- * this.set('Foo', ['bar', 'baz']);
1070
- * this.set('Accept', 'application/json');
1071
- * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
1072
- */
1073
- set(field, val) {
1074
- if (this.headerSent) return;
1075
- if (typeof field === "string") {
1076
- let value = val;
1077
- if (Array.isArray(val)) value = val.map((v) => typeof v === "string" ? v : String(v));
1078
- else if (typeof val !== "string") value = String(val);
1079
- this.res.setHeader(field, value);
1080
- } else for (const key in field) this.set(key, field[key]);
1081
- }
1082
- /**
1083
- * Append additional header `field` with value `val`.
1084
- *
1085
- * Examples:
1086
- *
1087
- * ```
1088
- * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
1089
- * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
1090
- * this.append('Warning', '199 Miscellaneous warning');
1091
- */
1092
- append(field, val) {
1093
- const prev = this.get(field);
1094
- let value = val;
1095
- if (prev) value = Array.isArray(prev) ? prev.concat(value) : [prev].concat(val);
1096
- return this.set(field, value);
1097
- }
1098
- /**
1099
- * Remove header `field`.
1100
- */
1101
- remove(field) {
1102
- if (this.headerSent) return;
1103
- this.res.removeHeader(field);
1104
- }
1105
- /**
1106
- * Checks if the request is writable.
1107
- * Tests for the existence of the socket
1108
- * as node sometimes does not set it.
1109
- */
1110
- get writable() {
1111
- if (this.res.writableEnded || this.res.finished) return false;
1112
- const socket = this.res.socket;
1113
- if (!socket) return true;
1114
- return socket.writable;
1115
- }
1116
- /**
1117
- * Inspect implementation.
1118
- */
1119
- inspect() {
1120
- if (!this.res) return;
1121
- const o = this.toJSON();
1122
- Reflect.set(o, "body", this.body);
1123
- return o;
1124
- }
1125
- [util.inspect.custom]() {
1126
- return this.inspect();
1127
- }
1128
- /**
1129
- * Return JSON representation.
1130
- */
1131
- toJSON() {
1132
- return {
1133
- status: this.status,
1134
- message: this.message,
1135
- header: this.header
1136
- };
1137
- }
1138
- /**
1139
- * Flush any set headers and begin the body
1140
- */
1141
- flushHeaders() {
1142
- this.res.flushHeaders();
1143
- }
1144
- };
1145
-
1146
- //#endregion
1147
- //#region src/application.ts
1148
- const debug = debuglog("egg/koa/application");
1149
- /**
1150
- * Expose `Application` class.
1151
- * Inherits from `Emitter.prototype`.
1152
- */
1153
- var Application = class extends Emitter {
1154
- /**
1155
- * Make HttpError available to consumers of the library so that consumers don't
1156
- * have a direct dependency upon `http-errors`
1157
- */
1158
- static HttpError = HttpError;
1159
- _proxy;
1160
- _env;
1161
- subdomainOffset;
1162
- proxyIpHeader;
1163
- maxIpsCount;
1164
- _keys;
1165
- middleware;
1166
- ctxStorage;
1167
- silent;
1168
- ContextClass;
1169
- context;
1170
- RequestClass;
1171
- request;
1172
- ResponseClass;
1173
- response;
1174
- /**
1175
- * Initialize a new `Application`.
1176
- *
1177
- * @param {object} [options] Application options
1178
- * @param {string} [options.env] Environment, default is `development`
1179
- * @param {string[]} [options.keys] Signed cookie keys
1180
- * @param {boolean} [options.proxy] Trust proxy headers
1181
- * @param {number} [options.subdomainOffset] Subdomain offset
1182
- * @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For
1183
- * @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity)
1184
- */
1185
- constructor(options) {
1186
- super();
1187
- options = options || {};
1188
- this._proxy = options.proxy || false;
1189
- this.subdomainOffset = options.subdomainOffset || 2;
1190
- this.proxyIpHeader = options.proxyIpHeader || "X-Forwarded-For";
1191
- this.maxIpsCount = options.maxIpsCount || 0;
1192
- this._env = options.env || process.env.NODE_ENV || "development";
1193
- if (options.keys) this._keys = options.keys;
1194
- this.middleware = [];
1195
- this.ctxStorage = getAsyncLocalStorage();
1196
- this.silent = false;
1197
- this.ContextClass = class ApplicationContext extends Context {};
1198
- this.context = this.ContextClass.prototype;
1199
- this.RequestClass = class ApplicationRequest extends Request {};
1200
- this.request = this.RequestClass.prototype;
1201
- this.ResponseClass = class ApplicationResponse extends Response {};
1202
- this.response = this.ResponseClass.prototype;
1203
- }
1204
- get keys() {
1205
- return this._keys;
1206
- }
1207
- set keys(value) {
1208
- this._keys = value;
1209
- }
1210
- get env() {
1211
- return this._env;
1212
- }
1213
- set env(value) {
1214
- this._env = value;
1215
- }
1216
- get proxy() {
1217
- return this._proxy;
1218
- }
1219
- set proxy(value) {
1220
- this._proxy = value;
1221
- }
1222
- /**
1223
- * Shorthand for:
1224
- *
1225
- * http.createServer(app.callback()).listen(...)
1226
- */
1227
- listen(...args) {
1228
- debug("listen with args: %o", args);
1229
- return http.createServer(this.callback()).listen(...args);
1230
- }
1231
- /**
1232
- * Return JSON representation.
1233
- * We only bother showing settings.
1234
- */
1235
- toJSON() {
1236
- return {
1237
- subdomainOffset: this.subdomainOffset,
1238
- proxy: this.proxy,
1239
- env: this.env
1240
- };
1241
- }
1242
- /**
1243
- * Inspect implementation.
1244
- */
1245
- inspect() {
1246
- return this.toJSON();
1247
- }
1248
- [util.inspect.custom]() {
1249
- return this.inspect();
1250
- }
1251
- /**
1252
- * Use the given middleware `fn`.
1253
- */
1254
- use(fn) {
1255
- if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
1256
- const name = fn._name || fn.name || "-";
1257
- if (isGeneratorFunction(fn)) throw new TypeError(`Support for generators was removed, middleware: ${name}. See the documentation for examples of how to convert old middleware https://github.com/koajs/koa/blob/master/docs/migration.md`);
1258
- debug("use %o #%d", name, this.middleware.length);
1259
- this.middleware.push(fn);
1260
- return this;
1261
- }
1262
- /**
1263
- * Return a request handler callback
1264
- * for node's native http server.
1265
- */
1266
- callback() {
1267
- const fn = compose(this.middleware);
1268
- if (!this.listenerCount("error")) this.on("error", this.onerror.bind(this));
1269
- const handleRequest = (req, res) => {
1270
- const ctx = this.createContext(req, res);
1271
- return this.ctxStorage.run(ctx, async () => {
1272
- return await this.handleRequest(ctx, fn);
1273
- });
1274
- };
1275
- return handleRequest;
1276
- }
1277
- /**
1278
- * return current context from async local storage
1279
- */
1280
- get currentContext() {
1281
- return this.ctxStorage.getStore();
1282
- }
1283
- /**
1284
- * Handle request in callback.
1285
- * @private
1286
- */
1287
- async handleRequest(ctx, fnMiddleware) {
1288
- this.emit("request", ctx);
1289
- const res = ctx.res;
1290
- res.statusCode = 404;
1291
- const onerror = (err) => ctx.onerror(err);
1292
- onFinished(res, (err) => {
1293
- if (err) onerror(err);
1294
- this.emit("response", ctx);
1295
- });
1296
- try {
1297
- await fnMiddleware(ctx);
1298
- return this._respond(ctx);
1299
- } catch (err) {
1300
- return onerror(err);
1301
- }
1302
- }
1303
- /**
1304
- * Initialize a new context.
1305
- * @private
1306
- */
1307
- createContext(req, res) {
1308
- return new this.ContextClass(this, req, res);
1309
- }
1310
- /**
1311
- * Default error handler.
1312
- * @private
1313
- */
1314
- onerror(err) {
1315
- if (!(err instanceof Error || Object.prototype.toString.call(err) === "[object Error]")) throw new TypeError(util.format("non-error thrown: %j", err));
1316
- if (err.status === 404 || err.expose) return;
1317
- if (this.silent) return;
1318
- const msg = err.stack || err.toString();
1319
- console.error(`\n${msg.replaceAll(/^/gm, " ")}\n`);
1320
- }
1321
- /**
1322
- * Response helper.
1323
- */
1324
- _respond(ctx) {
1325
- if (ctx.respond === false) return;
1326
- if (!ctx.writable) return;
1327
- const res = ctx.res;
1328
- let body = ctx.body;
1329
- const code = ctx.status;
1330
- if (statuses.empty[code]) {
1331
- ctx.body = null;
1332
- return res.end();
1333
- }
1334
- if (ctx.method === "HEAD") {
1335
- if (!res.headersSent && !ctx.response.has("Content-Length")) {
1336
- const { length } = ctx.response;
1337
- if (Number.isInteger(length)) ctx.length = length;
1338
- }
1339
- return res.end();
1340
- }
1341
- if (body === null || body === void 0) {
1342
- if (ctx.response._explicitNullBody) {
1343
- ctx.response.remove("Content-Type");
1344
- ctx.response.remove("Transfer-Encoding");
1345
- return res.end();
1346
- }
1347
- if (ctx.req.httpVersionMajor >= 2) body = String(code);
1348
- else body = ctx.message || String(code);
1349
- if (!res.headersSent) {
1350
- ctx.type = "text";
1351
- ctx.length = Buffer.byteLength(body);
1352
- }
1353
- return res.end(body);
1354
- }
1355
- if (Buffer.isBuffer(body)) return res.end(body);
1356
- if (typeof body === "string") return res.end(body);
1357
- if (body instanceof Stream) return body.pipe(res);
1358
- body = JSON.stringify(body);
1359
- if (!res.headersSent) ctx.length = Buffer.byteLength(body);
1360
- res.end(body);
1361
- }
1362
- };
1363
-
1364
- //#endregion
1365
- //#region src/index.ts
1366
- var src_default = Application;
1367
-
1368
- //#endregion
1369
- export { Application, Context, Request, Response, src_default as default };
1
+ import { Application } from "./application.js";
2
+ export default Application;
3
+ export * from "./application.js";
4
+ export * from "./context.js";
5
+ export * from "./request.js";
6
+ export * from "./response.js";
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRS9DLGVBQWUsV0FBVyxDQUFDO0FBRTNCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxlQUFlLENBQUMifQ==