/** * Generic Sync Strategy Interface * * All resource types (Projects, Integrations, AKB, Attributes) implement this interface. * This enables the SyncEngine to handle all resources uniformly. */ import type { CustomerConfig } from '../../resources/common/types.js'; /** * Validation result for pre-sync validation */ export interface ValidationResult { valid: boolean; errors: ValidationError[]; } export interface ValidationError { field: string; message: string; path?: string; } /** * Change operation types */ export type ChangeOperation = 'created' | 'modified' | 'deleted'; /** * Generic change item representing a resource change */ export interface ChangeItem { item: T; operation: ChangeOperation; path: string; } /** * Pull result containing resources and metadata */ export interface PullResult { items: T[]; count: number; hashes: Record; } /** * Push result containing operation outcomes */ export interface PushResult { created: number; updated: number; deleted: number; errors: string[]; } /** * Generic Sync Strategy Interface * * TRemote - Type from the API (e.g., API response types) * TLocal - Type for local storage (e.g., YAML/JSON file types) */ export interface ISyncStrategy<_TRemote = unknown, TLocal = unknown> { /** * Resource type identifier (e.g., 'projects', 'integrations', 'akb', 'attributes') */ readonly resourceType: string; /** * Display name for logging/UI (e.g., 'Projects', 'Integrations') */ readonly displayName: string; /** * Pull resources from NEWO platform to local filesystem * * @param customer - Customer configuration * @param options - Optional pull options * @returns Pull result with items and hashes */ pull(customer: CustomerConfig, options?: PullOptions): Promise>; /** * Push local changes to NEWO platform * * @param customer - Customer configuration * @param changes - Changes to push (if not provided, detect changes automatically) * @returns Push result with counts */ push(customer: CustomerConfig, changes?: ChangeItem[]): Promise; /** * Detect what has changed locally since last sync * * @param customer - Customer configuration * @returns Array of changed items */ getChanges(customer: CustomerConfig): Promise[]>; /** * Validate local state before push * * @param customer - Customer configuration * @param items - Items to validate * @returns Validation result */ validate(customer: CustomerConfig, items: TLocal[]): Promise; /** * Get status summary for display * * @param customer - Customer configuration * @returns Status summary */ getStatus(customer: CustomerConfig): Promise; } /** * Pull operation options */ export interface PullOptions { /** * Overwrite local changes without prompting */ silentOverwrite?: boolean; /** * Enable verbose logging */ verbose?: boolean; /** * Specific project ID to pull (for projects strategy) */ projectId?: string | null; /** * Skip deletion detection and cleanup */ skipCleanup?: boolean; } /** * Status summary for a resource type */ export interface StatusSummary { resourceType: string; displayName: string; changedCount: number; changes: Array<{ path: string; operation: ChangeOperation; details?: string; }>; } /** * Abstract base class with common strategy functionality */ export abstract class BaseSyncStrategy implements ISyncStrategy { abstract readonly resourceType: string; abstract readonly displayName: string; abstract pull(customer: CustomerConfig, options?: PullOptions): Promise>; abstract push(customer: CustomerConfig, changes?: ChangeItem[]): Promise; abstract getChanges(customer: CustomerConfig): Promise[]>; abstract validate(customer: CustomerConfig, items: TLocal[]): Promise; async getStatus(customer: CustomerConfig): Promise { const changes = await this.getChanges(customer); return { resourceType: this.resourceType, displayName: this.displayName, changedCount: changes.length, changes: changes.map(c => ({ path: c.path, operation: c.operation })) }; } }