feat(egg,tegg): add manifest write-side + tegg collection#5846
Conversation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughManifest extension data is now collected via ManifestStore.setExtension and embedded into generated manifests; Egg startup can dump manifests (with metadata-only mode); LoaderFactory and ModuleLoader support manifest-driven precomputed file lists; several tests added to validate manifest collection, roundtrips, and manifest-driven loading. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Egg App
participant Loader as EggLoader
participant ManifestStore as ManifestStore
participant FS as Filesystem
App->>App: ready callback
App->>ManifestStore: check manifest.data.generatedAt
alt already generated
ManifestStore-->>App: skip dump
else generate
App->>Loader: generateManifest()
Loader->>Loader: build StartupManifest (reads stored extensions)
Loader-->>App: StartupManifest
App->>ManifestStore: write(baseDir, manifest)
ManifestStore->>FS: persist manifest.json
FS-->>ManifestStore: OK
end
sequenceDiagram
participant TeggPlugin as Tegg Plugin
participant ManifestStore as ManifestStore
participant LoaderFactory as LoaderFactory
participant ModuleLoader as ModuleLoader
participant FS as Filesystem
TeggPlugin->>ManifestStore: getExtension(TEGG_MANIFEST_KEY)
alt manifest exists
ManifestStore-->>LoaderFactory: provide LoadAppManifest
LoaderFactory->>ModuleLoader: new(moduleDir, precomputedFiles)
ModuleLoader->>ModuleLoader: load from precomputedFiles (skip globby)
ModuleLoader-->>LoaderFactory: moduleDescriptors
else no manifest
LoaderFactory->>ModuleLoader: new(moduleDir)
ModuleLoader->>FS: globby(...) to discover files
FS-->>ModuleLoader: file list
ModuleLoader->>LoaderFactory: moduleDescriptors
LoaderFactory->>TeggPlugin: moduleDescriptors
TeggPlugin->>ManifestStore: setExtension(TEGG_MANIFEST_KEY, data)
end
LoaderFactory-->>TeggPlugin: moduleDescriptors
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Deploying egg-v3 with
|
| Latest commit: |
7c1c6ac
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://7f432223.egg-v3.pages.dev |
| Branch Preview URL: | https://feat-tegg-manifest.egg-v3.pages.dev |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## next #5846 +/- ##
==========================================
+ Coverage 85.37% 85.39% +0.02%
==========================================
Files 666 666
Lines 13125 13171 +46
Branches 1519 1522 +3
==========================================
+ Hits 11205 11248 +43
- Misses 1789 1791 +2
- Partials 131 132 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request introduces a startup manifest mechanism to accelerate the loading process by caching module references and decorated file paths. Key changes include the addition of ManifestStore extensions, a setExtension API for plugin data, and the implementation of dumpManifest in the application core to persist this metadata. The LoaderFactory and ModuleLoader have been updated to utilize these precomputed file lists, bypassing expensive globbing operations during subsequent starts. A high-severity issue was identified regarding cross-platform path compatibility in the manifest generation logic.
There was a problem hiding this comment.
Pull request overview
This PR adds a write-side startup manifest pipeline to Egg core and extends Tegg to both consume and produce Tegg-specific manifest data (module references + decorated file lists) to speed up subsequent startups by avoiding repeated file discovery.
Changes:
- Add manifest extension collection (
ManifestStore.setExtension) and automatic manifest dumping (dumpManifest) plusmetadataOnlystartup support. - Teach Tegg loader paths to use manifest-provided
decoratedFiles/module descriptors to skip globby-based discovery, and persist Tegg collection results into the manifest. - Add tests covering decorated file extraction, manifest-based loading roundtrips, and manifest extension persistence.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tegg/plugin/tegg/test/ManifestCollection.test.ts | Adds integration test expecting Tegg manifest extension to be present after app startup. |
| tegg/plugin/tegg/src/lib/EggModuleLoader.ts | Reads Tegg manifest extension to skip discovery; collects and stores Tegg manifest extension when needed. |
| tegg/plugin/tegg/src/app.ts | Adds loadMetadata() hook to collect/store Tegg manifest data in metadata-only mode. |
| tegg/plugin/config/src/app.ts | Uses manifest-provided moduleReferences when available to skip module scanning; supports metadata-only mode. |
| tegg/plugin/config/package.json | Adds @eggjs/tegg-loader dependency for manifest key/type usage. |
| tegg/core/metadata/test/ModuleDescriptorDumper.test.ts | Adds unit tests for ModuleDescriptorDumper.getDecoratedFiles(). |
| tegg/core/metadata/src/model/ModuleDescriptor.ts | Implements ModuleDescriptorDumper.getDecoratedFiles() for manifest generation. |
| tegg/core/loader/test/snapshots/index.test.ts.snap | Updates stable exports snapshot to include TEGG_MANIFEST_KEY. |
| tegg/core/loader/test/ModuleLoaderManifest.test.ts | Tests ModuleLoader behavior with precomputedFiles. |
| tegg/core/loader/test/LoaderFactoryManifest.test.ts | Tests LoaderFactory loadApp manifest roundtrip and fallbacks. |
| tegg/core/loader/src/impl/ModuleLoader.ts | Adds precomputedFiles support to skip globby when manifest data is available. |
| tegg/core/loader/src/LoaderFactory.ts | Adds manifest types/key and loadApp(..., manifest?) support to inject precomputed files. |
| pnpm-lock.yaml | Locks new workspace dependency wiring. |
| packages/egg/src/lib/start.ts | Adds metadataOnly option to skip agent startup in single-process mode. |
| packages/egg/src/lib/egg.ts | Dumps manifest after ready via dumpManifest(). |
| packages/core/test/loader/manifest_roundtrip.test.ts | Updates tests to use setExtension() rather than passing extensions directly. |
| packages/core/test/loader/manifest.test.ts | Updates/extends tests for setExtension() behavior and extension persistence. |
| packages/core/src/loader/manifest.ts | Adds #extensionCollector + setExtension() and wires it into manifest generation. |
| packages/core/src/loader/egg_loader.ts | Simplifies generateManifest() signature now that extensions come from setExtension(). |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
packages/egg/src/lib/start.ts:26
metadataOnlymode can return an application without an agent, butSingleModeApplicationstill requiresagent: SingleModeAgent. Consider makingagentoptional (or returning a different type whenmetadataOnlyis true) to avoid a TS API contract mismatch and potential runtimeundefinedaccess.
export interface SingleModeApplication extends Application {
agent: SingleModeAgent;
}
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
tegg/plugin/tegg/test/ManifestCollection.test.ts (1)
45-47: Consider typingapp.moduleReferencesto avoidany.The
anytype annotation could be replaced with the properModuleReferencetype for better type safety.const appRefNames = app.moduleReferences - .map((r: any) => r.name) + .map((r: { name: string }) => r.name) .sort((a: string, b: string) => a.localeCompare(b));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tegg/plugin/tegg/test/ManifestCollection.test.ts` around lines 45 - 47, The map currently types each entry as any when building appRefNames from app.moduleReferences; replace the any with the correct ModuleReference type (e.g., (r: ModuleReference) => r.name) and import or reference that type where the test file can access it so you get proper type-checking on r.name; leave the sort logic unchanged.tegg/core/loader/src/LoaderFactory.ts (1)
80-83: Keep the manifest fast-path behind the factory abstraction.This branch bypasses
LoaderFactory.createLoader(), so anyLoaderFactory.registerLoader(EggLoadUnitType.MODULE, ...)customization stops applying as soon as a manifest descriptor is present. Please threaddecoratedFilesthrough the factory path, or only use this fast-path when the registered MODULE creator is still the built-in one.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tegg/core/loader/src/LoaderFactory.ts` around lines 80 - 83, The current fast-path creates ModuleLoaderClass(...) directly when manifestDesc exists, bypassing LoaderFactory.createLoader and any custom registrations; update the branch so LoaderFactory.createLoader(moduleReference.path, loaderType, manifestDesc?.decoratedFiles) is used (i.e. thread manifestDesc.decoratedFiles through the factory), or alternatively guard the direct new ModuleLoaderClass(...) so it only runs if the registered MODULE creator is the built-in ModuleLoaderClass; adjust LoaderFactory.createLoader signature and any registerLoader handlers to accept the optional decoratedFiles parameter and pass it through to the MODULE creator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tegg/core/loader/src/LoaderFactory.ts`:
- Around line 70-72: The dynamic import in LoaderFactory.ts currently uses the
TypeScript source specifier './impl/ModuleLoader.ts' (setting ModuleLoaderClass
when manifestMap.size > 0), which causes runtime resolution to look for the .ts
file; change the import to use the emitted ESM specifier
'./impl/ModuleLoader.js' so (await import(...)).ModuleLoader loads the compiled
module at runtime, ensuring ModuleLoaderClass is set correctly. Ensure only the
specifier is updated where ModuleLoaderClass is assigned and any other dynamic
imports follow the same .js extension rule.
In `@tegg/core/metadata/test/ModuleDescriptorDumper.test.ts`:
- Line 12: The test uses __dirname (loadUnitPath) which is undefined in ESM;
replace it by deriving the directory from import.meta.url: import fileURLToPath
and path.dirname (from 'url' and 'path'), compute const __filename =
fileURLToPath(import.meta.url) and const __dirname = path.dirname(__filename),
then use that __dirname when building loadUnitPath (the variable referenced in
ModuleDescriptorDumper.test.ts) so the test works in ESM; add the imports and
the two-line directory computation near the top of the test file before
loadUnitPath is defined.
---
Nitpick comments:
In `@tegg/core/loader/src/LoaderFactory.ts`:
- Around line 80-83: The current fast-path creates ModuleLoaderClass(...)
directly when manifestDesc exists, bypassing LoaderFactory.createLoader and any
custom registrations; update the branch so
LoaderFactory.createLoader(moduleReference.path, loaderType,
manifestDesc?.decoratedFiles) is used (i.e. thread manifestDesc.decoratedFiles
through the factory), or alternatively guard the direct new
ModuleLoaderClass(...) so it only runs if the registered MODULE creator is the
built-in ModuleLoaderClass; adjust LoaderFactory.createLoader signature and any
registerLoader handlers to accept the optional decoratedFiles parameter and pass
it through to the MODULE creator.
In `@tegg/plugin/tegg/test/ManifestCollection.test.ts`:
- Around line 45-47: The map currently types each entry as any when building
appRefNames from app.moduleReferences; replace the any with the correct
ModuleReference type (e.g., (r: ModuleReference) => r.name) and import or
reference that type where the test file can access it so you get proper
type-checking on r.name; leave the sort logic unchanged.
🪄 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: 02228fce-5cf0-4390-afd9-901d58e4f7cb
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltegg/core/loader/test/__snapshots__/index.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (17)
packages/core/src/loader/egg_loader.tspackages/core/src/loader/manifest.tspackages/core/test/loader/manifest.test.tspackages/core/test/loader/manifest_roundtrip.test.tspackages/egg/src/lib/egg.tspackages/egg/src/lib/start.tstegg/core/loader/src/LoaderFactory.tstegg/core/loader/src/impl/ModuleLoader.tstegg/core/loader/test/LoaderFactoryManifest.test.tstegg/core/loader/test/ModuleLoaderManifest.test.tstegg/core/metadata/src/model/ModuleDescriptor.tstegg/core/metadata/test/ModuleDescriptorDumper.test.tstegg/plugin/config/package.jsontegg/plugin/config/src/app.tstegg/plugin/tegg/src/app.tstegg/plugin/tegg/src/lib/EggModuleLoader.tstegg/plugin/tegg/test/ManifestCollection.test.ts
99231b8 to
60902a3
Compare
Deploying egg with
|
| Latest commit: |
7c1c6ac
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://110655d6.egg-cci.pages.dev |
| Branch Preview URL: | https://feat-tegg-manifest.egg-cci.pages.dev |
|
关于
请问这个方法的预期调用时机是什么?是计划在后续 PR 中将 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/egg/src/lib/egg.ts (1)
541-554: Fire-and-forget async write may lose errors or corrupt manifest on shutdown.
ManifestStore.write()is async but the promise is not awaited. If the application shuts down while the write is in progress, the manifest file could be truncated or corrupted. While.catch()handles errors, the warning won't appear if the process exits before the promise settles.Consider either:
- Making
dumpManifestasync and awaiting the write, or- Tracking the pending write promise and awaiting it in
beforeCloseSince manifest data is non-critical for application functionality and only used for subsequent startup optimization, this is low severity but worth noting.
♻️ Option 1: Await the write (simplest)
- dumpManifest(): void { + async dumpManifest(): Promise<void> { try { // Skip if we loaded from a valid manifest (generatedAt is truthy) if (this.loader.manifest.data.generatedAt) { return; } const manifest = this.loader.generateManifest(); - ManifestStore.write(this.baseDir, manifest).catch((err: Error) => { - this.coreLogger.warn('[egg] dumpManifest write error: %s', err.message); - }); + await ManifestStore.write(this.baseDir, manifest); } catch (err: any) { this.coreLogger.warn('[egg] dumpManifest error: %s', err.message); } }Then update the caller at line 193:
- this.dumpManifest(); + this.dumpManifest().catch((err: Error) => { + this.coreLogger.warn('[egg] dumpManifest error: %s', err.message); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/egg/src/lib/egg.ts` around lines 541 - 554, dumpManifest currently calls ManifestStore.write(...) without awaiting it, risking truncated/corrupt manifest on shutdown; update dumpManifest (or surrounding lifecycle) to ensure the write completes: either make dumpManifest async and await ManifestStore.write(this.baseDir, manifest) (and propagate async to callers), or store the returned promise (e.g. this._pendingManifestWrite = ManifestStore.write(...)) and await that promise in the lifecycle method beforeClose so the process waits for completion; reference dumpManifest, ManifestStore.write, and beforeClose when implementing the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/egg/src/lib/egg.ts`:
- Around line 543-546: The manifest write is racy because both processes call
dumpManifest() when loader.manifest.data.generatedAt is falsy and
ManifestStore.write() uses fsp.writeFile() which can lead to concurrent
non-atomic overwrites; fix by making ManifestStore.write() perform an atomic
write (write to a temp file + fs.rename) or by ensuring only one process writes
(e.g., move dumpManifest() invocation to the agent-only ready hook or gate
writes behind an isAgent check), and update references to fsp.writeFile() in
ManifestStore.write() to use the temp-file-then-rename pattern so manifest.json
is replaced atomically.
In `@tegg/core/loader/src/LoaderFactory.ts`:
- Around line 13-17: The manifest shape ManifestModuleReference currently omits
loaderType causing modules rehydrated from manifestTegg.moduleReferences (used
by tegg/plugin/config/src/app.ts) to lose their original loader and default to
EggLoadUnitType.MODULE in loadApp(); fix by adding loaderType?: string to the
ManifestModuleReference interface and ensure any places that build manifest
moduleReferences (e.g., EggModuleLoader.moduleReferences mapping in
EggModuleLoader.ts) copy ref.loaderType through so app.moduleReferences
preserves loaderType when rehydrated.
In `@tegg/plugin/config/src/app.ts`:
- Around line 37-45: The code is using this.app.loader.manifest (manifestTegg)
as a fast-path in loadMetadata() which can read a stale .egg/manifest.json
during manifest generation and cause dumpManifest()/collectTeggManifest() to
miss new modules; fix by gating the fast-path behind an explicit runtime-only
flag or by disabling it when generating the manifest: add a boolean runtime flag
(e.g. app.config.tegg.useManifestFastPath) or detect a "generating manifest"
mode on the loader and only use manifestTegg.moduleReferences when that flag is
true (otherwise fall back to the globby scan and let
EggModuleLoader.collectTeggManifest() write the fresh TEGG_MANIFEST_KEY); update
loadMetadata() to check the new flag/loader state before assigning
moduleReferences from manifestTegg and ensure
dumpManifest()/collectTeggManifest() always generate a fresh manifest when the
flag is off.
---
Nitpick comments:
In `@packages/egg/src/lib/egg.ts`:
- Around line 541-554: dumpManifest currently calls ManifestStore.write(...)
without awaiting it, risking truncated/corrupt manifest on shutdown; update
dumpManifest (or surrounding lifecycle) to ensure the write completes: either
make dumpManifest async and await ManifestStore.write(this.baseDir, manifest)
(and propagate async to callers), or store the returned promise (e.g.
this._pendingManifestWrite = ManifestStore.write(...)) and await that promise in
the lifecycle method beforeClose so the process waits for completion; reference
dumpManifest, ManifestStore.write, and beforeClose when implementing the change.
🪄 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: 31cc8c1c-e4f7-44ce-8bda-f0e7ee5e4b1a
⛔ Files ignored due to path filters (3)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltegg/core/loader/test/__snapshots__/index.test.ts.snapis excluded by!**/*.snaptegg/core/tegg/test/__snapshots__/helper.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (17)
packages/core/src/loader/egg_loader.tspackages/core/src/loader/manifest.tspackages/core/test/loader/manifest.test.tspackages/core/test/loader/manifest_roundtrip.test.tspackages/egg/src/lib/egg.tspackages/egg/src/lib/start.tstegg/core/loader/src/LoaderFactory.tstegg/core/loader/src/impl/ModuleLoader.tstegg/core/loader/test/LoaderFactoryManifest.test.tstegg/core/loader/test/ModuleLoaderManifest.test.tstegg/core/metadata/src/model/ModuleDescriptor.tstegg/core/metadata/test/ModuleDescriptorDumper.test.tstegg/plugin/config/package.jsontegg/plugin/config/src/app.tstegg/plugin/tegg/src/app.tstegg/plugin/tegg/src/lib/EggModuleLoader.tstegg/plugin/tegg/test/ManifestCollection.test.ts
✅ Files skipped from review due to trivial changes (4)
- tegg/plugin/config/package.json
- packages/core/test/loader/manifest_roundtrip.test.ts
- tegg/core/loader/test/ModuleLoaderManifest.test.ts
- tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/core/test/loader/manifest.test.ts
- packages/core/src/loader/egg_loader.ts
- tegg/core/loader/src/impl/ModuleLoader.ts
- tegg/plugin/tegg/src/app.ts
- packages/egg/src/lib/start.ts
- tegg/core/metadata/src/model/ModuleDescriptor.ts
- tegg/core/loader/test/LoaderFactoryManifest.test.ts
- tegg/plugin/tegg/test/ManifestCollection.test.ts
- tegg/plugin/tegg/src/lib/EggModuleLoader.ts
https://github.com/eggjs/egg/pull/5844/changes 5844 的 PR 里定义也调用了这个 api 的。 |
Write-side: - ManifestStore.setExtension() for plugins to register manifest data - dumpManifest() in ready hook auto-generates .egg/manifest.json - metadataOnly mode in startEgg() skips agent for manifest generation Tegg collection: - ModuleDescriptorDumper.getDecoratedFiles() extracts decorated file paths - LoaderFactory.loadApp() accepts LoadAppManifest to skip globby - ModuleLoader supports precomputedFiles to skip file discovery - tegg-config reads moduleReferences from manifest extension - EggModuleLoader.collectTeggManifest() stores tegg data in manifest - TeggManifestExtension type + TEGG_MANIFEST_KEY constant Tests: - ModuleLoader precomputedFiles, LoaderFactory manifest roundtrip - ModuleDescriptorDumper.getDecoratedFiles, ManifestCollection integration - ManifestStore.setExtension roundtrip Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60902a3 to
7c1c6ac
Compare
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 20 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
packages/egg/src/lib/start.ts:30
startEgg()can now run withmetadataOnly: trueand skip creating an agent, butSingleModeApplicationstill typesagentas required. This makes the public API typings unsound (callers can compile whileapp.agentis actually undefined). Consider makingagentoptional whenmetadataOnlyis enabled (e.g. via function overloads oragent?: SingleModeAgenton the interface).
export interface SingleModeApplication extends Application {
agent: SingleModeAgent;
}
export interface SingleModeAgent extends Agent {
app: SingleModeApplication;
}
| if (filePath) { | ||
| const rel = path.relative(desc.unitPath, filePath).replaceAll(path.sep, '/'); | ||
| // Only include files within the module (multiInstanceClazzList is shared) | ||
| if (!rel.startsWith('..')) { |
There was a problem hiding this comment.
getDecoratedFiles() only filters out paths starting with .., but on Windows path.relative() returns an absolute path when unitPath and filePath are on different drives (e.g. C: vs D:). In that case rel will not start with .. and this will incorrectly include an absolute path in the manifest. Consider additionally rejecting path.isAbsolute(rel) (and/or verifying filePath is within desc.unitPath via normalized prefix check) before adding to fileSet.
| if (!rel.startsWith('..')) { | |
| // On Windows, path.relative may return an absolute path if on different drives | |
| if (!rel.startsWith('..') && !path.isAbsolute(rel)) { |
## Summary - `AppWorkerLoader.load()` 在 `metadataOnly` 模式下跳过 `loadRouter()`,避免不必要的路由注册 - 为 `egg-bin manifest generate` 命令的 metadata-only 启动服务 - 新增 6 个测试用例覆盖 metadataOnly 和正常模式对比 ## Context PR #5842 manifest 系列的第三部分。lifecycle 层的 `loadMetadata` / `triggerLoadMetadata` / `metadataOnly` flag 已在 #5844 和 #5846 中合入,本 PR 补全 AppWorkerLoader 侧的守卫。 ## Test plan - [x] metadataOnly 模式:仅调用 `loadMetadata`,跳过正常生命周期钩子 - [x] metadataOnly 模式:`router.stack` 为空(loadRouter 被跳过) - [x] metadataOnly 模式:不创建 agent - [x] 正常模式:生命周期钩子正常触发,路由正常注册 - [x] `pnpm --filter=egg run test test/start.test.ts` 6/6 通过 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added metadata-only initialization mode that conditionally skips router registration during app startup, enabling metadata extraction without full application initialization. * **Tests** * Added test coverage for metadata-only startup mode, verifying correct lifecycle behavior and router initialization is prevented. * Added test suite for standard initialization mode to validate baseline functionality remains unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Write-side:
Tegg collection:
Tests:
Summary by CodeRabbit
New Features
Tests
Chores