egg 4.1.2-beta.1 → 4.1.2-beta.10

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.d.ts CHANGED
@@ -14,11 +14,37 @@ import { BaseContextClass } from "./lib/core/base_context_class.js";
14
14
  import Helper from "./app/extend/helper.js";
15
15
  import { SingleModeAgent, SingleModeApplication, StartEggOptions, startEgg } from "./lib/start.js";
16
16
  import { EggConfigFactory, EggPluginFactory, EggPluginMeta, EggPluginOptions, PartialEggConfig, defineConfig, defineConfigFactory, definePluginFactory } from "./lib/define.js";
17
+ import { buildSnapshot, restoreSnapshot } from "./lib/snapshot.js";
17
18
  import { CookieLimitExceedError } from "./lib/error/CookieLimitExceedError.js";
18
19
  import { MessageUnhandledRejectionError } from "./lib/error/MessageUnhandledRejectionError.js";
20
+ import { CreateTransparentProxyOptions, createTransparentProxy } from "./lib/core/utils.js";
19
21
  import { BaseHookClass } from "./lib/core/base_hook_class.js";
20
22
  import { Singleton, SingletonCreateMethod, SingletonOptions } from "@eggjs/core";
21
23
  import { EggLogger, EggLogger as Logger, LoggerLevel } from "egg-logger";
22
24
  import { AccessLevel, Acl, BackgroundTaskHelper, ContextEventBus, ContextProto, Cookies, EggObjectFactory, EggObjectLifecycle, EggProtoImplClass, EggQualifier, EggType, Event, EventBus, EventContext, Events, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Host, ImplDecorator, IncomingHttpHeaders, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, MetadataUtil, Middleware, MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, ObjectInfo, ObjectInitType, ObjectInitTypeLike, QualifierImplDecoratorUtil, QualifierUtil, SingletonProto } from "@eggjs/tegg";
23
25
  export * from "@eggjs/cluster";
24
- export { AccessLevel, Acl, Agent, AgentWorkerLoader, AppWorkerLoader, Application, BackgroundTaskHelper, BaseContextClass, BaseHookClass as Boot, ClientErrorResponse, type EggAppConfig as Config, Context, type ContextEventBus, ContextHttpClient, ContextProto, BaseContextClass as Controller, BaseContextClass as Service, BaseContextClass as Subscription, CookieLimitExceedError, Cookies, CustomLoaderConfig, EggAppConfig, EggAppInfo, EggApplicationCore, EggApplicationCoreOptions, EggConfigFactory, EggContext, HttpClient as EggContextHttpClient, HttpClient as EggHttpClient, HttpClient, EggEnvType, type EggLogger, EggLoggerConfig, type EggObjectFactory, type EggObjectLifecycle, EggPlugin, EggPluginFactory, EggPluginItem, EggPluginMeta, EggPluginOptions, type EggProtoImplClass, EggQualifier, EggType, Event, type EventBus, EventContext, type Events, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Helper, type Helper as IHelper, Host, HttpClientConfig, HttpClientOptions, HttpClientRequestOptions, HttpClientRequestURL, HttpClientResponse, ILifecycleBoot as IBoot, ILifecycleBoot, IController, IEggPluginItem, IService, type ImplDecorator, type IncomingHttpHeaders, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, type Logger, type LoggerLevel, MessageUnhandledRejectionError, MetadataUtil, Middleware, MiddlewareFunc, MultiInstanceProto, type MultiInstancePrototypeGetObjectsContext, Next, type ObjectInfo, ObjectInitType, type ObjectInitTypeLike, PartialDeep, PartialEggConfig, PowerPartial, QualifierImplDecoratorUtil, QualifierUtil, Request, RequestObjectBody, Response, Router, type SingleModeAgent, type SingleModeApplication, Singleton, type SingletonCreateMethod, type SingletonOptions, SingletonProto, StartEggOptions, defineConfig, defineConfigFactory, definePluginFactory, startEgg as start, startEgg };
26
+
27
+ //#region src/index.d.ts
28
+ declare const Config: typeof EggAppConfig;
29
+ /**
30
+ * Egg Application Config, can be injected into Proto, e.g. SingletonProto/ContextProto/HttpController.
31
+ *
32
+ * Usage:
33
+ * ```ts
34
+ * import { Inject, Config } from 'egg';
35
+ *
36
+ * @SingletonProto()
37
+ * class FooService {
38
+ * @Inject()
39
+ * config: Config;
40
+ *
41
+ * async bar() {
42
+ * console.log(this.config.env);
43
+ * }
44
+ * }
45
+ * ```
46
+ * @since 4.1.0
47
+ */
48
+ type Config = EggAppConfig;
49
+ //#endregion
50
+ export { AccessLevel, Acl, Agent, AgentWorkerLoader, AppWorkerLoader, Application, BackgroundTaskHelper, BaseContextClass, BaseHookClass as Boot, ClientErrorResponse, Config, Context, type ContextEventBus, ContextHttpClient, ContextProto, BaseContextClass as Controller, BaseContextClass as Service, BaseContextClass as Subscription, CookieLimitExceedError, Cookies, type CreateTransparentProxyOptions, CustomLoaderConfig, EggAppConfig, EggAppInfo, EggApplicationCore, EggApplicationCoreOptions, EggConfigFactory, EggContext, HttpClient as EggContextHttpClient, HttpClient as EggHttpClient, HttpClient, EggEnvType, type EggLogger, EggLoggerConfig, type EggObjectFactory, type EggObjectLifecycle, EggPlugin, EggPluginFactory, EggPluginItem, EggPluginMeta, EggPluginOptions, type EggProtoImplClass, EggQualifier, EggType, Event, type EventBus, EventContext, type Events, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Helper, type Helper as IHelper, Host, HttpClientConfig, HttpClientOptions, HttpClientRequestOptions, HttpClientRequestURL, HttpClientResponse, ILifecycleBoot as IBoot, ILifecycleBoot, IController, IEggPluginItem, IService, type ImplDecorator, type IncomingHttpHeaders, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, Logger, type LoggerLevel, MessageUnhandledRejectionError, MetadataUtil, Middleware, MiddlewareFunc, MultiInstanceProto, type MultiInstancePrototypeGetObjectsContext, Next, type ObjectInfo, ObjectInitType, type ObjectInitTypeLike, PartialDeep, PartialEggConfig, PowerPartial, QualifierImplDecoratorUtil, QualifierUtil, Request, RequestObjectBody, Response, Router, type SingleModeAgent, type SingleModeApplication, Singleton, type SingletonCreateMethod, type SingletonOptions, SingletonProto, StartEggOptions, buildSnapshot, createTransparentProxy, defineConfig, defineConfigFactory, definePluginFactory, restoreSnapshot, startEgg as start, startEgg };
package/dist/index.js CHANGED
@@ -8,18 +8,25 @@ import { ContextHttpClient } from "./lib/core/context_httpclient.js";
8
8
  import { HttpClient } from "./lib/core/httpclient.js";
9
9
  import { CookieLimitExceedError } from "./lib/error/CookieLimitExceedError.js";
10
10
  import { MessageUnhandledRejectionError } from "./lib/error/MessageUnhandledRejectionError.js";
11
+ import { createTransparentProxy } from "./lib/core/utils.js";
11
12
  import { EggApplicationCore, Router } from "./lib/egg.js";
12
13
  import { AppWorkerLoader } from "./lib/loader/AppWorkerLoader.js";
13
14
  import { AgentWorkerLoader } from "./lib/loader/AgentWorkerLoader.js";
14
15
  import "./lib/loader/index.js";
15
16
  import { Agent } from "./lib/agent.js";
16
17
  import { Application } from "./lib/application.js";
17
- import "./lib/types.js";
18
+ import { EggAppConfig } from "./lib/types.js";
18
19
  import { startEgg } from "./lib/start.js";
19
20
  import { defineConfig, defineConfigFactory, definePluginFactory } from "./lib/define.js";
21
+ import { buildSnapshot, restoreSnapshot } from "./lib/snapshot.js";
20
22
  import { Singleton } from "@eggjs/core";
23
+ import { EggLogger as Logger } from "egg-logger";
21
24
  import { AccessLevel, Acl, BackgroundTaskHelper, ContextProto, Cookies, EggQualifier, EggType, Event, EventContext, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Host, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, MetadataUtil, Middleware, MultiInstanceProto, ObjectInitType, QualifierImplDecoratorUtil, QualifierUtil, SingletonProto } from "@eggjs/tegg";
22
25
 
23
26
  export * from "@eggjs/cluster"
24
27
 
25
- export { AccessLevel, Acl, Agent, AgentWorkerLoader, AppWorkerLoader, Application, BackgroundTaskHelper, BaseContextClass, BaseHookClass as Boot, Context, ContextHttpClient, ContextProto, BaseContextClass as Controller, BaseContextClass as Service, BaseContextClass as Subscription, CookieLimitExceedError, Cookies, EggApplicationCore, EggQualifier, EggType, Event, EventContext, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Helper, Host, HttpClient, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, MessageUnhandledRejectionError, MetadataUtil, Middleware, MultiInstanceProto, ObjectInitType, QualifierImplDecoratorUtil, QualifierUtil, Request, Response, Router, Singleton, SingletonProto, defineConfig, defineConfigFactory, definePluginFactory, startEgg as start, startEgg };
28
+ //#region src/index.ts
29
+ const Config = EggAppConfig;
30
+
31
+ //#endregion
32
+ export { AccessLevel, Acl, Agent, AgentWorkerLoader, AppWorkerLoader, Application, BackgroundTaskHelper, BaseContextClass, BaseHookClass as Boot, Config, Context, ContextHttpClient, ContextProto, BaseContextClass as Controller, BaseContextClass as Service, BaseContextClass as Subscription, CookieLimitExceedError, Cookies, EggAppConfig, EggApplicationCore, EggQualifier, EggType, Event, EventContext, HTTPBody, HTTPContext, HTTPController, HTTPCookies, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPParamType, HTTPQueries, HTTPQuery, HTTPRequest, Helper, Host, HttpClient, Inject, InjectOptional, LifecycleDestroy, LifecycleInit, LifecyclePostConstruct, LifecyclePostInject, LifecyclePreDestroy, LifecyclePreInject, LifecyclePreLoad, Logger, MessageUnhandledRejectionError, MetadataUtil, Middleware, MultiInstanceProto, ObjectInitType, QualifierImplDecoratorUtil, QualifierUtil, Request, Response, Router, Singleton, SingletonProto, buildSnapshot, createTransparentProxy, defineConfig, defineConfigFactory, definePluginFactory, restoreSnapshot, startEgg as start, startEgg };
@@ -15,6 +15,12 @@ declare class Agent extends EggApplicationCore {
15
15
  * @param {Object} options - see {@link EggApplicationCore}
16
16
  */
17
17
  constructor(options?: Omit<EggApplicationCoreOptions, "type">);
18
+ /**
19
+ * Start the keepalive timer that prevents the agent process from exiting
20
+ * when it has no pending I/O. Called from configDidLoad so that the timer
21
+ * is not created during snapshot build (configDidLoad is skipped in snapshot mode).
22
+ */
23
+ protected startKeepAlive(): void;
18
24
  protected customEggLoader(): typeof AgentWorkerLoader;
19
25
  _wrapMessenger(): void;
20
26
  close(): Promise<void>;
package/dist/lib/agent.js CHANGED
@@ -18,6 +18,15 @@ var Agent = class extends EggApplicationCore {
18
18
  ...options,
19
19
  type: "agent"
20
20
  });
21
+ this.lifecycle.addBootHook({ configDidLoad: () => this.startKeepAlive() });
22
+ }
23
+ /**
24
+ * Start the keepalive timer that prevents the agent process from exiting
25
+ * when it has no pending I/O. Called from configDidLoad so that the timer
26
+ * is not created during snapshot build (configDidLoad is skipped in snapshot mode).
27
+ */
28
+ startKeepAlive() {
29
+ if (this.#agentAliveHandler) return;
21
30
  this.#agentAliveHandler = setInterval(() => {
22
31
  this.coreLogger.info("[]");
23
32
  }, 1440 * 60 * 1e3);
@@ -46,7 +55,10 @@ var Agent = class extends EggApplicationCore {
46
55
  }
47
56
  }
48
57
  async close() {
49
- clearInterval(this.#agentAliveHandler);
58
+ if (this.#agentAliveHandler) {
59
+ clearInterval(this.#agentAliveHandler);
60
+ this.#agentAliveHandler = void 0;
61
+ }
50
62
  await super.close();
51
63
  }
52
64
  };
@@ -17,6 +17,10 @@ var HttpClient = class extends HttpClient$1 {
17
17
  };
18
18
  super(initOptions);
19
19
  this.#app = app;
20
+ if (config.interceptors?.length) {
21
+ const originalDispatcher = this.getDispatcher();
22
+ this.setDispatcher(originalDispatcher.compose(...config.interceptors));
23
+ }
20
24
  }
21
25
  async request(url, options) {
22
26
  options = options ?? {};
@@ -1,5 +1,30 @@
1
1
  //#region src/lib/core/utils.d.ts
2
2
  declare function convertObject(obj: any, ignore?: string | RegExp | (string | RegExp)[]): any;
3
3
  declare function safeParseURL(url: string): URL | null;
4
+ interface CreateTransparentProxyOptions<T> {
5
+ /**
6
+ * Factory function to lazily create the real object.
7
+ * Called at most once, on first property access.
8
+ */
9
+ createReal: () => T;
10
+ /**
11
+ * Whether to bind functions from the real object to the real instance.
12
+ * Defaults to true.
13
+ */
14
+ bindFunctions?: boolean;
15
+ }
16
+ /**
17
+ * Create a Proxy that behaves like the real object, but remains transparent to
18
+ * monkeypatch libraries (e.g. defineProperty-based overrides like egg-mock's `mm()`).
19
+ *
20
+ * - Lazily creates the real object on first access.
21
+ * - Allows overriding properties on the proxy target (overlay) — e.g. via `Object.defineProperty`.
22
+ * - Delegates everything else to the real object.
23
+ *
24
+ * This is used to defer HttpClient construction so plugins can modify
25
+ * `config.httpclient.lookup` after the first access to `app.httpClient` but
26
+ * before any actual HTTP request is made.
27
+ */
28
+ declare function createTransparentProxy<T extends object>(options: CreateTransparentProxyOptions<T>): T;
4
29
  //#endregion
5
- export { convertObject, safeParseURL };
30
+ export { CreateTransparentProxyOptions, convertObject, createTransparentProxy, safeParseURL };
@@ -43,6 +43,76 @@ function safeParseURL(url) {
43
43
  return null;
44
44
  }
45
45
  }
46
+ /**
47
+ * Create a Proxy that behaves like the real object, but remains transparent to
48
+ * monkeypatch libraries (e.g. defineProperty-based overrides like egg-mock's `mm()`).
49
+ *
50
+ * - Lazily creates the real object on first access.
51
+ * - Allows overriding properties on the proxy target (overlay) — e.g. via `Object.defineProperty`.
52
+ * - Delegates everything else to the real object.
53
+ *
54
+ * This is used to defer HttpClient construction so plugins can modify
55
+ * `config.httpclient.lookup` after the first access to `app.httpClient` but
56
+ * before any actual HTTP request is made.
57
+ */
58
+ function createTransparentProxy(options) {
59
+ const { createReal, bindFunctions = true } = options;
60
+ if (typeof createReal !== "function") throw new TypeError("createReal must be a function");
61
+ let real;
62
+ let cachedError;
63
+ const boundFnCache = /* @__PURE__ */ new WeakMap();
64
+ const getReal = () => {
65
+ if (real) return real;
66
+ if (cachedError) throw cachedError;
67
+ try {
68
+ return real = createReal();
69
+ } catch (err) {
70
+ cachedError = err;
71
+ throw err;
72
+ }
73
+ };
74
+ const hasOwn = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== void 0;
75
+ return new Proxy({}, {
76
+ get(target, prop, receiver) {
77
+ const r = getReal();
78
+ if (hasOwn(target, prop)) return Reflect.get(target, prop, receiver);
79
+ const value = Reflect.get(r, prop);
80
+ if (bindFunctions && typeof value === "function") {
81
+ let bound = boundFnCache.get(value);
82
+ if (!bound) {
83
+ bound = value.bind(r);
84
+ boundFnCache.set(value, bound);
85
+ }
86
+ return bound;
87
+ }
88
+ return value;
89
+ },
90
+ set(target, prop, value) {
91
+ const r = getReal();
92
+ if (hasOwn(target, prop)) return Reflect.set(target, prop, value);
93
+ return Reflect.set(r, prop, value);
94
+ },
95
+ has(target, prop) {
96
+ return Reflect.has(target, prop) || Reflect.has(getReal(), prop);
97
+ },
98
+ ownKeys(target) {
99
+ return [...new Set([...Reflect.ownKeys(getReal()), ...Reflect.ownKeys(target)])];
100
+ },
101
+ getOwnPropertyDescriptor(target, prop) {
102
+ return Reflect.getOwnPropertyDescriptor(target, prop) ?? Reflect.getOwnPropertyDescriptor(getReal(), prop);
103
+ },
104
+ deleteProperty(target, prop) {
105
+ if (hasOwn(target, prop)) return Reflect.deleteProperty(target, prop);
106
+ return Reflect.deleteProperty(getReal(), prop);
107
+ },
108
+ getPrototypeOf() {
109
+ return Reflect.getPrototypeOf(getReal());
110
+ },
111
+ defineProperty(target, prop, descriptor) {
112
+ return Reflect.defineProperty(target, prop, descriptor);
113
+ }
114
+ });
115
+ }
46
116
 
47
117
  //#endregion
48
- export { convertObject, safeParseURL };
118
+ export { convertObject, createTransparentProxy, safeParseURL };
package/dist/lib/egg.d.ts CHANGED
@@ -82,6 +82,12 @@ declare class EggApplicationCore extends EggCore {
82
82
  */
83
83
  Boot: typeof BaseHookClass;
84
84
  options: Required<EggApplicationCoreOptions>;
85
+ /**
86
+ * Promise that resolves when the `load()` method has finished.
87
+ * This is useful for callers that need to ensure config and metadata
88
+ * are fully loaded before proceeding.
89
+ */
90
+ readonly loadFinished: Promise<void>;
85
91
  readonly messenger: IMessenger;
86
92
  agent?: Agent;
87
93
  application?: Application;
@@ -194,6 +200,13 @@ declare class EggApplicationCore extends EggCore {
194
200
  createHttpClient(options?: HttpClientOptions): HttpClient;
195
201
  /**
196
202
  * HttpClient instance
203
+ *
204
+ * Returns a transparent proxy that defers actual HttpClient construction
205
+ * until a method/property is first accessed. This allows plugins to modify
206
+ * `config.httpclient.lookup` or other options during lifecycle hooks
207
+ * (e.g. `configWillLoad`, `didLoad`) even after `app.httpClient` is
208
+ * first referenced.
209
+ *
197
210
  * @see https://github.com/node-modules/urllib
198
211
  * @member {HttpClient}
199
212
  */
@@ -231,6 +244,19 @@ declare class EggApplicationCore extends EggCore {
231
244
  get coreLogger(): EggLogger;
232
245
  _unhandledRejectionHandler(err: any): void;
233
246
  /**
247
+ * Clean up non-serializable resources before V8 heap serialization.
248
+ * Closes messenger (IPC listeners), loggers (file descriptors),
249
+ * and removes the process-level unhandledRejection listener.
250
+ */
251
+ protected snapshotWillSerialize(): void;
252
+ /**
253
+ * Restore non-serializable resources after V8 heap deserialization.
254
+ * Recreates messenger, re-registers the egg-ready listener,
255
+ * and re-attaches the process-level unhandledRejection listener.
256
+ * Loggers are lazily re-created via the `loggers` getter.
257
+ */
258
+ protected snapshotDidDeserialize(): void;
259
+ /**
234
260
  * dump out the config and meta object
235
261
  * @private
236
262
  */
@@ -244,6 +270,11 @@ declare class EggApplicationCore extends EggCore {
244
270
  */
245
271
  dumpConfig(): void;
246
272
  dumpTiming(): void;
273
+ /**
274
+ * Generate and save startup manifest for faster subsequent startups.
275
+ * Only generates when no valid manifest was loaded (avoids overwriting during manifest-accelerated starts).
276
+ */
277
+ dumpManifest(): void;
247
278
  protected customEggPaths(): string[];
248
279
  get config(): EggAppConfig$1;
249
280
  /**
package/dist/lib/egg.js CHANGED
@@ -7,9 +7,9 @@ import { ContextHttpClient } from "./core/context_httpclient.js";
7
7
  import { HttpClient } from "./core/httpclient.js";
8
8
  import { createLoggers } from "./core/logger.js";
9
9
  import { create } from "./core/messenger/index.js";
10
- import { convertObject } from "./core/utils.js";
10
+ import { convertObject, createTransparentProxy } from "./core/utils.js";
11
11
  import assert from "node:assert";
12
- import { EggCore, Router, utils } from "@eggjs/core";
12
+ import { EggCore, ManifestStore, Router, utils } from "@eggjs/core";
13
13
  import path from "node:path";
14
14
  import fs from "node:fs";
15
15
  import http from "node:http";
@@ -79,7 +79,16 @@ var EggApplicationCore = class extends EggCore {
79
79
  Boot = BaseHookClass;
80
80
  #httpClient;
81
81
  #loggers;
82
+ #startTimeoutTimer;
82
83
  #clusterClients = [];
84
+ #loadFinishedResolve;
85
+ #loadFinishedReject;
86
+ /**
87
+ * Promise that resolves when the `load()` method has finished.
88
+ * This is useful for callers that need to ensure config and metadata
89
+ * are fully loaded before proceeding.
90
+ */
91
+ loadFinished;
83
92
  messenger;
84
93
  agent;
85
94
  application;
@@ -99,6 +108,10 @@ var EggApplicationCore = class extends EggCore {
99
108
  ...options
100
109
  };
101
110
  super(options);
111
+ this.loadFinished = new Promise((resolve, reject) => {
112
+ this.#loadFinishedResolve = resolve;
113
+ this.#loadFinishedReject = reject;
114
+ });
102
115
  /**
103
116
  * messenger instance
104
117
  * @member {Messenger}
@@ -108,8 +121,18 @@ var EggApplicationCore = class extends EggCore {
108
121
  this.messenger.once("egg-ready", () => {
109
122
  this.lifecycle.triggerServerDidReady();
110
123
  });
124
+ this.lifecycle.addBootHook({
125
+ snapshotWillSerialize: () => this.snapshotWillSerialize(),
126
+ snapshotDidDeserialize: () => this.snapshotDidDeserialize()
127
+ });
111
128
  this.lifecycle.registerBeforeStart(async () => {
112
- await this.load();
129
+ try {
130
+ await this.load();
131
+ this.#loadFinishedResolve();
132
+ } catch (err) {
133
+ this.#loadFinishedReject(err);
134
+ throw err;
135
+ }
113
136
  }, "load files");
114
137
  }
115
138
  /**
@@ -123,27 +146,38 @@ var EggApplicationCore = class extends EggCore {
123
146
  }
124
147
  async load() {
125
148
  await this.loadConfig();
126
- this.ready(() => process.nextTick(() => {
127
- const dumpStartTime = Date.now();
128
- this.dumpConfig();
129
- this.dumpTiming();
130
- this.coreLogger.info("[egg] dump config after ready, %sms", Date.now() - dumpStartTime);
131
- }));
132
- this.#setupTimeoutTimer();
149
+ if (!this.options.snapshot) {
150
+ this.ready(() => process.nextTick(() => {
151
+ const dumpStartTime = Date.now();
152
+ this.dumpConfig();
153
+ this.dumpTiming();
154
+ this.dumpManifest();
155
+ ManifestStore.flushCompileCache();
156
+ this.coreLogger.info("[egg] dump config after ready, %sms", Date.now() - dumpStartTime);
157
+ }));
158
+ this.#setupTimeoutTimer();
159
+ }
133
160
  this.console.info("[egg] App root: %s", this.baseDir);
134
161
  this.console.info("[egg] All *.log files save on %j", this.config.logger.dir);
135
162
  assert(this.config.logger.dir, "logger.dir is required");
136
163
  this.console.info("[egg] Loaded enabled plugin %j", this.loader.orderPlugins);
137
164
  this._unhandledRejectionHandler = this._unhandledRejectionHandler.bind(this);
138
165
  process.on("unhandledRejection", this._unhandledRejectionHandler);
139
- this.lifecycle.registerBeforeClose(async () => {
166
+ if (!this.lifecycle.registerBeforeClose(async () => {
140
167
  for (const clusterClient of this.#clusterClients) await close(clusterClient);
141
168
  this.#clusterClients = [];
142
- if (this.type === "application" && this.options.mode === "single") await this.agent.close();
169
+ if (this.type === "application" && this.options.mode === "single") await this.agent?.close();
170
+ ManifestStore.flushCompileCache();
143
171
  for (const logger of this.loggers.values()) logger.close();
144
172
  this.messenger.close();
145
173
  process.removeListener("unhandledRejection", this._unhandledRejectionHandler);
146
- });
174
+ })) {
175
+ this.#clearStartTimeoutTimer();
176
+ process.removeListener("unhandledRejection", this._unhandledRejectionHandler);
177
+ this.messenger.close();
178
+ if (this.#loggers) for (const logger of this.#loggers.values()) logger.close();
179
+ return;
180
+ }
147
181
  await this.loader.load();
148
182
  }
149
183
  /**
@@ -282,11 +316,18 @@ var EggApplicationCore = class extends EggCore {
282
316
  }
283
317
  /**
284
318
  * HttpClient instance
319
+ *
320
+ * Returns a transparent proxy that defers actual HttpClient construction
321
+ * until a method/property is first accessed. This allows plugins to modify
322
+ * `config.httpclient.lookup` or other options during lifecycle hooks
323
+ * (e.g. `configWillLoad`, `didLoad`) even after `app.httpClient` is
324
+ * first referenced.
325
+ *
285
326
  * @see https://github.com/node-modules/urllib
286
327
  * @member {HttpClient}
287
328
  */
288
329
  get httpClient() {
289
- if (!this.#httpClient) this.#httpClient = this.createHttpClient();
330
+ if (!this.#httpClient) this.#httpClient = createTransparentProxy({ createReal: () => this.createHttpClient() });
290
331
  return this.#httpClient;
291
332
  }
292
333
  /**
@@ -346,6 +387,32 @@ var EggApplicationCore = class extends EggCore {
346
387
  this.coreLogger.error(err);
347
388
  }
348
389
  /**
390
+ * Clean up non-serializable resources before V8 heap serialization.
391
+ * Closes messenger (IPC listeners), loggers (file descriptors),
392
+ * and removes the process-level unhandledRejection listener.
393
+ */
394
+ snapshotWillSerialize() {
395
+ this.messenger.close();
396
+ if (this.#loggers) {
397
+ for (const logger of this.#loggers.values()) logger.close();
398
+ this.#loggers = void 0;
399
+ }
400
+ process.removeListener("unhandledRejection", this._unhandledRejectionHandler);
401
+ }
402
+ /**
403
+ * Restore non-serializable resources after V8 heap deserialization.
404
+ * Recreates messenger, re-registers the egg-ready listener,
405
+ * and re-attaches the process-level unhandledRejection listener.
406
+ * Loggers are lazily re-created via the `loggers` getter.
407
+ */
408
+ snapshotDidDeserialize() {
409
+ this.messenger = create(this);
410
+ this.messenger.once("egg-ready", () => {
411
+ this.lifecycle.triggerServerDidReady();
412
+ });
413
+ process.on("unhandledRejection", this._unhandledRejectionHandler);
414
+ }
415
+ /**
349
416
  * dump out the config and meta object
350
417
  * @private
351
418
  */
@@ -397,11 +464,39 @@ var EggApplicationCore = class extends EggCore {
397
464
  this.coreLogger.warn(`[egg] dumpTiming error: ${err.message}`);
398
465
  }
399
466
  }
467
+ /**
468
+ * Generate and save startup manifest for faster subsequent startups.
469
+ * Only generates when no valid manifest was loaded (avoids overwriting during manifest-accelerated starts).
470
+ */
471
+ dumpManifest() {
472
+ try {
473
+ if (this.loader.serverEnv === "local" && process.env.EGG_MANIFEST !== "true") return;
474
+ if (this.loader.manifest.data.generatedAt) return;
475
+ const manifest = this.loader.generateManifest();
476
+ ManifestStore.enableCompileCache(this.baseDir);
477
+ ManifestStore.write(this.baseDir, manifest).catch((err) => {
478
+ this.coreLogger.warn("[egg] dumpManifest write error: %s", err.message);
479
+ });
480
+ } catch (err) {
481
+ this.coreLogger.warn("[egg] dumpManifest error: %s", err.message);
482
+ }
483
+ }
400
484
  customEggPaths() {
485
+ const bundleStore = ManifestStore.getBundleStore();
486
+ if (bundleStore && path.resolve(bundleStore.baseDir) === path.resolve(this.baseDir)) {
487
+ const bundledFrameworkDir = path.join(bundleStore.baseDir, "node_modules", "egg", "dist");
488
+ if (fs.existsSync(bundledFrameworkDir)) return [bundledFrameworkDir, ...super.customEggPaths()];
489
+ }
401
490
  return [path.dirname(import.meta.dirname), ...super.customEggPaths()];
402
491
  }
492
+ #clearStartTimeoutTimer() {
493
+ if (this.#startTimeoutTimer) {
494
+ clearTimeout(this.#startTimeoutTimer);
495
+ this.#startTimeoutTimer = void 0;
496
+ }
497
+ }
403
498
  #setupTimeoutTimer() {
404
- const startTimeoutTimer = setTimeout(() => {
499
+ this.#startTimeoutTimer = setTimeout(() => {
405
500
  this.coreLogger.error(this.timing.toString());
406
501
  this.coreLogger.error(`${this.type} still doesn't ready after ${this.config.workerStartTimeout} ms.`);
407
502
  const items = this.timing.toJSON();
@@ -414,7 +509,7 @@ var EggApplicationCore = class extends EggCore {
414
509
  this.dumpConfig();
415
510
  this.dumpTiming();
416
511
  }, this.config.workerStartTimeout);
417
- this.ready(() => clearTimeout(startTimeoutTimer));
512
+ this.ready(() => this.#clearStartTimeoutTimer());
418
513
  }
419
514
  get config() {
420
515
  return super.config;
@@ -29,7 +29,7 @@ var AppWorkerLoader = class extends EggApplicationLoader {
29
29
  await this.loadService();
30
30
  await this.loadMiddleware();
31
31
  await this.loadController();
32
- await this.loadRouter();
32
+ if (!this.options.metadataOnly) await this.loadRouter();
33
33
  }
34
34
  };
35
35
 
@@ -0,0 +1,46 @@
1
+ import { Application } from "./application.js";
2
+ import { StartEggOptions } from "./start.js";
3
+
4
+ //#region src/lib/snapshot.d.ts
5
+
6
+ /**
7
+ * Build a V8 startup snapshot of an egg application.
8
+ *
9
+ * Call this from the snapshot entry script passed to
10
+ * `node --snapshot-blob=snapshot.blob --build-snapshot snapshot_entry.js`.
11
+ *
12
+ * It loads all metadata (plugins, configs, extensions, services, controllers,
13
+ * router, tegg modules), triggers snapshotWillSerialize hooks to clean up
14
+ * non-serializable resources (file handles, timers, process listeners),
15
+ * then registers a V8 deserialize callback to stash the app for later restore.
16
+ *
17
+ * Example snapshot entry script:
18
+ * ```ts
19
+ * import { buildSnapshot } from 'egg';
20
+ * await buildSnapshot({ baseDir: __dirname });
21
+ * ```
22
+ *
23
+ * Example restoring from snapshot:
24
+ * ```ts
25
+ * import { restoreSnapshot } from 'egg';
26
+ * const app = await restoreSnapshot();
27
+ * // app is fully restored with resources recreated, ready for server creation
28
+ * ```
29
+ */
30
+ declare function buildSnapshot(options?: Pick<StartEggOptions, "framework" | "baseDir" | "env" | "plugins">): Promise<void>;
31
+ /**
32
+ * Restore an egg application from a V8 startup snapshot.
33
+ *
34
+ * Triggers the snapshotDidDeserialize lifecycle hooks to recreate
35
+ * non-serializable resources (messenger, loggers, process listeners)
36
+ * and resumes the lifecycle from configDidLoad through didReady.
37
+ *
38
+ * Returns the fully restored Application instance with all metadata
39
+ * pre-loaded (plugins, configs, extensions, services, controllers, router).
40
+ */
41
+ declare function restoreSnapshot(): Promise<Application>;
42
+ declare global {
43
+ var __egg_snapshot_app: unknown;
44
+ }
45
+ //#endregion
46
+ export { buildSnapshot, restoreSnapshot };
@@ -0,0 +1,59 @@
1
+ import { startEgg } from "./start.js";
2
+ import v8 from "node:v8";
3
+
4
+ //#region src/lib/snapshot.ts
5
+ /**
6
+ * Build a V8 startup snapshot of an egg application.
7
+ *
8
+ * Call this from the snapshot entry script passed to
9
+ * `node --snapshot-blob=snapshot.blob --build-snapshot snapshot_entry.js`.
10
+ *
11
+ * It loads all metadata (plugins, configs, extensions, services, controllers,
12
+ * router, tegg modules), triggers snapshotWillSerialize hooks to clean up
13
+ * non-serializable resources (file handles, timers, process listeners),
14
+ * then registers a V8 deserialize callback to stash the app for later restore.
15
+ *
16
+ * Example snapshot entry script:
17
+ * ```ts
18
+ * import { buildSnapshot } from 'egg';
19
+ * await buildSnapshot({ baseDir: __dirname });
20
+ * ```
21
+ *
22
+ * Example restoring from snapshot:
23
+ * ```ts
24
+ * import { restoreSnapshot } from 'egg';
25
+ * const app = await restoreSnapshot();
26
+ * // app is fully restored with resources recreated, ready for server creation
27
+ * ```
28
+ */
29
+ async function buildSnapshot(options = {}) {
30
+ const app = await startEgg({
31
+ ...options,
32
+ snapshot: true
33
+ });
34
+ if (app.agent) await app.agent.triggerSnapshotWillSerialize();
35
+ await app.triggerSnapshotWillSerialize();
36
+ v8.startupSnapshot.setDeserializeMainFunction((snapshotData) => {
37
+ globalThis.__egg_snapshot_app = snapshotData.app;
38
+ }, { app });
39
+ }
40
+ /**
41
+ * Restore an egg application from a V8 startup snapshot.
42
+ *
43
+ * Triggers the snapshotDidDeserialize lifecycle hooks to recreate
44
+ * non-serializable resources (messenger, loggers, process listeners)
45
+ * and resumes the lifecycle from configDidLoad through didReady.
46
+ *
47
+ * Returns the fully restored Application instance with all metadata
48
+ * pre-loaded (plugins, configs, extensions, services, controllers, router).
49
+ */
50
+ async function restoreSnapshot() {
51
+ const app = globalThis.__egg_snapshot_app;
52
+ if (!app) throw new Error("No egg application found in snapshot. Ensure the process was started from a snapshot built with buildSnapshot().");
53
+ if (app.agent) await app.agent.triggerSnapshotDidDeserialize();
54
+ await app.triggerSnapshotDidDeserialize();
55
+ return app;
56
+ }
57
+
58
+ //#endregion
59
+ export { buildSnapshot, restoreSnapshot };
@@ -1,6 +1,7 @@
1
1
  import { EggPlugin } from "./types.js";
2
2
  import { Agent } from "./agent.js";
3
3
  import { Application } from "./application.js";
4
+ import { LoaderFS } from "@eggjs/core";
4
5
 
5
6
  //#region src/lib/start.d.ts
6
7
  interface StartEggOptions {
@@ -13,6 +14,16 @@ interface StartEggOptions {
13
14
  mode?: "single";
14
15
  env?: string;
15
16
  plugins?: EggPlugin;
17
+ /** Skip lifecycle hooks, only trigger loadMetadata for manifest generation */
18
+ metadataOnly?: boolean;
19
+ /** Loader-facing filesystem abstraction */
20
+ loaderFS?: LoaderFS;
21
+ /**
22
+ * When true, load application metadata for V8 startup snapshot construction.
23
+ * The lifecycle stops after configWillLoad (no servers, timers, or connections)
24
+ * and skips `egg-ready` broadcast. `ready()` resolves when loading completes.
25
+ */
26
+ snapshot?: boolean;
16
27
  }
17
28
  interface SingleModeApplication extends Application {
18
29
  agent: SingleModeAgent;
@@ -21,7 +32,11 @@ interface SingleModeAgent extends Agent {
21
32
  app: SingleModeApplication;
22
33
  }
23
34
  /**
24
- * Start egg with single process
35
+ * Start egg with single process.
36
+ *
37
+ * When `options.snapshot` is true, loads application metadata for V8 startup
38
+ * snapshot construction. The lifecycle stops after `configWillLoad` (no servers,
39
+ * timers, or connections) and skips `egg-ready` broadcast.
25
40
  */
26
41
  declare function startEgg(options?: StartEggOptions): Promise<SingleModeApplication>;
27
42
  //#endregion
package/dist/lib/start.js CHANGED
@@ -1,17 +1,13 @@
1
1
  import { Agent } from "./agent.js";
2
2
  import { Application } from "./application.js";
3
3
  import "./types.js";
4
+ import { ManifestStore } from "@eggjs/core";
4
5
  import path from "node:path";
5
6
  import { importModule } from "@eggjs/utils";
6
7
  import { readJSON } from "utility";
7
8
 
8
9
  //#region src/lib/start.ts
9
- /**
10
- * Start egg with single process
11
- */
12
- async function startEgg(options = {}) {
13
- options.baseDir = options.baseDir ?? process.cwd();
14
- options.mode = "single";
10
+ async function resolveFrameworkClasses(options) {
15
11
  if (!options.framework) try {
16
12
  options.framework = (await readJSON(path.join(options.baseDir, "package.json"))).egg.framework;
17
13
  } catch {}
@@ -22,13 +18,35 @@ async function startEgg(options = {}) {
22
18
  AgentClass = framework.Agent;
23
19
  ApplicationClass = framework.Application;
24
20
  }
25
- const agent = new AgentClass({ ...options });
26
- await agent.ready();
21
+ return {
22
+ AgentClass,
23
+ ApplicationClass
24
+ };
25
+ }
26
+ /**
27
+ * Start egg with single process.
28
+ *
29
+ * When `options.snapshot` is true, loads application metadata for V8 startup
30
+ * snapshot construction. The lifecycle stops after `configWillLoad` (no servers,
31
+ * timers, or connections) and skips `egg-ready` broadcast.
32
+ */
33
+ async function startEgg(options = {}) {
34
+ options.baseDir = options.baseDir ?? process.cwd();
35
+ options.mode = "single";
36
+ if (!options.snapshot) ManifestStore.enableCompileCache(options.baseDir);
37
+ const { AgentClass, ApplicationClass } = await resolveFrameworkClasses(options);
38
+ let agent;
39
+ if (!options.metadataOnly) {
40
+ agent = new AgentClass({ ...options });
41
+ await agent.ready();
42
+ }
27
43
  const application = new ApplicationClass({ ...options });
28
- application.agent = agent;
29
- agent.application = application;
44
+ if (agent) {
45
+ application.agent = agent;
46
+ agent.application = application;
47
+ }
30
48
  await application.ready();
31
- application.messenger.broadcast("egg-ready");
49
+ if (!options.metadataOnly && !options.snapshot) application.messenger.broadcast("egg-ready");
32
50
  return application;
33
51
  }
34
52
 
@@ -7,7 +7,7 @@ import "./egg.js";
7
7
  import { Application } from "./application.js";
8
8
  import { EggAppConfig as EggAppConfig$1, EggAppInfo, FileLoaderOptions } from "@eggjs/core";
9
9
  import { EggLoggerOptions, EggLoggersOptions } from "egg-logger";
10
- import { RequestOptions } from "urllib";
10
+ import { Dispatcher, RequestOptions } from "urllib";
11
11
  import { LookupFunction, Socket } from "node:net";
12
12
  import { PartialDeep } from "type-fest";
13
13
 
@@ -61,6 +61,24 @@ interface HttpClientConfig {
61
61
  */
62
62
  allowH2?: boolean;
63
63
  lookup?: LookupFunction;
64
+ /**
65
+ * Interceptors for request composition, applied via `Dispatcher.compose()`.
66
+ * Each interceptor receives a `dispatch` function and returns a new `dispatch` function.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * // config.default.ts
71
+ * config.httpclient = {
72
+ * interceptors: [
73
+ * (dispatch) => (opts, handler) => {
74
+ * opts.headers = { ...opts.headers, 'x-trace-id': generateTraceId() };
75
+ * return dispatch(opts, handler);
76
+ * },
77
+ * ],
78
+ * };
79
+ * ```
80
+ */
81
+ interceptors?: Dispatcher.DispatcherComposeInterceptor[];
64
82
  }
65
83
  /**
66
84
  * Powerful Partial, Support adding ? modifier to a mapped property in deep level
@@ -73,6 +91,7 @@ interface HttpClientConfig {
73
91
  * @deprecated use `PartialDeep` instead
74
92
  */
75
93
  type PowerPartial<T> = PartialDeep<T>;
94
+ declare const EggAppConfig: ObjectConstructor;
76
95
  interface EggAppConfig extends EggAppConfig$1 {
77
96
  workerStartTimeout: number;
78
97
  baseDir: string;
package/dist/lib/types.js CHANGED
@@ -1,3 +1,7 @@
1
1
  import "./types.plugin.js";
2
2
 
3
- export { };
3
+ //#region src/lib/types.ts
4
+ const EggAppConfig = Object;
5
+
6
+ //#endregion
7
+ export { EggAppConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "egg",
3
- "version": "4.1.2-beta.1",
3
+ "version": "4.1.2-beta.10",
4
4
  "description": "A web application framework for Node.js",
5
5
  "keywords": [
6
6
  "app",
@@ -70,6 +70,7 @@
70
70
  "./lib/loader/AgentWorkerLoader": "./dist/lib/loader/AgentWorkerLoader.js",
71
71
  "./lib/loader/AppWorkerLoader": "./dist/lib/loader/AppWorkerLoader.js",
72
72
  "./lib/loader/EggApplicationLoader": "./dist/lib/loader/EggApplicationLoader.js",
73
+ "./lib/snapshot": "./dist/lib/snapshot.js",
73
74
  "./lib/start": "./dist/lib/start.js",
74
75
  "./lib/types": "./dist/lib/types.js",
75
76
  "./lib/types.plugin": "./dist/lib/types.plugin.js",
@@ -98,34 +99,34 @@
98
99
  "type-fest": "^5.0.1",
99
100
  "urllib": "^4.8.2",
100
101
  "utility": "^2.5.0",
101
- "@eggjs/ajv-plugin": "4.0.2-beta.1",
102
- "@eggjs/cluster": "4.0.2-beta.1",
103
- "@eggjs/aop-plugin": "4.0.2-beta.1",
104
- "@eggjs/core": "7.0.2-beta.1",
105
- "@eggjs/controller-plugin": "4.0.2-beta.1",
106
- "@eggjs/cookies": "4.0.2-beta.1",
107
- "@eggjs/development": "5.0.2-beta.1",
108
- "@eggjs/dal-plugin": "4.0.2-beta.1",
109
- "@eggjs/eventbus-plugin": "4.0.2-beta.1",
110
- "@eggjs/errors": "3.0.2-beta.1",
111
- "@eggjs/i18n": "4.0.2-beta.1",
112
- "@eggjs/multipart": "5.0.2-beta.1",
113
- "@eggjs/jsonp": "4.0.2-beta.1",
114
- "@eggjs/extend2": "5.0.2-beta.1",
115
- "@eggjs/logrotator": "5.0.2-beta.1",
116
- "@eggjs/onerror": "4.0.2-beta.1",
117
- "@eggjs/security": "5.0.2-beta.1",
118
- "@eggjs/schedule-plugin": "4.0.2-beta.1",
119
- "@eggjs/static": "4.0.2-beta.1",
120
- "@eggjs/session": "5.0.2-beta.1",
121
- "@eggjs/tegg-config": "4.0.2-beta.1",
122
- "@eggjs/schedule": "6.0.2-beta.1",
123
- "@eggjs/tegg-plugin": "4.0.2-beta.1",
124
- "@eggjs/orm-plugin": "4.0.2-beta.1",
125
- "@eggjs/tegg": "4.0.2-beta.1",
126
- "@eggjs/view": "4.0.2-beta.1",
127
- "@eggjs/utils": "5.0.2-beta.1",
128
- "@eggjs/watcher": "5.0.2-beta.1"
102
+ "@eggjs/ajv-plugin": "4.0.2-beta.10",
103
+ "@eggjs/cluster": "4.0.2-beta.10",
104
+ "@eggjs/controller-plugin": "4.0.2-beta.10",
105
+ "@eggjs/cookies": "4.0.2-beta.10",
106
+ "@eggjs/dal-plugin": "4.0.2-beta.10",
107
+ "@eggjs/development": "5.0.2-beta.10",
108
+ "@eggjs/core": "7.0.2-beta.10",
109
+ "@eggjs/aop-plugin": "4.0.2-beta.10",
110
+ "@eggjs/errors": "3.0.2-beta.10",
111
+ "@eggjs/eventbus-plugin": "4.0.2-beta.10",
112
+ "@eggjs/extend2": "5.0.2-beta.10",
113
+ "@eggjs/i18n": "4.0.2-beta.10",
114
+ "@eggjs/logrotator": "5.0.2-beta.10",
115
+ "@eggjs/multipart": "5.0.2-beta.10",
116
+ "@eggjs/jsonp": "4.0.2-beta.10",
117
+ "@eggjs/orm-plugin": "4.0.2-beta.10",
118
+ "@eggjs/schedule": "6.0.2-beta.10",
119
+ "@eggjs/schedule-plugin": "4.0.2-beta.10",
120
+ "@eggjs/onerror": "4.0.2-beta.10",
121
+ "@eggjs/session": "5.0.2-beta.10",
122
+ "@eggjs/static": "4.0.2-beta.10",
123
+ "@eggjs/security": "5.0.2-beta.10",
124
+ "@eggjs/tegg": "4.0.2-beta.10",
125
+ "@eggjs/tegg-plugin": "4.0.2-beta.10",
126
+ "@eggjs/utils": "5.0.2-beta.10",
127
+ "@eggjs/view": "4.0.2-beta.10",
128
+ "@eggjs/watcher": "5.0.2-beta.10",
129
+ "@eggjs/tegg-config": "4.0.2-beta.10"
129
130
  },
130
131
  "devDependencies": {
131
132
  "@types/koa-bodyparser": "^4.3.12",
@@ -141,10 +142,10 @@
141
142
  "sdk-base": "^5.0.1",
142
143
  "spy": "^1.0.0",
143
144
  "typescript": "^5.9.3",
144
- "@eggjs/koa": "3.1.2-beta.1",
145
- "@eggjs/mock": "7.0.2-beta.1",
146
- "@eggjs/tracer": "4.0.2-beta.1",
147
- "@eggjs/supertest": "9.0.2-beta.1"
145
+ "@eggjs/koa": "3.1.2-beta.10",
146
+ "@eggjs/mock": "7.0.2-beta.10",
147
+ "@eggjs/supertest": "9.0.2-beta.10",
148
+ "@eggjs/tracer": "4.0.2-beta.10"
148
149
  },
149
150
  "engines": {
150
151
  "node": ">=22.18.0"