Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
node: ['22', '24']
# os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
os: ['ubuntu-latest', 'windows-latest']
node: ['22']

name: Test egg-bin (${{ matrix.os }}, ${{ matrix.node }})
runs-on: ${{ matrix.os }}
Expand Down
13 changes: 0 additions & 13 deletions packages/egg/src/lib/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,3 @@ export class Application extends EggApplicationCore {
});
}
}

declare module '@eggjs/core' {
// add EggApplicationCore overrides types
interface EggCore {
onClientError(err: any, socket: Socket): void;
onServer(server: http.Server): void;
locals: Record<string, any>;
runInBackground(scope: (ctx: Context) => Promise<void>, req?: unknown): void;
toAsyncFunction(fn: (...args: any[]) => any): (...args: any[]) => any;
dumpConfig(): void;
get keys(): string[];
}
}
5 changes: 3 additions & 2 deletions packages/egg/src/lib/core/base_context_class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BaseContextClass as EggCoreBaseContextClass } from '@eggjs/core';

import type { Context, EggApplicationCore } from '../egg.ts';
import type { Context } from '../egg.ts';
import type { Application } from '../application.ts';
import { BaseContextLogger } from './base_context_logger.ts';

/**
Expand All @@ -12,7 +13,7 @@ export class BaseContextClass extends EggCoreBaseContextClass {
[key: string | symbol]: any;
declare ctx: Context;
declare pathName?: string;
declare app: EggApplicationCore;
declare app: Application;
declare service: BaseContextClass;
#logger?: BaseContextLogger;

Expand Down
5 changes: 3 additions & 2 deletions packages/egg/src/lib/core/context_httpclient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Context, EggApplicationCore } from '../egg.ts';
import type { Context } from '../egg.ts';
import type { Application } from '../application.ts';
import type { HttpClientRequestURL, HttpClientRequestOptions } from './httpclient.ts';

export class ContextHttpClient {
ctx: Context;
app: EggApplicationCore;
app: Application;

constructor(ctx: Context) {
this.ctx = ctx;
Expand Down
21 changes: 0 additions & 21 deletions packages/egg/src/lib/egg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -668,24 +668,3 @@ export class EggApplicationCore extends EggCore {
return context;
}
}

declare module '@eggjs/core' {
// add EggApplicationCore overrides types
interface EggCore {
inspect(): any;
get currentContext(): EggContext | undefined;
ctxStorage: AsyncLocalStorage<EggContext>;
getLogger(name: string): EggLogger;
get loggers(): EggLoggers;
createHttpClient(options?: HttpClientOptions): HttpClient;
HttpClient: typeof HttpClient;
get httpClient(): HttpClient;
curl<T = any>(url: HttpClientRequestURL, options?: HttpClientRequestOptions): Promise<HttpClientResponse<T>>;
createAnonymousContext(req?: any): EggContext;
runInAnonymousContextScope(scope: (ctx: Context) => Promise<void>, req?: unknown): Promise<void>;
readonly messenger: IMessenger;
Subscription: typeof BaseContextClass;
BaseHookClass: typeof BaseHookClass;
Boot: typeof BaseHookClass;
}
}
9 changes: 3 additions & 6 deletions packages/egg/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type { EggLoggerOptions, EggLoggersOptions } from 'egg-logger';
import type { FileLoaderOptions, EggAppConfig as EggCoreAppConfig, EggAppInfo } from '@eggjs/core';
import type { PartialDeep } from 'type-fest';

import type { EggApplicationCore, Context } from './egg.ts';
import type { Context } from './egg.ts';
import type { Application } from './application.ts';
import type { MetaMiddlewareOptions } from '../app/middleware/meta.ts';
import type { NotFoundMiddlewareOptions } from '../app/middleware/notfound.ts';
import type { SiteFileMiddlewareOptions } from '../app/middleware/site_file.ts';
Expand Down Expand Up @@ -266,11 +267,7 @@ export interface EggAppConfig extends EggCoreAppConfig {
allowedMethods: string[];
};

onClientError?(
err: Error,
socket: Socket,
app: EggApplicationCore
): ClientErrorResponse | Promise<ClientErrorResponse>;
onClientError?(err: Error, socket: Socket, app: Application): ClientErrorResponse | Promise<ClientErrorResponse>;

/**
* server timeout in milliseconds, default to 0 (no timeout).
Expand Down
12 changes: 6 additions & 6 deletions packages/egg/test/index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expectType } from 'tsd';
import { EggCore, Context } from '@eggjs/core';
import { LogRotator } from '@eggjs/logrotator';
import {
Context,
Application,
type IBoot,
type ILifecycleBoot,
Expand All @@ -19,7 +19,7 @@ import {
import { HttpClient } from '../src/urllib.js';
import { type IMessenger } from '../src/lib/core/messenger/IMessenger.js';

const app = {} as EggCore;
const app = {} as Application;
expectType<IMessenger>(app.messenger);
expectType<IMessenger>(app.messenger.broadcast('test'));
expectType<void>(app.loggers.reload());
Expand Down Expand Up @@ -52,10 +52,10 @@ expectType<boolean>(app.config.development.fastReady);
expectType<string[]>(app.config.development.watchDirs);

// jsonp plugin types
expectType<boolean>(app.config.jsonp.csrf);
expectType<string[] | string>(app.config.jsonp.callback);
expectType<number>(app.config.jsonp.limit);
expectType<string | RegExp | (string | RegExp)[]>(app.config.jsonp.whiteList!);
expectType<boolean>(app.config.jsonp!.csrf);
expectType<string[] | string>(app.config.jsonp!.callback);
expectType<number>(app.config.jsonp!.limit);
expectType<string | RegExp | (string | RegExp)[]>(app.config.jsonp!.whiteList!);
// @ts-ignore - jsonp plugin extends context
expectType<boolean>(ctx.acceptJSONP);
// @ts-ignore - jsonp plugin extends context
Expand Down
14 changes: 9 additions & 5 deletions packages/egg/test/lib/core/loader/load_boot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { describe, it, beforeAll } from 'vitest';
import { strict as assert } from 'node:assert';
import fs from 'node:fs/promises';
import path from 'node:path';
import { type MockApplication, createApp } from '../../../utils.js';
import { scheduler } from 'node:timers/promises';

import { type MockApplication, createApp } from '../../../utils.ts';

describe('test/lib/core/loader/load_boot.test.ts', () => {
describe('CommonJS', () => {
let app: MockApplication;

beforeAll(() => {
beforeAll(async () => {
app = createApp('apps/boot-app');
return app.ready();
await app.ready();
});

it('should load app.js', async () => {
await scheduler.wait(100);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Investigate why the delay is necessary instead of relying on arbitrary timing.

Adding a 100ms delay before app.close() suggests a race condition where app.ready() resolves before all boot lifecycle events have fully completed. This approach has several concerns:

  • The delay is arbitrary and may be insufficient on slower systems or in CI environments
  • It doesn't address the root cause of why app.ready() doesn't guarantee readiness
  • Time-based delays make tests brittle and slower

Consider:

  1. Adding a comment explaining what specific asynchronous operation needs to complete
  2. Waiting for a specific event or condition instead of a fixed delay
  3. Investigating whether the boot lifecycle should ensure all operations complete before ready() resolves

Run the following script to check if there are any pending async operations after app.ready():

Also applies to: 57-57


🏁 Script executed:

#!/bin/bash
# Description: Search for setTimeout, setImmediate, or process.nextTick in boot-related code
# that might explain why additional delay is needed

rg -n -C3 'setTimeout|setImmediate|process\.nextTick' --type ts -g '**/boot*.ts' -g '**/loader*.ts'

# Look for the boot app fixtures to understand their lifecycle
fd -t f "app.ts|app.js" -p "boot-app"

Length of output: 95


🏁 Script executed:

#!/bin/bash
# Locate scheduler implementation files
fd -t f scheduler*.ts

# Display the first 200 lines of each scheduler definition
for f in $(fd -t f scheduler*.ts); do
  echo "==> $f <=="
  sed -n '1,200p' "$f"
done

# Show usage of scheduler in the load_boot test file
rg -n "scheduler" -C3 packages/egg/test/lib/core/loader/load_boot.test.ts

Length of output: 6726


🏁 Script executed:

#!/bin/bash
# Locate all utils.ts files
fd -t f utils.ts

# Display contents to find createApp and MockApplication definitions
for f in $(fd -t f utils.ts); do
  echo "==> $f <=="
  sed -n '1,200p' "$f"
done

Length of output: 26197


Replace fixed delay with app.ready()

  • Remove await scheduler.wait(100) and instead await app.ready() to deterministically wait for the boot lifecycle to complete.
  • Eliminate the now-unused scheduler import.
🤖 Prompt for AI Agents
In packages/egg/test/lib/core/loader/load_boot.test.ts around line 19, replace
the nondeterministic await scheduler.wait(100) with await app.ready() so the
test waits deterministically for the application's boot lifecycle to complete,
and remove the now-unused scheduler import from the top of the file; ensure any
references to scheduler are deleted so there are no unused variables or imports
left.

await app.close();
app.expectLog('app is ready');

Expand Down Expand Up @@ -45,12 +48,13 @@ describe('test/lib/core/loader/load_boot.test.ts', () => {
describe('ESM', () => {
let app: MockApplication;

beforeAll(() => {
beforeAll(async () => {
app = createApp('apps/boot-app-esm');
return app.ready();
await app.ready();
});

it('should load app.js', async () => {
await scheduler.wait(100);
await app.close();
app.expectLog('app is ready');

Expand Down
32 changes: 16 additions & 16 deletions packages/egg/test/lib/core/loader/load_plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { mm } from '@eggjs/mock';
import { EggConsoleLogger } from 'egg-logger';

import { type MockApplication, createApp, getFilepath } from '../../../utils.ts';
import { AppWorkerLoader, AgentWorkerLoader, EggApplicationCore } from '../../../../src/index.ts';
import { AppWorkerLoader, AgentWorkerLoader } from '../../../../src/index.ts';

// const EGG_BASE = getFilepath('../..');

Expand All @@ -25,7 +25,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -91,7 +91,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -113,7 +113,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -139,7 +139,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -162,7 +162,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
env: 'unittest',
baseDir,
plugins,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand Down Expand Up @@ -195,7 +195,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -208,7 +208,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -221,7 +221,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'local',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -238,7 +238,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -251,7 +251,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -264,7 +264,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader1 = new AppWorkerLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader1.loadConfig();
Expand All @@ -281,7 +281,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader2 = new AppWorkerLoader({
env: 'local',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader2.loadConfig();
Expand Down Expand Up @@ -315,7 +315,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const appLoader = new CustomAppLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await appLoader.loadConfig();
Expand All @@ -331,7 +331,7 @@ describe('test/lib/core/loader/load_plugin.test.ts', () => {
const agentLoader = new CustomAgentLoader({
env: 'unittest',
baseDir,
app: app as unknown as EggApplicationCore,
app,
logger,
});
await agentLoader.loadConfig();
Expand Down
4 changes: 2 additions & 2 deletions plugins/schedule/src/lib/load_schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { stringify } from 'node:querystring';

import { isClass, isFunction, isGeneratorFunction } from 'is-type-of';
import { importResolve } from '@eggjs/utils';
import type { EggApplicationCore, EggContext } from 'egg';
import type { EggApplicationCore, Context } from 'egg';

import type { EggScheduleTask, EggScheduleItem } from './types.ts';
import type { EggScheduleConfig } from '../config/config.default.ts';
Expand All @@ -30,7 +30,7 @@ function getScheduleLoader(app: EggApplicationCore) {
!isGeneratorFunction(schedule.prototype.subscribe),
`schedule(${fullpath}): "schedule" generator function is not support, should use async function instead`
);
task = async (ctx: EggContext, ...args: any[]) => {
task = async (ctx: Context, ...args: any[]) => {
const instance = new schedule(ctx);
// s.subscribe = app.toAsyncFunction(s.subscribe);
return instance.subscribe(...args);
Expand Down
5 changes: 3 additions & 2 deletions plugins/tracer/src/boot.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { debuglog } from 'node:util';
import type { ILifecycleBoot, EggApplicationCore } from 'egg';

import type { ILifecycleBoot, Application } from 'egg';

import { isReady } from './app/extend/application.ts';

const debug = debuglog('egg/tracer/boot');

export class TracerBoot implements ILifecycleBoot {
private readonly app;
constructor(app: EggApplicationCore) {
constructor(app: Application) {
this.app = app;
}

Expand Down
Loading
Loading