@core → Pure TS, no platform deps. Defines interfaces.
@browser-runtime → Implements @core for Chrome. Depends on @core only.
@aipex-react → UI library. Depends on @core only (NOT browser-runtime).
browser-ext → Extension entry. Assembles all packages.
Key rule: @aipex-react must NOT depend on @browser-runtime. Browser-specific code (ChromeStorageAdapter, browser tools) stays in @browser-runtime or browser-ext.
Before submitting any changes, it is crucial to validate them by running the full preflight check. This command will build the repository, run all tests, check for type errors, and lint the code.
To run the full suite of checks, execute the following command:
npm run preflightThis project uses Vitest as its primary testing framework. When writing tests, aim to follow existing patterns. Key conventions include:
- Framework: All tests are written using Vitest (
describe,it,expect,vi). - File Location: Test files (
*.test.tsfor logic,*.test.tsxfor React components) are co-located with the source files they test. - Configuration: Test environments are defined in
vitest.config.tsfiles. - Setup/Teardown: Use
beforeEachandafterEach. Commonly,vi.resetAllMocks()is called inbeforeEachandvi.restoreAllMocks()inafterEach.
- ES Modules: Mock with
vi.mock('module-name', async (importOriginal) => { ... }). UseimportOriginalfor selective mocking.- Example:
vi.mock('os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: vi.fn() }; });
- Example:
- Mocking Order: For critical dependencies (e.g.,
os,fs) that affect module-level constants, placevi.mockat the very top of the test file, before other imports. - Hoisting: Use
const myMock = vi.hoisted(() => vi.fn());if a mock function needs to be defined before its use in avi.mockfactory. - Mock Functions: Create with
vi.fn(). Define behavior withmockImplementation(),mockResolvedValue(), ormockRejectedValue(). - Spying: Use
vi.spyOn(object, 'methodName'). Restore spies withmockRestore()inafterEach.
- Node.js built-ins:
fs,fs/promises,os(especiallyos.homedir()),path,child_process(execSync,spawn). - External SDKs:
@google/genai,@modelcontextprotocol/sdk. - Internal Project Modules: Dependencies from other project packages are often mocked.
- Use
async/await. - For timers, use
vi.useFakeTimers(),vi.advanceTimersByTimeAsync(),vi.runAllTimersAsync(). - Test promise rejections with
await expect(promise).rejects.toThrow(...).
- When adding tests, first examine existing tests to understand and conform to established conventions.
- Pay close attention to the mocks at the top of existing test files; they reveal critical dependencies and how they are managed in a test environment.
The main branch for this project is called "main"
When contributing to this React, Node, and TypeScript codebase, please prioritize the use of plain JavaScript objects with accompanying TypeScript interface or type declarations over JavaScript class syntax. This approach offers significant advantages, especially concerning interoperability with React and overall code maintainability.
JavaScript classes, by their nature, are designed to encapsulate internal state and behavior. While this can be useful in some object-oriented paradigms, it often introduces unnecessary complexity and friction when working with React's component-based architecture. Here's why plain objects are preferred:
-
Seamless React Integration: React components thrive on explicit props and state management. Classes' tendency to store internal state directly within instances can make prop and state propagation harder to reason about and maintain. Plain objects, on the other hand, are inherently immutable (when used thoughtfully) and can be easily passed as props, simplifying data flow and reducing unexpected side effects.
-
Reduced Boilerplate and Increased Conciseness: Classes often promote the use of constructors, this binding, getters, setters, and other boilerplate that can unnecessarily bloat code. TypeScript interface and type declarations provide powerful static type checking without the runtime overhead or verbosity of class definitions. This allows for more succinct and readable code, aligning with JavaScript's strengths in functional programming.
-
Enhanced Readability and Predictability: Plain objects, especially when their structure is clearly defined by TypeScript
-
Avoid adding unnecessary comments. Prefer good structure and naming over comments. Only comment when the intent cannot be made clear by the code itself.
-
Increase test coverage meaningfully — avoid redundant or trivial tests. Focus on real logic and edge cases.