refactor: restructure to monorepo with apps and packages directories - Move companion app to apps/companion with electron-vite - Move domain/application/infrastructure to packages/ - Fix ELECTRON_RUN_AS_NODE env var issue for VS Code terminal - Remove legacy esbuild bundler (replaced by electron-vite) - Update workspace scripts in root package.json

This commit is contained in:
2025-11-22 00:25:06 +01:00
parent d20554df55
commit 7eae6e3bd4
39 changed files with 515 additions and 1939 deletions

View File

@@ -0,0 +1,30 @@
export interface AutomationResult {
success: boolean;
error?: string;
metadata?: Record<string, unknown>;
}
export interface NavigationResult extends AutomationResult {
url: string;
loadTime: number;
}
export interface FormFillResult extends AutomationResult {
fieldName: string;
valueSet: string;
}
export interface ClickResult extends AutomationResult {
target: string;
}
export interface WaitResult extends AutomationResult {
target: string;
waitedMs: number;
found: boolean;
}
export interface ModalResult extends AutomationResult {
stepId: number;
action: string;
}

View File

@@ -0,0 +1,12 @@
import { HostedSessionConfig } from '../../domain/entities/HostedSessionConfig';
import { StepId } from '../../domain/value-objects/StepId';
export interface ValidationResult {
isValid: boolean;
error?: string;
}
export interface IAutomationEngine {
validateConfiguration(config: HostedSessionConfig): Promise<ValidationResult>;
executeStep(stepId: StepId, config: HostedSessionConfig): Promise<void>;
}

View File

@@ -0,0 +1,31 @@
import { StepId } from '../../domain/value-objects/StepId';
import {
NavigationResult,
FormFillResult,
ClickResult,
WaitResult,
ModalResult,
AutomationResult,
} from './AutomationResults';
export interface IBrowserAutomation {
navigateToPage(url: string): Promise<NavigationResult>;
fillFormField(fieldName: string, value: string): Promise<FormFillResult>;
clickElement(selector: string): Promise<ClickResult>;
waitForElement(selector: string, maxWaitMs?: number): Promise<WaitResult>;
handleModal(stepId: StepId, action: string): Promise<ModalResult>;
/**
* Execute a complete workflow step with all required browser operations.
* Uses IRacingSelectorMap to locate elements and performs appropriate actions.
*
* @param stepId - The step to execute (1-18)
* @param config - Session configuration with form field values
* @returns AutomationResult with success/failure and metadata
*/
executeStep?(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult>;
connect?(): Promise<void>;
disconnect?(): Promise<void>;
isConnected?(): boolean;
}

View File

@@ -0,0 +1,11 @@
import { AutomationSession } from '../../domain/entities/AutomationSession';
import { SessionStateValue } from '../../domain/value-objects/SessionState';
export interface ISessionRepository {
save(session: AutomationSession): Promise<void>;
findById(id: string): Promise<AutomationSession | null>;
update(session: AutomationSession): Promise<void>;
delete(id: string): Promise<void>;
findAll(): Promise<AutomationSession[]>;
findByState(state: SessionStateValue): Promise<AutomationSession[]>;
}

View File

@@ -0,0 +1,44 @@
import { AutomationSession } from '../../domain/entities/AutomationSession';
import { HostedSessionConfig } from '../../domain/entities/HostedSessionConfig';
import { IAutomationEngine } from '../ports/IAutomationEngine';
import { IBrowserAutomation } from '../ports/IBrowserAutomation';
import { ISessionRepository } from '../ports/ISessionRepository';
export interface SessionDTO {
sessionId: string;
state: string;
currentStep: number;
config: HostedSessionConfig;
startedAt?: Date;
completedAt?: Date;
errorMessage?: string;
}
export class StartAutomationSessionUseCase {
constructor(
private readonly automationEngine: IAutomationEngine,
private readonly browserAutomation: IBrowserAutomation,
private readonly sessionRepository: ISessionRepository
) {}
async execute(config: HostedSessionConfig): Promise<SessionDTO> {
const session = AutomationSession.create(config);
const validationResult = await this.automationEngine.validateConfiguration(config);
if (!validationResult.isValid) {
throw new Error(validationResult.error);
}
await this.sessionRepository.save(session);
return {
sessionId: session.id,
state: session.state.value,
currentStep: session.currentStep.value,
config: session.config,
startedAt: session.startedAt,
completedAt: session.completedAt,
errorMessage: session.errorMessage,
};
}
}