Skip to content

feat(tegg): declarative module plugin mechanism#6021

Open
gxkl wants to merge 71 commits into
nextfrom
feat/module-plugin-core
Open

feat(tegg): declarative module plugin mechanism#6021
gxkl wants to merge 71 commits into
nextfrom
feat/module-plugin-core

Conversation

@gxkl

@gxkl gxkl commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Motivation

Tegg's framework-level extensions (AOP, DAL, controller registration, config source, ...) all rely on the HOST hand-registering lifecycle hooks in its boot code. A module cannot declare "run this when a load unit is created" by itself, so:

  • the same registration logic is duplicated between the egg plugin boot and the standalone Runner;
  • every new runtime shape (e.g. a service worker runtime) has to copy the whole assembly again;
  • modules are not self-contained.

This PR ports the declarative module plugin design from eggjs/tegg#325 (merged to the stale standalone-next branch, never reached master) to the next architecture, and completes the two parts #325 left open: egg-host wiring (inner object protos used to be silently ignored in app mode) and bootstrapping the built-in hooks themselves (the #handleCompatibility TODO).

Design

A plain eggModule package can now provide framework extensions declaratively:

  • @InnerObjectProto — framework inner object (a SingletonProto with EGG_INNER_OBJECT_PROTO_IMPL_TYPE), diverted by the loader into ModuleDescriptor.innerObjectClazzList, never into business load units.
  • @EggLifecycleProto five typed variants (LoadUnit / LoadUnitInstance / EggPrototype / EggObject / EggContext) — a DI-capable hook object, auto-registered into the matching scope-aware LifecycleUtil and deregistered symmetrically.

Two-phase ordering (the load-bearing constraint). GlobalGraph.create() only adds nodes; build() adds inject edges and consumes registerBuildHook hooks once at its end (late registration is silently lost). Both hosts now boot as:

  1. scan modules → GlobalGraph.create (nodes only)
  2. create AND instantiate the InnerObjectLoadUnit (own topologically sorted proto graph, cycle detection, hard error on missing non-optional deps) — hooks register here, including graph build hooks from @LifecyclePostInject (see AopGraphHookRegistrar)
  3. build() / sort() → business load units → business instances
  4. destroy in reverse creation order (inner unit last)

Commits

commit scope
feat(core): add module plugin core mechanism decorators + metadata (EggInnerObjectPrototypeImpl, InjectObjectPrototypeFinder / ProtoGraphUtils extracted with a proto-name index replacing the O(n·m·n) scan) + runtime (EggInnerObjectImpl runs only decorator-declared self lifecycle; host-agnostic InnerObjectLoadUnit(Builder/Instance)) + loader diversion (manifest getDecoratedFiles covers the new list so bundle mode still re-imports those files)
feat(standalone): two-phase StandaloneApp with module plugin support BREAKING: Runner renamed to StandaloneApp (no alias; main()/preLoad() unchanged). Explicit boot phases, frameworkDeps option, bundle-mode manifest/loaderFS consumption + StandaloneApp.loadMetadata(), tegg#325 test suite ported
feat(tegg-plugin): instantiate InnerObjectLoadUnit in app mode ModuleHandler.init() phases via EggModuleLoader.initGraph()/load() split; the same module plugin package behaves identically under both hosts
feat(tegg): convert built-in framework hooks to module plugins AOP (incl. AopGraphHookRegistrar for the crossCut/pointCut build hooks), DAL (constructor args → @Inject of host-provided moduleConfigs/runtimeConfig/logger inner objects, PRIVATE on the egg host), ConfigSource; vestigial LoadUnitMultiInstanceProtoHook registration dropped (empty preCreate, unconsumed static set); builder dedupes by class since a package may be both hard-fed and scanned as an eggModule (e.g. @eggjs/dal-plugin)
docs(wiki): record tegg module plugin architecture concept page + log

Test evidence

  • New coverage: decorator metadata (7), loader diversion (3), InnerObjectLoadUnit integration incl. decorator-only-lifecycle semantics / auto register+deregister / cycle detection / missing-dep hard error (4), standalone module plugin suite ported from tegg#325 — five lifecycle proto types + inner object PUBLIC/PRIVATE semantics (7), app-mode module plugin fixture (2).
  • Regression: 15 tegg projects (core + plugins + standalone) — 76 files / 296 tests green, MultiApp isolation green; full-repo run has zero failures related to this change (remaining ones verified as pre-existing/env: dns-cache needs a fresh ut install, multipart fails on clean next too, onerror/development are flaky-on-rerun).

Notes

  • Independent of fix(controller): register middlewareGraphHook before global graph build #6020 (no overlapping files; git merge-tree clean; combined-tree run of both test suites green).
  • TeggScope rules kept: no new process-global mutable state; lifecycle registration happens inside the host scope via the scope-aware utils; type-keyed creator registries stay global per the multi-app guidelines.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added declarative support for inner objects and lifecycle prototypes, including scope-aware hook execution and request-context AOP advice wiring.
    • Introduced inner-object load units so module plugins participate in graph build and lifecycle handling.
    • Added standalone bundle support via StandaloneApp, including manifest reuse and loaderFS, and updated standalone entrypoints.
  • Bug Fixes
    • Improved module/proto resolution with clearer “multi-match” errors and safer optional/missing dependency handling.
    • Prevented silent graph-weaving omissions and ensured consistent hook teardown ordering.
  • Documentation
    • Updated module-plugin and standalone API docs (including the innerObjectHandlers naming).

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds inner-object and lifecycle decorators, updates metadata and graph resolution, introduces runtime/load-unit support, migrates AOP/config/DAL and boot wiring, and replaces the standalone Runner with StandaloneApp. Tests, fixtures, and docs are updated to match the new module-plugin flow.

Changes

Tegg Module Plugin Mechanism

Layer / File(s) Summary
Core decorators and metadata contracts
tegg/core/core-decorator/src/decorator/*, tegg/core/core-decorator/src/util/PrototypeUtil.ts, tegg/core/types/src/core-decorator/*, tests
Adds InnerObjectProto, EggLifecycleProto, and DefineModuleQualifier decorators, plus lifecycle/inner-object metadata and public type exports.
Prototype metadata and dependency graph
tegg/core/metadata/src/impl/*, tegg/core/metadata/src/model/*
Adds inner-object prototype implementations, inject-object resolution helpers, descriptor shape updates, and graph lookup utilities.
Loader and module config resolution
tegg/core/loader/src/LoaderFactory.ts, tegg/core/common-util/src/ModuleConfig.ts, tegg/core/test-util/src/LoaderUtil.ts, tests/fixtures
Updates module reference scanning, tolerant config resolution, manifest extension building, and loader test scaffolding.
Inner object runtime and load units
tegg/core/runtime/src/impl/*, runtime tests
Implements inner-object object creation, load units, lifecycle-instance registration, and host-provided object support.
AOP, config, and DAL migrations
tegg/core/aop-runtime/*, tegg/plugin/aop/*, tegg/plugin/config/*, tegg/plugin/dal/*
Converts AOP/config/DAL wiring to decorator-driven module plugins and updates package metadata, tests, and fixtures.
Tegg boot and standalone app
tegg/plugin/tegg/*, tegg/standalone/standalone/*
Reworks app/module loading order, replaces Runner with StandaloneApp, and adds standalone fixtures/tests for the new flow.
Wiki docs
wiki/*
Adds the module-plugin concept page and related wiki updates.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • eggjs/egg#5846: Both PRs affect manifest and decorated-file collection for inner-object-aware module loading.
  • eggjs/egg#5973: Both PRs adjust tegg/plugin/config/src/app.ts module-reference resolution and config loading around manifest-derived paths.
  • eggjs/egg#5993: Both PRs touch AOP graph-hook registration and the timing of build-hook wiring in app boot.

Suggested reviewers: jerryliang64, killagu, fengmk2

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a declarative module plugin mechanism to Tegg.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/module-plugin-core

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 4, 2026

Copy link
Copy Markdown

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5c42a6d
Status: ✅  Deploy successful!
Preview URL: https://618b1775.egg-cci.pages.dev
Branch Preview URL: https://feat-module-plugin-core.egg-cci.pages.dev

View logs

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.33816% with 80 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.19%. Comparing base (3ddbbe4) to head (5c42a6d).

Files with missing lines Patch % Lines
tegg/core/runtime/src/impl/EggInnerObjectImpl.ts 57.77% 34 Missing and 4 partials ⚠️
tegg/standalone/standalone/src/StandaloneApp.ts 88.05% 15 Missing and 1 partial ⚠️
...e/metadata/src/impl/InjectObjectPrototypeFinder.ts 85.00% 4 Missing and 2 partials ⚠️
...ore/runtime/src/impl/InnerObjectLoadUnitBuilder.ts 93.87% 3 Missing ⚠️
tegg/plugin/config/src/lib/ModuleScanner.ts 94.82% 3 Missing ⚠️
tegg/standalone/standalone/src/main.ts 82.35% 3 Missing ⚠️
...g/core/metadata/src/model/graph/ProtoGraphUtils.ts 94.11% 2 Missing ⚠️
.../core/runtime/src/impl/ProvidedInnerObjectProto.ts 95.83% 2 Missing ⚠️
tegg/standalone/standalone/src/EggModuleLoader.ts 90.47% 2 Missing ⚠️
...g/core/aop-runtime/src/AopContextAdviceRegistry.ts 90.90% 1 Missing ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #6021      +/-   ##
==========================================
+ Coverage   81.98%   82.19%   +0.20%     
==========================================
  Files         678      686       +8     
  Lines       20838    21195     +357     
  Branches     4154     4224      +70     
==========================================
+ Hits        17085    17422     +337     
- Misses       3235     3256      +21     
+ Partials      518      517       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@socket-security

socket-security Bot commented Jul 4, 2026

Copy link
Copy Markdown

Dependency limit exceeded — report not shown.

This pull request scan exceeded the 10,000-dependency limit applied to this scan, so the results are incomplete and may be inaccurate. To avoid reporting false positives, Socket has not posted a report.

Upgrade your plan to raise the dependency limit and get complete reports, or view the partial scan in the dashboard.

Socket is always free for open source. If this is a non-commercial open source project, contact us to request a free Team account.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 4, 2026

Copy link
Copy Markdown

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5c42a6d
Status: ✅  Deploy successful!
Preview URL: https://8bd664a5.egg-v3.pages.dev
Branch Preview URL: https://feat-module-plugin-core.egg-v3.pages.dev

View logs

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements a declarative module plugin mechanism using @InnerObjectProto and @EggLifecycleProto decorators, enabling framework extensions to be loaded into an InnerObjectLoadUnit before the business graph is built for both standalone and egg hosts. It also converts AOP, DAL, and ConfigSource hooks into module plugins and renames Runner to StandaloneApp. The review feedback suggests maintaining consistency by using .ts extensions instead of .js in import/export paths within aop-runtime, and defensively checking if caught exceptions are Error instances before accessing their message property in StandaloneApp.ts and main.ts to avoid runtime type errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +5 to +6
import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

According to the repository's general rules, TypeScript source file imports should use the .ts extension instead of .js to maintain consistency across the codebase.

Suggested change
import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';
import { crossCutGraphHook } from './CrossCutGraphHook.ts';
import { pointCutGraphHook } from './PointCutGraphHook.ts';
References
  1. In this repository, use '.ts' extensions in import/export paths for TypeScript source files to maintain consistency with the existing convention across source and test files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

不采纳这里的 .ts import 建议。tegg 源码当前按 ESM 输出规范使用 .js import specifier,同包其它 TS 源码也是这个模式;改成 .ts 反而会和现有构建/类型检查约定不一致。

Comment on lines +4 to +7
import { AopGraphHookRegistrar } from './AopGraphHookRegistrar.js';
import { EggObjectAopHook } from './EggObjectAopHook.js';
import { EggPrototypeCrossCutHook } from './EggPrototypeCrossCutHook.js';
import { LoadUnitAopHook } from './LoadUnitAopHook.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

According to the repository's general rules, TypeScript source file imports should use the .ts extension instead of .js to maintain consistency across the codebase.

Suggested change
import { AopGraphHookRegistrar } from './AopGraphHookRegistrar.js';
import { EggObjectAopHook } from './EggObjectAopHook.js';
import { EggPrototypeCrossCutHook } from './EggPrototypeCrossCutHook.js';
import { LoadUnitAopHook } from './LoadUnitAopHook.js';
import { AopGraphHookRegistrar } from './AopGraphHookRegistrar.ts';
import { EggObjectAopHook } from './EggObjectAopHook.ts';
import { EggPrototypeCrossCutHook } from './EggPrototypeCrossCutHook.ts';
import { LoadUnitAopHook } from './LoadUnitAopHook.ts';
References
  1. In this repository, use '.ts' extensions in import/export paths for TypeScript source files to maintain consistency with the existing convention across source and test files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

不改,原因同上:tegg TS 源码使用 .js import specifier 才符合当前 ESM 输出约定。这个 thread 现在也已经是 outdated。

Comment thread tegg/core/aop-runtime/src/index.ts Outdated
Comment on lines +7 to +8
export * from './AopGraphHookRegistrar.js';
export * from './AopInnerObjectClazzList.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

According to the repository's general rules, TypeScript source file exports should use the .ts extension instead of .js to maintain consistency across the codebase.

Suggested change
export * from './AopGraphHookRegistrar.js';
export * from './AopInnerObjectClazzList.js';
export * from './AopGraphHookRegistrar.ts';
export * from './AopInnerObjectClazzList.ts';
References
  1. In this repository, use '.ts' extensions in import/export paths for TypeScript source files to maintain consistency with the existing convention across source and test files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

不改,原因同上:这里的 export specifier 也应保持 .js,和 tegg 现有 ESM 源码约定一致。这个 thread 现在已 outdated。

Comment on lines +321 to +324
ctx.destroy(lifecycle).catch((e) => {
e.message = `[tegg/standalone] destroy tegg context failed: ${e.message}`;
console.warn(e);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

When handling a caught exception, defensively check if the caught value is an Error instance before accessing or mutating its message property. Mutating e.message directly can throw a TypeError in strict mode if e is a primitive value. Use String(e) as a fallback to safely format the error message.

Suggested change
ctx.destroy(lifecycle).catch((e) => {
e.message = `[tegg/standalone] destroy tegg context failed: ${e.message}`;
console.warn(e);
});
ctx.destroy(lifecycle).catch((e) => {
const msg = e instanceof Error ? e.message : String(e);
console.warn('[tegg/standalone] destroy tegg context failed: ' + msg);
});
References
  1. When creating a new Error from a caught exception, check if the caught value is an 'Error' instance before accessing its 'message' property. Use 'String(err)' as a fallback for non-Error values to ensure a meaningful error message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已按这个方向处理:destroy catch 里不再直接假设 caught value 一定是 Error;会先做 Error 判断,非 Error 用 String(e) 兜底格式化,避免 primitive throw 时二次抛错。这个 thread 目前已 outdated。

Comment thread tegg/standalone/standalone/src/main.ts Outdated
Comment on lines 32 to 35
app.destroy().catch((e) => {
e.message = `[tegg/standalone] destroy tegg failed: ${e.message}`;
console.warn(e);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

When handling a caught exception, defensively check if the caught value is an Error instance before accessing or mutating its message property. Mutating e.message directly can throw a TypeError in strict mode if e is a primitive value. Use String(e) as a fallback to safely format the error message.

Suggested change
app.destroy().catch((e) => {
e.message = `[tegg/standalone] destroy tegg failed: ${e.message}`;
console.warn(e);
});
app.destroy().catch((e) => {
const msg = e instanceof Error ? e.message : String(e);
console.warn('[tegg/standalone] destroy tegg failed: ' + msg);
});
References
  1. When creating a new Error from a caught exception, check if the caught value is an 'Error' instance before accessing its 'message' property. Use 'String(err)' as a fallback for non-Error values to ensure a meaningful error message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已处理:main 成功/失败路径里都会等待 app destroy,并且 destroy catch 对非 Error 值做 String(e) 兜底,不再直接改 e.message。这个 thread 目前已 outdated。

@gxkl gxkl changed the title [WIP] feat(tegg): declarative module plugin mechanism feat(tegg): declarative module plugin mechanism Jul 6, 2026
@gxkl gxkl requested a review from killagu July 6, 2026 00:20
@gxkl gxkl marked this pull request as ready for review July 6, 2026 01:38
Copilot AI review requested due to automatic review settings July 6, 2026 01:38
@gxkl gxkl force-pushed the feat/module-plugin-core branch from 5001fb6 to 79594a6 Compare July 6, 2026 01:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a declarative “module plugin” mechanism for Tegg so eggModule packages can contribute framework inner objects and lifecycle hooks without host boot-code registration, and updates both the standalone host and egg host to boot in a two-phase order that guarantees graph build hooks are registered before GlobalGraph.build() consumes them.

Changes:

  • Add @InnerObjectProto / @EggLifecycleProto (five lifecycle variants) and runtime support via an InnerObjectLoadUnit instantiated before business load units.
  • Refactor standalone host: rename RunnerStandaloneApp, add bundle-manifest support, and update/expand module-plugin test coverage.
  • Update egg host boot wiring (ModuleHandler / EggModuleLoader) to instantiate the InnerObjectLoadUnit before loading business units; migrate built-in AOP/DAL/ConfigSource hooks to module-plugin form; document the architecture in wiki/.

Reviewed changes

Copilot reviewed 127 out of 127 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
wiki/log.md Logs the new module-plugin architecture work and host ordering constraints.
wiki/index.md Adds an index entry for the new module-plugin concept page.
wiki/concepts/tegg-module-plugin.md Documents the declarative module-plugin design and two-phase boot ordering.
tegg/standalone/standalone/tsdown.config.ts Configures unused-check ignore list for framework module-plugin deps.
tegg/standalone/standalone/test/ModulePlugin.test.ts Adds standalone integration coverage for lifecycle protos + inner objects.
tegg/standalone/standalone/test/index.test.ts Updates tests for StandaloneApp, adds logger option and DAL cleanup coverage.
tegg/standalone/standalone/test/fixtures/logger-option/package.json Adds a fixture eggModule for logger injection tests.
tegg/standalone/standalone/test/fixtures/logger-option/foo.ts Fixture runner validating injected logger inner object.
tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Runner.ts Fixture validating LoadUnit lifecycle proto timing + DI.
tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/package.json Adds eggModule metadata for the lifecycle-proto fixture.
tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/FooLoadUnitHook.ts Fixture hook that mutates business load unit protos during preCreate.
tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Foo.ts Fixture inner object proto used by lifecycle hook DI.
tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/package.json Adds eggModule metadata for LoadUnitInstance lifecycle fixture.
tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/FooLoadUnitInstanceHook.ts Fixture LoadUnitInstance hook mutating created objects.
tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/Foo.ts Fixture runner exposing mutation performed by instance hook.
tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/package.json Adds eggModule metadata for private-inner-object injection failure test.
tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/innerBar.ts Fixture private inner object proto.
tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/foo.ts Fixture business proto that incorrectly injects private inner object.
tegg/standalone/standalone/test/fixtures/inner-object-proto/package.json Adds eggModule metadata for inner-object injection fixture.
tegg/standalone/standalone/test/fixtures/inner-object-proto/innerBar.ts Fixture PUBLIC inner object proto with DI dependency on another inner object.
tegg/standalone/standalone/test/fixtures/inner-object-proto/foo.ts Fixture business proto injecting a PUBLIC inner object.
tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/package.json Adds eggModule metadata for EggPrototype lifecycle fixture.
tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/FooEggPrototypeHook.ts Fixture prototype hook writing metadata from prototype creation.
tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/Foo.ts Fixture runner reading data set by prototype hook.
tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/package.json Adds eggModule metadata for EggObject lifecycle fixture.
tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/FooEggObjectHook.ts Fixture object hook mutating created object.
tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/Foo.ts Fixture runner reading mutation from object hook.
tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/package.json Adds eggModule metadata for EggContext lifecycle fixture.
tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/FooEggContextHook.ts Fixture context hook initializing context state.
tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/Foo.ts Fixture runner reading ctx state via ContextHandler.
tegg/standalone/standalone/test/fixtures/dal-manager-inject/package.json Adds eggModule metadata for DAL manager injection fixture.
tegg/standalone/standalone/test/fixtures/dal-manager-inject/foo.ts Fixture pinning PUBLIC mysqlDataSourceManager injection surface.
tegg/standalone/standalone/src/StandaloneLoadUnit.ts Removes legacy standalone “inner object as a load unit” implementation.
tegg/standalone/standalone/src/StandaloneInnerObjectProto.ts Removes legacy standalone inner-object prototype implementation.
tegg/standalone/standalone/src/StandaloneInnerObject.ts Removes legacy standalone inner-object EggObject implementation.
tegg/standalone/standalone/src/StandaloneApp.ts New standalone host implementing two-phase boot with InnerObjectLoadUnit.
tegg/standalone/standalone/src/Runner.ts Removes old standalone host (replaced by StandaloneApp).
tegg/standalone/standalone/src/main.ts Updates main entry to use StandaloneApp and introduces appMain.
tegg/standalone/standalone/src/index.ts Updates exports to expose StandaloneApp instead of removed legacy types.
tegg/standalone/standalone/src/EggModuleLoader.ts Adds manifest/loaderFS support and exposes moduleDescriptors for inner unit build.
tegg/standalone/standalone/src/ConfigSourceLoadUnitHook.ts Removes standalone-specific ConfigSource hook (now host-agnostic module plugin).
tegg/standalone/standalone/package.json Switches framework deps to module-plugin packages and adds loader-fs/tegg-config.
tegg/plugin/tegg/test/ModulePlugin.test.ts Adds app-mode integration coverage for module-plugin lifecycle protos + inner objects.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/package.json Adds ESM test app fixture root.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/PluginHooks.ts Fixture module plugin hooks (LoadUnit + EggObject lifecycle protos).
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/package.json Adds eggModule metadata for the fixture module.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/InnerRegistry.ts Fixture inner objects with PRIVATE/PUBLIC semantics and DI.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/HelloService.ts Fixture business singleton injecting a PUBLIC inner object.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/plugin.ts Enables teggConfig and tegg plugin for app-mode fixture.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/module.json Declares the fixture module path for scanning.
tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/config.default.ts Minimal keys config for egg mock app boot.
tegg/plugin/tegg/test/BundledAppBoot.test.ts Extends timeout to accommodate additional boots/manifest generation.
tegg/plugin/tegg/src/lib/ModuleHandler.ts Instantiates InnerObjectLoadUnit before business units; reverses teardown ordering.
tegg/plugin/tegg/src/lib/ModuleConfigLoader.ts Adds bundle-mode tolerance when module dirs don’t exist on disk.
tegg/plugin/tegg/src/lib/EggModuleLoader.ts Splits initGraph() vs load(), adds manifest collection changes, promotes enabled plugins reliably.
tegg/plugin/tegg/src/app.ts Removes imperative registration for config-source + multi-instance hook (now module plugins / vestigial removed).
tegg/plugin/tegg/package.json Removes export for deleted ConfigSourceLoadUnitHook.
tegg/plugin/dal/src/lib/TransactionPrototypeHook.ts Converts DAL transaction hook to module-plugin lifecycle proto + DI-injected deps.
tegg/plugin/dal/src/lib/MysqlDataSourceManagerObject.ts Adds PUBLIC inner-object wrapper exposing DAL manager to business modules.
tegg/plugin/dal/src/lib/DalTableEggPrototypeHook.ts Converts DAL table hook to module-plugin lifecycle proto with DI logger.
tegg/plugin/dal/src/lib/DalModuleLoadUnitHook.ts Converts DAL load-unit hook to module-plugin + adds @LifecycleDestroy cleanup.
tegg/plugin/dal/src/index.ts Exports the new MysqlDataSourceManagerObject.
tegg/plugin/dal/src/app.ts Removes imperative hook registration; retains egg-side manager cleanup in beforeClose.
tegg/plugin/dal/package.json Adds export mapping for MysqlDataSourceManagerObject.
tegg/plugin/config/test/ReadModule.test.ts Updates expectations to account for optional framework module references.
tegg/plugin/config/src/lib/ModuleScanner.ts Resolves framework dir via getFrameworkPath and scans framework modules as optional.
tegg/plugin/config/src/lib/ConfigSourceLoadUnitHook.ts Converts config-source hook into host-agnostic module-plugin lifecycle proto.
tegg/plugin/config/src/app.ts Adds bundle-mode tolerance for missing external module dirs/configs.
tegg/plugin/config/package.json Marks tegg-config as an eggModule and exports ConfigSourceLoadUnitHook.
tegg/plugin/aop/test/aop.test.ts Increases timeout to avoid slow-runner failures.
tegg/plugin/aop/src/InnerObjects.ts Re-exports AOP module-plugin classes for scanning under the aop plugin eggModule.
tegg/plugin/aop/src/app.ts Removes imperative hook registration; documents which hooks remain host-registered.
tegg/plugin/aop/package.json Marks aop plugin as an eggModule and exports InnerObjects entry.
tegg/core/types/test/snapshots/index.test.ts.snap Snapshot updates for new exported constants/types.
tegg/core/types/src/metadata/model/ProtoDescriptor.ts Extends InjectObjectDescriptor with optional?: boolean.
tegg/core/types/src/core-decorator/Prototype.ts Adds EGG_INNER_OBJECT_PROTO_IMPL_TYPE constant.
tegg/core/types/src/core-decorator/model/index.ts Exposes EggLifecycleInfo type.
tegg/core/types/src/core-decorator/model/EggLifecycleInfo.ts Adds EggLifecycleInfo model type.
tegg/core/types/src/core-decorator/InnerObjectProto.ts Adds InnerObjectProto params type.
tegg/core/types/src/core-decorator/index.ts Exports EggLifecycleProto + InnerObjectProto types.
tegg/core/types/src/core-decorator/EggLifecycleProto.ts Adds EggLifecycleProto param types.
tegg/core/types/package.json Adds export map entries for new core-decorator typings.
tegg/core/test-util/src/LoaderUtil.ts Reuses production loader classification to match inner-object diversion behavior in tests.
tegg/core/tegg/test/snapshots/helper.test.ts.snap Snapshot updates for new exports.
tegg/core/tegg/test/snapshots/exports.test.ts.snap Snapshot updates for new exports/decorators.
tegg/core/tegg/test/snapshots/dal.test.ts.snap Snapshot updates for DAL exports.
tegg/core/runtime/test/InnerObjectLoadUnit.test.ts Adds integration tests for inner objects, DI wiring, lifecycle proto registration, and error cases.
tegg/core/runtime/test/snapshots/index.test.ts.snap Snapshot updates for new runtime exports.
tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts Adds proto/object for host-provided inner objects (provided-instance factory).
tegg/core/runtime/src/impl/InnerObjectLoadUnitInstance.ts Implements instantiation + auto register/deregister of lifecycle protos by type.
tegg/core/runtime/src/impl/InnerObjectLoadUnitBuilder.ts Builds a dedicated inner-object proto graph (toposort/cycle detect/missing dep errors).
tegg/core/runtime/src/impl/InnerObjectLoadUnit.ts Introduces the host-agnostic InnerObjectLoadUnit type.
tegg/core/runtime/src/impl/index.ts Exposes new inner-object runtime types and EggInnerObjectImpl.
tegg/core/runtime/src/impl/EggInnerObjectImpl.ts Adds inner-object EggObject implementation that only runs decorator-declared self lifecycle.
tegg/core/metadata/test/ModuleDescriptorDumper.test.ts Updates dumper tests to include innerObjectClazzList.
tegg/core/metadata/test/snapshots/index.test.ts.snap Snapshot updates for new metadata exports.
tegg/core/metadata/src/model/ProtoDescriptorHelper.ts Supports define-vs-instance module/unit fields for diverted protos.
tegg/core/metadata/src/model/ModuleDescriptor.ts Adds innerObjectClazzList and fixes JSON dumping on Windows path escaping.
tegg/core/metadata/src/model/graph/ProtoGraphUtils.ts Extracts proto dependency resolution utilities + name index for perf.
tegg/core/metadata/src/model/graph/index.ts Exports ProtoGraphUtils.
tegg/core/metadata/src/model/graph/GlobalGraph.ts Uses ProtoGraphUtils + lazy name index to avoid O(n·m·n) scanning.
tegg/core/metadata/src/impl/InjectObjectPrototypeFinder.ts Extracts inject-proto selection logic for reuse and optional handling.
tegg/core/metadata/src/impl/index.ts Exports EggInnerObjectPrototypeImpl + InjectObjectPrototypeFinder.
tegg/core/metadata/src/impl/EggPrototypeBuilder.ts Refactors prototype building to use InjectObjectPrototypeFinder.
tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts Adds proto impl type for inner objects built from metadata descriptors.
tegg/core/loader/test/LoaderInnerObject.test.ts Adds coverage that inner-object classes are diverted and included in manifest decorated files.
tegg/core/loader/test/fixtures/modules/module-with-inner-object/package.json Adds test module fixture with eggModule metadata.
tegg/core/loader/test/fixtures/modules/module-with-inner-object/HelloService.ts Fixture business singleton proto.
tegg/core/loader/test/fixtures/modules/module-with-inner-object/FetchRouter.ts Fixture inner object proto.
tegg/core/loader/test/fixtures/modules/module-with-inner-object/ControllerHook.ts Fixture lifecycle proto injecting inner object.
tegg/core/loader/src/LoaderUtil.ts Tightens glob excludes to also omit test/** and coverage/**.
tegg/core/loader/src/LoaderFactory.ts Diverts inner-object protos into innerObjectClazzList ahead of business clazzList.
tegg/core/core-decorator/test/inner-object-decorators.test.ts Adds tests for InnerObjectProto/EggLifecycleProto metadata semantics.
tegg/core/core-decorator/test/snapshots/index.test.ts.snap Snapshot updates for new decorator exports.
tegg/core/core-decorator/src/util/PrototypeUtil.ts Adds metadata flags for inner objects + lifecycle protos and stores lifecycle type metadata.
tegg/core/core-decorator/src/decorator/InnerObjectProto.ts Adds InnerObjectProto decorator mapping to inner-object proto impl type + flagging.
tegg/core/core-decorator/src/decorator/index.ts Exports new decorators.
tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts Adds EggLifecycleProto decorator + five typed variants.
tegg/core/aop-runtime/test/aop-runtime.test.ts Updates tests to inject CrosscutAdviceFactory via DI field instead of constructor.
tegg/core/aop-runtime/test/snapshots/index.test.ts.snap Snapshot updates for new AOP runtime export.
tegg/core/aop-runtime/src/LoadUnitAopHook.ts Converts to module-plugin lifecycle proto with injected CrosscutAdviceFactory.
tegg/core/aop-runtime/src/index.ts Exports AopGraphHookRegistrar.
tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts Converts to module-plugin lifecycle proto with injected CrosscutAdviceFactory.
tegg/core/aop-runtime/src/EggObjectAopHook.ts Converts to module-plugin EggObject lifecycle proto.
tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts Adds declarative registration of GlobalGraph build hooks via @LifecyclePostInject.
tegg/core/aop-runtime/package.json Adds lifecycle dep and removes eggModule declaration from aop-runtime package.
tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts Marks CrosscutAdviceFactory as an inner-object proto for module-plugin instantiation.

Comment thread tegg/standalone/standalone/src/main.ts Outdated
Comment on lines 10 to 13
export async function preLoad(cwd: string, dependencies?: StandaloneAppOptions['dependencies']): Promise<void> {
try {
await Runner.preLoad(cwd, dependencies);
await StandaloneApp.preLoad(cwd, dependencies);
} catch (e) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已处理:preLoad() 增加并透传 frameworkDeps,和 StandaloneApp.preLoad() 保持一致,避免 framework module plugins 在 preload metadata 中缺失。这个 thread 目前已 outdated。

Copilot AI review requested due to automatic review settings July 6, 2026 01:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 127 out of 127 changed files in this pull request and generated 1 comment.

Comment on lines 59 to 63
`"name": ${JSON.stringify(clazz.name)},` +
(PrototypeUtil.getFilePath(clazz)
? `"filePath": "${path.relative(moduleDescriptor.unitPath, PrototypeUtil.getFilePath(clazz)!)}"`
? `"filePath": ${JSON.stringify(path.relative(moduleDescriptor.unitPath, PrototypeUtil.getFilePath(clazz)!))}`
: '') +
'}'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已处理:stringifyClazz() 改成先组装普通对象,再 JSON.stringify,避免缺 filePath 时生成带尾逗号的非法 JSON。这个 thread 目前已 outdated。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tegg/core/loader/src/LoaderFactory.ts (1)

61-62: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Give each module its own multiInstanceClazzList. Declaring the array outside the loop makes every ModuleDescriptor share the same list, so later modules mutate earlier descriptors and GlobalGraph.create() attributes multi-instance classes to the wrong module.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/loader/src/LoaderFactory.ts` around lines 61 - 62, Each
ModuleDescriptor is currently sharing the same multiInstanceClazzList, so later
iterations can mutate earlier descriptors and confuse GlobalGraph.create()
attribution. Move the multiInstanceClazzList initialization so it is created per
module inside the LoaderFactory logic that builds each ModuleDescriptor, and
make sure each descriptor receives its own fresh array before pushing
multi-instance classes.
tegg/standalone/standalone/src/main.ts (1)

10-19: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Forward frameworkDeps through preLoad
preLoad only accepts dependencies, so callers can’t warm the same framework module set that main/StandaloneApp.preLoad scan at boot. Add frameworkDeps here and pass it through.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/standalone/standalone/src/main.ts` around lines 10 - 19, The preLoad
wrapper only forwards dependencies, so callers cannot pass the same
frameworkDeps set that main and StandaloneApp.preLoad use during boot. Update
preLoad to accept frameworkDeps alongside dependencies, and pass both through to
StandaloneApp.preLoad so the same module set can be warmed consistently.
🧹 Nitpick comments (5)
tegg/plugin/config/src/lib/ModuleScanner.ts (1)

25-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider logging swallowed errors for diagnosability.

The catch-all silently discards any error from getFrameworkPath (not just "no framework resolvable" cases, e.g. malformed package.json). A debug(...) call here would help diagnose unexpected misconfiguration without changing behavior.

♻️ Suggested addition
   private resolveFrameworkDir(): string | undefined {
     try {
       return getFrameworkPath({ baseDir: this.baseDir });
-    } catch {
+    } catch (err) {
+      debug('resolveFrameworkDir failed: %o', err);
       // No package.json or no resolvable framework next to the app (e.g.
       // bare unit fixtures without node_modules) — app modules only.
       return undefined;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/plugin/config/src/lib/ModuleScanner.ts` around lines 25 - 33, The
resolveFrameworkDir() method in ModuleScanner swallows all errors from
getFrameworkPath(), so add a debug(...) log inside the catch before returning
undefined. Use the same baseDir context from this.baseDir and keep the fallback
behavior unchanged, but make sure unexpected failures (not just missing
framework cases) are observable for diagnosis.
tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts (1)

99-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared prototype-construction path EggInnerObjectPrototypeImpl.create() repeats the same field assembly and InjectObjectPrototypeFinder/IdenticalUtil.createProtoId flow as EggPrototypeBuilder.create()/build(). A shared helper would reduce the chance of the two implementations drifting apart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts` around lines 99 -
141, EggInnerObjectPrototypeImpl.create() duplicates the prototype assembly
logic already present in EggPrototypeBuilder.create()/build(), so refactor the
shared construction path into a common helper and have both call it. Keep the
existing behavior for filepath, qualifiers, inject type/objects,
InjectObjectPrototypeFinder.findInjectObjectPrototypes, and
IdenticalUtil.createProtoId, but centralize the field mapping so the two
implementations cannot drift apart.
tegg/core/types/src/core-decorator/EggLifecycleProto.ts (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Literal union collapses to string, losing autocomplete for the five known lifecycle types.

TypeScript widens 'LoadUnit' | ... | string to just string, so IDEs won't suggest the five official lifecycle type names when authors declare custom EggLifecycleProto({ type: ... }) calls.

♻️ Proposed fix using the `string & {}` trick to retain literal autocomplete
 export interface CommonEggLifecycleProtoParams extends InnerObjectProtoParams {
-  type: 'LoadUnit' | 'LoadUnitInstance' | 'EggObject' | 'EggPrototype' | 'EggContext' | string;
+  type: 'LoadUnit' | 'LoadUnitInstance' | 'EggObject' | 'EggPrototype' | 'EggContext' | (string & {});
 }

Please confirm this project's TypeScript version supports this pattern as expected (it's a long-standing, TS-team-acknowledged trick, not an official feature).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/types/src/core-decorator/EggLifecycleProto.ts` around lines 3 - 5,
The CommonEggLifecycleProtoParams.type union in EggLifecycleProto is collapsing
to plain string, which removes autocomplete for the known lifecycle names.
Update the type definition to preserve the five literal options while still
allowing custom strings, using the standard string & {}-style workaround in
CommonEggLifecycleProtoParams. Keep the change localized to the type declaration
so EggLifecycleProto and related EggLifecycleProto({ type: ... }) call sites
retain IDE suggestions without changing runtime behavior.
tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts (1)

78-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

getMetaData looks up metadata on a plain factory closure, which never carries decorator metadata.

this.objFactory is an arrow function created ad hoc by the builder (() => innerObject.obj), never a decorated class, so MetadataUtil.getMetaData here will effectively always resolve to undefined. If any consumer of EggPrototype.getMetaData (e.g. AOP/crosscut logic) relies on this for provided inner objects, it will silently no-op instead of surfacing a clear "not supported" signal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts` around lines 78 - 80,
getMetaData on ProvidedInnerObjectProto is reading decorator metadata from
objFactory, but objFactory is just the builder’s plain arrow closure and will
never have metadata. Update ProvidedInnerObjectProto.getMetaData to stop
delegating to MetadataUtil.getMetaData on objFactory, and instead either return
a clear unsupported result or throw a descriptive error for provided inner
objects so EggPrototype consumers like AOP/crosscut logic do not silently no-op.
tegg/plugin/tegg/test/ModulePlugin.test.ts (1)

9-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider adding multi-app regression coverage for this lifecycle change.

This exercises the new module-plugin boot order in ModuleHandler (inner-object load unit instantiated before business load units) with a single app instance. As per coding guidelines, "When changing loader, runtime, lifecycle, or eventbus behavior, add or update multi-app regression coverage (for example tests like MultiApp.test.ts) to verify two concurrent apps do not cross-talk." Given ModuleHandler.init()/destroy() ordering was substantially reworked, a companion test booting two concurrent module-plugin-app-style apps would guard against state leaking across TeggScope bags.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/plugin/tegg/test/ModulePlugin.test.ts` around lines 9 - 51, Add
multi-app regression coverage for the ModuleHandler lifecycle change by creating
a test that boots two concurrent module-plugin-app instances and verifies their
TeggScope state does not cross-talk. Use the existing ModulePlugin.test.ts setup
and the ModuleHandler init/destroy behavior as reference points, and assert both
apps independently load HelloService, initialize inner-object load units before
business load units, and keep their createdLoadUnits and innerRegistry state
isolated.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tegg/plugin/tegg/src/lib/ModuleConfigLoader.ts`:
- Around line 59-71: The module resolution tolerance logic is duplicated in
ModuleConfigLoader and is already drifting from the similar code in the config
plugin and StandaloneApp. Extract the shared “resolve path, check fs.existsSync,
then fall back to reference.name and skip config loading” flow into a common
helper on ModuleConfigUtil (or equivalent) and update ModuleConfigLoader to call
it, so the modulePath/moduleDirExists/moduleName/defaultConfig handling stays
consistent across all callers.

In `@tegg/plugin/tegg/src/lib/ModuleHandler.ts`:
- Around line 116-132: The teardown logic in ModuleHandler currently stops on
the first thrown error in the load unit destruction loops, which can skip later
cleanup including the final `#innerObjectLoadUnit` teardown. Update the destroy
path around the loadUnitInstances, loadUnits, and `#innerObjectLoadUnit` blocks to
isolate each destroyLoadUnitInstance/destroyLoadUnit call so one failure does
not abort the remaining cleanup. Collect any errors during the loops in
ModuleHandler and rethrow an aggregate after all teardown steps have been
attempted.
- Around line 84-107: The init flow in ModuleHandler.init only assigns
loadUnitInstances after all later steps succeed, which leaves the inner-object
instance untracked if a boot error happens after
instantiateInnerObjectLoadUnit(). Update ModuleHandler.init so the
innerObjectInstance is stored in this.loadUnitInstances immediately after it is
created, then append each LoadUnitInstance created in the loop; keep the later
CompatibleUtil and loadUnit handling unchanged.

In `@tegg/standalone/standalone/src/main.ts`:
- Around line 40-47: The success-path cleanup in main/appMain is inconsistent
because app.destroy() is not awaited before returning, which can let
StandaloneApp resolve before TeggScope teardown finishes; change the finally
block to await the destroy promise just like the init-failure path so scope
cleanup completes before exit. Also update the .catch in that same app.destroy()
call to follow the existing Error-guard pattern used in this file, so non-Error
rejections are handled safely without mutating e.message directly. Use the
app.destroy() cleanup logic and the appMain/main flow as the key locations, and
add or update a regression test covering sequential StandaloneApp/MultiApp-style
teardown ordering to confirm no scope cross-talk.

In `@tegg/standalone/standalone/src/StandaloneApp.ts`:
- Around line 160-164: The per-app config selection in StandaloneApp should not
mutate the process-global ModuleConfigUtil.configNames, since that breaks
isolation between concurrent StandaloneApp instances. Update `#initRuntime`,
doDestroy, and the ModuleConfigUtil.readModuleNameSync/loadModuleConfigSync path
so configNames is passed or stored via TeggScope-backed instance state instead
of a shared static, keeping each app’s env-specific module.default/module.<env>
lookup independent.

In `@wiki/concepts/tegg-module-plugin.md`:
- Around line 5-13: The source_files list is missing the actual host-entrypoint
sources that back the egg-host feeding claim. Update the wiki page’s
source_files in the tegg-module-plugin section to include the plugin boot
entrypoints that register the hard-fed inner-object lists, such as the app.ts
entrypoints in the AOP, config, and DAL plugins, alongside the existing runtime
and handler symbols. Keep the listed sources aligned with the claim’s
traceability requirements so the major code paths are represented.

---

Outside diff comments:
In `@tegg/core/loader/src/LoaderFactory.ts`:
- Around line 61-62: Each ModuleDescriptor is currently sharing the same
multiInstanceClazzList, so later iterations can mutate earlier descriptors and
confuse GlobalGraph.create() attribution. Move the multiInstanceClazzList
initialization so it is created per module inside the LoaderFactory logic that
builds each ModuleDescriptor, and make sure each descriptor receives its own
fresh array before pushing multi-instance classes.

In `@tegg/standalone/standalone/src/main.ts`:
- Around line 10-19: The preLoad wrapper only forwards dependencies, so callers
cannot pass the same frameworkDeps set that main and StandaloneApp.preLoad use
during boot. Update preLoad to accept frameworkDeps alongside dependencies, and
pass both through to StandaloneApp.preLoad so the same module set can be warmed
consistently.

---

Nitpick comments:
In `@tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts`:
- Around line 99-141: EggInnerObjectPrototypeImpl.create() duplicates the
prototype assembly logic already present in
EggPrototypeBuilder.create()/build(), so refactor the shared construction path
into a common helper and have both call it. Keep the existing behavior for
filepath, qualifiers, inject type/objects,
InjectObjectPrototypeFinder.findInjectObjectPrototypes, and
IdenticalUtil.createProtoId, but centralize the field mapping so the two
implementations cannot drift apart.

In `@tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts`:
- Around line 78-80: getMetaData on ProvidedInnerObjectProto is reading
decorator metadata from objFactory, but objFactory is just the builder’s plain
arrow closure and will never have metadata. Update
ProvidedInnerObjectProto.getMetaData to stop delegating to
MetadataUtil.getMetaData on objFactory, and instead either return a clear
unsupported result or throw a descriptive error for provided inner objects so
EggPrototype consumers like AOP/crosscut logic do not silently no-op.

In `@tegg/core/types/src/core-decorator/EggLifecycleProto.ts`:
- Around line 3-5: The CommonEggLifecycleProtoParams.type union in
EggLifecycleProto is collapsing to plain string, which removes autocomplete for
the known lifecycle names. Update the type definition to preserve the five
literal options while still allowing custom strings, using the standard string &
{}-style workaround in CommonEggLifecycleProtoParams. Keep the change localized
to the type declaration so EggLifecycleProto and related EggLifecycleProto({
type: ... }) call sites retain IDE suggestions without changing runtime
behavior.

In `@tegg/plugin/config/src/lib/ModuleScanner.ts`:
- Around line 25-33: The resolveFrameworkDir() method in ModuleScanner swallows
all errors from getFrameworkPath(), so add a debug(...) log inside the catch
before returning undefined. Use the same baseDir context from this.baseDir and
keep the fallback behavior unchanged, but make sure unexpected failures (not
just missing framework cases) are observable for diagnosis.

In `@tegg/plugin/tegg/test/ModulePlugin.test.ts`:
- Around line 9-51: Add multi-app regression coverage for the ModuleHandler
lifecycle change by creating a test that boots two concurrent module-plugin-app
instances and verifies their TeggScope state does not cross-talk. Use the
existing ModulePlugin.test.ts setup and the ModuleHandler init/destroy behavior
as reference points, and assert both apps independently load HelloService,
initialize inner-object load units before business load units, and keep their
createdLoadUnits and innerRegistry state isolated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5819719-d3f0-4e63-9f9f-28a26cccc964

📥 Commits

Reviewing files that changed from the base of the PR and between d55efa3 and 79594a6.

⛔ Files ignored due to path filters (8)
  • tegg/core/aop-runtime/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/core-decorator/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/metadata/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/runtime/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/dal.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/helper.test.ts.snap is excluded by !**/*.snap
  • tegg/core/types/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (119)
  • tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts
  • tegg/core/aop-runtime/package.json
  • tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts
  • tegg/core/aop-runtime/src/EggObjectAopHook.ts
  • tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts
  • tegg/core/aop-runtime/src/LoadUnitAopHook.ts
  • tegg/core/aop-runtime/src/index.ts
  • tegg/core/aop-runtime/test/aop-runtime.test.ts
  • tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts
  • tegg/core/core-decorator/src/decorator/InnerObjectProto.ts
  • tegg/core/core-decorator/src/decorator/index.ts
  • tegg/core/core-decorator/src/util/PrototypeUtil.ts
  • tegg/core/core-decorator/test/inner-object-decorators.test.ts
  • tegg/core/loader/src/LoaderFactory.ts
  • tegg/core/loader/src/LoaderUtil.ts
  • tegg/core/loader/test/LoaderInnerObject.test.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/ControllerHook.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/FetchRouter.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/HelloService.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/package.json
  • tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts
  • tegg/core/metadata/src/impl/EggPrototypeBuilder.ts
  • tegg/core/metadata/src/impl/InjectObjectPrototypeFinder.ts
  • tegg/core/metadata/src/impl/index.ts
  • tegg/core/metadata/src/model/ModuleDescriptor.ts
  • tegg/core/metadata/src/model/ProtoDescriptorHelper.ts
  • tegg/core/metadata/src/model/graph/GlobalGraph.ts
  • tegg/core/metadata/src/model/graph/ProtoGraphUtils.ts
  • tegg/core/metadata/src/model/graph/index.ts
  • tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
  • tegg/core/runtime/src/impl/EggInnerObjectImpl.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnit.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitBuilder.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitInstance.ts
  • tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts
  • tegg/core/runtime/src/impl/index.ts
  • tegg/core/runtime/test/InnerObjectLoadUnit.test.ts
  • tegg/core/test-util/src/LoaderUtil.ts
  • tegg/core/types/package.json
  • tegg/core/types/src/core-decorator/EggLifecycleProto.ts
  • tegg/core/types/src/core-decorator/InnerObjectProto.ts
  • tegg/core/types/src/core-decorator/Prototype.ts
  • tegg/core/types/src/core-decorator/index.ts
  • tegg/core/types/src/core-decorator/model/EggLifecycleInfo.ts
  • tegg/core/types/src/core-decorator/model/index.ts
  • tegg/core/types/src/metadata/model/ProtoDescriptor.ts
  • tegg/plugin/aop/package.json
  • tegg/plugin/aop/src/InnerObjects.ts
  • tegg/plugin/aop/src/app.ts
  • tegg/plugin/aop/test/aop.test.ts
  • tegg/plugin/config/package.json
  • tegg/plugin/config/src/app.ts
  • tegg/plugin/config/src/lib/ConfigSourceLoadUnitHook.ts
  • tegg/plugin/config/src/lib/ModuleScanner.ts
  • tegg/plugin/config/test/ReadModule.test.ts
  • tegg/plugin/dal/package.json
  • tegg/plugin/dal/src/app.ts
  • tegg/plugin/dal/src/index.ts
  • tegg/plugin/dal/src/lib/DalModuleLoadUnitHook.ts
  • tegg/plugin/dal/src/lib/DalTableEggPrototypeHook.ts
  • tegg/plugin/dal/src/lib/MysqlDataSourceManagerObject.ts
  • tegg/plugin/dal/src/lib/TransactionPrototypeHook.ts
  • tegg/plugin/tegg/package.json
  • tegg/plugin/tegg/src/app.ts
  • tegg/plugin/tegg/src/lib/EggModuleLoader.ts
  • tegg/plugin/tegg/src/lib/ModuleConfigLoader.ts
  • tegg/plugin/tegg/src/lib/ModuleHandler.ts
  • tegg/plugin/tegg/test/BundledAppBoot.test.ts
  • tegg/plugin/tegg/test/ModulePlugin.test.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/config.default.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/module.json
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/plugin.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/HelloService.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/InnerRegistry.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/PluginHooks.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/package.json
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/package.json
  • tegg/standalone/standalone/package.json
  • tegg/standalone/standalone/src/ConfigSourceLoadUnitHook.ts
  • tegg/standalone/standalone/src/EggModuleLoader.ts
  • tegg/standalone/standalone/src/Runner.ts
  • tegg/standalone/standalone/src/StandaloneApp.ts
  • tegg/standalone/standalone/src/StandaloneInnerObject.ts
  • tegg/standalone/standalone/src/StandaloneInnerObjectProto.ts
  • tegg/standalone/standalone/src/StandaloneLoadUnit.ts
  • tegg/standalone/standalone/src/index.ts
  • tegg/standalone/standalone/src/main.ts
  • tegg/standalone/standalone/test/ModulePlugin.test.ts
  • tegg/standalone/standalone/test/fixtures/dal-manager-inject/foo.ts
  • tegg/standalone/standalone/test/fixtures/dal-manager-inject/package.json
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/FooEggContextHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/FooEggObjectHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/FooEggPrototypeHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/foo.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/innerBar.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/package.json
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/foo.ts
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/innerBar.ts
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/FooLoadUnitInstanceHook.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/FooLoadUnitHook.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Runner.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/logger-option/foo.ts
  • tegg/standalone/standalone/test/fixtures/logger-option/package.json
  • tegg/standalone/standalone/test/index.test.ts
  • tegg/standalone/standalone/tsdown.config.ts
  • wiki/concepts/tegg-module-plugin.md
  • wiki/index.md
  • wiki/log.md
💤 Files with no reviewable changes (6)
  • tegg/standalone/standalone/src/ConfigSourceLoadUnitHook.ts
  • tegg/standalone/standalone/src/StandaloneInnerObject.ts
  • tegg/standalone/standalone/src/StandaloneLoadUnit.ts
  • tegg/standalone/standalone/src/Runner.ts
  • tegg/plugin/tegg/package.json
  • tegg/standalone/standalone/src/StandaloneInnerObjectProto.ts

Comment thread tegg/plugin/tegg/src/lib/ModuleConfigLoader.ts Outdated
Comment thread tegg/plugin/tegg/src/lib/ModuleHandler.ts Outdated
Comment thread tegg/plugin/tegg/src/lib/ModuleHandler.ts Outdated
Comment thread tegg/standalone/standalone/src/main.ts
Comment on lines +160 to +164
// load module.yml and module.env.yml by default
// Always set configNames for this app invocation, since destroy() clears it
// asynchronously and may not have completed before the next app is created.
ModuleConfigUtil.configNames = opts.env ? ['module.default', `module.${opts.env}`] : ['module.default'];
}

@coderabbitai coderabbitai Bot Jul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Mutating the process-global ModuleConfigUtil.configNames breaks the per-app isolation this file otherwise guarantees via TeggScope.

Every other piece of per-app mutable state in this class (#moduleConfigs, #runtimeConfig, #moduleReferences, loadUnits, scopeBag, ...) is instance-scoped, and the class comments explicitly promise "multiple StandaloneApps in one process stay isolated." ModuleConfigUtil.configNames, however, is set as a static/global value in #initRuntime and cleared globally in doDestroy via ModuleConfigUtil.setConfigNames(undefined). The comment on Line 162 itself concedes the race: "since destroy() clears it asynchronously and may not have completed before the next app is created." If two StandaloneApp instances are alive concurrently (e.g. two appMain() calls in flight, or app B booting while app A is still tearing down), one app's env-specific config file names (module.<env>) can silently leak into or get cleared out from under the other, causing the wrong module config to load — undermining the multi-app isolation this rewrite is meant to provide.

As per coding guidelines, "In Tegg source code, do not introduce new process-global mutable runtime state (for example static fields, Maps, or singletons that hold per-app data); use TeggScope-backed slots for per-app state instead." Consider threading configNames explicitly through ModuleConfigUtil.readModuleNameSync/loadModuleConfigSync calls (or backing it with a TeggScope slot resolved via this.scopeBag) instead of a shared static field.

Also applies to: 389-389

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/standalone/standalone/src/StandaloneApp.ts` around lines 160 - 164, The
per-app config selection in StandaloneApp should not mutate the process-global
ModuleConfigUtil.configNames, since that breaks isolation between concurrent
StandaloneApp instances. Update `#initRuntime`, doDestroy, and the
ModuleConfigUtil.readModuleNameSync/loadModuleConfigSync path so configNames is
passed or stored via TeggScope-backed instance state instead of a shared static,
keeping each app’s env-specific module.default/module.<env> lookup independent.

Source: Coding guidelines

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

这里不改。当前 ModuleConfigUtil.configNames 已经不是 process-global mutable state:getter/setter 内部走 TeggScope slot(CONFIG_NAMES_SLOT)。Standalone 的 init() / destroy() 都包在 this.runInScope(this.scopeBag, ...) 里,所以 set/clear configNames 都落在当前 app scope,不会跨 StandaloneApp 泄漏。这里保留 ModuleConfigUtil.configNames 这个 API 入口,是为了复用既有 module config 读取路径,不再额外引入一套参数穿透。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f "ModuleConfig.ts" tegg/core/common-util/src
echo "---"
rg -n "configNames|CONFIG_NAMES_SLOT|setConfigNames" tegg/core/common-util/src/ModuleConfig.ts -A3 -B3

Length of output: 2980


Confirmed — configNames is backed by a TeggScope slot, not process-global state, so the isolation concern doesn't apply as originally described.

@gxkl 确认了,ModuleConfigUtil.configNames 的 getter/setter 确实通过 TeggScope slot(CONFIG_NAMES_SLOT)存取,而不是进程级全局静态字段,init()/destroy() 也都在各自 scopeBag 内执行,所以不存在跨 StandaloneApp 实例互相污染的问题。撤回这条意见,感谢澄清。

Comment thread wiki/concepts/tegg-module-plugin.md
Comment thread tegg/plugin/aop/src/app.ts Outdated
this.eggObjectAopHook = new EggObjectAopHook();
}

configDidLoad(): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

直接删掉就好了?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已改:空 hook 方法直接删掉,不再保留空实现。后续把 AOP context hook 也改成 inner object 后,app boot 里不再需要这类手动空占位。

Comment thread tegg/plugin/aop/src/app.ts Outdated
this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook);
this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook);
this.app.eggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook);
this.app.eggContextLifecycleUtil.deleteLifecycle(this.aopContextHook);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

这行为什么还在?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已改:这里原来是手动注册/删除 AopContextHook,现在改成 AOP 模块自己的 inner object/lifecycle proto,由 inner object load unit 管理生命周期,不再需要 app boot 里手动挂载这行。

@InnerObjectProto({ name: 'mysqlDataSourceManager', accessLevel: AccessLevel.PUBLIC })
export class MysqlDataSourceManagerObject {
constructor() {
return MysqlDataSourceManager.instance;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

这个显得很奇怪了,应该可以改成直接继承而不是返回一个 instance?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已改,而且不再只处理 MysqlDataSourceManager 这个特例:DAL 三个 manager 都改成由 dal 模块自己声明 inner object(MysqlDataSourceManager / SqlMapManager / TableModelManager),消费侧通过 DI 获取;同时移除了原来的 constructor return instance wrapper 和 app.mysqlDataSourceManager 兼容扩展,避免静态单例与 inner object 混用。

// every object they may hook has been destroyed.
if (this.loadUnitInstances) {
for (const instance of this.loadUnitInstances) {
for (const instance of [...this.loadUnitInstances].reverse()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

这里行为变了?

@gxkl gxkl Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

更新:这块后续 review 又指出,不能只保证 InnerObjectLoadUnit 最后销毁,还必须保证 inner-object instance 不早于 business LoadUnitFactory.destroyLoadUnit() 销毁,否则 lifecycle proto 会提前注销。已在 commit f347717 精确调整为:先反向销毁 business load unit instances,再反向销毁 business load units,最后销毁 inner-object instance 和 inner-object load unit。这样 AOP/DAL/config 等 lifecycle protos 在业务实例销毁和业务 load unit metadata 销毁期间都仍然可用;同时保留逐个 safe destroy + AggregateError 汇总,避免前一个销毁失败导致后续 load unit 不清理。

@killagu

killagu commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Code Review: 声明式 module plugin 机制

整体架构清晰,happy path 测试充分。但本次评审最核心的主题是:hook 的投递方式从"无条件命令式注册"变成了"扫描 + 提升(promote)流水线",而这条流水线的每一种失败模式都是静默跳过 —— 若干现实部署形态会在没有任何报错的情况下丢失 @Transactional 和 AOP 织入。以下发现经过逐条对抗性验证(14 个候选中 12 个确认、2 个证伪)。

发现(按严重程度排序)

1. tegg/plugin/config/src/lib/ModuleScanner.ts:25 — 多层自定义框架会静默丢失所有内置 DAL/AOP hook

扫描器只解析一个 framework 目录(getFrameworkPath,不走继承链),且只读它 package.json 里的直接依赖。在 app → my-framework → egg 的分层结构下(@eggjs/dal-plugin/aop-plugin 是 egg 的依赖而非 my-framework 的),egg 的插件加载器仍会沿完整 eggPath 链启用这些插件,但 module 扫描永远找不到它们的 eggModule 包 —— 而插件 app.ts 里原有的无条件 registerLifecycle 已被删除。结果:@Transactional 静默失去事务、crosscut/pointcut 通知静默不织入,且没有任何断言能检测到 hook 缺失。现有唯一的自定义框架 fixture 把 module 直接声明为框架依赖,所以测试覆盖不到这条路径。

2. tegg/plugin/tegg/src/lib/EggModuleLoader.ts:62 — bundle 模式下框架 module plugin 永远无法被 promote,hook 被静默跳过

manifest 生成只跑 loadMetadata(不经过 didLoad 阶段的 promote 循环),框架引用以 optional: true 被固化进 manifest;restore 时 ref.path 指向不存在<outputDir>/node_modules/<pkg>,而 #findPackageRoot 对真实存在的目录做 realpath —— 字符串精确比较永远不可能相等,promote 静默落空,ModuleHandler.ts:48 直接 continue 跳过 teggAop/teggDal。打包后的应用原本必然获得的 AOP 织入现在会静默消失。建议:改用 module 名字匹配(两侧都有)而不是磁盘路径相等,并至少在静默 continue 处加一行 coreLogger 日志。(另有一个潜在隐患:plugin.path 指向的是入口目录(src//dist/),任何在该目录带嵌套 package.json stub 的包会让 #findPackageRoot 停在错误的根 —— 仓库内暂无包命中,但第三方 tshy 双构建布局会。)

3. tegg/plugin/tegg/src/lib/ModuleHandler.ts:48 — 被禁用插件的 hook 现在照样运行

该处只判断 moduleDescriptor.optional === true,没有任何 plugin enable 检查。插件直接声明在应用自身 package.json dependencies 里(常规安装方式)时,扫描产出的引用是非 optional(optionalundefined),于是配置了 dal: { enable: false } 之后 DalModuleLoadUnitHook 仍被实例化、其 preCreate 仍会 fork/创建数据源 —— 插件已禁用却照样开 DB 连接、照样织入 AOP。本 PR 之前注册在插件 app.ts 启动逻辑里,天然受 enable 门控。

4. tegg/core/runtime/src/impl/InnerObjectLoadUnitBuilder.ts:58 — inner object 的 proto id 跨模块冲突,同名即启动崩溃

所有 module plugin 的 inner object 共用 instanceModuleName = INNER_OBJECT_LOAD_UNIT_NAME 和相同的默认 qualifier,而 createProtoId 既不含 defineModuleName 也不含 accessLevel。两个互不相识的 module plugin 各自声明 @InnerObjectProto() class FooHook {}(即使都是 PRIVATE)会产生相同 id → 抛 duplicate inner object proto(业务 proto 依靠各自 module 的 LoadUnitName qualifier 可以共存,inner object 不行)。同一 id 机制也破坏了 line 138 的 provided-object 路径:standalone 调用方传 innerObjectHandlers: { mysqlDataSourceManager: [{obj: mock}] }(旧 Runner 上合法的覆盖用法)现在会抛 duplicate provided inner object,因为 dal-plugin 总会被扫描并注册该名字。且报错信息无法定位是哪两个模块冲突。

5. tegg/core/runtime/src/impl/InnerObjectLoadUnitInstance.ts:41 — 自定义 lifecycle type 被 API 接受但必然启动崩溃

装饰器参数类型是刻意开放的(type: 'LoadUnit' | ... | string),core-decorator 还有测试明确断言 'CustomLifecycle' 能正常装饰 —— 但运行时用硬编码的 5 键 map 分发,未命中直接抛 unknown lifecycle type,且没有任何注册 API 可扩展。建议:要么把类型收窄为 5 个字面量,要么提供 lifecycle type 注册表。

6. tegg/standalone/standalone/src/main.ts:63 — 已弃用但仍被支持的 RunnerOptions.innerObjects 被静默丢弃

旧 Runner 支持它(且优先级高于 innerObjectHandlers);新 main() 只转发 innerObjectHandlers,StandaloneAppOptions 没有 innerObjects 字段。JS 调用方的 main(cwd, { innerObjects: { httpClient } }) 对象静默消失 → @Inject() httpClient 启动即失败且毫无提示。PR 描述声称"main() 不变",与此矛盾 —— 要么继续支持,要么抛出弃用错误。

7. tegg/standalone/standalone/src/StandaloneApp.ts:143 — 覆盖优先级反转:框架占位符现在覆盖用户提供的 moduleConfigs/moduleConfig/runtimeConfig

旧 Runner 最后应用 options.innerObjectHandlers(用户 stub 获胜,例如测试里的假 runtimeConfig);新 #createInnerObjects 把框架三件套放在 Object.assign 最后一个参数,用户这几个 key 的条目被静默丢弃并从磁盘重新填充。代码注释表明这是有意为之,但对签名未变的公开 innerObjectHandlers 契约来说是静默的行为破坏 —— 至少应对被覆盖的 key 抛错或告警。

8. tegg/plugin/tegg/src/lib/ModuleHandler.ts:107 — 启动中途失败会泄漏已完全初始化的 inner LoadUnitInstance

init() 先创建 inner instance,但 this.loadUnitInstances 直到最后才赋值,instance 本身没有存到任何字段(只存了 LoadUnit)。若某个业务 module 启动失败,destroy()(经 beforeCloseapp.close() 时可达 —— egg-mock 断言启动失败后的标准 teardown 路径)只会销毁 LoadUnit,永远够不到 instance:lifecycle hook 留在 scope 级 util 里、@LifecycleDestroy 清理(如 DAL 关数据源)不执行、instance 滞留在 LoadUnitInstanceFactory.instanceMap。StandaloneApp 是增量 push 的,没有这个问题 —— 建议对齐该模式,或在 catch 里销毁 inner instance。

已确认但排在前 8 之外

  • wiki 与代码不符:wiki/concepts/tegg-module-plugin.md:52-60 描述的 moduleHandler.registerInnerObjectClazzList()AOP_INNER_OBJECT_CLAZZ_LIST/DAL_INNER_OBJECT_CLAZZ_LIST、"builder 按 class 去重"均不存在;builder 遇重复是抛错,内置 hook 完全经 eggModule 扫描进入。PR 描述也重复了去重的说法。需重写这两条(违反 AGENTS.md "every nontrivial wiki claim should be traceable to raw sources")。
  • 启动开销:framework 扫描现在对每个 app+agent 无条件执行(原先以 pkg.egg.framework 为门控)—— 对框架包递归 glob 加上对 egg 42 个依赖逐个 importResolve+读文件;standalone bundle 模式仍做全量多根磁盘 glob,尽管 buildTeggManifestData 写出的 manifest.moduleReferences 从未被读回(egg 插件侧是消费了的,见 tegg/plugin/config/src/app.ts:57)。
  • 重复代码:EggInnerObjectImplEggObjectImpl 约 220 行的拷贝,唯一差异是一条 hook 解析规则;EggInnerObjectPrototypeImplEggPrototypeImpl + 内联 builder 的逐字拷贝(空子类即可 —— EggObjectFactory 按精确 constructor 分发);standalone 的 buildTeggManifestData 与插件侧逐字节相同。
  • 缺失覆盖:tegg/CLAUDE.md 规则 7 要求 loader/runtime/lifecycle 变更附带多 app 回归覆盖;本 diff 未增加(未发现隔离 bug —— scoped util 与 per-app creator overlay 检查无误 —— 但本 PR 是该 overlay 的第一个真实消费者,而 MultiApp.test.ts 未覆盖 inner object)。

验证阶段被证伪的候选(供参考)

  • LoaderUtil.ts 新增的 !**/test/** 不是行为变化 —— 实测 globby 11 在旧的 !**/test 下就已剪掉 test/ 子树(新代码注释的说法与事实不符)。
  • 怀疑 teggAop barrel 重导出的 hook 类会缺席 bundle manifest 的问题不成立 —— loader 会把 FILE_PATH 重新盖章为被扫描的 barrel 文件(LoaderUtil.ts:134),getDecoratedFiles 因此包含它们;已用真实 LoaderFactory.loadApp 实测验证。

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 9, 2026 12:07
@gxkl gxkl force-pushed the feat/module-plugin-core branch from 79594a6 to facb7f1 Compare July 9, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 154 out of 170 changed files in this pull request and generated 2 comments.

Comment thread tegg/core/aop-runtime/src/LoadUnitAopHook.ts Outdated
Comment on lines 61 to 65
reference: {
name: moduleName,
name: resolved.name,
package: reference.package,
path: reference.path,
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

已改,方案是只修正派生出来的 moduleConfigs[resolved.name].reference.path:这里现在存 resolved.path,与 @eggjs/tegg-config 侧保持一致,避免 relative path 泄漏进 moduleConfigs。注意这不反写 app.moduleReferences 本体,manifest/module reference 的原始语义仍保持不变。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tegg/standalone/standalone/README.md (1)

48-58: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Label the example fence as ts.

This is a TypeScript example, so the bare fence trips markdownlint and is less explicit for readers.

Fix
-```
+```ts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/standalone/standalone/README.md` around lines 48 - 58, The README
example fence is unlabeled, so update the fenced code block in the standalone
example to use the TypeScript language tag. Locate the sample around the await
main call in the README and change the bare triple-backtick fence to a
ts-labeled fence so markdownlint passes and the snippet is clearer for readers.

Source: Linters/SAST tools

🧹 Nitpick comments (3)
tegg/core/core-decorator/src/decorator/DefineModuleQualifier.ts (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use .js extension in import specifier.

As per coding guidelines, Tegg code must use ESM-only imports and .js extensions in import specifiers. The import ../util/index.ts uses a .ts extension instead of .js.

♻️ Proposed fix
-import { QualifierUtil } from '../util/index.ts';
+import { QualifierUtil } from '../util/index.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/core-decorator/src/decorator/DefineModuleQualifier.ts` at line 4,
The import in DefineModuleQualifier should use the ESM .js specifier instead of
a .ts extension. Update the QualifierUtil import in DefineModuleQualifier.ts to
point at the generated .js path in the same relative location, following the
repo’s ESM-only import convention.

Source: Coding guidelines

tegg/core/types/src/core-decorator/EggLifecycleProto.ts (1)

1-2: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use .js extension in import specifiers.

As per coding guidelines, Tegg code must use ESM-only imports and .js extensions in import specifiers. Both imports use .ts extensions.

♻️ Proposed fix
-import type { InnerObjectProtoParams } from './InnerObjectProto.ts';
-import type { EggLifecycleType } from './model/EggLifecycleInfo.ts';
+import type { InnerObjectProtoParams } from './InnerObjectProto.js';
+import type { EggLifecycleType } from './model/EggLifecycleInfo.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/core/types/src/core-decorator/EggLifecycleProto.ts` around lines 1 - 2,
The imports in EggLifecycleProto should follow the ESM guideline by using .js
specifiers instead of .ts. Update the type-only imports from InnerObjectProto
and EggLifecycleInfo in EggLifecycleProto.ts to reference the corresponding .js
paths so the module resolution stays consistent with the rest of Tegg.

Source: Coding guidelines

tegg/plugin/config/src/lib/ModuleScanner.ts (1)

48-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: resolveParentFrameworkDir reads frameworkDir/package.json twice.

The method explicitly reads and parses frameworkDir/package.json to check pkg.egg.framework, then calls this.resolveFrameworkDir(frameworkDir) which internally re-reads the same file via getFrameworkPath. The explicit check is necessary to avoid defaulting to 'egg' when no parent is declared, but the double-read is avoidable if getFrameworkPath accepts a pre-resolved framework name.

♻️ Optional: avoid double-read if getFrameworkPath supports explicit framework
 private resolveParentFrameworkDir(frameworkDir: string): string | undefined {
-  let pkg: { egg?: { framework?: unknown } };
-  try {
-    pkg = JSON.parse(fs.readFileSync(path.join(frameworkDir, 'package.json'), 'utf8'));
-  } catch (err) {
-    debug(
-      'read framework package failed, frameworkDir: %s, err: %s',
-      frameworkDir,
-      err instanceof Error ? err.message : String(err),
-    );
-    return undefined;
-  }
-  const framework = pkg.egg?.framework;
-  if (typeof framework !== 'string' || !framework) {
-    return undefined;
-  }
-  return this.resolveFrameworkDir(frameworkDir);
+  // If getFrameworkPath supports an explicit framework option,
+  // pass the resolved name directly to avoid re-reading package.json:
+  // return getFrameworkPath({ baseDir: frameworkDir, framework });
+  // Otherwise, the current approach is correct — keep as-is.
+  return this.resolveFrameworkDir(frameworkDir);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tegg/plugin/config/src/lib/ModuleScanner.ts` around lines 48 - 65,
`resolveParentFrameworkDir` in `ModuleScanner` is reading
`frameworkDir/package.json` twice: once to inspect `pkg.egg.framework`, then
again indirectly through `resolveFrameworkDir`/`getFrameworkPath`. Keep the
explicit parent-framework check, but refactor so the parsed framework value is
passed through to the framework resolution path instead of re-reading the same
package file, or otherwise update `getFrameworkPath`/`resolveFrameworkDir` to
accept an explicit framework name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tegg/core/aop-runtime/src/AopContextAdviceRegistry.ts`:
- Line 10: `AopContextAdviceRegistry` is currently marked with
`@InnerObjectProto()`, which makes it private and prevents `AopContextHook` from
injecting it across package boundaries. Update the declaration in
`AopContextAdviceRegistry` so the registry is public/exported in the proto
metadata, keeping the existing class name and related injection points intact.

In `@tegg/core/aop-runtime/src/LoadUnitAopHook.ts`:
- Around line 19-20: The injected aopContextAdviceRegistry field in
LoadUnitAopHook should not create its own AopContextAdviceRegistry fallback;
remove the default initializer from the `@Inject`()-decorated property so DI
resolution is the single source of truth. Keep the field injection in
LoadUnitAopHook aligned with AopContextHook by relying on container injection
only, which will make both hooks fail fast on missing dependencies instead of
silently diverging.

In `@tegg/plugin/config/test/ModuleScanner.test.ts`:
- Around line 5-6: The imports in ModuleScanner.test.ts use TypeScript
extensions, which violates the ESM-only import guideline for tegg test files.
Update the ModuleScanner and getFixtures import specifiers to use .js
extensions, matching the pattern used in other tests such as
ModuleConfig.test.ts, and keep the relative paths otherwise unchanged.

In `@wiki/concepts/tegg-module-plugin.md`:
- Around line 5-16: The wiki page’s source_files list is missing the egg-host
loader referenced in the boot-order discussion. Update the list in
tegg-module-plugin docs to include EggModuleLoader so the documented
initGraph()/load() flow is traceable; keep the existing source_files block
aligned with the other plugin/runtime symbols already listed.

---

Outside diff comments:
In `@tegg/standalone/standalone/README.md`:
- Around line 48-58: The README example fence is unlabeled, so update the fenced
code block in the standalone example to use the TypeScript language tag. Locate
the sample around the await main call in the README and change the bare
triple-backtick fence to a ts-labeled fence so markdownlint passes and the
snippet is clearer for readers.

---

Nitpick comments:
In `@tegg/core/core-decorator/src/decorator/DefineModuleQualifier.ts`:
- Line 4: The import in DefineModuleQualifier should use the ESM .js specifier
instead of a .ts extension. Update the QualifierUtil import in
DefineModuleQualifier.ts to point at the generated .js path in the same relative
location, following the repo’s ESM-only import convention.

In `@tegg/core/types/src/core-decorator/EggLifecycleProto.ts`:
- Around line 1-2: The imports in EggLifecycleProto should follow the ESM
guideline by using .js specifiers instead of .ts. Update the type-only imports
from InnerObjectProto and EggLifecycleInfo in EggLifecycleProto.ts to reference
the corresponding .js paths so the module resolution stays consistent with the
rest of Tegg.

In `@tegg/plugin/config/src/lib/ModuleScanner.ts`:
- Around line 48-65: `resolveParentFrameworkDir` in `ModuleScanner` is reading
`frameworkDir/package.json` twice: once to inspect `pkg.egg.framework`, then
again indirectly through `resolveFrameworkDir`/`getFrameworkPath`. Keep the
explicit parent-framework check, but refactor so the parsed framework value is
passed through to the framework resolution path instead of re-reading the same
package file, or otherwise update `getFrameworkPath`/`resolveFrameworkDir` to
accept an explicit framework name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e20b350-6955-41a9-bcbf-d5591ccb540a

📥 Commits

Reviewing files that changed from the base of the PR and between 79594a6 and facb7f1.

⛔ Files ignored due to path filters (23)
  • tegg/core/aop-runtime/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/core-decorator/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/metadata/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/runtime/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/exports.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/helper.test.ts.snap is excluded by !**/*.snap
  • tegg/core/types/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/base-framework/node_modules/base-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/base-framework/node_modules/shared-module-base/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/base-framework/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/chair-framework/node_modules/chair-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/chair-framework/node_modules/shared-module-chair/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-chain/app/node_modules/chair-framework/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-cycle/app/node_modules/base-framework/node_modules/base-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-cycle/app/node_modules/base-framework/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-cycle/app/node_modules/chair-framework/node_modules/chair-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-cycle/app/node_modules/chair-framework/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-module-json/app/node_modules/chair-framework/config/module.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-module-json/app/node_modules/chair-framework/node_modules/framework-config-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-module-json/app/node_modules/chair-framework/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-module-json/app/node_modules/framework-config-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-same-path/app/node_modules/chair-framework/node_modules/chair-module/package.json is excluded by !**/node_modules/**
  • tegg/plugin/config/test/fixtures/framework-same-path/app/node_modules/chair-framework/package.json is excluded by !**/node_modules/**
📒 Files selected for processing (147)
  • tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts
  • tegg/core/aop-runtime/package.json
  • tegg/core/aop-runtime/src/AopContextAdviceRegistry.ts
  • tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts
  • tegg/core/aop-runtime/src/EggObjectAopHook.ts
  • tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts
  • tegg/core/aop-runtime/src/LoadUnitAopHook.ts
  • tegg/core/aop-runtime/src/index.ts
  • tegg/core/aop-runtime/test/aop-runtime.test.ts
  • tegg/core/common-util/src/ModuleConfig.ts
  • tegg/core/common-util/test/ModuleConfig.test.ts
  • tegg/core/core-decorator/src/decorator/DefineModuleQualifier.ts
  • tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts
  • tegg/core/core-decorator/src/decorator/InnerObjectProto.ts
  • tegg/core/core-decorator/src/decorator/index.ts
  • tegg/core/core-decorator/src/util/PrototypeUtil.ts
  • tegg/core/core-decorator/test/decorators.test.ts
  • tegg/core/core-decorator/test/fixtures/decators/QualifierCacheService.ts
  • tegg/core/core-decorator/test/inner-object-decorators.test.ts
  • tegg/core/loader/src/LoaderFactory.ts
  • tegg/core/loader/test/LoaderInnerObject.test.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/ControllerHook.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/FetchRouter.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/HelloService.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/package.json
  • tegg/core/metadata/src/impl/EggInnerObjectPrototypeImpl.ts
  • tegg/core/metadata/src/impl/EggPrototypeBuilder.ts
  • tegg/core/metadata/src/impl/EggPrototypeImpl.ts
  • tegg/core/metadata/src/impl/InjectObjectPrototypeFinder.ts
  • tegg/core/metadata/src/impl/index.ts
  • tegg/core/metadata/src/model/ModuleDescriptor.ts
  • tegg/core/metadata/src/model/ProtoDescriptorHelper.ts
  • tegg/core/metadata/src/model/graph/GlobalGraph.ts
  • tegg/core/metadata/src/model/graph/ProtoGraphUtils.ts
  • tegg/core/metadata/src/model/graph/index.ts
  • tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
  • tegg/core/runtime/src/impl/EggInnerObjectImpl.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnit.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitBuilder.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitInstance.ts
  • tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts
  • tegg/core/runtime/src/impl/index.ts
  • tegg/core/runtime/test/InnerObjectLoadUnit.test.ts
  • tegg/core/test-util/src/LoaderUtil.ts
  • tegg/core/types/package.json
  • tegg/core/types/src/common/ModuleConfig.ts
  • tegg/core/types/src/core-decorator/EggLifecycleProto.ts
  • tegg/core/types/src/core-decorator/InnerObjectProto.ts
  • tegg/core/types/src/core-decorator/Prototype.ts
  • tegg/core/types/src/core-decorator/enum/Qualifier.ts
  • tegg/core/types/src/core-decorator/index.ts
  • tegg/core/types/src/core-decorator/model/EggLifecycleInfo.ts
  • tegg/core/types/src/core-decorator/model/index.ts
  • tegg/core/types/src/metadata/model/ProtoDescriptor.ts
  • tegg/plugin/aop/package.json
  • tegg/plugin/aop/src/InnerObjects.ts
  • tegg/plugin/aop/src/app.ts
  • tegg/plugin/aop/src/lib/AopContextHook.ts
  • tegg/plugin/aop/test/aop.test.ts
  • tegg/plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts
  • tegg/plugin/config/package.json
  • tegg/plugin/config/src/app.ts
  • tegg/plugin/config/src/lib/ConfigSourceLoadUnitHook.ts
  • tegg/plugin/config/src/lib/ModuleScanner.ts
  • tegg/plugin/config/test/ManifestModuleReference.test.ts
  • tegg/plugin/config/test/ModuleScanner.test.ts
  • tegg/plugin/config/test/ReadModule.test.ts
  • tegg/plugin/config/test/fixtures/framework-chain/app/package.json
  • tegg/plugin/config/test/fixtures/framework-cycle/app/package.json
  • tegg/plugin/config/test/fixtures/framework-module-json/app/package.json
  • tegg/plugin/config/test/fixtures/framework-same-path/app/config/module.json
  • tegg/plugin/config/test/fixtures/framework-same-path/app/package.json
  • tegg/plugin/dal/package.json
  • tegg/plugin/dal/src/app.ts
  • tegg/plugin/dal/src/app/extend/application.ts
  • tegg/plugin/dal/src/lib/DalModuleLoadUnitHook.ts
  • tegg/plugin/dal/src/lib/DalTableEggPrototypeHook.ts
  • tegg/plugin/dal/src/lib/DataSource.ts
  • tegg/plugin/dal/src/lib/MysqlDataSourceManager.ts
  • tegg/plugin/dal/src/lib/SqlMapManager.ts
  • tegg/plugin/dal/src/lib/TableModelManager.ts
  • tegg/plugin/dal/src/lib/TransactionPrototypeHook.ts
  • tegg/plugin/dal/test/transaction.test.ts
  • tegg/plugin/tegg/package.json
  • tegg/plugin/tegg/src/app.ts
  • tegg/plugin/tegg/src/lib/EggModuleLoader.ts
  • tegg/plugin/tegg/src/lib/ModuleConfigLoader.ts
  • tegg/plugin/tegg/src/lib/ModuleHandler.ts
  • tegg/plugin/tegg/test/BundledAppBoot.test.ts
  • tegg/plugin/tegg/test/ManifestCollection.test.ts
  • tegg/plugin/tegg/test/ModulePlugin.test.ts
  • tegg/plugin/tegg/test/MultiApp.test.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/config.default.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/module.json
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/plugin.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/HelloService.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/InnerRegistry.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/PluginHooks.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/package.json
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/package.json
  • tegg/plugin/tegg/test/fixtures/apps/multi-app-isolation/modules/counter-module/CounterService.ts
  • tegg/plugin/tegg/test/lib/EggModuleLoader.test.ts
  • tegg/plugin/tegg/test/lib/ModuleHandler.test.ts
  • tegg/standalone/standalone/README.md
  • tegg/standalone/standalone/package.json
  • tegg/standalone/standalone/src/ConfigSourceLoadUnitHook.ts
  • tegg/standalone/standalone/src/EggModuleLoader.ts
  • tegg/standalone/standalone/src/Runner.ts
  • tegg/standalone/standalone/src/StandaloneApp.ts
  • tegg/standalone/standalone/src/StandaloneInnerObject.ts
  • tegg/standalone/standalone/src/StandaloneInnerObjectProto.ts
  • tegg/standalone/standalone/src/StandaloneLoadUnit.ts
  • tegg/standalone/standalone/src/index.ts
  • tegg/standalone/standalone/src/main.ts
  • tegg/standalone/standalone/test/ModulePlugin.test.ts
  • tegg/standalone/standalone/test/fixtures/dal-manager-inject/foo.ts
  • tegg/standalone/standalone/test/fixtures/dal-manager-inject/package.json
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/FooEggContextHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/FooEggObjectHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/FooEggPrototypeHook.ts
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/foo.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/innerBar.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/package.json
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/foo.ts
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/innerBar.ts
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/FooLoadUnitInstanceHook.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/FooLoadUnitHook.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Runner.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/logger-option/foo.ts
  • tegg/standalone/standalone/test/fixtures/logger-option/package.json
  • tegg/standalone/standalone/test/index.test.ts
  • tegg/standalone/standalone/tsdown.config.ts
  • tools/egg-bundler/src/lib/ManifestLoader.ts
  • wiki/concepts/tegg-module-plugin.md
  • wiki/index.md
  • wiki/log.md
💤 Files with no reviewable changes (9)
  • tegg/plugin/dal/src/app/extend/application.ts
  • tegg/plugin/dal/package.json
  • tegg/standalone/standalone/src/StandaloneInnerObject.ts
  • tegg/standalone/standalone/src/StandaloneInnerObjectProto.ts
  • tegg/standalone/standalone/src/Runner.ts
  • tegg/standalone/standalone/src/ConfigSourceLoadUnitHook.ts
  • tegg/plugin/tegg/package.json
  • tegg/standalone/standalone/src/StandaloneLoadUnit.ts
  • tegg/plugin/dal/src/app.ts
✅ Files skipped from review due to trivial changes (31)
  • tegg/plugin/config/test/fixtures/framework-chain/app/package.json
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/package.json
  • tegg/core/types/src/core-decorator/model/index.ts
  • tegg/core/metadata/src/model/graph/index.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/FetchRouter.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/FooEggContextHook.ts
  • wiki/index.md
  • tegg/standalone/standalone/test/fixtures/dal-manager-inject/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/package.json
  • tegg/plugin/config/test/fixtures/framework-same-path/app/package.json
  • tegg/plugin/config/test/fixtures/framework-cycle/app/package.json
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/package.json
  • tegg/plugin/config/test/fixtures/framework-module-json/app/package.json
  • tegg/core/metadata/src/impl/EggPrototypeImpl.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/module.json
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/package.json
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/innerBar.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Foo.ts
  • tegg/plugin/config/src/lib/ConfigSourceLoadUnitHook.ts
  • tegg/standalone/standalone/tsdown.config.ts
  • tegg/core/metadata/src/impl/index.ts
  • tegg/standalone/standalone/test/fixtures/logger-option/package.json
  • tegg/core/types/src/core-decorator/enum/Qualifier.ts
  • tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
  • tegg/plugin/aop/src/InnerObjects.ts
  • wiki/log.md
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/package.json
  • tegg/plugin/tegg/test/BundledAppBoot.test.ts
🚧 Files skipped from review as they are similar to previous changes (68)
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/FooEggPrototypeHook.ts
  • tegg/core/types/src/core-decorator/index.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/ControllerHook.ts
  • tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts
  • tegg/standalone/standalone/test/fixtures/egg-context-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/FooLoadUnitInstanceHook.ts
  • tegg/core/aop-runtime/src/EggObjectAopHook.ts
  • tegg/core/types/src/core-decorator/Prototype.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/config.default.ts
  • tegg/core/aop-runtime/src/index.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/Runner.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/package.json
  • tegg/standalone/standalone/test/fixtures/load-unit-instance-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/foo.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/HelloService.ts
  • tegg/standalone/standalone/test/fixtures/logger-option/foo.ts
  • tegg/standalone/standalone/test/fixtures/invalid-inner-object-inject/foo.ts
  • tegg/core/types/src/core-decorator/InnerObjectProto.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/InnerRegistry.ts
  • tegg/core/loader/test/fixtures/modules/module-with-inner-object/HelloService.ts
  • tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts
  • tegg/core/types/src/metadata/model/ProtoDescriptor.ts
  • tegg/plugin/aop/package.json
  • tegg/standalone/standalone/src/index.ts
  • tegg/core/core-decorator/src/decorator/index.ts
  • tegg/core/core-decorator/src/decorator/InnerObjectProto.ts
  • tegg/standalone/standalone/test/ModulePlugin.test.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/FooEggObjectHook.ts
  • tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/package.json
  • tegg/plugin/aop/test/aop.test.ts
  • tegg/plugin/tegg/test/ModulePlugin.test.ts
  • tegg/core/loader/test/LoaderInnerObject.test.ts
  • tegg/core/metadata/src/model/ProtoDescriptorHelper.ts
  • tegg/core/metadata/src/model/graph/GlobalGraph.ts
  • tegg/plugin/config/package.json
  • tegg/core/aop-runtime/test/aop-runtime.test.ts
  • tegg/core/aop-runtime/package.json
  • tegg/core/metadata/src/impl/InjectObjectPrototypeFinder.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/modules/plugin-module/PluginHooks.ts
  • tegg/standalone/standalone/test/fixtures/inner-object-proto/innerBar.ts
  • tegg/plugin/config/test/ReadModule.test.ts
  • tegg/standalone/standalone/test/fixtures/load-unit-lifecycle-proto/FooLoadUnitHook.ts
  • tegg/plugin/tegg/test/fixtures/apps/module-plugin-app/config/plugin.ts
  • tegg/core/runtime/src/impl/ProvidedInnerObjectProto.ts
  • tegg/standalone/standalone/test/fixtures/egg-object-lifecycle-proto/Foo.ts
  • tegg/standalone/standalone/test/fixtures/egg-prototype-lifecycle-proto/Foo.ts
  • tegg/plugin/dal/src/lib/DalModuleLoadUnitHook.ts
  • tegg/core/types/package.json
  • tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts
  • tegg/standalone/standalone/src/main.ts
  • tegg/core/test-util/src/LoaderUtil.ts
  • tegg/core/runtime/src/impl/index.ts
  • tegg/core/core-decorator/src/util/PrototypeUtil.ts
  • tegg/core/metadata/src/model/ModuleDescriptor.ts
  • tegg/core/metadata/src/impl/EggPrototypeBuilder.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnit.ts
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitInstance.ts
  • tegg/core/core-decorator/test/inner-object-decorators.test.ts
  • tegg/plugin/tegg/src/app.ts
  • tegg/standalone/standalone/package.json
  • tegg/core/runtime/src/impl/InnerObjectLoadUnitBuilder.ts
  • tegg/standalone/standalone/src/EggModuleLoader.ts
  • tegg/plugin/tegg/src/lib/ModuleHandler.ts
  • tegg/core/metadata/src/model/graph/ProtoGraphUtils.ts
  • tegg/core/runtime/src/impl/EggInnerObjectImpl.ts
  • tegg/standalone/standalone/src/StandaloneApp.ts
  • tegg/standalone/standalone/test/index.test.ts

Comment thread tegg/core/aop-runtime/src/AopContextAdviceRegistry.ts
Comment thread tegg/core/aop-runtime/src/LoadUnitAopHook.ts Outdated
Comment thread tegg/plugin/config/test/ModuleScanner.test.ts Outdated
Comment thread wiki/concepts/tegg-module-plugin.md Outdated
@gxkl gxkl force-pushed the feat/module-plugin-core branch from facb7f1 to 80b9d49 Compare July 9, 2026 12:33
Copilot AI review requested due to automatic review settings July 9, 2026 12:39
Copilot AI review requested due to automatic review settings July 9, 2026 15:33
@gxkl gxkl force-pushed the feat/module-plugin-core branch from f347717 to b774e07 Compare July 9, 2026 15:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 167 out of 188 changed files in this pull request and generated 1 comment.

Comment on lines +99 to +102
try {
await fs.writeFile(tmpPath, ModuleDescriptorDumper.stringifyDescriptor(desc));
await fs.rename(tmpPath, dumpPath);
} finally {
Copilot AI review requested due to automatic review settings July 9, 2026 15:46
@gxkl gxkl force-pushed the feat/module-plugin-core branch from b774e07 to 5c42a6d Compare July 9, 2026 15:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 168 out of 189 changed files in this pull request and generated 2 comments.

Comment on lines +1 to +11
import assert from 'node:assert/strict';
import path from 'node:path';

import { EggPrototypeNotFound } from '@eggjs/metadata';
import { describe, it } from 'vitest';

import { main } from '../src/index.ts';

describe('standalone/standalone/test/ModulePlugin.test.ts', () => {
const getFixture = (name: string) => path.join(__dirname, 'fixtures', name);

Comment on lines +181 to +187
const resolvedRef = {
path: resolved.path,
name: reference.name,
package: reference.package,
optional: reference.optional,
loaderType: reference.loaderType,
};
@gxkl gxkl requested a review from killagu July 9, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants