/** * SMI-2277: Approval Repository * * Persists multi-approval state to SQLite instead of in-memory Map. * This ensures pending approvals survive service restarts. * * @module @skillsmith/core/repositories/quarantine/ApprovalRepository */ import type { Database as DatabaseType } from '../../db/database-interface.js'; /** * Database row for quarantine_approvals table */ export interface ApprovalRow { id: string; skill_id: string; reviewer_id: string; reviewer_email: string; decision: 'approved' | 'rejected'; reason: string | null; created_at: string; completed_at: string | null; required_approvals: number; is_complete: number; } /** * Domain object for a recorded approval */ export interface ApprovalEntry { id: string; skillId: string; reviewerId: string; reviewerEmail: string; decision: 'approved' | 'rejected'; reason: string | null; createdAt: string; completedAt: string | null; requiredApprovals: number; isComplete: boolean; } /** * Input for recording a new approval */ export interface RecordApprovalInput { skillId: string; reviewerId: string; reviewerEmail: string; decision: 'approved' | 'rejected'; reason?: string; requiredApprovals?: number; } /** * Repository for persisting multi-approval workflow state. * * Replaces the in-memory Map with * database-backed storage to survive service restarts. * * @example * ```typescript * const repo = new ApprovalRepository(db) * * // Record a reviewer's approval * repo.recordApproval({ * skillId: 'quarantine-123', * reviewerId: 'user-456', * reviewerRole: 'reviewer@example.com', * decision: 'approved', * reason: 'Code verified safe', * }) * * // Check if enough approvals have been collected * const complete = repo.isComplete('quarantine-123') * ``` */ export declare class ApprovalRepository { private db; constructor(db: DatabaseType); /** * Ensure the quarantine_approvals table exists */ private ensureTableExists; /** * Record a new approval or rejection for a quarantine entry * * @param input - Approval details * @returns The created approval entry * @throws Error if reviewer has already submitted for this skill */ recordApproval(input: RecordApprovalInput): ApprovalEntry; /** * Get all approvals for a quarantine entry (both pending and complete) * * @param skillId - The quarantine entry ID * @returns All approval entries for this skill */ getApprovals(skillId: string): ApprovalEntry[]; /** * Get only pending (non-complete) approvals for a quarantine entry * * @param skillId - The quarantine entry ID * @returns Pending approval entries */ getPendingApprovals(skillId: string): ApprovalEntry[]; /** * Check if a specific reviewer has already submitted for a skill * * @param skillId - The quarantine entry ID * @param reviewerId - The reviewer's user ID * @returns True if the reviewer already has a pending approval */ hasReviewerApproved(skillId: string, reviewerId: string): boolean; /** * Check if the required number of approvals have been reached * * @param skillId - The quarantine entry ID * @param requiredApprovals - Number of approvals needed (default: 2) * @returns True if approval count meets or exceeds required */ isComplete(skillId: string, requiredApprovals?: number): boolean; /** * Mark all pending approvals for a skill as complete * * @param skillId - The quarantine entry ID * @returns Number of rows updated */ markComplete(skillId: string): number; /** * Delete all approvals for a skill (for cleanup/reset/cancellation) * * @param skillId - The quarantine entry ID * @returns Number of rows deleted */ clearApprovals(skillId: string): number; /** * Get the timestamp of the first pending approval for timeout checks * * @param skillId - The quarantine entry ID * @returns ISO date string of the first approval, or null if none */ getWorkflowStartTime(skillId: string): string | null; /** * Get the count of pending approvals for a skill * * @param skillId - The quarantine entry ID * @returns Count of pending approved entries */ getPendingApprovalCount(skillId: string): number; } //# sourceMappingURL=ApprovalRepository.d.ts.map