egg 4.1.0-beta.19 → 4.1.0-beta.20

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 (105) hide show
  1. package/dist/AgentWorkerLoader-DG_hAClt.d.ts +17 -0
  2. package/dist/AgentWorkerLoader-D_hceBRW.js +24 -0
  3. package/dist/AppWorkerLoader-CAtxJvGl.js +37 -0
  4. package/dist/AppWorkerLoader-CT875rYM.d.ts +22 -0
  5. package/dist/CookieLimitExceedError-CAW0HYJw.d.ts +8 -0
  6. package/dist/CookieLimitExceedError-DLGakbeu.js +15 -0
  7. package/dist/EggApplicationLoader-CMe1VQt1.js +7 -0
  8. package/dist/EggApplicationLoader-PnIvd5oV.d.ts +8 -0
  9. package/dist/IMessenger-C9g6ypSI.d.ts +54 -0
  10. package/dist/MessageUnhandledRejectionError-Lq5fWw24.d.ts +8 -0
  11. package/dist/MessageUnhandledRejectionError-oD_E1Ewl.js +15 -0
  12. package/dist/agent-BfFWeJj4.js +55 -0
  13. package/dist/agent.d.ts +11 -1
  14. package/dist/agent.js +1 -1
  15. package/dist/app/extend/context.d.ts +11 -182
  16. package/dist/app/extend/context.js +1 -242
  17. package/dist/app/extend/helper.d.ts +11 -41
  18. package/dist/app/extend/helper.js +3 -45
  19. package/dist/app/extend/request.d.ts +11 -135
  20. package/dist/app/extend/request.js +2 -224
  21. package/dist/app/extend/response.d.ts +1 -28
  22. package/dist/app/extend/response.js +1 -34
  23. package/dist/app/middleware/meta.d.ts +11 -10
  24. package/dist/app/middleware/notfound.d.ts +11 -10
  25. package/dist/app/middleware/site_file.d.ts +11 -12
  26. package/dist/application-DVwFQSa9.js +208 -0
  27. package/dist/base-KLVtlzUD.d.ts +12 -0
  28. package/dist/base_context_class-Xc1OZql9.js +19 -0
  29. package/dist/base_context_logger-CZU59PGA.js +58 -0
  30. package/dist/base_hook_class-D0Gu2p8r.js +26 -0
  31. package/dist/base_hook_class-R8A8gm1s.d.ts +1109 -0
  32. package/dist/config/config.default.d.ts +11 -2
  33. package/dist/config/config.default.js +26 -2
  34. package/dist/context-D1Wg7CXs.js +244 -0
  35. package/dist/context_httpclient-BpRMdJhf.js +29 -0
  36. package/dist/egg-DVo5e_lr.js +800 -0
  37. package/dist/error-BYo_LRnd.js +1 -0
  38. package/dist/helper-B3PKMPXq.js +47 -0
  39. package/dist/httpclient-C1QPc_R7.js +36 -0
  40. package/dist/index-CUPkUUOR.d.ts +1 -0
  41. package/dist/index-CkgLZdB4.d.ts +1 -0
  42. package/dist/index.d.ts +12 -20
  43. package/dist/index.js +26 -19
  44. package/dist/lib/agent.d.ts +11 -21
  45. package/dist/lib/agent.js +20 -54
  46. package/dist/lib/application.d.ts +11 -59
  47. package/dist/lib/application.js +21 -207
  48. package/dist/lib/core/base_context_class.d.ts +11 -22
  49. package/dist/lib/core/base_context_class.js +2 -17
  50. package/dist/lib/core/base_context_logger.d.ts +11 -39
  51. package/dist/lib/core/base_context_logger.js +1 -56
  52. package/dist/lib/core/base_hook_class.d.ts +11 -17
  53. package/dist/lib/core/base_hook_class.js +1 -24
  54. package/dist/lib/core/context_httpclient.d.ts +11 -21
  55. package/dist/lib/core/context_httpclient.js +1 -27
  56. package/dist/lib/core/httpclient.d.ts +12 -17
  57. package/dist/lib/core/httpclient.js +1 -34
  58. package/dist/lib/core/logger.d.ts +11 -1
  59. package/dist/lib/core/logger.js +1 -29
  60. package/dist/lib/core/messenger/IMessenger.d.ts +1 -53
  61. package/dist/lib/core/messenger/base.d.ts +12 -11
  62. package/dist/lib/core/messenger/base.js +15 -27
  63. package/dist/lib/core/messenger/index.d.ts +12 -11
  64. package/dist/lib/core/messenger/index.js +15 -11
  65. package/dist/lib/core/messenger/ipc.d.ts +12 -4
  66. package/dist/lib/core/messenger/ipc.js +15 -128
  67. package/dist/lib/core/messenger/local.d.ts +12 -4
  68. package/dist/lib/core/messenger/local.js +15 -124
  69. package/dist/lib/core/utils.js +1 -46
  70. package/dist/lib/egg.d.ts +12 -285
  71. package/dist/lib/egg.js +15 -518
  72. package/dist/lib/error/CookieLimitExceedError.d.ts +1 -7
  73. package/dist/lib/error/CookieLimitExceedError.js +1 -13
  74. package/dist/lib/error/MessageUnhandledRejectionError.d.ts +1 -7
  75. package/dist/lib/error/MessageUnhandledRejectionError.js +1 -13
  76. package/dist/lib/error/index.d.ts +3 -2
  77. package/dist/lib/error/index.js +3 -2
  78. package/dist/lib/loader/AgentWorkerLoader.d.ts +2 -16
  79. package/dist/lib/loader/AgentWorkerLoader.js +2 -22
  80. package/dist/lib/loader/AppWorkerLoader.d.ts +2 -21
  81. package/dist/lib/loader/AppWorkerLoader.js +2 -35
  82. package/dist/lib/loader/EggApplicationLoader.d.ts +1 -7
  83. package/dist/lib/loader/EggApplicationLoader.js +1 -5
  84. package/dist/lib/loader/index.d.ts +4 -3
  85. package/dist/lib/loader/index.js +4 -3
  86. package/dist/lib/start.d.ts +11 -27
  87. package/dist/lib/start.js +25 -34
  88. package/dist/lib/types.d.ts +12 -283
  89. package/dist/lib/types.js +2 -32
  90. package/dist/lib/types.plugin.d.ts +1 -12
  91. package/dist/lib/types.plugin.js +1 -12
  92. package/dist/lib/utils.js +1 -12
  93. package/dist/loader-3myZ-rpm.js +1 -0
  94. package/dist/logger-C4tIcO3S.js +31 -0
  95. package/dist/request-Cy_1DlaX.js +225 -0
  96. package/dist/response-CDeQ9Sx2.js +36 -0
  97. package/dist/response-DlNYDj00.d.ts +29 -0
  98. package/dist/src-BuOjXSrB.js +3 -0
  99. package/dist/start-4E84z796.js +35 -0
  100. package/dist/types-dKSyDnVp.js +32 -0
  101. package/dist/types.plugin-B2v0K0I8.js +14 -0
  102. package/dist/types.plugin-C3D5I7VD.d.ts +12 -0
  103. package/dist/utils-B1Rjsoi9.js +48 -0
  104. package/dist/utils-BDoYg6z6.js +14 -0
  105. package/package.json +22 -22
@@ -0,0 +1,800 @@
1
+ import { BaseHookClass } from "./base_hook_class-D0Gu2p8r.js";
2
+ import { BaseContextClass as BaseContextClass$1 } from "./base_context_class-Xc1OZql9.js";
3
+ import { MessageUnhandledRejectionError } from "./MessageUnhandledRejectionError-oD_E1Ewl.js";
4
+ import { ContextHttpClient } from "./context_httpclient-BpRMdJhf.js";
5
+ import { HttpClient } from "./httpclient-C1QPc_R7.js";
6
+ import { createLoggers } from "./logger-C4tIcO3S.js";
7
+ import { convertObject } from "./utils-B1Rjsoi9.js";
8
+ import { getSourceDirname } from "./utils-BDoYg6z6.js";
9
+ import { EggCore, Router as Router$1, utils } from "@eggjs/core";
10
+ import path from "node:path";
11
+ import { performance } from "node:perf_hooks";
12
+ import fs from "node:fs";
13
+ import http from "node:http";
14
+ import inspector from "node:inspector";
15
+ import { AsyncLocalStorage } from "node:async_hooks";
16
+ import createClusterClient, { close } from "cluster-client";
17
+ import { extend } from "@eggjs/extend2";
18
+ import { EggContextLogger, EggLogger, EggLoggers } from "egg-logger";
19
+ import { Cookies } from "@eggjs/cookies";
20
+ import CircularJSON from "circular-json-for-egg";
21
+ import { debuglog } from "node:util";
22
+ import { EventEmitter, captureRejectionSymbol } from "node:events";
23
+ import workerThreads from "node:worker_threads";
24
+ import { sendmessage } from "sendmessage";
25
+
26
+ //#region src/lib/core/messenger/base.ts
27
+ var BaseMessenger = class extends EventEmitter {
28
+ egg;
29
+ constructor(egg) {
30
+ super({ captureRejections: true });
31
+ this.egg = egg;
32
+ }
33
+ [captureRejectionSymbol](err, event, ...args) {
34
+ this.egg.coreLogger.error(new MessageUnhandledRejectionError(err, event, args));
35
+ }
36
+ emit(eventName, ...args) {
37
+ const hasListeners = this.listenerCount(eventName) > 0;
38
+ try {
39
+ return super.emit(eventName, ...args);
40
+ } catch (e) {
41
+ let err = e;
42
+ if (!(err instanceof Error)) err = new Error(String(err));
43
+ this.egg.coreLogger.error(new MessageUnhandledRejectionError(err, eventName, args));
44
+ return hasListeners;
45
+ }
46
+ }
47
+ };
48
+
49
+ //#endregion
50
+ //#region src/lib/core/messenger/local.ts
51
+ const debug$1 = debuglog("egg/lib/core/messenger/local");
52
+ /**
53
+ * Communication between app worker and agent worker with EventEmitter
54
+ */
55
+ var Messenger = class extends BaseMessenger {
56
+ pid;
57
+ constructor(egg) {
58
+ super(egg);
59
+ this.pid = String(process.pid);
60
+ }
61
+ /**
62
+ * Send message to all agent and app
63
+ * @param {String} action - message key
64
+ * @param {Object} data - message value
65
+ * @return {Messenger} this
66
+ */
67
+ broadcast(action, data) {
68
+ debug$1("[%s:%s] broadcast %s with %j", this.egg.type, this.pid, action, data);
69
+ this.send(action, data, "both");
70
+ return this;
71
+ }
72
+ /**
73
+ * send message to the specified process
74
+ * Notice: in single process mode, it only can send to self process,
75
+ * and it will send to both agent and app's messengers.
76
+ * @param {String} workerId - the workerId of the receiver
77
+ * @param {String} action - message key
78
+ * @param {Object} data - message value
79
+ * @return {Messenger} this
80
+ */
81
+ sendTo(workerId, action, data) {
82
+ debug$1("[%s:%s] send %s with %j to %s", this.egg.type, this.pid, action, data, workerId);
83
+ if (String(workerId) !== this.pid) return this;
84
+ this.send(action, data, "both");
85
+ return this;
86
+ }
87
+ /**
88
+ * send message to one worker by random
89
+ * Notice: in single process mode, we only start one agent worker and one app worker
90
+ * - if it's running in agent, it will send to one of app workers
91
+ * - if it's running in app, it will send to agent
92
+ * @param {String} action - message key
93
+ * @param {Object} data - message value
94
+ * @return {Messenger} this
95
+ */
96
+ sendRandom(action, data) {
97
+ debug$1("[%s:%s] send %s with %j to opposite", this.egg.type, this.pid, action, data);
98
+ this.send(action, data, "opposite");
99
+ return this;
100
+ }
101
+ /**
102
+ * send message to app
103
+ * @param {String} action - message key
104
+ * @param {Object} data - message value
105
+ * @return {Messenger} this
106
+ */
107
+ sendToApp(action, data) {
108
+ debug$1("[%s:%s] send %s with %j to all app", this.egg.type, this.pid, action, data);
109
+ this.send(action, data, "application");
110
+ return this;
111
+ }
112
+ /**
113
+ * send message to agent
114
+ * @param {String} action - message key
115
+ * @param {Object} data - message value
116
+ * @return {Messenger} this
117
+ */
118
+ sendToAgent(action, data) {
119
+ debug$1("[%s:%s] send %s with %j to all agent", this.egg.type, this.pid, action, data);
120
+ this.send(action, data, "agent");
121
+ return this;
122
+ }
123
+ /**
124
+ * @param {String} action - message key
125
+ * @param {Object} data - message value
126
+ * @param {String} to - let master know how to send message
127
+ * @return {Messenger} this
128
+ */
129
+ send(action, data, to) {
130
+ process.nextTick(() => {
131
+ const { egg } = this;
132
+ let application;
133
+ let agent;
134
+ let opposite;
135
+ if (egg.type === "application") {
136
+ application = egg;
137
+ agent = egg.agent;
138
+ opposite = agent;
139
+ } else {
140
+ agent = egg;
141
+ application = egg.application;
142
+ opposite = application;
143
+ }
144
+ if (!to) to = egg.type === "application" ? "agent" : "application";
145
+ if (application && application.messenger && (to === "application" || to === "both")) application.messenger.onMessage({
146
+ action,
147
+ data
148
+ });
149
+ if (agent && agent.messenger && (to === "agent" || to === "both")) agent.messenger.onMessage({
150
+ action,
151
+ data
152
+ });
153
+ if (opposite && opposite.messenger && to === "opposite") opposite.messenger.onMessage({
154
+ action,
155
+ data
156
+ });
157
+ });
158
+ return this;
159
+ }
160
+ onMessage(message) {
161
+ if (typeof message?.action === "string") {
162
+ debug$1("[%s:%s] got message %s with %j", this.egg.type, this.pid, message.action, message.data);
163
+ this.emit(message.action, message.data);
164
+ } else debug$1("[%s:%s] got an invalid message %j", this.egg.type, this.pid, message);
165
+ }
166
+ close() {
167
+ this.removeAllListeners();
168
+ }
169
+ };
170
+
171
+ //#endregion
172
+ //#region src/lib/core/messenger/ipc.ts
173
+ const debug = debuglog("egg/lib/core/messenger/ipc");
174
+ /**
175
+ * Communication between app worker and agent worker by IPC channel
176
+ */
177
+ var Messenger$1 = class extends BaseMessenger {
178
+ pid;
179
+ opids = [];
180
+ constructor(egg) {
181
+ super(egg);
182
+ this.pid = String(process.pid);
183
+ this.on("egg-pids", (workerIds) => {
184
+ debug("[%s:%s] got egg-pids %j", this.egg.type, this.pid, workerIds);
185
+ this.opids = workerIds.map((workerId) => String(workerId));
186
+ });
187
+ this.onMessage = this.onMessage.bind(this);
188
+ if (this.egg.options.mode === "all-in-one-process") process.on("sendmessage-to-self", this.onMessage);
189
+ else process.on("message", this.onMessage);
190
+ if (!workerThreads.isMainThread) workerThreads.parentPort.on("message", this.onMessage);
191
+ }
192
+ /**
193
+ * Send message to all agent and app
194
+ * @param {String} action - message key
195
+ * @param {Object} data - message value
196
+ * @return {Messenger} this
197
+ */
198
+ broadcast(action, data) {
199
+ debug("[%s:%s] broadcast %s with %j", this.egg.type, this.pid, action, data);
200
+ this.send(action, data, "app");
201
+ this.send(action, data, "agent");
202
+ return this;
203
+ }
204
+ /**
205
+ * send message to the specified process
206
+ * @param {String} workerId - the workerId of the receiver
207
+ * @param {String} action - message key
208
+ * @param {Object} data - message value
209
+ * @return {Messenger} this
210
+ */
211
+ sendTo(workerId, action, data) {
212
+ debug("[%s:%s] send %s with %j to workerId:%s", this.egg.type, this.pid, action, data, workerId);
213
+ const message = {
214
+ action,
215
+ data,
216
+ receiverPid: String(workerId),
217
+ receiverWorkerId: String(workerId)
218
+ };
219
+ this.#sendMessage(message);
220
+ return this;
221
+ }
222
+ /**
223
+ * send message to one app worker by random
224
+ * - if it's running in agent, it will send to one of app workers
225
+ * - if it's running in app, it will send to agent
226
+ * @param {String} action - message key
227
+ * @param {Object} data - message value
228
+ * @return {Messenger} this
229
+ */
230
+ sendRandom(action, data) {
231
+ if (this.opids.length === 0) {
232
+ debug("[%s:%s] no pids, ignore sendRandom %s with %j", this.egg.type, this.pid, action, data);
233
+ return this;
234
+ }
235
+ const index = Math.floor(Math.random() * this.opids.length);
236
+ const workerId = this.opids[index];
237
+ this.sendTo(workerId, action, data);
238
+ return this;
239
+ }
240
+ /**
241
+ * send message to app
242
+ * @param {String} action - message key
243
+ * @param {Object} data - message value
244
+ * @return {Messenger} this
245
+ */
246
+ sendToApp(action, data) {
247
+ debug("[%s:%s] send %s with %j to all app", this.egg.type, this.pid, action, data);
248
+ this.send(action, data, "app");
249
+ return this;
250
+ }
251
+ /**
252
+ * send message to agent
253
+ * @param {String} action - message key
254
+ * @param {Object} data - message value
255
+ * @return {Messenger} this
256
+ */
257
+ sendToAgent(action, data) {
258
+ debug("[%s:%s] send %s with %j to all agent", this.egg.type, this.pid, action, data);
259
+ this.send(action, data, "agent");
260
+ return this;
261
+ }
262
+ /**
263
+ * @param {String} action - message key
264
+ * @param {Object} data - message value
265
+ * @param {String} to - let master know how to send message
266
+ * @return {Messenger} this
267
+ */
268
+ send(action, data, to) {
269
+ debug("send message %s with %j to %s", action, data, to);
270
+ this.#sendMessage({
271
+ action,
272
+ data,
273
+ to
274
+ });
275
+ return this;
276
+ }
277
+ #sendMessage(message) {
278
+ debug("[%s:%s] send message %j, mode: %s", this.egg.type, this.pid, message, this.egg.options.mode);
279
+ if (this.egg.options.mode === "all-in-one-process") process.emit("sendmessage-to-self", message);
280
+ else sendmessage(process, message);
281
+ }
282
+ onMessage(message) {
283
+ if (typeof message?.action === "string") {
284
+ debug("[%s:%s] got message %s with %j, receiverWorkerId: %s", this.egg.type, this.pid, message.action, message.data, message.receiverWorkerId ?? message.receiverPid);
285
+ this.emit(message.action, message.data);
286
+ } else if (message?.type === "Buffer") debug("[%s:%s] got an invalid message: %s", this.egg.type, this.pid, Buffer.from(message.data));
287
+ else debug("[%s:%s] got an invalid message %j", this.egg.type, this.pid, message);
288
+ }
289
+ close() {
290
+ process.removeListener("message", this.onMessage);
291
+ this.removeAllListeners();
292
+ }
293
+ };
294
+
295
+ //#endregion
296
+ //#region src/lib/core/messenger/index.ts
297
+ /**
298
+ * @class Messenger
299
+ */
300
+ function create(egg) {
301
+ return egg.options.mode === "single" ? new Messenger(egg) : new Messenger$1(egg);
302
+ }
303
+
304
+ //#endregion
305
+ //#region src/lib/egg.ts
306
+ const EGG_PATH = Symbol.for("egg#eggPath");
307
+ /**
308
+ * Based on koa's Application
309
+ * @see https://github.com/eggjs/egg-core
310
+ * @see https://github.com/eggjs/koa/blob/master/src/application.ts
311
+ * @augments EggCore
312
+ */
313
+ var EggApplicationCore = class extends EggCore {
314
+ /**
315
+ * Get the current request context from AsyncLocalStorage.
316
+ * This provides access to the context object for the current request lifecycle.
317
+ * @returns {Context | undefined} The current request context, or undefined if not in a request scope.
318
+ */
319
+ get currentContext() {
320
+ return this.ctxStorage.getStore();
321
+ }
322
+ ContextCookies = Cookies;
323
+ ContextLogger = EggContextLogger;
324
+ ContextHttpClient = ContextHttpClient;
325
+ HttpClient = HttpClient;
326
+ HttpClientNext = HttpClient;
327
+ /**
328
+ * Retrieve base context class
329
+ * @member {BaseContextClass} BaseContextClass
330
+ * @since 1.0.0
331
+ */
332
+ BaseContextClass = BaseContextClass$1;
333
+ /**
334
+ * Retrieve base controller
335
+ * @member {Controller} Controller
336
+ * @since 1.0.0
337
+ */
338
+ Controller = BaseContextClass$1;
339
+ /**
340
+ * Retrieve base service
341
+ * @member {Service} Service
342
+ * @since 1.0.0
343
+ */
344
+ Service = BaseContextClass$1;
345
+ /**
346
+ * Retrieve base subscription
347
+ * @member {Subscription} Subscription
348
+ * @since 2.12.0
349
+ */
350
+ Subscription = BaseContextClass$1;
351
+ /**
352
+ * Retrieve base context class
353
+ * @member {BaseHookClass} BaseHookClass
354
+ */
355
+ BaseHookClass = BaseHookClass;
356
+ /**
357
+ * Retrieve base boot
358
+ * @member {Boot}
359
+ * @alias BaseHookClass
360
+ */
361
+ Boot = BaseHookClass;
362
+ #httpClient;
363
+ #loggers;
364
+ #clusterClients = [];
365
+ messenger;
366
+ agent;
367
+ application;
368
+ /**
369
+ * @class
370
+ * @param {Object} options
371
+ * - {Object} [type] - type of instance, Agent and Application both extend koa, type can determine what it is.
372
+ * - {String} [baseDir] - app root dir, default is `process.cwd()`
373
+ * - {Object} [plugins] - custom plugin config, use it in unittest
374
+ * - {String} [mode] - process mode, can be cluster / single, default is `cluster`
375
+ */
376
+ constructor(options) {
377
+ options = {
378
+ mode: "cluster",
379
+ type: "application",
380
+ baseDir: process.cwd(),
381
+ ...options
382
+ };
383
+ super(options);
384
+ /**
385
+ * messenger instance
386
+ * @member {Messenger}
387
+ * @since 1.0.0
388
+ */
389
+ this.messenger = create(this);
390
+ this.messenger.once("egg-ready", () => {
391
+ this.lifecycle.triggerServerDidReady();
392
+ });
393
+ this.lifecycle.registerBeforeStart(async () => {
394
+ await this.load();
395
+ }, "load files");
396
+ }
397
+ /**
398
+ * @deprecated please use `options` property instead
399
+ */
400
+ get _options() {
401
+ return this.options;
402
+ }
403
+ async loadConfig() {
404
+ await this.loader.loadConfig();
405
+ }
406
+ async load() {
407
+ await this.loadConfig();
408
+ this.ready(() => process.nextTick(() => {
409
+ const dumpStartTime = Date.now();
410
+ this.dumpConfig();
411
+ this.dumpTiming();
412
+ this.coreLogger.info("[egg] dump config after ready, %sms", Date.now() - dumpStartTime);
413
+ }));
414
+ this.#setupTimeoutTimer();
415
+ this.console.info("[egg] App root: %s", this.baseDir);
416
+ this.console.info("[egg] All *.log files save on %j", this.config.logger.dir);
417
+ this.console.info("[egg] Loaded enabled plugin %j", this.loader.orderPlugins);
418
+ this._unhandledRejectionHandler = this._unhandledRejectionHandler.bind(this);
419
+ process.on("unhandledRejection", this._unhandledRejectionHandler);
420
+ this.lifecycle.registerBeforeClose(async () => {
421
+ for (const clusterClient of this.#clusterClients) await close(clusterClient);
422
+ this.#clusterClients = [];
423
+ if (this.type === "application" && this.options.mode === "single") await this.agent.close();
424
+ for (const logger of this.loggers.values()) logger.close();
425
+ this.messenger.close();
426
+ process.removeListener("unhandledRejection", this._unhandledRejectionHandler);
427
+ });
428
+ await this.loader.load();
429
+ }
430
+ /**
431
+ * Usage: new ApiClient({ cluster: app.cluster })
432
+ */
433
+ get cluster() {
434
+ return this.clusterWrapper.bind(this);
435
+ }
436
+ /**
437
+ * Wrap the Client with Leader/Follower Pattern
438
+ *
439
+ * @description almost the same as Agent.cluster API, the only different is that this method create Follower.
440
+ *
441
+ * @see https://github.com/node-modules/cluster-client
442
+ * @param {Function} clientClass - client class function
443
+ * @param {Object} [options]
444
+ * - {Boolean} [autoGenerate] - whether generate delegate rule automatically, default is true
445
+ * - {Function} [formatKey] - a method to transform the subscription info into a string,default is JSON.stringify
446
+ * - {Object} [transcode|JSON.stringify/parse]
447
+ * - {Function} encode - custom serialize method
448
+ * - {Function} decode - custom deserialize method
449
+ * - {Boolean} [isBroadcast] - whether broadcast subscription result to all followers or just one, default is true
450
+ * - {Number} [responseTimeout] - response timeout, default is 3 seconds
451
+ * - {Number} [maxWaitTime|30000] - leader startup max time, default is 30 seconds
452
+ * @return {ClientWrapper} wrapper
453
+ */
454
+ clusterWrapper(clientClass, options) {
455
+ const clientClassOptions = {
456
+ ...this.config.clusterClient,
457
+ ...options,
458
+ singleMode: this.options.mode === "single",
459
+ port: this.options.clusterPort,
460
+ isLeader: this.type === "agent",
461
+ logger: this.coreLogger,
462
+ isCheckHeartbeat: this.config.env === "prod" ? true : inspector.url() === void 0
463
+ };
464
+ const client = createClusterClient(clientClass, clientClassOptions);
465
+ this.#patchClusterClient(client);
466
+ return client;
467
+ }
468
+ /**
469
+ * print the information when console.log(app)
470
+ * @return {Object} inspected app.
471
+ * @since 1.0.0
472
+ * @example
473
+ * ```js
474
+ * console.log(app);
475
+ * =>
476
+ * {
477
+ * name: 'mock-app',
478
+ * env: 'test',
479
+ * subdomainOffset: 2,
480
+ * config: '<egg config>',
481
+ * controller: '<egg controller>',
482
+ * service: '<egg service>',
483
+ * middlewares: '<egg middlewares>',
484
+ * urllib: '<egg urllib>',
485
+ * loggers: '<egg loggers>'
486
+ * }
487
+ * ```
488
+ */
489
+ inspect() {
490
+ const res = { env: this.config.env };
491
+ function delegate(res$1, app, keys) {
492
+ for (const key of keys) if (app[key]) res$1[key] = app[key];
493
+ }
494
+ function abbr(res$1, app, keys) {
495
+ for (const key of keys) if (app[key]) res$1[key] = `<egg ${key}>`;
496
+ }
497
+ delegate(res, this, [
498
+ "name",
499
+ "baseDir",
500
+ "subdomainOffset"
501
+ ]);
502
+ abbr(res, this, [
503
+ "config",
504
+ "controller",
505
+ "httpclient",
506
+ "loggers",
507
+ "middlewares",
508
+ "router",
509
+ "serviceClasses"
510
+ ]);
511
+ return res;
512
+ }
513
+ toJSON() {
514
+ return this.inspect();
515
+ }
516
+ /**
517
+ * http request helper base on {@link httpclient}, it will auto save httpclient log.
518
+ * Keep the same api with `httpclient.request(url, args)`.
519
+ *
520
+ * See https://github.com/node-modules/urllib#api-doc for more details.
521
+ *
522
+ * @param {String} url request url address.
523
+ * @param {Object} options
524
+ * - method {String} - Request method, defaults to GET. Could be GET, POST, DELETE or PUT. Alias 'type'.
525
+ * - data {Object} - Data to be sent. Will be stringify automatically.
526
+ * - dataType {String} - String - Type of response data. Could be `text` or `json`.
527
+ * If it's `text`, the callback data would be a String.
528
+ * If it's `json`, the data of callback would be a parsed JSON Object.
529
+ * Default callback data would be a Buffer.
530
+ * - headers {Object} - Request headers.
531
+ * - timeout {Number} - Request timeout in milliseconds. Defaults to exports.TIMEOUT.
532
+ * Include remote server connecting timeout and response timeout.
533
+ * When timeout happen, will return ConnectionTimeout or ResponseTimeout.
534
+ * - auth {String} - `username:password` used in HTTP Basic Authorization.
535
+ * - followRedirect {Boolean} - follow HTTP 3xx responses as redirects. defaults to false.
536
+ * - gzip {Boolean} - let you get the res object when request connected, default false. alias customResponse
537
+ * - nestedQuerystring {Boolean} - urllib default use querystring to stringify form data which don't
538
+ * support nested object, will use qs instead of querystring to support nested object by set this option to true.
539
+ * - more options see https://github.com/node-modules/urllib
540
+ * @return {Object}
541
+ * - status {Number} - HTTP response status
542
+ * - headers {Object} - HTTP response headers
543
+ * - res {Object} - HTTP response meta
544
+ * - data {Object} - HTTP response body
545
+ *
546
+ * @example
547
+ * ```js
548
+ * const result = await app.curl('http://example.com/foo.json', {
549
+ * method: 'GET',
550
+ * dataType: 'json',
551
+ * });
552
+ * console.log(result.status, result.headers, result.data);
553
+ * ```
554
+ */
555
+ async curl(url, options) {
556
+ return await this.httpClient.request(url, options);
557
+ }
558
+ /**
559
+ * Create a new HttpClient instance with custom options
560
+ * @param {Object} [options] HttpClient init options
561
+ */
562
+ createHttpClient(options) {
563
+ return new this.HttpClient(this, options);
564
+ }
565
+ /**
566
+ * HttpClient instance
567
+ * @see https://github.com/node-modules/urllib
568
+ * @member {HttpClient}
569
+ */
570
+ get httpClient() {
571
+ if (!this.#httpClient) this.#httpClient = this.createHttpClient();
572
+ return this.#httpClient;
573
+ }
574
+ /**
575
+ * @deprecated please use httpClient instead
576
+ * @alias httpClient
577
+ * @member {HttpClient}
578
+ */
579
+ get httpclient() {
580
+ return this.httpClient;
581
+ }
582
+ /**
583
+ * All loggers contain logger, coreLogger and customLogger
584
+ * @member {Object}
585
+ * @since 1.0.0
586
+ */
587
+ get loggers() {
588
+ if (!this.#loggers) this.#loggers = createLoggers(this);
589
+ return this.#loggers;
590
+ }
591
+ /**
592
+ * Get logger by name, it's equal to app.loggers['name'],
593
+ * but you can extend it with your own logical.
594
+ * @param {String} name - logger name
595
+ * @return {Logger} logger
596
+ */
597
+ getLogger(name) {
598
+ return this.loggers[name] || null;
599
+ }
600
+ /**
601
+ * application logger, log file is `$HOME/logs/{appname}/{appname}-web`
602
+ * @member {Logger}
603
+ * @since 1.0.0
604
+ */
605
+ get logger() {
606
+ return this.getLogger("logger");
607
+ }
608
+ /**
609
+ * core logger for framework and plugins, log file is `$HOME/logs/{appname}/egg-web`
610
+ * @member {Logger}
611
+ * @since 1.0.0
612
+ */
613
+ get coreLogger() {
614
+ return this.getLogger("coreLogger");
615
+ }
616
+ _unhandledRejectionHandler(err) {
617
+ this.coreLogger.error("[egg:unhandledRejection] %s", err && err.message || err);
618
+ if (!(err instanceof Error)) {
619
+ const newError = new Error(String(err));
620
+ if (err) {
621
+ if (err.name) newError.name = err.name;
622
+ if (err.message) newError.message = err.message;
623
+ if (err.stack) newError.stack = err.stack;
624
+ }
625
+ err = newError;
626
+ }
627
+ if (err.name === "Error") err.name = "unhandledRejectionError";
628
+ this.coreLogger.error(err);
629
+ }
630
+ /**
631
+ * dump out the config and meta object
632
+ * @private
633
+ */
634
+ dumpConfigToObject() {
635
+ let ignoreList;
636
+ try {
637
+ ignoreList = Array.from(this.config.dump.ignore);
638
+ } catch {
639
+ ignoreList = [];
640
+ }
641
+ const config = extend(true, {}, {
642
+ config: this.config,
643
+ plugins: this.loader.allPlugins,
644
+ appInfo: this.loader.appInfo
645
+ });
646
+ convertObject(config, ignoreList);
647
+ return {
648
+ config,
649
+ meta: this.loader.configMeta
650
+ };
651
+ }
652
+ /**
653
+ * save app.config to `run/${type}_config.json`
654
+ * @private
655
+ */
656
+ dumpConfig() {
657
+ const rundir = this.config.rundir;
658
+ try {
659
+ if (!fs.existsSync(rundir)) fs.mkdirSync(rundir);
660
+ const { config, meta } = this.dumpConfigToObject();
661
+ const dumpFile = path.join(rundir, `${this.type}_config.json`);
662
+ fs.writeFileSync(dumpFile, CircularJSON.stringify(config, null, 2));
663
+ const dumpMetaFile = path.join(rundir, `${this.type}_config_meta.json`);
664
+ fs.writeFileSync(dumpMetaFile, CircularJSON.stringify(meta, null, 2));
665
+ } catch (err) {
666
+ this.coreLogger.warn(`[egg] dumpConfig error: ${err.message}`);
667
+ }
668
+ }
669
+ dumpTiming() {
670
+ try {
671
+ const items = this.timing.toJSON();
672
+ const rundir = this.config.rundir;
673
+ const dumpFile = path.join(rundir, `${this.type}_timing_${process.pid}.json`);
674
+ fs.writeFileSync(dumpFile, CircularJSON.stringify(items, null, 2));
675
+ this.coreLogger.info(this.timing.toString());
676
+ this.timing.disable();
677
+ for (const item of items) if (item.index > 0 && item.duration && item.duration >= this.config.dump.timing.slowBootActionMinDuration) this.coreLogger.warn("[egg][dumpTiming][slow-boot-action] #%d %dms, name: %s", item.index, item.duration, item.name);
678
+ } catch (err) {
679
+ this.coreLogger.warn(`[egg] dumpTiming error: ${err.message}`);
680
+ }
681
+ }
682
+ get [EGG_PATH]() {
683
+ return getSourceDirname();
684
+ }
685
+ #setupTimeoutTimer() {
686
+ const startTimeoutTimer = setTimeout(() => {
687
+ this.coreLogger.error(this.timing.toString());
688
+ this.coreLogger.error(`${this.type} still doesn't ready after ${this.config.workerStartTimeout} ms.`);
689
+ const items = this.timing.toJSON();
690
+ for (const item of items) {
691
+ if (item.end) continue;
692
+ this.coreLogger.error(`unfinished timing item: ${CircularJSON.stringify(item)}`);
693
+ }
694
+ this.coreLogger.error("[egg][setupTimeoutTimer] check run/%s_timing_%s.json for more details.", this.type, process.pid);
695
+ this.emit("startTimeout");
696
+ this.dumpConfig();
697
+ this.dumpTiming();
698
+ }, this.config.workerStartTimeout);
699
+ this.ready(() => clearTimeout(startTimeoutTimer));
700
+ }
701
+ get config() {
702
+ return super.config;
703
+ }
704
+ /**
705
+ * app.env delegate app.config.env
706
+ * @deprecated
707
+ */
708
+ get env() {
709
+ this.deprecate("please use app.config.env instead");
710
+ return this.config.env;
711
+ }
712
+ set env(_) {}
713
+ /**
714
+ * app.proxy delegate app.config.proxy
715
+ * @deprecated
716
+ */
717
+ get proxy() {
718
+ return this.config.proxy;
719
+ }
720
+ set proxy(_) {}
721
+ #patchClusterClient(client) {
722
+ const rawCreate = client.create;
723
+ client.create = (...args) => {
724
+ const realClient = rawCreate.apply(client, args);
725
+ this.#clusterClients.push(realClient);
726
+ return realClient;
727
+ };
728
+ }
729
+ /**
730
+ * Create an anonymous context, the context isn't request level, so the request is mocked.
731
+ * then you can use context level API like `ctx.service`
732
+ * @member {String} EggApplication#createAnonymousContext
733
+ * @param {Request} [req] - if you want to mock request like querystring, you can pass an object to this function.
734
+ * @return {Context} context
735
+ */
736
+ createAnonymousContext(req) {
737
+ const request = {
738
+ headers: {
739
+ host: "127.0.0.1",
740
+ "x-forwarded-for": "127.0.0.1"
741
+ },
742
+ query: {},
743
+ querystring: "",
744
+ host: "127.0.0.1",
745
+ hostname: "127.0.0.1",
746
+ protocol: "http",
747
+ secure: "false",
748
+ method: "GET",
749
+ url: "/",
750
+ path: "/",
751
+ socket: {
752
+ remoteAddress: "127.0.0.1",
753
+ remotePort: 7001
754
+ }
755
+ };
756
+ if (req) for (const key in req) if (key === "headers" || key === "query" || key === "socket") Object.assign(request[key], req[key]);
757
+ else request[key] = req[key];
758
+ const response = new http.ServerResponse(request);
759
+ return this.createContext(request, response);
760
+ }
761
+ /**
762
+ * Run async function in the anonymous context scope
763
+ * @see Context#runInAnonymousContextScope
764
+ * @param {Function} scope - the first args is an anonymous ctx, scope should be async function
765
+ * @param {Request} [req] - if you want to mock request like querystring, you can pass an object to this function.
766
+ */
767
+ async runInAnonymousContextScope(scope, req) {
768
+ const ctx = this.createAnonymousContext(req);
769
+ if (!scope.name) Reflect.set(scope, "_name", utils.getCalleeFromStack(true));
770
+ return await this.ctxStorage.run(ctx, async () => {
771
+ return await scope(ctx);
772
+ });
773
+ }
774
+ /**
775
+ * Create egg context
776
+ * @function EggApplication#createContext
777
+ * @param {Req} req - node native Request object
778
+ * @param {Res} res - node native Response object
779
+ * @return {Context} context object
780
+ */
781
+ createContext(req, res) {
782
+ const context = Object.create(this.context);
783
+ const request = context.request = Object.create(this.request);
784
+ const response = context.response = Object.create(this.response);
785
+ context.app = request.app = response.app = this;
786
+ context.req = request.req = response.req = req;
787
+ context.res = request.res = response.res = res;
788
+ request.ctx = response.ctx = context;
789
+ request.response = response;
790
+ response.request = request;
791
+ context.onerror = context.onerror.bind(context);
792
+ context.originalUrl = request.originalUrl = req.url;
793
+ context.starttime = Date.now();
794
+ context.performanceStarttime = performance.now();
795
+ return context;
796
+ }
797
+ };
798
+
799
+ //#endregion
800
+ export { BaseMessenger, EggApplicationCore, Messenger$1 as Messenger, Messenger as Messenger$1, Router$1 as Router, create };