Skip to content

feat(egg,tegg): add manifest write-side + tegg collection#5846

Merged
killagu merged 1 commit intonextfrom
feat/tegg-manifest
Mar 30, 2026
Merged

feat(egg,tegg): add manifest write-side + tegg collection#5846
killagu merged 1 commit intonextfrom
feat/tegg-manifest

Conversation

@killagu
Copy link
Copy Markdown
Contributor

@killagu killagu commented Mar 30, 2026

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

Summary by CodeRabbit

  • New Features

    • Metadata-only startup option; app can generate and persist a startup manifest and load metadata without starting the agent.
    • Module loading can consume manifest-provided file lists to speed discovery and avoid full filesystem scans.
    • Runtime API to register and persist extension metadata for reuse.
  • Tests

    • Added suites validating manifest-driven loading, manifest roundtrips, decorated-file extraction, and extension registration persistence.
  • Chores

    • Added a loader dependency for manifest integration.

Copilot AI review requested due to automatic review settings March 30, 2026 06:01
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Manifest 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

Cohort / File(s) Summary
Core Manifest API
packages/core/src/loader/egg_loader.ts, packages/core/src/loader/manifest.ts
Removed extensions parameter from EggLoader.generateManifest(); added private extension collector and ManifestStore.setExtension(name, data); ManifestGenerateOptions.extensions removed and manifest generation uses stored extensions.
Core Manifest Tests
packages/core/test/loader/manifest.test.ts, packages/core/test/loader/manifest_roundtrip.test.ts
Tests updated to use setExtension() for supplying extension data; new tests for set/overwrite/multiple keys and persistence across write/load.
Egg App Lifecycle Integration
packages/egg/src/lib/egg.ts, packages/egg/src/lib/start.ts
Added dumpManifest() and integrated it into ready flow (skips on local unless EGG_MANIFEST=true and if already generated); added metadataOnly?: boolean to startup to skip agent creation/ready and conditional ready broadcast; safer agent close.
Tegg Loader Manifest Support
tegg/core/loader/src/LoaderFactory.ts, tegg/core/loader/src/impl/ModuleLoader.ts
LoaderFactory.loadApp() accepts optional LoadAppManifest and maps manifest descriptors by unitPath; when available, constructs ModuleLoader with precomputedFiles (decoratedFiles) to skip globby. ModuleLoader constructor accepts optional precomputedFiles and loads from it when provided.
Tegg Loader Tests
tegg/core/loader/test/LoaderFactoryManifest.test.ts, tegg/core/loader/test/ModuleLoaderManifest.test.ts
New tests verifying manifest-driven loading, equivalence to normal loading, fallback behavior for mismatched manifests, empty/precomputed behavior, and caching.
Tegg Metadata Export
tegg/core/metadata/src/model/ModuleDescriptor.ts, tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
Added ModuleDescriptorDumper.getDecoratedFiles() to derive relative decorated file paths (dedupe and filter out-of-unit files); tests for correctness and edge cases.
Tegg Plugin - Config & Tegg
tegg/plugin/config/src/app.ts, tegg/plugin/tegg/src/app.ts, tegg/plugin/tegg/src/lib/EggModuleLoader.ts, tegg/plugin/config/package.json
Refactored module discovery to prefer TEGG_MANIFEST_KEY manifest extension; added loadMetadata() lifecycle; EggModuleLoader builds/collects Tegg manifest data and writes it to manifest store; added @eggjs/tegg-loader dependency.
Tegg Plugin Tests
tegg/plugin/tegg/test/ManifestCollection.test.ts
Integration test added to assert Tegg manifest extension presence, moduleReferences/moduleDescriptors content, and decorated files metadata produced during startup.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gxkl
  • fengmk2
  • jerryliang64
  • akitaSummer

Poem

🐰 I hopped through files and left a trace,
Collected extensions in a safer place,
Precomputed lists skip the globbing race,
Manifests saved to disk with gentle pace,
Egg and Tegg now dance — metadata's embrace.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main changes: adding manifest write-side capabilities (dumpManifest, setExtension) and tegg collection/integration (collectTeggManifest, manifest-aware loaders).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tegg-manifest

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 and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 30, 2026

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

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

View logs

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 82.08955% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.39%. Comparing base (6cf9a09) to head (7c1c6ac).
⚠️ Report is 1 commits behind head on next.

Files with missing lines Patch % Lines
tegg/plugin/config/src/app.ts 60.00% 5 Missing and 1 partial ⚠️
tegg/plugin/tegg/src/app.ts 0.00% 2 Missing and 1 partial ⚠️
packages/egg/src/lib/egg.ts 77.77% 2 Missing ⚠️
tegg/plugin/tegg/src/lib/EggModuleLoader.ts 90.90% 1 Missing ⚠️
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.
📢 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.

Copy link
Copy Markdown
Contributor

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

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 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 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) plus metadataOnly startup 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

  • metadataOnly mode can return an application without an agent, but SingleModeApplication still requires agent: SingleModeAgent. Consider making agent optional (or returning a different type when metadataOnly is true) to avoid a TS API contract mismatch and potential runtime undefined access.
export interface SingleModeApplication extends Application {
  agent: SingleModeAgent;
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tegg/plugin/tegg/test/ManifestCollection.test.ts (1)

45-47: Consider typing app.moduleReferences to avoid any.

The any type annotation could be replaced with the proper ModuleReference type 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 any LoaderFactory.registerLoader(EggLoadUnitType.MODULE, ...) customization stops applying as soon as a manifest descriptor is present. Please thread decoratedFiles through 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf9a09 and 99231b8.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • tegg/core/loader/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (17)
  • packages/core/src/loader/egg_loader.ts
  • packages/core/src/loader/manifest.ts
  • packages/core/test/loader/manifest.test.ts
  • packages/core/test/loader/manifest_roundtrip.test.ts
  • packages/egg/src/lib/egg.ts
  • packages/egg/src/lib/start.ts
  • tegg/core/loader/src/LoaderFactory.ts
  • tegg/core/loader/src/impl/ModuleLoader.ts
  • tegg/core/loader/test/LoaderFactoryManifest.test.ts
  • tegg/core/loader/test/ModuleLoaderManifest.test.ts
  • tegg/core/metadata/src/model/ModuleDescriptor.ts
  • tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
  • tegg/plugin/config/package.json
  • tegg/plugin/config/src/app.ts
  • tegg/plugin/tegg/src/app.ts
  • tegg/plugin/tegg/src/lib/EggModuleLoader.ts
  • tegg/plugin/tegg/test/ManifestCollection.test.ts

Copy link
Copy Markdown
Contributor

@jerryliang64 jerryliang64 left a comment

Choose a reason for hiding this comment

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

LGTM

@killagu killagu force-pushed the feat/tegg-manifest branch from 99231b8 to 60902a3 Compare March 30, 2026 06:30
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 30, 2026

Deploying egg with  Cloudflare Pages  Cloudflare Pages

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

View logs

@jerryliang64
Copy link
Copy Markdown
Contributor

关于 loadMetadata() 方法有个疑问:

tegg/plugin/config/src/app.tstegg/plugin/tegg/src/app.ts 中都定义了 async loadMetadata() 方法,但目前 ILifecycleBoot 接口中没有这个 hook,Lifecycle 类也没有调用它的逻辑。

请问这个方法的预期调用时机是什么?是计划在后续 PR 中将 loadMetadata 加入生命周期编排,还是通过其他方式触发(比如 metadataOnly 模式下手动调用)?如果是为后续 PR 预留的,建议加个 // TODO 注释说明。

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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:

  1. Making dumpManifest async and awaiting the write, or
  2. Tracking the pending write promise and awaiting it in beforeClose

Since 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

📥 Commits

Reviewing files that changed from the base of the PR and between 99231b8 and 60902a3.

⛔ Files ignored due to path filters (3)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • tegg/core/loader/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tegg/core/tegg/test/__snapshots__/helper.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (17)
  • packages/core/src/loader/egg_loader.ts
  • packages/core/src/loader/manifest.ts
  • packages/core/test/loader/manifest.test.ts
  • packages/core/test/loader/manifest_roundtrip.test.ts
  • packages/egg/src/lib/egg.ts
  • packages/egg/src/lib/start.ts
  • tegg/core/loader/src/LoaderFactory.ts
  • tegg/core/loader/src/impl/ModuleLoader.ts
  • tegg/core/loader/test/LoaderFactoryManifest.test.ts
  • tegg/core/loader/test/ModuleLoaderManifest.test.ts
  • tegg/core/metadata/src/model/ModuleDescriptor.ts
  • tegg/core/metadata/test/ModuleDescriptorDumper.test.ts
  • tegg/plugin/config/package.json
  • tegg/plugin/config/src/app.ts
  • tegg/plugin/tegg/src/app.ts
  • tegg/plugin/tegg/src/lib/EggModuleLoader.ts
  • tegg/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

@killagu
Copy link
Copy Markdown
Contributor Author

killagu commented Mar 30, 2026

关于 loadMetadata() 方法有个疑问:

tegg/plugin/config/src/app.tstegg/plugin/tegg/src/app.ts 中都定义了 async loadMetadata() 方法,但目前 ILifecycleBoot 接口中没有这个 hook,Lifecycle 类也没有调用它的逻辑。

请问这个方法的预期调用时机是什么?是计划在后续 PR 中将 loadMetadata 加入生命周期编排,还是通过其他方式触发(比如 metadataOnly 模式下手动调用)?如果是为后续 PR 预留的,建议加个 // TODO 注释说明。

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>
Copilot AI review requested due to automatic review settings March 30, 2026 09:34
@killagu killagu force-pushed the feat/tegg-manifest branch from 60902a3 to 7c1c6ac Compare March 30, 2026 09:34
@socket-security
Copy link
Copy Markdown

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.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm ioredis is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: plugins/redis/package.jsonnpm/ioredis@5.8.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ioredis@5.8.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm js-beautify is 100.0% likely obfuscated

Confidence: 1.00

Location: Package overview

From: pnpm-lock.yamlnpm/js-beautify@1.15.4

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/js-beautify@1.15.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@killagu killagu merged commit aa1c3b2 into next Mar 30, 2026
26 of 30 checks passed
@killagu killagu deleted the feat/tegg-manifest branch March 30, 2026 09:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 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 with metadataOnly: true and skip creating an agent, but SingleModeApplication still types agent as required. This makes the public API typings unsound (callers can compile while app.agent is actually undefined). Consider making agent optional when metadataOnly is enabled (e.g. via function overloads or agent?: SingleModeAgent on 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('..')) {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
if (!rel.startsWith('..')) {
// On Windows, path.relative may return an absolute path if on different drives
if (!rel.startsWith('..') && !path.isAbsolute(rel)) {

Copilot uses AI. Check for mistakes.
killagu added a commit that referenced this pull request Mar 30, 2026
## 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>
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