Skip to content

fix: add StandaloneAopContextHook to pre-create ContextProto advice#400

Open
jerryliang64 wants to merge 2 commits intoeggjs:masterfrom
jerryliang64:master
Open

fix: add StandaloneAopContextHook to pre-create ContextProto advice#400
jerryliang64 wants to merge 2 commits intoeggjs:masterfrom
jerryliang64:master

Conversation

@jerryliang64
Copy link
Contributor

@jerryliang64 jerryliang64 commented Feb 4, 2026

Checklist
  • npm test passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)
Description of change

Summary by CodeRabbit

  • New Features
    • Standalone mode: improved AOP context lifecycle so context-scoped advice is initialized before and cleaned up after requests, supporting singleton/prototype interactions across multiple runs.
  • Tests
    • Added tests covering singleton prototypes with context-aware advice across multiple requests to validate lifecycle and reuse behavior.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds a StandaloneAopContextHook to pre-create context-scoped AOP advice in standalone mode, wires it into Runner lifecycle for registration and cleanup, exposes the hook via the public index, and includes fixtures and tests demonstrating SingletonProto + ContextProto advice behavior.

Changes

Cohort / File(s) Summary
Public API & Runner
standalone/standalone/index.ts, standalone/standalone/src/Runner.ts
Exported StandaloneAopContextHook; Runner now instantiates and registers the hook with EggContext lifecycle and unregisters it on destruction.
AOP Hook Implementation
standalone/standalone/src/StandaloneAopContextHook.ts
New StandaloneAopContextHook class plus EggPrototypeWithClazz and ProtoToCreate interfaces. Scans load units for aspect advice, validates prototypes, collects CONTEXT-init advices, and registers them in preCreate.
Test fixtures
standalone/standalone/test/fixtures/singleton-aop-module/Tool.ts, standalone/standalone/test/fixtures/singleton-aop-module/main.ts, standalone/standalone/test/fixtures/singleton-aop-module/package.json
Added a singleton-aop-module demonstrating Tool (with ContextProto advice), QueryNode, Graph, and a Main runner to exercise singleton reuse and per-request advice creation.
Tests
standalone/standalone/test/index.test.ts
Added tests verifying AOP behavior for SingletonProto with ContextProto advice across multiple requests and Runner lifecycle use/cleanup.

Sequence Diagram

sequenceDiagram
    participant R as Runner
    participant LU as LoadUnitInstance
    participant H as StandaloneAopContextHook
    participant C as EggContext

    R->>H: new StandaloneAopContextHook(loadUnitInstances)
    H->>LU: scan load units for aspects & advice prototypes
    LU-->>H: return aspect/advice metadata
    H->>H: validate prototypes, collect CONTEXT advices
    R->>C: register H as lifecycle hook (preCreate)
    C->>H: preCreate(ctx)
    H->>C: addProtoToCreate(name, proto) for each collected advice
    R->>H: on destroy -> unregister/cleanup via lifecycle util
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

Suggested Reviewers

  • gxkl
  • killagu

Poem

🐇 A rabbit nudges threads anew,

singletons rest while advice springs through,
hooks prepare the context space,
each request finds a fresh, kind face,
standalone hops — AOP in view.

🚥 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 title accurately describes the main change: introducing StandaloneAopContextHook to pre-create ContextProto advice objects in standalone mode.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jerryliang64, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical runtime issue in the standalone module concerning Aspect-Oriented Programming (AOP). It resolves the 'EggObject not found' error that previously occurred when singleton-scoped objects (SingletonProto) attempted to use context-scoped AOP advice (ContextProto) across different request contexts. By introducing and integrating a StandaloneAopContextHook, the system now ensures that all necessary ContextProto advice objects are instantiated at the start of each new context, thereby guaranteeing consistent and reliable AOP functionality for long-lived objects.

Highlights

  • New StandaloneAopContextHook: Introduced a new StandaloneAopContextHook to manage the lifecycle of AOP advice objects within the standalone module.
  • Pre-creation of ContextProto Advice: The new hook ensures that ContextProto advice objects are proactively identified and created at the beginning of each new EggContext. This is crucial for AOP functionality when SingletonProtos are involved.
  • Fixes AOP 'EggObject not found' Error: This change resolves a bug where AOP would fail for SingletonProtos that depend on ContextProto advice across multiple request contexts, preventing 'EggObject not found' errors.
  • Comprehensive Test Coverage: New test fixtures and test cases have been added to specifically validate the fix, demonstrating the correct behavior of AOP with SingletonProtos and ContextProto advice in multi-request scenarios.
Changelog
  • standalone/standalone/index.ts
    • Exports the newly added StandaloneAopContextHook.
  • standalone/standalone/src/Runner.ts
    • Imports EggContextLifecycleUtil and StandaloneAopContextHook.
    • Adds a private member standaloneAopContextHook to the Runner class.
    • Registers the StandaloneAopContextHook with EggContextLifecycleUtil during the Runner's initialization (init() method).
    • Deletes the StandaloneAopContextHook from EggContextLifecycleUtil during the Runner's destruction (destroy() method).
  • standalone/standalone/src/StandaloneAopContextHook.ts
    • New file defining the StandaloneAopContextHook class.
    • Implements LifecycleHook for EggContext.
    • Constructor iterates through LoadUnitInstances to find EggPrototypes with aspects and identifies ContextProto advice objects.
    • The preCreate method adds these identified ContextProto advice objects to the EggContext for pre-creation.
  • standalone/standalone/test/fixtures/singleton-aop-module/Tool.ts
    • New test fixture file.
    • Defines ToolCallAdvice as a ContextProto advice.
    • Defines Tool as a SingletonProto that uses ToolCallAdvice via @Pointcut.
    • Defines QueryNode as a SingletonProto that injects Tool.
    • Defines Graph as a SingletonProto that simulates storing a bound function, crucial for reproducing the bug scenario.
  • standalone/standalone/test/fixtures/singleton-aop-module/main.ts
    • New test fixture file.
    • Defines a Main runner that orchestrates the test scenario, demonstrating the interaction between SingletonProtos and ContextProto advice across multiple requests.
  • standalone/standalone/test/fixtures/singleton-aop-module/package.json
    • New package.json file for the singleton-aop-module test fixture.
  • standalone/standalone/test/index.test.ts
    • Adds a new test suite 'aop runtime with SingletonProto + ContextProto Advice'.
    • Includes a test case to verify basic functionality when SingletonProto uses ContextProto Advice.
    • Adds a critical test case to ensure the fix works correctly across multiple requests, where SingletonProtos are reused and ContextProto advice needs to be re-created.
Activity
  • The pull request was created by jerryliang64.
  • The primary goal is to fix an AOP issue where ContextProto advice objects were not correctly managed for SingletonProtos across different request contexts.
  • A new StandaloneAopContextHook has been introduced to proactively create ContextProto advice objects when a new context is initialized.
  • The Runner's lifecycle has been updated to register and deregister this new hook.
  • Extensive new test cases have been added to validate the fix, specifically targeting multi-request scenarios that previously led to 'EggObject not found' errors.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 fix for an issue where ContextProto advices were not being created in subsequent requests when used by a SingletonProto in standalone mode. The fix introduces StandaloneAopContextHook, which proactively discovers all context-scoped advices during initialization and ensures they are created with each new context. The implementation is accompanied by a clear test case that reproduces the bug and verifies the fix.

My review focuses on improving the API surface hygiene. I've suggested making a couple of helper interfaces internal to the module as they don't seem to be intended for public consumption.

Overall, this is a well-executed fix for a tricky AOP-related issue.

Comment on lines +7 to +14
export interface EggPrototypeWithClazz extends EggPrototype {
clazz?: EggProtoImplClass;
}

export interface ProtoToCreate {
name: string;
proto: EggPrototype;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The interfaces EggPrototypeWithClazz and ProtoToCreate appear to be internal implementation details for StandaloneAopContextHook. Since index.ts uses export * from './src/StandaloneAopContextHook', these interfaces are exposed as part of the package's public API. To maintain a clean public API and signal that these are for internal use, it's better to define them without exporting.

Suggested change
export interface EggPrototypeWithClazz extends EggPrototype {
clazz?: EggProtoImplClass;
}
export interface ProtoToCreate {
name: string;
proto: EggPrototype;
}
interface EggPrototypeWithClazz extends EggPrototype {
clazz?: EggProtoImplClass;
}
interface ProtoToCreate {
name: string;
proto: EggPrototype;
}

Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@standalone/standalone/test/fixtures/singleton-aop-module/main.ts`:
- Around line 15-19: Replace the private-field access this.graph['initialized']
with a public accessor on Graph (e.g., add a method isInitialized(): boolean)
and call that here (if (!this.graph.isInitialized())). Update the Graph class to
expose the new isInitialized() method and ensure existing mutations still set
the private initialized flag internally; keep the rest of the flow (calling
EggContainerFactory.getOrCreateEggObjectFromClazz(QueryNode), casting to
QueryNode, and calling
this.graph.setBoundExecute(queryNode.run.bind(queryNode))) unchanged.
🧹 Nitpick comments (1)
standalone/standalone/test/index.test.ts (1)

238-255: Ensure Runner.destroy() always runs even on assertion failures.

A failed assertion will skip the current runner.destroy() call, potentially leaking load units into later tests. Consider wrapping the test body in try/finally (or an afterEach) to guarantee cleanup.

♻️ Suggested fix
       const runner = new Runner(fixturePath);
       await runner.init();
 
-      // First request - Singleton is created, Advice is created in this context
-      const result1 = await runner.run();
-      assert.equal(result1, 'Tool executed: [advised:1] test-input');
-
-      // Second request - Singleton is reused, Advice must be re-created
-      // This would fail without StandaloneAopContextHook!
-      const result2 = await runner.run();
-      assert.equal(result2, 'Tool executed: [advised:1] test-input');
-
-      // Third request - verify it continues to work
-      const result3 = await runner.run();
-      assert.equal(result3, 'Tool executed: [advised:1] test-input');
-
-      await runner.destroy();
+      try {
+        // First request - Singleton is created, Advice is created in this context
+        const result1 = await runner.run();
+        assert.equal(result1, 'Tool executed: [advised:1] test-input');
+
+        // Second request - Singleton is reused, Advice must be re-created
+        // This would fail without StandaloneAopContextHook!
+        const result2 = await runner.run();
+        assert.equal(result2, 'Tool executed: [advised:1] test-input');
+
+        // Third request - verify it continues to work
+        const result3 = await runner.run();
+        assert.equal(result3, 'Tool executed: [advised:1] test-input');
+      } finally {
+        await runner.destroy();
+      }

Comment on lines +15 to +19
if (!this.graph['initialized']) {
// Get QueryNode only during graph initialization (first request)
const queryNodeObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(QueryNode);
const queryNode = queryNodeObj.obj as QueryNode;
this.graph.setBoundExecute(queryNode.run.bind(queryNode));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the main.ts file mentioned in the review
find . -path "*/standalone/standalone/test/fixtures/singleton-aop-module/main.ts" -type f

Repository: eggjs/tegg

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Find Tool.ts in the same directory structure
find . -path "*/singleton-aop-module/Tool.ts" -type f

Repository: eggjs/tegg

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Also search more broadly for Tool.ts that might contain Graph class
find . -name "Tool.ts" -type f

Repository: eggjs/tegg

Length of output: 120


🏁 Script executed:

cat -n ./standalone/standalone/test/fixtures/singleton-aop-module/main.ts

Repository: eggjs/tegg

Length of output: 1623


🏁 Script executed:

cat -n ./standalone/standalone/test/fixtures/singleton-aop-module/Tool.ts

Repository: eggjs/tegg

Length of output: 3071


🏁 Script executed:

find . -name "tsconfig.json" | head -20

Repository: eggjs/tegg

Length of output: 862


🏁 Script executed:

# Check the root and standalone directory tsconfig files
cat -n ./tsconfig.json | head -50

Repository: eggjs/tegg

Length of output: 1329


🏁 Script executed:

# Check if there's a standalone-specific tsconfig
find ./standalone -name "tsconfig.json" -type f

Repository: eggjs/tegg

Length of output: 229


🏁 Script executed:

cat -n ./standalone/standalone/tsconfig.json

Repository: eggjs/tegg

Length of output: 309


🏁 Script executed:

# Let me also check if the code actually compiles or if there are errors
# Check the test setup to understand how this fixture is used
find ./standalone/standalone/test -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "singleton-aop-module"

Repository: eggjs/tegg

Length of output: 96


🏁 Script executed:

cat -n ./standalone/standalone/test/index.test.ts

Repository: eggjs/tegg

Length of output: 16123


🏁 Script executed:

# Check if there are any TypeScript compilation errors by looking at the test output
# First, let's check if the fixture has its own tsconfig
find ./standalone/standalone/test/fixtures/singleton-aop-module -name "tsconfig.json"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Let's verify the actual behavior of TypeScript with bracket notation on private fields
# by checking if there are any build/compilation logs or errors
ls -la ./standalone/standalone/test/fixtures/singleton-aop-module/

Repository: eggjs/tegg

Length of output: 346


🏁 Script executed:

# Check if main.ts is compiled or used as-is
# Look for any .d.ts or .js files
find ./standalone/standalone/test/fixtures/singleton-aop-module -type f

Repository: eggjs/tegg

Length of output: 259


🏁 Script executed:

# Check the package.json to understand how the test fixture is set up
cat -n ./standalone/standalone/test/fixtures/singleton-aop-module/package.json

Repository: eggjs/tegg

Length of output: 187


🏁 Script executed:

# Let's verify whether TypeScript actually rejects bracket notation access to private fields
# by checking TypeScript documentation or looking at similar patterns in the codebase
rg -A 5 -B 5 "\['.*'\]" ./standalone/standalone/test/fixtures/singleton-aop-module/main.ts

Repository: eggjs/tegg

Length of output: 581


🏁 Script executed:

# Search for other usages of private field bracket notation in the codebase
rg "\['[a-zA-Z_]" --type ts | head -30

Repository: eggjs/tegg

Length of output: 3203


🏁 Script executed:

# Let's verify whether the code compiles without errors
# Check if there's a build script or test output
cd ./standalone/standalone && npm test 2>&1 | grep -A 10 "singleton-aop-module" || echo "Test output not available"

Repository: eggjs/tegg

Length of output: 79


🏁 Script executed:

# Let me search for TypeScript documentation or comments about bracket notation and private fields
rg "bracket|private.*\[" --type ts -B 2 -A 2 | head -40

Repository: eggjs/tegg

Length of output: 2881


🏁 Script executed:

# Search the codebase for any documentation about private field access patterns
rg -A 3 "private.*=.*false" ./standalone/standalone/test/fixtures/singleton-aop-module/Tool.ts

Repository: eggjs/tegg

Length of output: 213


🏁 Script executed:

# Create a simple test to verify TypeScript behavior
cat > /tmp/test-private.ts << 'EOF'
class TestClass {
  private value: boolean = false;
}

const obj = new TestClass();
const result = obj['value']; // Does TypeScript reject this?
EOF

# Check what the TypeScript compiler would say
cat /tmp/test-private.ts

Repository: eggjs/tegg

Length of output: 198


🏁 Script executed:

# Let's verify if TypeScript actually allows bracket notation access to private fields
# by checking the TypeScript compiler documentation or examining similar patterns
# First, let's check how the test fixture is being imported
rg "singleton-aop-module" ./standalone/standalone/test/index.test.ts -B 2 -A 2

Repository: eggjs/tegg

Length of output: 293


🏁 Script executed:

# Check if there's any transpilation or compilation happening for test fixtures
find ./standalone/standalone/test -name "*.ts" -exec grep -l "singleton-aop-module" {} \;

Repository: eggjs/tegg

Length of output: 96


🏁 Script executed:

# Let's check the actual TypeScript behavior by looking at whether bracket notation
# is allowed in strict mode. Check if there are any other examples of bracket notation
# access to private fields in the codebase
rg "private.*=" ./standalone/standalone/test/fixtures/singleton-aop-module/ -A 5 | grep -E "\['|\.get\(|getter"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# The critical check: see if Graph class has any public getter or if initialized is accessed differently elsewhere
rg "initialized" ./standalone/standalone/test/fixtures/singleton-aop-module/

Repository: eggjs/tegg

Length of output: 450


🏁 Script executed:

# Let's check if there's a tsconfig that extends from the root and test settings
find ./standalone/standalone/test/fixtures -name "tsconfig.json"

Repository: eggjs/tegg

Length of output: 191


🏁 Script executed:

# Check how tests are run - look at package.json or jest config
cat ./standalone/standalone/package.json | head -50

Repository: eggjs/tegg

Length of output: 1434


🏁 Script executed:

# Search for test configuration
ls -la ./standalone/standalone/ | grep -E "jest|mocha|vitest"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Check the .mocharc or test configuration
find ./standalone/standalone -name ".mocharc*" -o -name "mocha.opts"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Check if test fixtures are type-checked during compilation
cat ./standalone/standalone/tsconfig.json

Repository: eggjs/tegg

Length of output: 225


🏁 Script executed:

# Let's verify the behavior of TypeScript with bracket notation
# by checking if there's any documentation or comments in the code
rg "noCheck|skipLib|@ts-expect-error|@ts-ignore" ./standalone/standalone/test/fixtures/singleton-aop-module/

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Let me check if the compilation actually includes test fixtures
cat ./standalone/standalone/tsconfig.pub.json 2>/dev/null || echo "File not found"

Repository: eggjs/tegg

Length of output: 225


🏁 Script executed:

# The critical question: are test fixtures compiled by TypeScript?
ls -la ./standalone/standalone/dist/test/ 2>/dev/null || echo "Test files not in dist"

Repository: eggjs/tegg

Length of output: 76


🏁 Script executed:

# Key finding: test files are excluded from TypeScript compilation
# Let's verify that the code actually works at runtime despite accessing private field
# Check if there's any way test files are type-checked separately
find ./standalone/standalone -name ".mocharc.yml" -o -name ".mocharc.json" -o -name ".mocharc.cjs"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Let's see the actual mocha test configuration
cat ./standalone/standalone/.mocharc.yml 2>/dev/null || cat ./standalone/standalone/.mocharc.json 2>/dev/null || find ./standalone/standalone -maxdepth 2 -name ".mocharc*" -exec cat {} \;

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Check the root-level mocha config that might apply to all tests
cat ./.mocharc.yml 2>/dev/null || cat ./.mocharc.json 2>/dev/null || echo "No root mocha config found"

Repository: eggjs/tegg

Length of output: 223


🏁 Script executed:

# Critical check: Look at the actual compile settings
# Since test files are excluded from tsconfig, they're never compiled by tsc
# But they might be type-checked by mocha or another tool
grep -r "ts-node\|ts-mocha\|fork-ts-checker" ./standalone/standalone/ 2>/dev/null || echo "No TypeScript runtime type-checking found"

Repository: eggjs/tegg

Length of output: 116


🏁 Script executed:

# Now let's verify the critical fact: Does TypeScript actually reject bracket notation on private fields?
# ts-node uses tsconfig.json with ts-node settings. Let's check if there are any type errors
# by looking at the actual test execution or TypeScript settings

cat ./tsconfig.json | grep -A 20 "ts-node"

Repository: eggjs/tegg

Length of output: 92


🏁 Script executed:

# Let me check whether bracket notation access to private fields is actually allowed or rejected
# by looking for any `@ts-ignore` or similar suppressions in the file
cat -n ./standalone/standalone/test/fixtures/singleton-aop-module/main.ts | grep -i "ts-"

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Create a definitive test: check if TypeScript with strict mode allows bracket notation on private fields
# by examining what the actual TypeScript compiler does
python3 << 'EOF'
# According to TypeScript documentation:
# - Bracket notation does NOT bypass access modifiers
# - obj['privateField'] is rejected just like obj.privateField
# - This is a compile-time check in TypeScript strict mode

print("TypeScript behavior with bracket notation on private fields:")
print("In strict mode, TypeScript rejects BOTH:")
print("  1. obj.privateField (dot notation)")
print("  2. obj['privateField'] (bracket notation)")
print("")
print("Both use the same type checking, bracket notation does NOT bypass access control.")
EOF

Repository: eggjs/tegg

Length of output: 319


🏁 Script executed:

# Check if there's any CI configuration that might show whether tests pass or fail
find . -maxdepth 3 -name ".github" -type d

Repository: eggjs/tegg

Length of output: 63


🏁 Script executed:

# Let me look at git history or any pull request information about this fixture
git log --oneline ./standalone/standalone/test/fixtures/singleton-aop-module/main.ts 2>/dev/null | head -5

Repository: eggjs/tegg

Length of output: 130


🏁 Script executed:

# Check if the Graph class provides any alternative accessor method
grep -A 20 "class Graph" ./standalone/standalone/test/fixtures/singleton-aop-module/Tool.ts

Repository: eggjs/tegg

Length of output: 593


🏁 Script executed:

# Let me check if there's any CI workflow that would validate this
find ./.github/workflows -name "*.yml" -o -name "*.yaml" | head -5

Repository: eggjs/tegg

Length of output: 156


🏁 Script executed:

# Check if there are any recent issues or discussions about this
grep -r "initialized" ./.github/workflows/ 2>/dev/null | head -5

Repository: eggjs/tegg

Length of output: 36


🏁 Script executed:

# Most importantly, let's verify whether TypeScript actually rejects this by checking
# if there's a way this fixture could be loaded/imported without type checking
find ./standalone/standalone/test -name "*.ts" -exec grep -l "require\|import.*main" {} \;

Repository: eggjs/tegg

Length of output: 96


🏁 Script executed:

# Let's check what the test actually does - does it import and execute the fixture
cat -n ./standalone/standalone/test/index.test.ts | sed -n '220,256p'

Repository: eggjs/tegg

Length of output: 2105


🏁 Script executed:

# Let me verify whether ts-node actually type-checks these files or just transpiles them
# by checking if there are any type errors reported when running the tests
grep -A 10 "Runner\|main(" ./standalone/standalone/test/index.test.ts | head -20

Repository: eggjs/tegg

Length of output: 791


🏁 Script executed:

# Check the actual import/usage of the singleton-aop-module fixture
rg "singleton-aop-module" ./standalone/standalone/test/index.test.ts -B 5 -A 10

Repository: eggjs/tegg

Length of output: 923


🏁 Script executed:

# Let me understand how the main() function loads the fixture
# by checking the Runner and main() implementation
find ./standalone/standalone/src -name "*.ts" | xargs grep -l "class Runner\|function main"

Repository: eggjs/tegg

Length of output: 127


🏁 Script executed:

# Check if fixture files are statically type-checked or dynamically loaded
cat -n ./standalone/standalone/src/index.ts 2>/dev/null || find ./standalone/standalone/src -name "index.ts" | head -1 | xargs cat -n | head -50

Repository: eggjs/tegg

Length of output: 36


Accessing a private Graph field through bracket notation violates TypeScript's access control.

this.graph['initialized'] accesses a private field and will be rejected by TypeScript even in bracket notation—both dot and bracket access are type-checked for private modifiers in strict mode. Expose a public accessor method on Graph and use that instead.

Proposed fix in this file
-    if (!this.graph['initialized']) {
+    if (!this.graph.isInitialized()) {
       // Get QueryNode only during graph initialization (first request)
       const queryNodeObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(QueryNode);
       const queryNode = queryNodeObj.obj as QueryNode;
       this.graph.setBoundExecute(queryNode.run.bind(queryNode));
     }
Corresponding update in Tool.ts
 export class Graph {
   private boundExecute: ((input: string) => Promise<string>) | null = null;
   private initialized = false;
+
+  isInitialized(): boolean {
+    return this.initialized;
+  }
🤖 Prompt for AI Agents
In `@standalone/standalone/test/fixtures/singleton-aop-module/main.ts` around
lines 15 - 19, Replace the private-field access this.graph['initialized'] with a
public accessor on Graph (e.g., add a method isInitialized(): boolean) and call
that here (if (!this.graph.isInitialized())). Update the Graph class to expose
the new isInitialized() method and ensure existing mutations still set the
private initialized flag internally; keep the rest of the flow (calling
EggContainerFactory.getOrCreateEggObjectFromClazz(QueryNode), casting to
QueryNode, and calling
this.graph.setBoundExecute(queryNode.run.bind(queryNode))) unchanged.

* Pre-creates ContextProto advice objects when a new context is initialized.
* This ensures that advice objects are available when AOP methods are called.
*/
export class StandaloneAopContextHook implements LifecycleHook<EggContextLifecycleContext, EggContext> {
Copy link
Contributor

Choose a reason for hiding this comment

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

這個放在 aop runtime 裏是不是更加合適?

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.

2 participants