egg 4.0.0-beta.4 → 4.0.0-beta.6

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.
Files changed (78) hide show
  1. package/dist/commonjs/app/extend/context.d.ts +151 -2
  2. package/dist/commonjs/app/extend/context.js +81 -84
  3. package/dist/commonjs/app/extend/helper.d.ts +37 -0
  4. package/dist/commonjs/app/extend/helper.js +49 -0
  5. package/dist/commonjs/app/extend/request.d.ts +128 -0
  6. package/dist/commonjs/app/extend/request.js +270 -0
  7. package/dist/commonjs/app/extend/response.d.ts +25 -0
  8. package/dist/commonjs/app/extend/response.js +37 -0
  9. package/dist/commonjs/app/middleware/meta.d.ts +2 -3
  10. package/dist/commonjs/app/middleware/meta.js +1 -1
  11. package/dist/commonjs/app/middleware/notfound.d.ts +2 -3
  12. package/dist/commonjs/app/middleware/notfound.js +1 -1
  13. package/dist/commonjs/app/middleware/site_file.d.ts +2 -2
  14. package/dist/commonjs/app/middleware/site_file.js +1 -2
  15. package/dist/commonjs/config/config.default.js +3 -2
  16. package/dist/commonjs/config/plugin.js +2 -2
  17. package/dist/commonjs/lib/application.d.ts +5 -12
  18. package/dist/commonjs/lib/application.js +9 -20
  19. package/dist/commonjs/lib/core/base_context_class.d.ts +2 -2
  20. package/dist/commonjs/lib/core/context_httpclient.d.ts +3 -3
  21. package/dist/commonjs/lib/core/context_httpclient.js +1 -1
  22. package/dist/commonjs/lib/core/httpclient.d.ts +2 -3
  23. package/dist/commonjs/lib/core/httpclient.js +3 -6
  24. package/dist/commonjs/lib/core/messenger/ipc.js +1 -2
  25. package/dist/commonjs/lib/egg.d.ts +19 -15
  26. package/dist/commonjs/lib/egg.js +14 -11
  27. package/dist/commonjs/lib/type.d.ts +4 -7
  28. package/dist/commonjs/lib/utils.d.ts +2 -0
  29. package/dist/commonjs/lib/utils.js +21 -0
  30. package/dist/esm/app/extend/context.d.ts +151 -2
  31. package/dist/esm/app/extend/context.js +81 -85
  32. package/dist/esm/app/extend/helper.d.ts +37 -0
  33. package/dist/esm/app/extend/helper.js +43 -0
  34. package/dist/esm/app/extend/request.d.ts +128 -0
  35. package/dist/esm/app/extend/request.js +264 -0
  36. package/dist/esm/app/extend/response.d.ts +25 -0
  37. package/dist/esm/app/extend/response.js +34 -0
  38. package/dist/esm/app/middleware/meta.d.ts +2 -3
  39. package/dist/esm/app/middleware/meta.js +1 -1
  40. package/dist/esm/app/middleware/notfound.d.ts +2 -3
  41. package/dist/esm/app/middleware/notfound.js +1 -1
  42. package/dist/esm/app/middleware/site_file.d.ts +2 -2
  43. package/dist/esm/app/middleware/site_file.js +1 -2
  44. package/dist/esm/config/config.default.js +3 -2
  45. package/dist/esm/config/plugin.js +2 -2
  46. package/dist/esm/lib/application.d.ts +5 -12
  47. package/dist/esm/lib/application.js +9 -20
  48. package/dist/esm/lib/core/base_context_class.d.ts +2 -2
  49. package/dist/esm/lib/core/context_httpclient.d.ts +3 -3
  50. package/dist/esm/lib/core/context_httpclient.js +1 -1
  51. package/dist/esm/lib/core/httpclient.d.ts +2 -3
  52. package/dist/esm/lib/core/httpclient.js +2 -2
  53. package/dist/esm/lib/core/messenger/ipc.js +1 -2
  54. package/dist/esm/lib/egg.d.ts +19 -15
  55. package/dist/esm/lib/egg.js +12 -12
  56. package/dist/esm/lib/type.d.ts +4 -7
  57. package/dist/esm/lib/utils.d.ts +2 -0
  58. package/dist/esm/lib/utils.js +14 -0
  59. package/dist/package.json +1 -1
  60. package/package.json +7 -10
  61. package/src/app/extend/context.ts +116 -100
  62. package/src/app/extend/{helper.js → helper.ts} +14 -13
  63. package/src/app/extend/{request.js → request.ts} +81 -79
  64. package/src/app/extend/response.ts +36 -0
  65. package/src/app/middleware/meta.ts +2 -3
  66. package/src/app/middleware/notfound.ts +2 -3
  67. package/src/app/middleware/site_file.ts +3 -5
  68. package/src/config/config.default.ts +2 -1
  69. package/src/config/plugin.ts +1 -1
  70. package/src/lib/application.ts +14 -21
  71. package/src/lib/core/base_context_class.ts +2 -2
  72. package/src/lib/core/context_httpclient.ts +3 -3
  73. package/src/lib/core/httpclient.ts +4 -5
  74. package/src/lib/core/messenger/ipc.ts +0 -1
  75. package/src/lib/egg.ts +45 -24
  76. package/src/lib/type.ts +3 -13
  77. package/src/lib/utils.ts +16 -0
  78. package/src/app/extend/response.js +0 -101
@@ -1,38 +1,75 @@
1
- import { performance } from 'node:perf_hooks';
2
1
  import delegate from 'delegates';
3
2
  import { assign } from 'utility';
4
- import { utils } from '@eggjs/core';
3
+ import { now, diff } from 'performance-ms';
4
+ import {
5
+ utils, Context as EggCoreContext, Router,
6
+ type ContextDelegation as EggCoreContextDelegation,
7
+ } from '@eggjs/core';
8
+ import type { Cookies as ContextCookies } from '@eggjs/cookies';
9
+ import type { Application } from '../../lib/application.js';
10
+ import type { ContextHttpClient } from '../../lib/core/context_httpclient.js';
11
+ import type { BaseContextClass } from '../../lib//core/base_context_class.js';
12
+ import Request from './request.js';
13
+ import Response from './response.js';
14
+ import { EggLogger } from 'egg-logger';
5
15
 
6
- const HELPER = Symbol('Context#helper');
7
- const LOCALS = Symbol('Context#locals');
8
- const LOCALS_LIST = Symbol('Context#localsList');
9
- const COOKIES = Symbol('Context#cookies');
10
- const CONTEXT_LOGGERS = Symbol('Context#logger');
11
- const CONTEXT_HTTPCLIENT = Symbol('Context#httpclient');
12
- const CONTEXT_ROUTER = Symbol('Context#router');
16
+ const HELPER = Symbol('ctx helper');
17
+ const LOCALS = Symbol('ctx locals');
18
+ const LOCALS_LIST = Symbol('ctx localsList');
19
+ const COOKIES = Symbol('ctx cookies');
20
+ const CONTEXT_HTTPCLIENT = Symbol('ctx httpclient');
21
+ const CONTEXT_ROUTER = Symbol('ctx router');
22
+
23
+ interface Cookies extends ContextCookies {
24
+ request: any;
25
+ response: any;
26
+ }
27
+
28
+ export default class Context extends EggCoreContext {
29
+ declare app: Application;
30
+ declare request: Request;
31
+ declare service: BaseContextClass;
32
+
33
+ /**
34
+ * Request start time
35
+ * @member {Number} Context#starttime
36
+ */
37
+ starttime: number;
38
+ /**
39
+ * Request start timer using `performance.now()`
40
+ * @member {Number} Context#performanceStarttime
41
+ */
42
+ performanceStarttime: number;
13
43
 
14
- const Context = {
15
44
  /**
16
45
  * Get the current visitor's cookies.
17
46
  */
18
47
  get cookies() {
19
- if (!this[COOKIES]) {
20
- this[COOKIES] = new this.app.ContextCookies(this, this.app.keys, this.app.config.cookies);
48
+ let cookies = this[COOKIES];
49
+ if (!cookies) {
50
+ this[COOKIES] = cookies = new this.app.ContextCookies(this, this.app.keys, this.app.config.cookies);
21
51
  }
22
- return this[COOKIES];
23
- },
52
+ return cookies as Cookies;
53
+ }
24
54
 
25
55
  /**
26
56
  * Get a wrapper httpclient instance contain ctx in the hold request process
27
57
  *
28
58
  * @return {ContextHttpClient} the wrapper httpclient instance
29
59
  */
30
- get httpclient() {
60
+ get httpclient(): ContextHttpClient {
31
61
  if (!this[CONTEXT_HTTPCLIENT]) {
32
- this[CONTEXT_HTTPCLIENT] = new this.app.ContextHttpClient(this);
62
+ this[CONTEXT_HTTPCLIENT] = new this.app.ContextHttpClient(this as any);
33
63
  }
34
- return this[CONTEXT_HTTPCLIENT];
35
- },
64
+ return this[CONTEXT_HTTPCLIENT] as ContextHttpClient;
65
+ }
66
+
67
+ /**
68
+ * Alias to {@link Context#httpclient}
69
+ */
70
+ get httpClient(): ContextHttpClient {
71
+ return this.httpclient;
72
+ }
36
73
 
37
74
  /**
38
75
  * Shortcut for httpclient.curl
@@ -42,9 +79,9 @@ const Context = {
42
79
  * @param {Object} [options] - options for request.
43
80
  * @return {Object} see {@link ContextHttpClient#curl}
44
81
  */
45
- curl(url: string, options?: object) {
46
- return this.httpclient.curl(url, options);
47
- },
82
+ async curl(url: string, options?: object): ReturnType<ContextHttpClient['curl']> {
83
+ return await this.httpclient.curl(url, options);
84
+ }
48
85
 
49
86
  /**
50
87
  * Alias to {@link Application#router}
@@ -56,20 +93,20 @@ const Context = {
56
93
  * this.router.pathFor('post', { id: 12 });
57
94
  * ```
58
95
  */
59
- get router() {
60
- if (!this[CONTEXT_ROUTER]) {
61
- this[CONTEXT_ROUTER] = this.app.router;
96
+ get router(): Router {
97
+ if (this[CONTEXT_ROUTER]) {
98
+ return this[CONTEXT_ROUTER] as Router;
62
99
  }
63
- return this[CONTEXT_ROUTER];
64
- },
100
+ return this.app.router;
101
+ }
65
102
 
66
103
  /**
67
104
  * Set router to Context, only use on EggRouter
68
- * @param {EggRouter} val router instance
105
+ * @param {Router} val router instance
69
106
  */
70
- set router(val) {
107
+ set router(val: Router) {
71
108
  this[CONTEXT_ROUTER] = val;
72
- },
109
+ }
73
110
 
74
111
  /**
75
112
  * Get helper instance from {@link Application#Helper}
@@ -79,43 +116,25 @@ const Context = {
79
116
  */
80
117
  get helper() {
81
118
  if (!this[HELPER]) {
82
- this[HELPER] = new this.app.Helper(this);
119
+ this[HELPER] = new this.app.Helper(this as any);
83
120
  }
84
121
  return this[HELPER];
85
- },
122
+ }
86
123
 
87
124
  /**
88
125
  * Wrap app.loggers with context information,
89
126
  * if a custom logger is defined by naming aLogger, then you can `ctx.getLogger('aLogger')`
90
127
  *
91
128
  * @param {String} name - logger name
92
- * @return {Logger} logger
93
129
  */
94
- getLogger(name: string) {
95
- if (this.app.config.logger.enableFastContextLogger) {
96
- return this.app.getLogger(name);
97
- }
98
- let cache = this[CONTEXT_LOGGERS];
99
- if (!cache) {
100
- cache = this[CONTEXT_LOGGERS] = {};
101
- }
102
-
103
- // read from cache
104
- if (cache[name]) return cache[name];
105
-
106
- // get no exist logger
107
- const appLogger = this.app.getLogger(name);
108
- if (!appLogger) return null;
109
-
110
- // write to cache
111
- cache[name] = new this.app.ContextLogger(this, appLogger);
112
- return cache[name];
113
- },
130
+ getLogger(name: string): EggLogger {
131
+ return this.app.getLogger(name);
132
+ }
114
133
 
115
134
  /**
116
- * Logger for Application, wrapping app.coreLogger with context infomation
135
+ * Logger for Application
117
136
  *
118
- * @member {ContextLogger} Context#logger
137
+ * @member {Logger} Context#logger
119
138
  * @since 1.0.0
120
139
  * @example
121
140
  * ```js
@@ -123,20 +142,19 @@ const Context = {
123
142
  * this.logger.warn('WARNING!!!!');
124
143
  * ```
125
144
  */
126
- get logger() {
145
+ get logger(): EggLogger {
127
146
  return this.getLogger('logger');
128
- },
147
+ }
129
148
 
130
149
  /**
131
- * Logger for frameworks and plugins,
132
- * wrapping app.coreLogger with context infomation
150
+ * Logger for frameworks and plugins
133
151
  *
134
- * @member {ContextLogger} Context#coreLogger
152
+ * @member {Logger} Context#coreLogger
135
153
  * @since 1.0.0
136
154
  */
137
- get coreLogger() {
155
+ get coreLogger(): EggLogger {
138
156
  return this.getLogger('coreLogger');
139
- },
157
+ }
140
158
 
141
159
  /**
142
160
  * locals is an object for view, you can use `app.locals` and `ctx.locals` to set variables,
@@ -169,19 +187,18 @@ const Context = {
169
187
  if (!this[LOCALS]) {
170
188
  this[LOCALS] = assign({}, this.app.locals);
171
189
  }
172
- if (this[LOCALS_LIST] && this[LOCALS_LIST].length) {
190
+ if (Array.isArray(this[LOCALS_LIST]) && this[LOCALS_LIST].length > 0) {
173
191
  assign(this[LOCALS], this[LOCALS_LIST]);
174
192
  this[LOCALS_LIST] = null;
175
193
  }
176
- return this[LOCALS];
177
- },
194
+ return this[LOCALS] as Record<string, any>;
195
+ }
178
196
 
179
197
  set locals(val) {
180
- if (!this[LOCALS_LIST]) {
181
- this[LOCALS_LIST] = [];
182
- }
183
- this[LOCALS_LIST].push(val);
184
- },
198
+ const localsList = this[LOCALS_LIST] as Record<string, any>[] ?? [];
199
+ localsList.push(val);
200
+ this[LOCALS_LIST] = localsList;
201
+ }
185
202
 
186
203
  /**
187
204
  * alias to {@link Context#locals}, compatible with koa that use this variable
@@ -190,11 +207,11 @@ const Context = {
190
207
  */
191
208
  get state() {
192
209
  return this.locals;
193
- },
210
+ }
194
211
 
195
212
  set state(val) {
196
213
  this.locals = val;
197
- },
214
+ }
198
215
 
199
216
  /**
200
217
  * Run async function in the background
@@ -208,43 +225,40 @@ const Context = {
208
225
  * });
209
226
  * ```
210
227
  */
211
- runInBackground(scope: (ctx: any) => Promise<void>) {
228
+ runInBackground(scope: (ctx: ContextDelegation) => Promise<void>, taskName?: string): void {
212
229
  // try to use custom function name first
213
- /* istanbul ignore next */
214
- const taskName = Reflect.get(scope, '_name') || scope.name || utils.getCalleeFromStack(true);
215
- this._runInBackground(scope, taskName);
216
- },
230
+ if (!taskName) {
231
+ taskName = Reflect.get(scope, '_name') || scope.name || utils.getCalleeFromStack(true);
232
+ }
233
+ // use setImmediate to ensure all sync logic will run async
234
+ setImmediate(() => {
235
+ this._runInBackground(scope, taskName!);
236
+ });
237
+ }
217
238
 
218
239
  // let plugins or frameworks to reuse _runInBackground in some cases.
219
240
  // e.g.: https://github.com/eggjs/egg-mock/pull/78
220
- _runInBackground(scope: (ctx: any) => Promise<void>, taskName: string) {
221
- // eslint-disable-next-line @typescript-eslint/no-this-alias
222
- const ctx = this;
223
- const start = performance.now();
224
- // use setImmediate to ensure all sync logic will run async
225
- return new Promise(resolve => setImmediate(resolve))
226
- .then(() => scope(ctx))
227
- .then(() => {
228
- ctx.coreLogger.info('[egg:background] task:%s success (%dms)',
229
- taskName, Math.floor((performance.now() - start) * 1000) / 1000);
230
- })
231
- .catch(err => {
232
- // background task process log
233
- ctx.coreLogger.info('[egg:background] task:%s fail (%dms)',
234
- taskName, Math.floor((performance.now() - start) * 1000) / 1000);
241
+ async _runInBackground(scope: (ctx: ContextDelegation) => Promise<void>, taskName: string) {
242
+ const startTime = now();
243
+ try {
244
+ await scope(this as any);
245
+ this.coreLogger.info('[egg:background] task:%s success (%dms)', taskName, diff(startTime));
246
+ } catch (err: any) {
247
+ // background task process log
248
+ this.coreLogger.info('[egg:background] task:%s fail (%dms)', taskName, diff(startTime));
235
249
 
236
- // emit error when promise catch, and set err.runInBackground flag
237
- err.runInBackground = true;
238
- ctx.app.emit('error', err, ctx);
239
- });
240
- },
241
- } as any;
250
+ // emit error when promise catch, and set err.runInBackground flag
251
+ err.runInBackground = true;
252
+ this.app.emit('error', err, this);
253
+ }
254
+ }
255
+ }
242
256
 
243
257
  /**
244
258
  * Context delegation.
245
259
  */
246
260
 
247
- delegate(Context, 'request')
261
+ delegate(Context.prototype, 'request')
248
262
  /**
249
263
  * @member {Boolean} Context#acceptJSON
250
264
  * @see Request#acceptJSON
@@ -270,7 +284,7 @@ delegate(Context, 'request')
270
284
  */
271
285
  .access('ip');
272
286
 
273
- delegate(Context, 'response')
287
+ delegate(Context.prototype, 'response')
274
288
  /**
275
289
  * @member {Number} Context#realStatus
276
290
  * @see Response#realStatus
@@ -278,4 +292,6 @@ delegate(Context, 'response')
278
292
  */
279
293
  .access('realStatus');
280
294
 
281
- export default Context;
295
+ export type ContextDelegation = EggCoreContextDelegation & Context
296
+ & Pick<Request, 'acceptJSON' | 'queries' | 'accept' | 'ip'>
297
+ & Pick<Response, 'realStatus'>;
@@ -1,10 +1,12 @@
1
- 'use strict';
2
-
3
- const url = require('url');
4
-
5
-
6
- module.exports = {
1
+ import url from 'node:url';
2
+ import { BaseContextClass } from '../../lib/core/base_context_class.js';
7
3
 
4
+ /**
5
+ * The Helper class which can be used as utility function.
6
+ * We support developers to extend Helper through ${baseDir}/app/extend/helper.js ,
7
+ * then you can use all method on `ctx.helper` that is a instance of Helper.
8
+ */
9
+ export default class Helper extends BaseContextClass {
8
10
  /**
9
11
  * Generate URL path(without host) for route. Takes the route name and a map of named params.
10
12
  * @function Helper#pathFor
@@ -19,9 +21,9 @@ module.exports = {
19
21
  * ```
20
22
  * @return {String} url path(without host)
21
23
  */
22
- pathFor(name, params) {
24
+ pathFor(name: string, params: Record<string, any>): string {
23
25
  return this.app.router.url(name, params);
24
- },
26
+ }
25
27
 
26
28
  /**
27
29
  * Generate full URL(with host) for route. Takes the route name and a map of named params.
@@ -36,8 +38,7 @@ module.exports = {
36
38
  * ```
37
39
  * @return {String} full url(with host)
38
40
  */
39
- urlFor(name, params) {
40
- return this.ctx.protocol + '://' + this.ctx.host + url.resolve('/', this.app.router.url(name, params));
41
- },
42
-
43
- };
41
+ urlFor(name: string, params: Record<string, any>): string {
42
+ return this.ctx.protocol + '://' + this.ctx.host + url.resolve('/', this.pathFor(name, params));
43
+ }
44
+ }
@@ -1,17 +1,21 @@
1
- 'use strict';
1
+ import querystring from 'node:querystring';
2
+ import { Request as EggCoreRequest } from '@eggjs/core';
3
+ import type { Application } from '../../lib/application.js';
4
+ import type { ContextDelegation } from './context.js';
5
+ import Response from './response.js';
2
6
 
3
- const querystring = require('querystring');
4
- const accepts = require('accepts');
5
-
6
- const _querycache = Symbol('_querycache');
7
- const _queriesCache = Symbol('_queriesCache');
8
- const PROTOCOL = Symbol('PROTOCOL');
9
- const HOST = Symbol('HOST');
10
- const ACCEPTS = Symbol('ACCEPTS');
11
- const IPS = Symbol('IPS');
7
+ const QUERY_CACHE = Symbol('request query cache');
8
+ const QUERIES_CACHE = Symbol('request queries cache');
9
+ const PROTOCOL = Symbol('request protocol');
10
+ const HOST = Symbol('request host');
11
+ const IPS = Symbol('request ips');
12
12
  const RE_ARRAY_KEY = /[^\[\]]+\[\]$/;
13
13
 
14
- module.exports = {
14
+ export default class Request extends EggCoreRequest {
15
+ declare app: Application;
16
+ declare ctx: ContextDelegation;
17
+ declare response: Response;
18
+
15
19
  /**
16
20
  * Parse the "Host" header field host
17
21
  * and support X-Forwarded-Host when a
@@ -29,17 +33,19 @@ module.exports = {
29
33
  * => 'demo.eggjs.org'
30
34
  * ```
31
35
  */
32
- get host() {
33
- if (this[HOST]) return this[HOST];
36
+ get host(): string {
37
+ let host = this[HOST] as string | undefined;
38
+ if (host) {
39
+ return host;
40
+ }
34
41
 
35
- let host;
36
42
  if (this.app.config.proxy) {
37
43
  host = getFromHeaders(this, this.app.config.hostHeaders);
38
44
  }
39
45
  host = host || this.get('host') || '';
40
- this[HOST] = host.split(/\s*,\s*/)[0];
41
- return this[HOST];
42
- },
46
+ this[HOST] = host = host.split(',')[0].trim();
47
+ return host;
48
+ }
43
49
 
44
50
  /**
45
51
  * @member {String} Request#protocol
@@ -49,25 +55,28 @@ module.exports = {
49
55
  * => 'https'
50
56
  * ```
51
57
  */
52
- get protocol() {
53
- if (this[PROTOCOL]) return this[PROTOCOL];
58
+ get protocol(): string {
59
+ let protocol = this[PROTOCOL] as string;
60
+ if (protocol) {
61
+ return protocol;
62
+ }
54
63
  // detect encrypted socket
55
- if (this.socket && this.socket.encrypted) {
56
- this[PROTOCOL] = 'https';
57
- return this[PROTOCOL];
64
+ if (this.socket?.encrypted) {
65
+ this[PROTOCOL] = protocol = 'https';
66
+ return protocol;
58
67
  }
59
68
  // get from headers specified in `app.config.protocolHeaders`
60
69
  if (this.app.config.proxy) {
61
70
  const proto = getFromHeaders(this, this.app.config.protocolHeaders);
62
71
  if (proto) {
63
- this[PROTOCOL] = proto.split(/\s*,\s*/)[0];
64
- return this[PROTOCOL];
72
+ this[PROTOCOL] = protocol = proto.split(/\s*,\s*/)[0];
73
+ return protocol;
65
74
  }
66
75
  }
67
- // use protocol specified in `app.conig.protocol`
68
- this[PROTOCOL] = this.app.config.protocol || 'http';
69
- return this[PROTOCOL];
70
- },
76
+ // use protocol specified in `app.config.protocol`
77
+ this[PROTOCOL] = protocol = this.app.config.protocol || 'http';
78
+ return protocol;
79
+ }
71
80
 
72
81
  /**
73
82
  * Get all pass through ip addresses from the request.
@@ -80,29 +89,34 @@ module.exports = {
80
89
  * => ['100.23.1.2', '201.10.10.2']
81
90
  * ```
82
91
  */
83
- get ips() {
84
- if (this[IPS]) return this[IPS];
92
+ get ips(): string[] {
93
+ let ips = this[IPS] as string[] | undefined;
94
+ if (ips) {
95
+ return ips;
96
+ }
85
97
 
86
98
  // return empty array when proxy=false
87
99
  if (!this.app.config.proxy) {
88
- this[IPS] = [];
89
- return this[IPS];
100
+ this[IPS] = ips = [];
101
+ return ips;
90
102
  }
91
103
 
92
- const val = getFromHeaders(this, this.app.config.ipHeaders) || '';
93
- this[IPS] = val ? val.split(/\s*,\s*/) : [];
104
+ const val = getFromHeaders(this, this.app.config.ipHeaders);
105
+ this[IPS] = ips = val ? val.split(/\s*,\s*/) : [];
94
106
 
95
107
  let maxIpsCount = this.app.config.maxIpsCount;
96
108
  // Compatible with maxProxyCount logic (previous logic is wrong, only for compatibility with legacy logic)
97
- if (!maxIpsCount && this.app.config.maxProxyCount) maxIpsCount = this.app.config.maxProxyCount + 1;
109
+ if (!maxIpsCount && this.app.config.maxProxyCount) {
110
+ maxIpsCount = this.app.config.maxProxyCount + 1;
111
+ }
98
112
 
99
113
  if (maxIpsCount > 0) {
100
114
  // if maxIpsCount present, only keep `maxIpsCount` ips
101
115
  // [ illegalIp, clientRealIp, proxyIp1, proxyIp2 ...]
102
- this[IPS] = this[IPS].slice(-maxIpsCount);
116
+ this[IPS] = ips = ips.slice(-maxIpsCount);
103
117
  }
104
- return this[IPS];
105
- },
118
+ return ips;
119
+ }
106
120
 
107
121
  /**
108
122
  * Get the request remote IPv4 address
@@ -115,16 +129,16 @@ module.exports = {
115
129
  * => '111.10.2.1'
116
130
  * ```
117
131
  */
118
- get ip() {
132
+ get ip(): string {
119
133
  if (this._ip) {
120
134
  return this._ip;
121
135
  }
122
- const ip = this.ips[0] || this.socket.remoteAddress;
136
+ const ip = this.ips[0] ?? this.socket.remoteAddress;
123
137
  // will be '::ffff:x.x.x.x', should convert to standard IPv4 format
124
138
  // https://zh.wikipedia.org/wiki/IPv6
125
- this._ip = ip && ip.indexOf('::ffff:') > -1 ? ip.substring(7) : ip;
139
+ this._ip = ip && ip.startsWith('::ffff:') ? ip.substring(7) : ip;
126
140
  return this._ip;
127
- },
141
+ }
128
142
 
129
143
  /**
130
144
  * Set the request remote IPv4 address
@@ -137,9 +151,9 @@ module.exports = {
137
151
  * => '111.10.2.1'
138
152
  * ```
139
153
  */
140
- set ip(ip) {
154
+ set ip(ip: string) {
141
155
  this._ip = ip;
142
- },
156
+ }
143
157
 
144
158
  /**
145
159
  * detect if response should be json
@@ -150,25 +164,25 @@ module.exports = {
150
164
  * @member {Boolean} Request#acceptJSON
151
165
  * @since 1.0.0
152
166
  */
153
- get acceptJSON() {
167
+ get acceptJSON(): boolean {
154
168
  if (this.path.endsWith('.json')) return true;
155
169
  if (this.response.type && this.response.type.indexOf('json') >= 0) return true;
156
170
  if (this.accepts('html', 'text', 'json') === 'json') return true;
157
171
  return false;
158
- },
172
+ }
159
173
 
160
174
  // How to read query safely
161
175
  // https://github.com/koajs/qs/issues/5
162
- _customQuery(cacheName, filter) {
176
+ _customQuery(cacheName: symbol, filter: (value: string | string[]) => string | string[]) {
163
177
  const str = this.querystring || '';
164
- let c = this[cacheName];
178
+ let c = this[cacheName] as Record<string, Record<string, string | string[]>>;
165
179
  if (!c) {
166
180
  c = this[cacheName] = {};
167
181
  }
168
182
  let cacheQuery = c[str];
169
183
  if (!cacheQuery) {
170
184
  cacheQuery = c[str] = {};
171
- const isQueries = cacheName === _queriesCache;
185
+ const isQueries = cacheName === QUERIES_CACHE;
172
186
  // `querystring.parse` CANNOT parse something like `a[foo]=1&a[bar]=2`
173
187
  const query = str ? querystring.parse(str) : {};
174
188
  for (const key in query) {
@@ -176,20 +190,19 @@ module.exports = {
176
190
  // key is '', like `a=b&`
177
191
  continue;
178
192
  }
179
- const value = filter(query[key]);
193
+ const value = filter(query[key]!);
180
194
  cacheQuery[key] = value;
181
195
  if (isQueries && RE_ARRAY_KEY.test(key)) {
182
196
  // `this.queries['key'] => this.queries['key[]']` is compatibly supported
183
- const subkey = key.substring(0, key.length - 2);
184
-
185
- if (!cacheQuery[subkey]) {
186
- cacheQuery[subkey] = value;
197
+ const subKey = key.substring(0, key.length - 2);
198
+ if (!cacheQuery[subKey]) {
199
+ cacheQuery[subKey] = value;
187
200
  }
188
201
  }
189
202
  }
190
203
  }
191
204
  return cacheQuery;
192
- },
205
+ }
193
206
 
194
207
  /**
195
208
  * get params pass by querystring, all values are of string type.
@@ -212,8 +225,8 @@ module.exports = {
212
225
  * ```
213
226
  */
214
227
  get query() {
215
- return this._customQuery(_querycache, firstValue);
216
- },
228
+ return this._customQuery(QUERY_CACHE, firstValue) as Record<string, string>;
229
+ }
217
230
 
218
231
  /**
219
232
  * get params pass by querystring, all value are Array type. {@link Request#query}
@@ -232,50 +245,39 @@ module.exports = {
232
245
  * ```
233
246
  */
234
247
  get queries() {
235
- return this._customQuery(_queriesCache, arrayValue);
236
- },
237
-
238
- get accept() {
239
- let accept = this[ACCEPTS];
240
- if (accept) {
241
- return accept;
242
- }
243
- accept = this[ACCEPTS] = accepts(this.req);
244
- return accept;
245
- },
248
+ return this._customQuery(QUERIES_CACHE, arrayValue) as Record<string, string[]>;
249
+ }
246
250
 
247
251
  /**
248
252
  * Set query-string as an object.
249
253
  *
250
254
  * @function Request#query
251
255
  * @param {Object} obj set querystring and query object for request.
252
- * @return {void}
253
256
  */
254
- set query(obj) {
257
+ set query(obj: Record<string, string>) {
255
258
  this.querystring = querystring.stringify(obj);
256
- },
257
- };
258
-
259
+ }
260
+ }
259
261
 
260
- function firstValue(value) {
262
+ function firstValue(value: string | string[]) {
261
263
  if (Array.isArray(value)) {
262
264
  value = value[0];
263
265
  }
264
266
  return value;
265
267
  }
266
268
 
267
- function arrayValue(value) {
269
+ function arrayValue(value: string | string[]) {
268
270
  if (!Array.isArray(value)) {
269
271
  value = [ value ];
270
272
  }
271
273
  return value;
272
274
  }
273
275
 
274
- function getFromHeaders(ctx, names) {
276
+ function getFromHeaders(request: Request, names: string) {
275
277
  if (!names) return '';
276
- names = names.split(/\s*,\s*/);
277
- for (const name of names) {
278
- const value = ctx.get(name);
278
+ const fields = names.split(/\s*,\s*/);
279
+ for (const name of fields) {
280
+ const value = request.get<string>(name);
279
281
  if (value) return value;
280
282
  }
281
283
  return '';