This commit is contained in:
2025-12-04 11:54:42 +01:00
parent 9d5caa87f3
commit b7d5551ea7
223 changed files with 5473 additions and 885 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,76 @@
import { AuthenticationState } from '@gridpilot/automation/domain/value-objects/AuthenticationState';
import { BrowserAuthenticationState } from '@gridpilot/automation/domain/value-objects/BrowserAuthenticationState';
import { Result } from '../../shared/result/Result';
/**
* Port for authentication services implementing zero-knowledge login.
*
* GridPilot never sees, stores, or transmits user credentials.
* Authentication is handled by opening a visible browser window where
* the user logs in directly with iRacing. GridPilot only observes
* URL changes to detect successful authentication.
*/
export interface IAuthenticationService {
/**
* Check if user has a valid session without prompting login.
* Navigates to a protected iRacing page and checks for login redirects.
*
* @returns Result containing the current authentication state
*/
checkSession(): Promise<Result<AuthenticationState>>;
/**
* Open browser for user to login manually.
* The browser window is visible so user can verify they're on the real iRacing site.
* GridPilot waits for URL change indicating successful login.
*
* @returns Result indicating success (login complete) or failure (cancelled/timeout)
*/
initiateLogin(): Promise<Result<void>>;
/**
* Clear the persistent session (logout).
* Removes stored browser context and cookies.
*
* @returns Result indicating success or failure
*/
clearSession(): Promise<Result<void>>;
/**
* Get current authentication state.
* Returns cached state without making network requests.
*
* @returns The current AuthenticationState
*/
getState(): AuthenticationState;
/**
* Validate session with server-side check.
* Makes a lightweight HTTP request to verify cookies are still valid on the server.
*
* @returns Result containing true if server confirms validity, false otherwise
*/
validateServerSide(): Promise<Result<boolean>>;
/**
* Refresh session state from cookie store.
* Re-reads cookies and updates internal state without server validation.
*
* @returns Result indicating success or failure
*/
refreshSession(): Promise<Result<void>>;
/**
* Get session expiry date.
* Returns the expiry time extracted from session cookies.
*
* @returns Result containing the expiry Date or null if no expiration
*/
getSessionExpiry(): Promise<Result<Date | null>>;
/**
* Verify browser page shows authenticated state.
* Checks page content for authentication indicators.
*/
verifyPageAuthentication(): Promise<Result<BrowserAuthenticationState>>;
}

View File

@@ -0,0 +1,13 @@
import { HostedSessionConfig } from '@gridpilot/automation/domain/entities/HostedSessionConfig';
import { StepId } from '@gridpilot/automation/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>;
stopAutomation(): void;
}

View File

@@ -0,0 +1,10 @@
export type AutomationEvent = {
actionId?: string
type: 'panel-attached'|'modal-opened'|'action-started'|'action-complete'|'action-failed'|'panel-missing'
timestamp: number
payload?: any
}
export interface IAutomationEventPublisher {
publish(event: AutomationEvent): Promise<void>
}

View File

@@ -0,0 +1,21 @@
import { Result } from '../../shared/result/Result';
import { CheckoutConfirmation } from '@gridpilot/automation/domain/value-objects/CheckoutConfirmation';
import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState';
export interface CheckoutConfirmationRequest {
price: CheckoutPrice;
state: CheckoutState;
sessionMetadata: {
sessionName: string;
trackId: string;
carIds: string[];
};
timeoutMs: number;
}
export interface ICheckoutConfirmationPort {
requestCheckoutConfirmation(
request: CheckoutConfirmationRequest
): Promise<Result<CheckoutConfirmation>>;
}

View File

@@ -0,0 +1,14 @@
import { Result } from '../../shared/result/Result';
import { CheckoutPrice } from '@gridpilot/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from '@gridpilot/automation/domain/value-objects/CheckoutState';
export interface CheckoutInfo {
price: CheckoutPrice | null;
state: CheckoutState;
buttonHtml: string;
}
export interface ICheckoutService {
extractCheckoutInfo(): Promise<Result<CheckoutInfo>>;
proceedWithCheckout(): Promise<Result<void>>;
}

View File

@@ -0,0 +1,35 @@
/**
* Log levels in order of severity (lowest to highest)
*/
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';
/**
* Contextual metadata attached to log entries
*/
export interface LogContext {
/** Unique session identifier for correlation */
sessionId?: string;
/** Current automation step (1-18) */
stepId?: number;
/** Step name for human readability */
stepName?: string;
/** Adapter or component name */
adapter?: string;
/** Operation duration in milliseconds */
durationMs?: number;
/** Additional arbitrary metadata */
[key: string]: unknown;
}
/**
* ILogger - Port interface for application-layer logging.
*/
export interface ILogger {
debug(message: string, context?: LogContext): void;
info(message: string, context?: LogContext): void;
warn(message: string, context?: LogContext): void;
error(message: string, error?: Error, context?: LogContext): void;
fatal(message: string, error?: Error, context?: LogContext): void;
child(context: LogContext): ILogger;
flush(): Promise<void>;
}

View File

@@ -0,0 +1,7 @@
export type OverlayAction = { id: string; label: string; meta?: Record<string, unknown>; timeoutMs?: number }
export type ActionAck = { id: string; status: 'confirmed' | 'tentative' | 'failed'; reason?: string }
export interface IOverlaySyncPort {
startAction(action: OverlayAction): Promise<ActionAck>
cancelAction(actionId: string): Promise<void>
}

View File

@@ -0,0 +1,70 @@
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import {
NavigationResult,
FormFillResult,
ClickResult,
WaitResult,
ModalResult,
AutomationResult,
} from './AutomationResults';
/**
* Browser automation interface for Playwright-based automation.
*
* This interface defines the contract for browser automation using
* standard DOM manipulation via Playwright. All automation is done
* through browser DevTools protocol - no OS-level automation.
*/
export interface IBrowserAutomation {
/**
* Navigate to a URL.
*/
navigateToPage(url: string): Promise<NavigationResult>;
/**
* Fill a form field by name or selector.
*/
fillFormField(fieldName: string, value: string): Promise<FormFillResult>;
/**
* Click an element by selector or action name.
*/
clickElement(target: string): Promise<ClickResult>;
/**
* Wait for an element to appear.
*/
waitForElement(target: string, maxWaitMs?: number): Promise<WaitResult>;
/**
* Handle modal dialogs.
*/
handleModal(stepId: StepId, action: string): Promise<ModalResult>;
/**
* Execute a complete workflow step.
*/
executeStep?(stepId: StepId, config: Record<string, unknown>): Promise<AutomationResult>;
/**
* Initialize the browser connection.
* Returns an AutomationResult indicating success or failure.
*/
connect?(): Promise<AutomationResult>;
/**
* Clean up browser resources.
*/
disconnect?(): Promise<void>;
/**
* Check if browser is connected and ready.
*/
isConnected?(): boolean;
}
/**
* @deprecated Use IBrowserAutomation directly. IScreenAutomation was for OS-level
* automation which has been removed in favor of browser-only automation.
*/
export type IScreenAutomation = IBrowserAutomation;

View File

@@ -0,0 +1,11 @@
import { AutomationSession } from '@gridpilot/automation/domain/entities/AutomationSession';
import { SessionStateValue } from '@gridpilot/automation/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,3 @@
export interface IUserConfirmationPort {
confirm(message: string): Promise<boolean>;
}