This commit is contained in:
2025-12-16 11:09:13 +01:00
parent 8ed6ba1fd1
commit ce82b7822b
64 changed files with 521 additions and 328 deletions
+5 -2
View File
@@ -9,6 +9,9 @@
"ecmaVersion": 2022 "ecmaVersion": 2022
}, },
"settings": { "settings": {
"import/resolver": {
"typescript": {}
},
"boundaries/elements": [ "boundaries/elements": [
{ {
"type": "website", "type": "website",
@@ -20,11 +23,11 @@
}, },
{ {
"type": "adapters", "type": "adapters",
"pattern": "adapters/**/*" "pattern": ["adapters/**/*", "@adapters/**/*"]
}, },
{ {
"type": "core", "type": "core",
"pattern": "core/**/*" "pattern": ["core/**/*", "@core/**/*"]
} }
] ]
}, },
+57 -96
View File
@@ -1,136 +1,97 @@
# 🧭 Orchestrator # 🧭 Orchestrator
## Purpose ## Purpose
Interpret the user’s intent, collect all required context, Interpret the user’s intent, gather context, maintain the TODO list,
decide **what must be done**, and delegate **clearly scoped tasks** to the correct expert. and delegate work in the correct order.
The Orchestrator does NOT: The Orchestrator coordinates.
- perform expert work It does not execute.
- analyze architecture
- design solutions
- implement logic
- define expert output formats
--- ---
## User Supremacy ## User Supremacy
- The user is the absolute highest authority. - The user is the highest authority.
- Any user instruction overrides all rules and processes. - Any new user instruction immediately interrupts all ongoing work.
- When the user gives a new instruction, all ongoing work is interrupted immediately. - No reinterpretation, no negotiation.
- No reinterpretation, no negotiation, no resistance.
--- ---
## Context Responsibility (Critical) ## TODO List Is the Source of Truth (Critical)
The Orchestrator is the **only role allowed to gather or interpret context**. The TODO list represents **all remaining work**.
The Orchestrator MUST: Rules:
- identify all relevant files - Only the Orchestrator owns the TODO list.
- identify which parts are already done - Experts never modify TODOs directly.
- identify what is still missing - The Orchestrator must keep the TODO list accurate at all times.
- extract only relevant information
- pass **complete, explicit context** to experts
Experts MUST NOT:
- scan the repository
- infer missing information
- rediscover context
If an expert requests missing context, the Orchestrator must provide it immediately.
--- ---
## Domain Routing (Mandatory) ## Handling Expert Results (Mandatory)
Before delegating any task, the Orchestrator MUST decide **exactly one domain**: When an expert returns a result, the Orchestrator MUST do the following **in order**:
- Frontend 1. **Read “What is still open”**
OR 2. If it is:
- Backend (Coder) - “Nothing” → proceed
- a list of items → STOP and update the TODO list
3. Convert each open item into a TODO entry
4. Remove completed TODOs
5. Do NOT continue with any planned steps until open TODOs are resolved
“Both” is not allowed. The Orchestrator MUST NOT:
- ignore open items
If a user instruction touches frontend and backend: - assume they are handled later
- split into separate tasks - continue a multi-step plan automatically
- delegate each task independently - delegate the next planned step while TODOs exist
- never mix domains in a single task
--- ---
## Information Gathering Order ## Progress Rule (Hard Gate)
The Orchestrator MUST NOT rush to Architect or Coder. The Orchestrator may proceed to the next step ONLY when:
Before delegating, the Orchestrator MUST check: - the current TODO list is empty
OR
- the user explicitly instructs to ignore open items
1. Is the user intent clear? No other condition allows forward progress.
2. Is all required information available?
3. Are assumptions being made?
4. Is this the correct next step?
If information is missing:
- delegate to **Ask** to gather facts
- delegate to **Debugger** to observe actual behavior/logs
- delegate to **Designer** if UI/UX understanding is required
Only when context is complete may the Orchestrator delegate to Architect or Coders.
--- ---
## Task Grouping ## Delegation Rule
The Orchestrator MUST: When delegating:
- bundle work that belongs to the same goal and same domain - delegate ONLY the next TODO
- split work that does not belong together - include full context
- avoid micro-tasks - scope to a single purpose
- avoid over-scoped tasks - assign to exactly one expert
Each delegated task must be: Never delegate multiple TODOs at once unless they are explicitly bundled.
- single-purpose
- fully scoped
- executable without guessing
--- ---
## TODO List (Required) ## Planning Discipline
When the user gives a task, the Orchestrator MUST: The Orchestrator must NOT treat a plan as a script.
1. Create a TODO list containing **only outstanding work**. Plans are:
2. Exclude anything already completed. - intentions
3. Update the TODO list after every expert result. - not guarantees
4. Remove completed items immediately. - always subordinate to actual execution results
5. Never invent TODOs.
The TODO list is the **single source of truth** for remaining work. Reality (expert output) always overrides plan order.
---
## Delegation Rules
A delegation to an expert MUST include:
- exact file paths
- exact scope of change
- explicit constraints
- what must NOT be touched
- expected outcome
The Orchestrator states **WHAT** to do, never **HOW**.
--- ---
## Forbidden ## Forbidden
The Orchestrator MUST NOT: The Orchestrator MUST NOT:
- perform expert analysis - continue a plan when TODOs remain
- solve problems itself - treat “still open” as informational only
- explain architecture or design - assume the expert will handle remaining work implicitly
- define deliverables for experts - batch unresolved items
- define expert completion formats - skip TODO reconciliation
- rush to execution - push work forward “for momentum”
- delegate with incomplete context
- merge frontend and backend work
- generate long explanations
- override user intent
--- ---
## Completion ## Completion
A step is complete when: A workflow step is complete ONLY when:
- the expert finishes the delegated task - the expert reports “What is still open: Nothing”
- the TODO list reflects only remaining work - the TODO list is empty
- the Orchestrator either delegates the next task or waits for user input - or the user explicitly overrides and says to proceed anyway
@@ -1,7 +1,7 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { AnalyticsSnapshotOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity'; import { AnalyticsSnapshotOrmEntity } from '../../../../..//persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
import { EngagementOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/EngagementOrmEntity'; import { EngagementOrmEntity } from '../../../../..//persistence/typeorm/analytics/EngagementOrmEntity';
@Module({ @Module({
imports: [ imports: [
@@ -1,6 +1,6 @@
import { Global, Module } from '@nestjs/common'; import { Global, Module } from '@nestjs/common';
import { Logger } from '@gridpilot/shared/application/Logger'; import { Logger } from '@gridpilot/shared/application/Logger';
import { ConsoleLogger } from '@gridpilot/adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '@gridpilot//logging/ConsoleLogger';
@Global() @Global()
@Module({ @Module({
@@ -1,20 +1,26 @@
import { Provider } from '@nestjs/common'; import { Provider } from '@nestjs/common';
import { AnalyticsService } from './AnalyticsService'; import { AnalyticsService } from './AnalyticsService';
import { RecordPageViewUseCase } from './use-cases/RecordPageViewUseCase';
import { RecordEngagementUseCase } from './use-cases/RecordEngagementUseCase';
const Logger_TOKEN = 'Logger_TOKEN'; const Logger_TOKEN = 'Logger_TOKEN';
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN'; const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN'; const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
const RECORD_PAGE_VIEW_USE_CASE_TOKEN = 'RecordPageViewUseCase_TOKEN';
const RECORD_ENGAGEMENT_USE_CASE_TOKEN = 'RecordEngagementUseCase_TOKEN';
import { Logger } from '@gridpilot/shared/logging/Logger'; import { Logger } from '@gridpilot/shared/logging/Logger';
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository'; import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository'; import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
import { ConsoleLogger } from '../../../../adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '../../../..//logging/ConsoleLogger';
import { InMemoryPageViewRepository } from '../../../../adapters/analytics/persistence/inmemory/InMemoryPageViewRepository'; import { InMemoryPageViewRepository } from '../../../..//analytics/persistence/inmemory/InMemoryPageViewRepository';
import { InMemoryEngagementRepository } from '../../../../adapters/analytics/persistence/inmemory/InMemoryEngagementRepository'; import { InMemoryEngagementRepository } from '../../../..//analytics/persistence/inmemory/InMemoryEngagementRepository';
export const AnalyticsProviders: Provider[] = [ export const AnalyticsProviders: Provider[] = [
AnalyticsService, AnalyticsService,
RecordPageViewUseCase,
RecordEngagementUseCase,
{ {
provide: Logger_TOKEN, provide: Logger_TOKEN,
useClass: ConsoleLogger, useClass: ConsoleLogger,
@@ -27,4 +33,12 @@ export const AnalyticsProviders: Provider[] = [
provide: IENGAGEMENT_REPO_TOKEN, provide: IENGAGEMENT_REPO_TOKEN,
useClass: InMemoryEngagementRepository, useClass: InMemoryEngagementRepository,
}, },
{
provide: RECORD_PAGE_VIEW_USE_CASE_TOKEN,
useClass: RecordPageViewUseCase,
},
{
provide: RECORD_ENGAGEMENT_USE_CASE_TOKEN,
useClass: RecordEngagementUseCase,
},
]; ];
@@ -1,83 +1,26 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { RecordEngagementInput, RecordEngagementOutput, RecordPageViewInput, RecordPageViewOutput } from './dto/AnalyticsDto'; import { RecordEngagementInput, RecordEngagementOutput, RecordPageViewInput, RecordPageViewOutput } from './dto/AnalyticsDto';
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
import { Logger } from '@gridpilot/shared/logging/Logger'; import { Logger } from '@gridpilot/shared/logging/Logger';
import { PageView } from '@gridpilot/analytics/domain/entities/PageView'; import { RecordPageViewUseCase } from './use-cases/RecordPageViewUseCase';
import { EngagementEvent } from '@gridpilot/analytics/domain/entities/EngagementEvent'; import { RecordEngagementUseCase } from './use-cases/RecordEngagementUseCase';
const Logger_TOKEN = 'Logger_TOKEN'; const Logger_TOKEN = 'Logger_TOKEN';
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN'; const RECORD_PAGE_VIEW_USE_CASE_TOKEN = 'RecordPageViewUseCase_TOKEN';
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN'; const RECORD_ENGAGEMENT_USE_CASE_TOKEN = 'RecordEngagementUseCase_TOKEN';
@Injectable() @Injectable()
export class AnalyticsService { export class AnalyticsService {
constructor( constructor(
@Inject(IPAGE_VIEW_REPO_TOKEN) private readonly pageViewRepository: IPageViewRepository, @Inject(RECORD_PAGE_VIEW_USE_CASE_TOKEN) private readonly recordPageViewUseCase: RecordPageViewUseCase,
@Inject(IENGAGEMENT_REPO_TOKEN) private readonly engagementRepository: IEngagementRepository, @Inject(RECORD_ENGAGEMENT_USE_CASE_TOKEN) private readonly recordEngagementUseCase: RecordEngagementUseCase,
@Inject(Logger_TOKEN) private readonly logger: Logger, @Inject(Logger_TOKEN) private readonly logger: Logger,
) {} ) {}
async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> { async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
this.logger.debug('Executing RecordPageViewUseCase', { input }); return await this.recordPageViewUseCase.execute(input);
try {
const pageViewId = `pv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const baseProps: Omit<Parameters<typeof PageView.create>[0], 'timestamp'> = {
id: pageViewId,
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityId: input.entityId,
visitorType: input.visitorType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
sessionId: input.sessionId,
};
const pageView = PageView.create({
...baseProps,
...(input.visitorId !== undefined ? { visitorId: input.visitorId } : {}),
...(input.referrer !== undefined ? { referrer: input.referrer } : {}),
...(input.userAgent !== undefined ? { userAgent: input.userAgent } : {}),
...(input.country !== undefined ? { country: input.country } : {}),
});
await this.pageViewRepository.save(pageView);
this.logger.info('Page view recorded successfully', { pageViewId, input });
return { pageViewId };
} catch (error) {
this.logger.error('Error recording page view', error, { input });
throw error;
}
} }
async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> { async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
this.logger.debug('Executing RecordEngagementUseCase', { input }); return await this.recordEngagementUseCase.execute(input);
try {
const eventId = `eng-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const baseProps: Omit<Parameters<typeof EngagementEvent.create>[0], 'timestamp'> = {
id: eventId,
action: input.action as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityId: input.entityId,
actorType: input.actorType,
sessionId: input.sessionId,
};
const event = EngagementEvent.create({
...baseProps,
...(input.actorId !== undefined ? { actorId: input.actorId } : {}),
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
});
await this.engagementRepository.save(event);
this.logger.info('Engagement recorded successfully', { eventId, input });
return {
eventId,
engagementWeight: event.getEngagementWeight(),
};
} catch (error) {
this.logger.error('Error recording engagement', error, { input });
throw error;
}
} }
} }
@@ -0,0 +1,88 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RecordEngagementUseCase } from './RecordEngagementUseCase';
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
import { Logger } from '@gridpilot/shared/logging/Logger';
describe('RecordEngagementUseCase', () => {
let useCase: RecordEngagementUseCase;
let engagementRepository: jest.Mocked<IEngagementRepository>;
let logger: jest.Mocked<Logger>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
RecordEngagementUseCase,
{
provide: 'IEngagementRepository_TOKEN',
useValue: {
save: jest.fn(),
},
},
{
provide: 'Logger_TOKEN',
useValue: {
debug: jest.fn(),
info: jest.fn(),
error: jest.fn(),
},
},
],
}).compile();
useCase = module.get<RecordEngagementUseCase>(RecordEngagementUseCase);
engagementRepository = module.get('IEngagementRepository_TOKEN');
logger = module.get('Logger_TOKEN');
});
describe('execute', () => {
it('should save the engagement event and return the eventId and engagementWeight', async () => {
const input = {
action: 'like' as any,
entityType: 'race' as any,
entityId: 'race-123',
actorType: 'driver',
sessionId: 'session-456',
actorId: 'actor-789',
metadata: { some: 'data' },
};
const mockEvent = {
getEngagementWeight: jest.fn().mockReturnValue(10),
};
// Mock the create function to return the mock event
const originalCreate = require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create;
require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create = jest.fn().mockReturnValue(mockEvent);
engagementRepository.save.mockResolvedValue(undefined);
const result = await useCase.execute(input);
expect(logger.debug).toHaveBeenCalledWith('Executing RecordEngagementUseCase', { input });
expect(engagementRepository.save).toHaveBeenCalledWith(mockEvent);
expect(logger.info).toHaveBeenCalledWith('Engagement recorded successfully', expect.objectContaining({ eventId: expect.any(String), input }));
expect(result).toHaveProperty('eventId');
expect(result).toHaveProperty('engagementWeight', 10);
expect(typeof result.eventId).toBe('string');
// Restore original
require('@gridpilot/analytics/domain/entities/EngagementEvent').EngagementEvent.create = originalCreate;
});
it('should handle errors and throw them', async () => {
const input = {
action: 'like' as any,
entityType: 'race' as any,
entityId: 'race-123',
actorType: 'driver',
sessionId: 'session-456',
};
const error = new Error('Save failed');
engagementRepository.save.mockRejectedValue(error);
await expect(useCase.execute(input)).rejects.toThrow('Save failed');
expect(logger.error).toHaveBeenCalledWith('Error recording engagement', error, { input });
});
});
});
@@ -0,0 +1,49 @@
import { Injectable, Inject } from '@nestjs/common';
import { RecordEngagementInput, RecordEngagementOutput } from '../dto/AnalyticsDto';
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
import { Logger } from '@gridpilot/shared/logging/Logger';
import { EngagementEvent } from '@gridpilot/analytics/domain/entities/EngagementEvent';
const Logger_TOKEN = 'Logger_TOKEN';
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
@Injectable()
export class RecordEngagementUseCase {
constructor(
@Inject(IENGAGEMENT_REPO_TOKEN) private readonly engagementRepository: IEngagementRepository,
@Inject(Logger_TOKEN) private readonly logger: Logger,
) {}
async execute(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
this.logger.debug('Executing RecordEngagementUseCase', { input });
try {
const eventId = `eng-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const baseProps: Omit<Parameters<typeof EngagementEvent.create>[0], 'timestamp'> = {
id: eventId,
action: input.action as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityId: input.entityId,
actorType: input.actorType,
sessionId: input.sessionId,
};
const event = EngagementEvent.create({
...baseProps,
...(input.actorId !== undefined ? { actorId: input.actorId } : {}),
...(input.metadata !== undefined ? { metadata: input.metadata } : {}),
});
await this.engagementRepository.save(event);
this.logger.info('Engagement recorded successfully', { eventId, input });
return {
eventId,
engagementWeight: event.getEngagementWeight(),
};
} catch (error) {
this.logger.error('Error recording engagement', error, { input });
throw error;
}
}
}
@@ -0,0 +1,76 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RecordPageViewUseCase } from './RecordPageViewUseCase';
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
import { Logger } from '@gridpilot/shared/logging/Logger';
describe('RecordPageViewUseCase', () => {
let useCase: RecordPageViewUseCase;
let pageViewRepository: jest.Mocked<IPageViewRepository>;
let logger: jest.Mocked<Logger>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
RecordPageViewUseCase,
{
provide: 'IPageViewRepository_TOKEN',
useValue: {
save: jest.fn(),
},
},
{
provide: 'Logger_TOKEN',
useValue: {
debug: jest.fn(),
info: jest.fn(),
error: jest.fn(),
},
},
],
}).compile();
useCase = module.get<RecordPageViewUseCase>(RecordPageViewUseCase);
pageViewRepository = module.get('IPageViewRepository_TOKEN');
logger = module.get('Logger_TOKEN');
});
describe('execute', () => {
it('should save the page view and return the pageViewId', async () => {
const input = {
entityType: 'race' as any,
entityId: 'race-123',
visitorType: 'anonymous' as any,
sessionId: 'session-456',
visitorId: 'visitor-789',
referrer: 'https://example.com',
userAgent: 'Mozilla/5.0',
country: 'US',
};
pageViewRepository.save.mockResolvedValue(undefined);
const result = await useCase.execute(input);
expect(logger.debug).toHaveBeenCalledWith('Executing RecordPageViewUseCase', { input });
expect(pageViewRepository.save).toHaveBeenCalledTimes(1);
expect(logger.info).toHaveBeenCalledWith('Page view recorded successfully', expect.objectContaining({ pageViewId: expect.any(String), input }));
expect(result).toHaveProperty('pageViewId');
expect(typeof result.pageViewId).toBe('string');
});
it('should handle errors and throw them', async () => {
const input = {
entityType: 'race' as any,
entityId: 'race-123',
visitorType: 'anonymous' as any,
sessionId: 'session-456',
};
const error = new Error('Save failed');
pageViewRepository.save.mockRejectedValue(error);
await expect(useCase.execute(input)).rejects.toThrow('Save failed');
expect(logger.error).toHaveBeenCalledWith('Error recording page view', error, { input });
});
});
});
@@ -0,0 +1,46 @@
import { Injectable, Inject } from '@nestjs/common';
import { RecordPageViewInput, RecordPageViewOutput } from '../dto/AnalyticsDto';
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
import { Logger } from '@gridpilot/shared/logging/Logger';
import { PageView } from '@gridpilot/analytics/domain/entities/PageView';
const Logger_TOKEN = 'Logger_TOKEN';
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
@Injectable()
export class RecordPageViewUseCase {
constructor(
@Inject(IPAGE_VIEW_REPO_TOKEN) private readonly pageViewRepository: IPageViewRepository,
@Inject(Logger_TOKEN) private readonly logger: Logger,
) {}
async execute(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
this.logger.debug('Executing RecordPageViewUseCase', { input });
try {
const pageViewId = `pv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const baseProps: Omit<Parameters<typeof PageView.create>[0], 'timestamp'> = {
id: pageViewId,
entityType: input.entityType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
entityId: input.entityId,
visitorType: input.visitorType as any, // Cast to any to bypass strict type checking, will resolve with proper domain layer alignment
sessionId: input.sessionId,
};
const pageView = PageView.create({
...baseProps,
...(input.visitorId !== undefined ? { visitorId: input.visitorId } : {}),
...(input.referrer !== undefined ? { referrer: input.referrer } : {}),
...(input.userAgent !== undefined ? { userAgent: input.userAgent } : {}),
...(input.country !== undefined ? { country: input.country } : {}),
});
await this.pageViewRepository.save(pageView);
this.logger.info('Page view recorded successfully', { pageViewId, input });
return { pageViewId };
} catch (error) {
this.logger.error('Error recording page view', error, { input });
throw error;
}
}
}
+5 -5
View File
@@ -7,12 +7,12 @@ import { IUserRepository, StoredUser } from '@gridpilot/core/identity/domain/rep
import { IPasswordHashingService } from '@gridpilot/core/identity/domain/services/PasswordHashingService'; import { IPasswordHashingService } from '@gridpilot/core/identity/domain/services/PasswordHashingService';
import { Logger } from '@gridpilot/core/shared/logging/Logger'; import { Logger } from '@gridpilot/core/shared/logging/Logger';
import { InMemoryAuthRepository } from '../../../adapters/identity/persistence/inmemory/InMemoryAuthRepository'; import { InMemoryAuthRepository } from '../../..//identity/persistence/inmemory/InMemoryAuthRepository';
import { InMemoryUserRepository } from '../../../adapters/identity/persistence/inmemory/InMemoryUserRepository'; import { InMemoryUserRepository } from '../../..//identity/persistence/inmemory/InMemoryUserRepository';
import { InMemoryPasswordHashingService } from '../../../adapters/identity/services/InMemoryPasswordHashingService'; import { InMemoryPasswordHashingService } from '../../..//identity/services/InMemoryPasswordHashingService';
import { ConsoleLogger } from '../../../adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '../../..//logging/ConsoleLogger';
import { IdentitySessionPort } from '../../../../core/identity/application/ports/IdentitySessionPort'; // Path from apps/api/src/modules/auth import { IdentitySessionPort } from '../../../../core/identity/application/ports/IdentitySessionPort'; // Path from apps/api/src/modules/auth
import { CookieIdentitySessionAdapter } from '../../../adapters/identity/session/CookieIdentitySessionAdapter'; import { CookieIdentitySessionAdapter } from '../../..//identity/session/CookieIdentitySessionAdapter';
// Define the tokens for dependency injection // Define the tokens for dependency injection
export const AUTH_REPOSITORY_TOKEN = 'IAuthRepository'; export const AUTH_REPOSITORY_TOKEN = 'IAuthRepository';
@@ -17,14 +17,14 @@ import { GetTotalDriversUseCase } from '../../../../core/racing/application/use-
import { CompleteDriverOnboardingUseCase } from '../../../../core/racing/application/use-cases/CompleteDriverOnboardingUseCase'; import { CompleteDriverOnboardingUseCase } from '../../../../core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository'; import { InMemoryDriverRepository } from '../../..//racing/persistence/inmemory/InMemoryDriverRepository';
import { InMemoryRankingService } from '../../../adapters/racing/services/InMemoryRankingService'; import { InMemoryRankingService } from '../../..//racing/services/InMemoryRankingService';
import { InMemoryDriverStatsService } from '../../../adapters/racing/services/InMemoryDriverStatsService'; import { InMemoryDriverStatsService } from '../../..//racing/services/InMemoryDriverStatsService';
import { InMemoryDriverRatingProvider } from '../../../adapters/racing/ports/InMemoryDriverRatingProvider'; import { InMemoryDriverRatingProvider } from '../../..//racing/ports/InMemoryDriverRatingProvider';
import { InMemoryImageServiceAdapter } from '../../../adapters/media/ports/InMemoryImageServiceAdapter'; import { InMemoryImageServiceAdapter } from '../../..//media/ports/InMemoryImageServiceAdapter';
import { InMemoryRaceRegistrationRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository'; import { InMemoryRaceRegistrationRepository } from '../../..//racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
import { InMemoryNotificationPreferenceRepository } from '../../../adapters/notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository'; import { InMemoryNotificationPreferenceRepository } from '../../..//notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository';
import { ConsoleLogger } from '../../../adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '../../..//logging/ConsoleLogger';
// Define injection tokens // Define injection tokens
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository'; export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
+11 -11
View File
@@ -5,17 +5,17 @@ import { LeagueService } from './LeagueService';
import { Logger } from '@gridpilot/shared/application/Logger'; import { Logger } from '@gridpilot/shared/application/Logger';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository'; import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryLeagueMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository'; import { InMemoryLeagueMembershipRepository } from '/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
import { InMemoryLeagueStandingsRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueStandingsRepository'; import { InMemoryLeagueStandingsRepository } from '/racing/persistence/inmemory/InMemoryLeagueStandingsRepository';
import { InMemorySeasonRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonRepository'; import { InMemorySeasonRepository } from '/racing/persistence/inmemory/InMemorySeasonRepository';
import { InMemoryLeagueScoringConfigRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository'; import { InMemoryLeagueScoringConfigRepository } from '/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository';
import { InMemoryGameRepository } from 'adapters/racing/persistence/inmemory/InMemoryGameRepository'; import { InMemoryGameRepository } from '/racing/persistence/inmemory/InMemoryGameRepository';
import { InMemoryProtestRepository } from 'adapters/racing/persistence/inmemory/InMemoryProtestRepository'; import { InMemoryProtestRepository } from '/racing/persistence/inmemory/InMemoryProtestRepository';
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository'; import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
import { InMemoryDriverRepository } from 'adapters/racing/persistence/inmemory/InMemoryDriverRepository'; import { InMemoryDriverRepository } from '/racing/persistence/inmemory/InMemoryDriverRepository';
import { InMemoryStandingRepository } from 'adapters/racing/persistence/inmemory/InMemoryStandingRepository'; import { InMemoryStandingRepository } from '/racing/persistence/inmemory/InMemoryStandingRepository';
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '/logging/ConsoleLogger';
// Import use cases // Import use cases
import { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase'; import { GetAllLeaguesWithCapacityUseCase } from '@gridpilot/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
@@ -23,11 +23,11 @@ import { GetWalletUseCase } from '@gridpilot/payments/application/use-cases/GetW
import { ProcessWalletTransactionUseCase } from '@gridpilot/payments/application/use-cases/ProcessWalletTransactionUseCase'; import { ProcessWalletTransactionUseCase } from '@gridpilot/payments/application/use-cases/ProcessWalletTransactionUseCase';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryPaymentRepository } from 'adapters/payments/persistence/inmemory/InMemoryPaymentRepository'; import { InMemoryPaymentRepository } from '/payments/persistence/inmemory/InMemoryPaymentRepository';
import { InMemoryMembershipFeeRepository, InMemoryMemberPaymentRepository } from 'adapters/payments/persistence/inmemory/InMemoryMembershipFeeRepository'; import { InMemoryMembershipFeeRepository, InMemoryMemberPaymentRepository } from '/payments/persistence/inmemory/InMemoryMembershipFeeRepository';
import { InMemoryPrizeRepository } from 'adapters/payments/persistence/inmemory/InMemoryPrizeRepository'; import { InMemoryPrizeRepository } from '/payments/persistence/inmemory/InMemoryPrizeRepository';
import { InMemoryWalletRepository, InMemoryTransactionRepository } from 'adapters/payments/persistence/inmemory/InMemoryWalletRepository'; import { InMemoryWalletRepository, InMemoryTransactionRepository } from '/payments/persistence/inmemory/InMemoryWalletRepository';
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '/logging/ConsoleLogger';
// Repository injection tokens // Repository injection tokens
export const PAYMENT_REPOSITORY_TOKEN = 'IPaymentRepository'; export const PAYMENT_REPOSITORY_TOKEN = 'IPaymentRepository';
+3 -3
View File
@@ -7,9 +7,9 @@ import { IRaceRepository } from '@gridpilot/racing/domain/repositories/IRaceRepo
import { ILeagueRepository } from '@gridpilot/racing/domain/repositories/ILeagueRepository'; import { ILeagueRepository } from '@gridpilot/racing/domain/repositories/ILeagueRepository';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository'; import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository'; import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '/logging/ConsoleLogger';
// Import use cases // Import use cases
import { GetAllRacesUseCase } from '@gridpilot/racing/application/use-cases/GetAllRacesUseCase'; import { GetAllRacesUseCase } from '@gridpilot/racing/application/use-cases/GetAllRacesUseCase';
@@ -21,15 +21,15 @@ import { GetSponsorSponsorshipsUseCase } from '@gridpilot/racing/application/use
import { GetEntitySponsorshipPricingUseCase } from '@gridpilot/racing/application/use-cases/GetEntitySponsorshipPricingUseCase'; import { GetEntitySponsorshipPricingUseCase } from '@gridpilot/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemorySponsorRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorRepository'; import { InMemorySponsorRepository } from '/racing/persistence/inmemory/InMemorySponsorRepository';
import { InMemorySeasonSponsorshipRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository'; import { InMemorySeasonSponsorshipRepository } from '/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
import { InMemorySeasonRepository } from 'adapters/racing/persistence/inmemory/InMemorySeasonRepository'; import { InMemorySeasonRepository } from '/racing/persistence/inmemory/InMemorySeasonRepository';
import { InMemoryLeagueRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueRepository'; import { InMemoryLeagueRepository } from '/racing/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryLeagueMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository'; import { InMemoryLeagueMembershipRepository } from '/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
import { InMemoryRaceRepository } from 'adapters/racing/persistence/inmemory/InMemoryRaceRepository'; import { InMemoryRaceRepository } from '/racing/persistence/inmemory/InMemoryRaceRepository';
import { InMemorySponsorshipPricingRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository'; import { InMemorySponsorshipPricingRepository } from '/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
import { InMemorySponsorshipRequestRepository } from 'adapters/racing/persistence/inmemory/InMemorySponsorshipRequestRepository'; import { InMemorySponsorshipRequestRepository } from '/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '/logging/ConsoleLogger';
// Define injection tokens // Define injection tokens
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository'; export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
+4 -4
View File
@@ -9,10 +9,10 @@ import { IImageServicePort } from '@gridpilot/racing/application/ports/IImageSer
import { Logger } from '@gridpilot/shared/application/Logger'; import { Logger } from '@gridpilot/shared/application/Logger';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryTeamRepository } from 'adapters/racing/persistence/inmemory/InMemoryTeamRepository'; import { InMemoryTeamRepository } from '/racing/persistence/inmemory/InMemoryTeamRepository';
import { InMemoryTeamMembershipRepository } from 'adapters/racing/persistence/inmemory/InMemoryTeamMembershipRepository'; import { InMemoryTeamMembershipRepository } from '/racing/persistence/inmemory/InMemoryTeamMembershipRepository';
import { InMemoryDriverRepository } from 'adapters/racing/persistence/inmemory/InMemoryDriverRepository'; import { InMemoryDriverRepository } from '/racing/persistence/inmemory/InMemoryDriverRepository';
import { ConsoleLogger } from 'adapters/logging/ConsoleLogger'; import { ConsoleLogger } from '/logging/ConsoleLogger';
// Import use cases // Import use cases
import { GetAllTeamsUseCase } from '@gridpilot/racing/application/use-cases/GetAllTeamsUseCase'; import { GetAllTeamsUseCase } from '@gridpilot/racing/application/use-cases/GetAllTeamsUseCase';
+15 -15
View File
@@ -28,49 +28,49 @@
"types": ["node", "express", "jest"], "types": ["node", "express", "jest"],
"strictPropertyInitialization": false, "strictPropertyInitialization": false,
"paths": { "paths": {
"@gridpilot/shared/*": [ "@core/shared/*": [
"../../core/shared/*" "../../core/shared/*"
], ],
"@gridpilot/shared/application/*": [ "@core/shared/application/*": [
"../../core/shared/application/*" "../../core/shared/application/*"
], ],
"@gridpilot/payments/*": [ "@core/payments/*": [
"../../core/payments/*" "../../core/payments/*"
], ],
"@gridpilot/payments/application/*": [ "@core/payments/application/*": [
"../../core/payments/application/*" "../../core/payments/application/*"
], ],
"@gridpilot/payments/domain/*": [ "@core/payments/domain/*": [
"../../core/payments/domain/*" "../../core/payments/domain/*"
], ],
"@gridpilot/racing/*": [ "@core/racing/*": [
"../../core/racing/*" "../../core/racing/*"
], ],
"@gridpilot/league/*": [ "@core/league/*": [
"../../core/league/*" "../../core/league/*"
], ],
"@gridpilot/analytics/*": [ "@core/analytics/*": [
"../../core/analytics/*" "../../core/analytics/*"
], ],
"@gridpilot/analytics/domain/repositories/*": [ "@core/analytics/domain/repositories/*": [
"../../core/analytics/domain/repositories/*" "../../core/analytics/domain/repositories/*"
], ],
"@gridpilot/analytics/domain/entities/*": [ "@core/analytics/domain/entities/*": [
"../../core/analytics/domain/entities/*" "../../core/analytics/domain/entities/*"
], ],
"@gridpilot/core/identity/domain/repositories/*": [ "@core/core/identity/domain/repositories/*": [
"../../core/identity/domain/repositories/*" "../../core/identity/domain/repositories/*"
], ],
"@gridpilot/core/identity/domain/services/*": [ "@core/core/identity/domain/services/*": [
"../../core/identity/domain/services/*" "../../core/identity/domain/services/*"
], ],
"@gridpilot/core/identity/domain/entities/*": [ "@core/core/identity/domain/entities/*": [
"../../core/identity/domain/entities/*" "../../core/identity/domain/entities/*"
], ],
"@gridpilot/core/shared/logging/*": [ "@core/core/shared/logging/*": [
"../../core/shared/logging/*" "../../core/shared/logging/*"
], ],
"adapters/*": [ "@adapters/*": [
"../../adapters/*" "../../adapters/*"
], ],
"@nestjs/testing": [ "@nestjs/testing": [
+6 -6
View File
@@ -18,7 +18,7 @@ import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-case
import { ClearSessionUseCase } from '@gridpilot/automation/application/use-cases/ClearSessionUseCase'; import { ClearSessionUseCase } from '@gridpilot/automation/application/use-cases/ClearSessionUseCase';
import { ConfirmCheckoutUseCase } from '@gridpilot/automation/application/use-cases/ConfirmCheckoutUseCase'; import { ConfirmCheckoutUseCase } from '@gridpilot/automation/application/use-cases/ConfirmCheckoutUseCase';
import { OverlaySyncService } from '@gridpilot/automation/application/services/OverlaySyncService'; import { OverlaySyncService } from '@gridpilot/automation/application/services/OverlaySyncService';
import type { IAutomationLifecycleEmitter } from '@gridpilot/automation/infrastructure/adapters/IAutomationLifecycleEmitter'; import type { IAutomationLifecycleEmitter } from '@gridpilot/automation/infrastructure//IAutomationLifecycleEmitter';
// Infrastructure // Infrastructure
import { InMemorySessionRepository } from '@gridpilot/automation/infrastructure/repositories/InMemorySessionRepository'; import { InMemorySessionRepository } from '@gridpilot/automation/infrastructure/repositories/InMemorySessionRepository';
@@ -27,11 +27,11 @@ import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
AutomationAdapterMode, AutomationAdapterMode,
FixtureServer, FixtureServer,
} from '@gridpilot/automation/infrastructure/adapters/automation'; } from '@gridpilot/automation/infrastructure//automation';
import { MockAutomationEngineAdapter } from '@gridpilot/automation/infrastructure/adapters/automation/engine/MockAutomationEngineAdapter'; import { MockAutomationEngineAdapter } from '@gridpilot/automation/infrastructure//automation/engine/MockAutomationEngineAdapter';
import { AutomationEngineAdapter } from '@gridpilot/automation/infrastructure/adapters/automation/engine/AutomationEngineAdapter'; import { AutomationEngineAdapter } from '@gridpilot/automation/infrastructure//automation/engine/AutomationEngineAdapter';
import { ConsoleLogAdapter } from '@gridpilot/automation/infrastructure/adapters/logging/ConsoleLogAdapter'; import { ConsoleLogAdapter } from '@gridpilot/automation/infrastructure//logging/ConsoleLogAdapter';
import { NoOpLogAdapter } from '@gridpilot/automation/infrastructure/adapters/logging/NoOpLogAdapter'; import { NoOpLogAdapter } from '@gridpilot/automation/infrastructure//logging/NoOpLogAdapter';
import { import {
loadAutomationConfig, loadAutomationConfig,
getAutomationMode, getAutomationMode,
+1 -1
View File
@@ -1,7 +1,7 @@
import 'reflect-metadata'; import 'reflect-metadata';
import { configureDIContainer, resetDIContainer, getDIContainer, resolveSessionDataPath, resolveTemplatePath } from './di-config'; import { configureDIContainer, resetDIContainer, getDIContainer, resolveSessionDataPath, resolveTemplatePath } from './di-config';
import { DI_TOKENS } from './di-tokens'; import { DI_TOKENS } from './di-tokens';
import { PlaywrightAutomationAdapter, FixtureServer } from '@gridpilot/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter, FixtureServer } from '@gridpilot/automation/infrastructure//automation';
import { StartAutomationSessionUseCase } from '@gridpilot/automation/application/use-cases/StartAutomationSessionUseCase'; import { StartAutomationSessionUseCase } from '@gridpilot/automation/application/use-cases/StartAutomationSessionUseCase';
import { CheckAuthenticationUseCase } from '@gridpilot/automation/application/use-cases/CheckAuthenticationUseCase'; import { CheckAuthenticationUseCase } from '@gridpilot/automation/application/use-cases/CheckAuthenticationUseCase';
import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-cases/InitiateLoginUseCase'; import { InitiateLoginUseCase } from '@gridpilot/automation/application/use-cases/InitiateLoginUseCase';
+3 -3
View File
@@ -4,9 +4,9 @@ import { DIContainer } from './di-container';
import type { HostedSessionConfig } from 'core/automation/domain/types/HostedSessionConfig'; import type { HostedSessionConfig } from 'core/automation/domain/types/HostedSessionConfig';
import { StepId } from 'core/automation/domain/value-objects/StepId'; import { StepId } from 'core/automation/domain/value-objects/StepId';
import { AuthenticationState } from 'core/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from 'core/automation/domain/value-objects/AuthenticationState';
import { ElectronCheckoutConfirmationAdapter } from 'core/automation/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter'; import { ElectronCheckoutConfirmationAdapter } from 'core/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
import type { OverlayAction } from 'core/automation/application/ports/OverlaySyncPort'; import type { OverlayAction } from 'core/automation/application/ports/OverlaySyncPort';
import type { IAutomationLifecycleEmitter } from 'core/automation/infrastructure/adapters/IAutomationLifecycleEmitter'; import type { IAutomationLifecycleEmitter } from 'core/automation/infrastructure//IAutomationLifecycleEmitter';
let progressMonitorInterval: NodeJS.Timeout | null = null; let progressMonitorInterval: NodeJS.Timeout | null = null;
let lifecycleSubscribed = false; let lifecycleSubscribed = false;
@@ -335,7 +335,7 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
loader.setDevelopmentMode(mode); loader.setDevelopmentMode(mode);
// Ensure runtime automation wiring reflects the new browser mode // Ensure runtime automation wiring reflects the new browser mode
if ('refreshBrowserAutomation' in container) { if ('refreshBrowserAutomation' in container) {
// Call method to refresh adapters/use-cases that depend on browser mode // Call method to refresh /use-cases that depend on browser mode
container.refreshBrowserAutomation(); container.refreshBrowserAutomation();
} }
logger.info('Browser mode updated', { mode }); logger.info('Browser mode updated', { mode });
+14 -1
View File
@@ -1,9 +1,22 @@
{ {
"extends": "next/core-web-vitals", "extends": "next/core-web-vitals",
"plugins": ["boundaries"],
"rules": { "rules": {
"react/no-unescaped-entities": "off", "react/no-unescaped-entities": "off",
"@next/next/no-img-element": "warn", "@next/next/no-img-element": "warn",
"react-hooks/exhaustive-deps": "warn", "react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/no-explicit-any": "error" "@typescript-eslint/no-explicit-any": "error",
"boundaries/element-types": [
2,
{
"default": "disallow",
"rules": [
{
"from": ["website"],
"allow": ["website"]
}
]
}
]
} }
} }
+14 -14
View File
@@ -18,20 +18,20 @@
"@/lib/*": ["./lib/*"], "@/lib/*": ["./lib/*"],
"@/components/*": ["./components/*"], "@/components/*": ["./components/*"],
"@/app/*": ["./app/*"], "@/app/*": ["./app/*"],
"@gridpilot/identity": ["../../core/identity/index.ts"], "@core/identity": ["../../core/identity/index.ts"],
"@gridpilot/identity/*": ["../../core/identity/*"], "@core/identity/*": ["../../core/identity/*"],
"@gridpilot/racing": ["../../core/racing/index.ts"], "@core/racing": ["../../core/racing/index.ts"],
"@gridpilot/racing/*": ["../../core/racing/*"], "@core/racing/*": ["../../core/racing/*"],
"@gridpilot/social": ["../../core/social/index.ts"], "@core/social": ["../../core/social/index.ts"],
"@gridpilot/social/*": ["../../core/social/*"], "@core/social/*": ["../../core/social/*"],
"@gridpilot/testing-support": ["../../core/testing-support/index.ts"], "@core/testing-support": ["../../core/testing-support/index.ts"],
"@gridpilot/testing-support/*": ["../../core/testing-support/*"], "@core/testing-support/*": ["../../core/testing-support/*"],
"@gridpilot/media": ["../../core/media/index.ts"], "@core/media": ["../../core/media/index.ts"],
"@gridpilot/media/*": ["../../core/media/*"], "@core/media/*": ["../../core/media/*"],
"@gridpilot/shared/logging": ["../../core/shared/logging"], "@core/shared/logging": ["../../core/shared/logging"],
"@gridpilot/shared/*": ["../../core/shared/*"], "@core/shared/*": ["../../core/shared/*"],
"@gridpilot/core/*": ["../../core/*"], "@core/core/*": ["../../core/*"],
"@gridpilot/api/*": ["../../apps/api/src/*"] "@core/api/*": ["../../apps/api/src/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+2 -2
View File
@@ -7,5 +7,5 @@ export { InMemoryNotificationRepository } from './repositories/InMemoryNotificat
export { InMemoryNotificationPreferenceRepository } from './repositories/InMemoryNotificationPreferenceRepository'; export { InMemoryNotificationPreferenceRepository } from './repositories/InMemoryNotificationPreferenceRepository';
// Adapters // Adapters
export { InAppNotificationAdapter } from './adapters/InAppNotificationAdapter'; export { InAppNotificationAdapter } from './/InAppNotificationAdapter';
export { NotificationGatewayRegistry } from './adapters/NotificationGatewayRegistry'; export { NotificationGatewayRegistry } from './/NotificationGatewayRegistry';
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { OverlayAction, ActionAck } from '../../../../core/automation/application/ports/IOverlaySyncPort' import { OverlayAction, ActionAck } from '../../../../core/automation/application/ports/IOverlaySyncPort'
import { IAutomationEventPublisher, AutomationEvent } from '../../../../core/automation/application/ports/IAutomationEventPublisher' import { IAutomationEventPublisher, AutomationEvent } from '../../../../core/automation/application/ports/IAutomationEventPublisher'
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure/adapters/IAutomationLifecycleEmitter' import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure//IAutomationLifecycleEmitter'
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService' import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
class MockLifecycleEmitter implements IAutomationLifecycleEmitter { class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { OverlayAction } from '../../../../core/automation/application/ports/OverlaySyncPort' import { OverlayAction } from '../../../../core/automation/application/ports/OverlaySyncPort'
import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure/adapters/IAutomationLifecycleEmitter' import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure//IAutomationLifecycleEmitter'
import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService' import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService'
class MockLifecycleEmitter implements IAutomationLifecycleEmitter { class MockLifecycleEmitter implements IAutomationLifecycleEmitter {
+3 -3
View File
@@ -3,9 +3,9 @@ import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { import {
FixtureServer, FixtureServer,
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
describe('Real Playwright hosted-session smoke (fixtures, steps 2–7)', () => { describe('Real Playwright hosted-session smoke (fixtures, steps 2–7)', () => {
let server: FixtureServer; let server: FixtureServer;
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { DIContainer } from '../../../apps/companion/main/di-container'; import { DIContainer } from '../../../apps/companion/main/di-container';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig'; import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
describe('Companion UI - hosted workflow via fixture-backed real stack', () => { describe('Companion UI - hosted workflow via fixture-backed real stack', () => {
let container: DIContainer; let container: DIContainer;
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { import {
IRACING_SELECTORS, IRACING_SELECTORS,
IRACING_TIMEOUTS, IRACING_TIMEOUTS,
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
const shouldRun = process.env.HOSTED_REAL_E2E === '1'; const shouldRun = process.env.HOSTED_REAL_E2E === '1';
const describeMaybe = shouldRun ? describe : describe.skip; const describeMaybe = shouldRun ? describe : describe.skip;
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { import {
IRACING_SELECTORS, IRACING_SELECTORS,
IRACING_TIMEOUTS, IRACING_TIMEOUTS,
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
const shouldRun = process.env.HOSTED_REAL_E2E === '1'; const shouldRun = process.env.HOSTED_REAL_E2E === '1';
@@ -4,12 +4,12 @@ import path from 'path';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { import {
IRACING_SELECTORS, IRACING_SELECTORS,
IRACING_TIMEOUTS, IRACING_TIMEOUTS,
} from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
const shouldRun = process.env.HOSTED_REAL_E2E === '1'; const shouldRun = process.env.HOSTED_REAL_E2E === '1';
const describeMaybe = shouldRun ? describe : describe.skip; const describeMaybe = shouldRun ? describe : describe.skip;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 2 – create race', () => { describe('Step 2 – create race', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 3 – race information', () => { describe('Step 3 – race information', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 4 – server details', () => { describe('Step 4 – server details', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 5 – set admins', () => { describe('Step 5 – set admins', () => {
let harness: StepHarness; let harness: StepHarness;
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 6 – admins', () => { describe('Step 6 – admins', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 7 – time limits', () => { describe('Step 7 – time limits', () => {
let harness: StepHarness; let harness: StepHarness;
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 8 – cars', () => { describe('Step 8 – cars', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 13 – track options', () => { describe('Step 13 – track options', () => {
let harness: StepHarness; let harness: StepHarness;
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 14 – time of day', () => { describe('Step 14 – time of day', () => {
let harness: StepHarness; let harness: StepHarness;
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import type { StepHarness } from '../support/StepHarness'; import type { StepHarness } from '../support/StepHarness';
import { createStepHarness } from '../support/StepHarness'; import { createStepHarness } from '../support/StepHarness';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
describe('Step 15 – weather', () => { describe('Step 15 – weather', () => {
let harness: StepHarness; let harness: StepHarness;
+1 -1
View File
@@ -1,5 +1,5 @@
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import type { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'; import type { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
import type { AutomationResult } from 'core/automation/application/ports/AutomationResults'; import type { AutomationResult } from 'core/automation/application/ports/AutomationResults';
export function assertAutoNavigationConfig(config: Record<string, unknown>): void { export function assertAutoNavigationConfig(config: Record<string, unknown>): void {
+2 -2
View File
@@ -3,8 +3,8 @@ import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
FixtureServer, FixtureServer,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
export interface StepHarness { export interface StepHarness {
server: FixtureServer; server: FixtureServer;
@@ -2,9 +2,9 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
FixtureServer, FixtureServer,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard'; import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
describe('Hosted validator guards (fixture-backed, real stack)', () => { describe('Hosted validator guards (fixture-backed, real stack)', () => {
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
FixtureServer, FixtureServer,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard'; import { executeStepWithAutoNavigationGuard } from '../support/AutoNavGuard';
describe('Workflow – hosted session autonav slice (fixture-backed, real stack)', () => { describe('Workflow – hosted session autonav slice (fixture-backed, real stack)', () => {
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
FixtureServer, FixtureServer,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { InMemorySessionRepository } from 'core/automation/infrastructure/repositories/InMemorySessionRepository'; import { InMemorySessionRepository } from 'core/automation/infrastructure/repositories/InMemorySessionRepository';
import { AutomationEngineAdapter } from 'core/automation/infrastructure/adapters/automation/engine/AutomationEngineAdapter'; import { AutomationEngineAdapter } from 'core/automation/infrastructure//automation/engine/AutomationEngineAdapter';
import { StartAutomationSessionUseCase } from 'core/automation/application/use-cases/StartAutomationSessionUseCase'; import { StartAutomationSessionUseCase } from 'core/automation/application/use-cases/StartAutomationSessionUseCase';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
describe('Workflow – hosted session end-to-end (fixture-backed, real stack)', () => { describe('Workflow – hosted session end-to-end (fixture-backed, real stack)', () => {
let server: FixtureServer; let server: FixtureServer;
@@ -2,10 +2,10 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { import {
PlaywrightAutomationAdapter, PlaywrightAutomationAdapter,
FixtureServer, FixtureServer,
} from 'core/automation/infrastructure/adapters/automation'; } from 'core/automation/infrastructure//automation';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { IRACING_SELECTORS } from 'core/automation/infrastructure/adapters/automation/dom/IRacingSelectors'; import { IRACING_SELECTORS } from 'core/automation/infrastructure//automation/dom/IRacingSelectors';
import { PinoLogAdapter } from 'core/automation/infrastructure/adapters/logging/PinoLogAdapter'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter';
describe('Workflow – steps 7–9 cars flow (fixture-backed, real stack)', () => { describe('Workflow – steps 7–9 cars flow (fixture-backed, real stack)', () => {
let adapter: PlaywrightAutomationAdapter; let adapter: PlaywrightAutomationAdapter;
@@ -1,6 +1,6 @@
import { describe, test, expect, beforeEach, vi } from 'vitest'; import { describe, test, expect, beforeEach, vi } from 'vitest';
import type { Page } from 'playwright'; import type { Page } from 'playwright';
import { AuthenticationGuard } from '@gridpilot/automation/infrastructure/adapters/automation/auth/AuthenticationGuard'; import { AuthenticationGuard } from '@gridpilot/automation/infrastructure//automation/auth/AuthenticationGuard';
describe('AuthenticationGuard', () => { describe('AuthenticationGuard', () => {
let mockPage: Page; let mockPage: Page;
@@ -9,7 +9,7 @@ vi.mock('electron', () => ({
}, },
})); }));
import { ElectronCheckoutConfirmationAdapter } from '@gridpilot/automation/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter'; import { ElectronCheckoutConfirmationAdapter } from '@gridpilot/automation/infrastructure//ipc/ElectronCheckoutConfirmationAdapter';
import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice'; import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState'; import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState';
import { ipcMain } from 'electron'; import { ipcMain } from 'electron';
@@ -1,9 +1,9 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { Page, BrowserContext } from 'playwright'; import type { Page, BrowserContext } from 'playwright';
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService'; import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService';
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure/adapters/automation/core/PlaywrightBrowserSession'; import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure//automation/core/PlaywrightBrowserSession';
import type { SessionCookieStore } from '../../../../core/automation/infrastructure/adapters/automation/auth/SessionCookieStore'; import type { SessionCookieStore } from '../../../../core/automation/infrastructure//automation/auth/SessionCookieStore';
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthFlow'; import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthFlow';
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort'; import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
import { Result } from '../../../../core/shared/result/Result'; import { Result } from '../../../../core/shared/result/Result';
@@ -1,13 +1,13 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import type { Page, Locator } from 'playwright'; import type { Page, Locator } from 'playwright';
import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService'; import { PlaywrightAuthSessionService } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthSessionService';
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState'; import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '@gridpilot/automation/domain/value-objects/BrowserAuthenticationState'; import { BrowserAuthenticationState } from '@gridpilot/automation/domain/value-objects/BrowserAuthenticationState';
import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort'; import type { LoggerPort as Logger } from '../../../../core/automation/application/ports/LoggerPort';
import type { Result } from '../../../../core/shared/result/Result'; import type { Result } from '../../../../core/shared/result/Result';
import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure/adapters/automation/core/PlaywrightBrowserSession'; import type { PlaywrightBrowserSession } from '../../../../core/automation/infrastructure//automation/core/PlaywrightBrowserSession';
import type { SessionCookieStore } from '../../../../core/automation/infrastructure/adapters/automation/auth/SessionCookieStore'; import type { SessionCookieStore } from '../../../../core/automation/infrastructure//automation/auth/SessionCookieStore';
import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure/adapters/automation/auth/PlaywrightAuthFlow'; import type { IPlaywrightAuthFlow } from '../../../../core/automation/infrastructure//automation/auth/PlaywrightAuthFlow';
describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => { describe('PlaywrightAuthSessionService.verifyPageAuthentication', () => {
function createService(deps: { function createService(deps: {
@@ -1,5 +1,5 @@
import { describe, test, expect, beforeEach } from 'vitest'; import { describe, test, expect, beforeEach } from 'vitest';
import { SessionCookieStore } from '@gridpilot/automation/infrastructure/adapters/automation/auth/SessionCookieStore'; import { SessionCookieStore } from '@gridpilot/automation/infrastructure//automation/auth/SessionCookieStore';
import type { Cookie } from 'playwright'; import type { Cookie } from 'playwright';
const logger = console as any; const logger = console as any;
@@ -4,9 +4,9 @@ import { configureDIContainer, resetDIContainer } from "../../../apps/companion/
import { DI_TOKENS } from "../../../apps/companion/main/di-tokens"; import { DI_TOKENS } from "../../../apps/companion/main/di-tokens";
import { OverlaySyncService } from "@gridpilot/automation/application/services/OverlaySyncService"; import { OverlaySyncService } from "@gridpilot/automation/application/services/OverlaySyncService";
import { LoggerPort } from "@gridpilot/automation/application/ports/LoggerPort"; import { LoggerPort } from "@gridpilot/automation/application/ports/LoggerPort";
import { IAutomationLifecycleEmitter, LifecycleCallback } from "@gridpilot/automation/infrastructure/adapters/IAutomationLifecycleEmitter"; import { IAutomationLifecycleEmitter, LifecycleCallback } from "@gridpilot/automation/infrastructure//IAutomationLifecycleEmitter";
import { AutomationEventPublisherPort, AutomationEvent } from "@gridpilot/automation/application/ports/AutomationEventPublisherPort"; import { AutomationEventPublisherPort, AutomationEvent } from "@gridpilot/automation/application/ports/AutomationEventPublisherPort";
import { ConsoleLogAdapter } from "@gridpilot/automation/infrastructure/adapters/logging/ConsoleLogAdapter"; import { ConsoleLogAdapter } from "@gridpilot/automation/infrastructure//logging/ConsoleLogAdapter";
import { describe, it, expect, beforeEach, afterEach, vi, SpyInstance } from 'vitest'; import { describe, it, expect, beforeEach, afterEach, vi, SpyInstance } from 'vitest';
describe("OverlaySyncService Integration with ConsoleLogAdapter", () => { describe("OverlaySyncService Integration with ConsoleLogAdapter", () => {
@@ -87,7 +87,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter({ adapter = new PlaywrightAutomationAdapter({
@@ -111,7 +111,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter({ adapter = new PlaywrightAutomationAdapter({
@@ -135,7 +135,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter({ adapter = new PlaywrightAutomationAdapter({
@@ -164,7 +164,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter({ adapter = new PlaywrightAutomationAdapter({
@@ -185,7 +185,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter({ adapter = new PlaywrightAutomationAdapter({
@@ -228,7 +228,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}; };
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
adapter = new PlaywrightAutomationAdapter( adapter = new PlaywrightAutomationAdapter(
@@ -259,7 +259,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
const userDataDir = path.join(process.cwd(), 'test-browser-data'); const userDataDir = path.join(process.cwd(), 'test-browser-data');
@@ -290,7 +290,7 @@ describe('Browser Mode Integration - GREEN Phase', () => {
configurable: true configurable: true
}); });
const { PlaywrightAutomationAdapter } = await import( const { PlaywrightAutomationAdapter } = await import(
'core/automation/infrastructure/adapters/automation' 'core/automation/infrastructure//automation'
); );
const { BrowserModeConfigLoader } = await import( const { BrowserModeConfigLoader } = await import(
'../../../core/automation/infrastructure/config/BrowserModeConfig' '../../../core/automation/infrastructure/config/BrowserModeConfig'
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { describe, it, expect, beforeEach, vi } from 'vitest';
import { Result } from '../../../core/shared/result/Result'; import { Result } from '../../../core/shared/result/Result';
import { CheckoutPriceExtractor } from '../../../core/automation/infrastructure/adapters/automation/CheckoutPriceExtractor'; import { CheckoutPriceExtractor } from '../../../core/automation/infrastructure//automation/CheckoutPriceExtractor';
import { CheckoutStateEnum } from '@gridpilot/automation/domain/value-objects/CheckoutState'; import { CheckoutStateEnum } from '@gridpilot/automation/domain/value-objects/CheckoutState';
/** /**
@@ -3,7 +3,7 @@
*/ */
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { FixtureServer, getAllStepFixtureMappings, PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'; import { FixtureServer, getAllStepFixtureMappings, PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
declare const getComputedStyle: any; declare const getComputedStyle: any;
declare const document: any; declare const document: any;
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { MockBrowserAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'; import { MockBrowserAutomationAdapter } from 'core/automation/infrastructure//automation';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
describe('MockBrowserAutomationAdapter Integration Tests', () => { describe('MockBrowserAutomationAdapter Integration Tests', () => {
@@ -1,6 +1,6 @@
import { describe, test, expect } from 'vitest' import { describe, test, expect } from 'vitest'
import type { Page } from 'playwright' import type { Page } from 'playwright'
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation' import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation'
describe('CarsFlow integration', () => { describe('CarsFlow integration', () => {
test('adapter emits panel-attached then action-started then action-complete for performAddCar', async () => { test('adapter emits panel-attached then action-started then action-complete for performAddCar', async () => {
@@ -4,7 +4,7 @@ import type { AutomationEvent } from 'core/automation/application/ports/IAutomat
import type { import type {
IAutomationLifecycleEmitter, IAutomationLifecycleEmitter,
LifecycleCallback, LifecycleCallback,
} from 'core/automation/infrastructure/adapters/IAutomationLifecycleEmitter'; } from 'core/automation/infrastructure//IAutomationLifecycleEmitter';
import type { import type {
OverlayAction, OverlayAction,
ActionAck, ActionAck,
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIContainer } from '../../../..//apps/companion/main/di-container'; import { DIContainer } from '../../../..//apps/companion/main/di-container';
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig'; import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
describe('companion start automation - browser mode refresh wiring', () => { describe('companion start automation - browser mode refresh wiring', () => {
const originalEnv = { ...process.env }; const originalEnv = { ...process.env };
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { DIContainer } from '../../../..//apps/companion/main/di-container'; import { DIContainer } from '../../../..//apps/companion/main/di-container';
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig'; import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId'; import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
describe('companion start automation - browser not connected at step 1', () => { describe('companion start automation - browser not connected at step 1', () => {
const originalEnv = { ...process.env }; const originalEnv = { ...process.env };
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { DIContainer } from '../../../..//apps/companion/main/di-container'; import { DIContainer } from '../../../..//apps/companion/main/di-container';
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig'; import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
describe('companion start automation - browser connection failure before steps', () => { describe('companion start automation - browser connection failure before steps', () => {
const originalEnv = { ...process.env }; const originalEnv = { ...process.env };
+2 -2
View File
@@ -5,9 +5,9 @@ import { CheckAuthenticationUseCase } from '../../core/automation/application/us
import { InitiateLoginUseCase } from '../../core/automation/application/use-cases/InitiateLoginUseCase'; import { InitiateLoginUseCase } from '../../core/automation/application/use-cases/InitiateLoginUseCase';
import { ClearSessionUseCase } from '../../core/automation/application/use-cases/ClearSessionUseCase'; import { ClearSessionUseCase } from '../../core/automation/application/use-cases/ClearSessionUseCase';
import { ConfirmCheckoutUseCase } from '../../core/automation/application/use-cases/ConfirmCheckoutUseCase'; import { ConfirmCheckoutUseCase } from '../../core/automation/application/use-cases/ConfirmCheckoutUseCase';
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
import { InMemorySessionRepository } from '../../core/automation/infrastructure/repositories/InMemorySessionRepository'; import { InMemorySessionRepository } from '../../core/automation/infrastructure/repositories/InMemorySessionRepository';
import { NoOpLogAdapter } from '../../core/automation/infrastructure/adapters/logging/NoOpLogAdapter'; import { NoOpLogAdapter } from '../../core/automation/infrastructure//logging/NoOpLogAdapter';
// Mock Electron's app module // Mock Electron's app module
vi.mock('electron', () => ({ vi.mock('electron', () => ({
+2 -2
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, afterEach, beforeAll, afterAll } from 'vitest'; import { describe, it, expect, afterEach, beforeAll, afterAll } from 'vitest';
import { PlaywrightAutomationAdapter, FixtureServer } from 'core/automation/infrastructure/adapters/automation'; import { PlaywrightAutomationAdapter, FixtureServer } from 'core/automation/infrastructure//automation';
import { NoOpLogAdapter } from '../../core/automation/infrastructure/adapters/logging/NoOpLogAdapter'; import { NoOpLogAdapter } from '../../core/automation/infrastructure//logging/NoOpLogAdapter';
describe('Playwright Adapter Smoke Tests', () => { describe('Playwright Adapter Smoke Tests', () => {
let adapter: PlaywrightAutomationAdapter | undefined; let adapter: PlaywrightAutomationAdapter | undefined;