File size: 5,631 Bytes
1dbc34b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
 * Validation Storage - CRUD operations for GitHub issue validation results
 *
 * Stores validation results in .automaker/validations/{issueNumber}/validation.json
 * Results include the validation verdict, metadata, and timestamp for cache invalidation.
 */

import * as secureFs from './secure-fs.js';
import { getValidationsDir, getValidationDir, getValidationPath } from '@automaker/platform';
import type { StoredValidation } from '@automaker/types';

// Re-export StoredValidation for convenience
export type { StoredValidation };

/** Number of hours before a validation is considered stale */
const VALIDATION_CACHE_TTL_HOURS = 24;

/**
 * Write validation result to storage
 *
 * Creates the validation directory if needed and stores the result as JSON.
 *
 * @param projectPath - Absolute path to project directory
 * @param issueNumber - GitHub issue number
 * @param data - Validation data to store
 */
export async function writeValidation(
  projectPath: string,
  issueNumber: number,
  data: StoredValidation
): Promise<void> {
  const validationDir = getValidationDir(projectPath, issueNumber);
  const validationPath = getValidationPath(projectPath, issueNumber);

  // Ensure directory exists
  await secureFs.mkdir(validationDir, { recursive: true });

  // Write validation result
  await secureFs.writeFile(validationPath, JSON.stringify(data, null, 2), 'utf-8');
}

/**
 * Read validation result from storage
 *
 * @param projectPath - Absolute path to project directory
 * @param issueNumber - GitHub issue number
 * @returns Stored validation or null if not found
 */
export async function readValidation(
  projectPath: string,
  issueNumber: number
): Promise<StoredValidation | null> {
  try {
    const validationPath = getValidationPath(projectPath, issueNumber);
    const content = (await secureFs.readFile(validationPath, 'utf-8')) as string;
    return JSON.parse(content) as StoredValidation;
  } catch {
    // File doesn't exist or can't be read
    return null;
  }
}

/**
 * Get all stored validations for a project
 *
 * @param projectPath - Absolute path to project directory
 * @returns Array of stored validations
 */
export async function getAllValidations(projectPath: string): Promise<StoredValidation[]> {
  const validationsDir = getValidationsDir(projectPath);

  try {
    const dirs = await secureFs.readdir(validationsDir, { withFileTypes: true });

    // Read all validation files in parallel for better performance
    const promises = dirs
      .filter((dir) => dir.isDirectory())
      .map((dir) => {
        const issueNumber = parseInt(dir.name, 10);
        if (!isNaN(issueNumber)) {
          return readValidation(projectPath, issueNumber);
        }
        return Promise.resolve(null);
      });

    const results = await Promise.all(promises);
    const validations = results.filter((v): v is StoredValidation => v !== null);

    // Sort by issue number
    validations.sort((a, b) => a.issueNumber - b.issueNumber);

    return validations;
  } catch {
    // Directory doesn't exist
    return [];
  }
}

/**
 * Delete a validation from storage
 *
 * @param projectPath - Absolute path to project directory
 * @param issueNumber - GitHub issue number
 * @returns true if validation was deleted, false if not found
 */
export async function deleteValidation(projectPath: string, issueNumber: number): Promise<boolean> {
  try {
    const validationDir = getValidationDir(projectPath, issueNumber);
    await secureFs.rm(validationDir, { recursive: true, force: true });
    return true;
  } catch {
    return false;
  }
}

/**
 * Check if a validation is stale (older than TTL)
 *
 * @param validation - Stored validation to check
 * @returns true if validation is older than 24 hours
 */
export function isValidationStale(validation: StoredValidation): boolean {
  const validatedAt = new Date(validation.validatedAt);
  const now = new Date();
  const hoursDiff = (now.getTime() - validatedAt.getTime()) / (1000 * 60 * 60);
  return hoursDiff > VALIDATION_CACHE_TTL_HOURS;
}

/**
 * Get validation with freshness info
 *
 * @param projectPath - Absolute path to project directory
 * @param issueNumber - GitHub issue number
 * @returns Object with validation and isStale flag, or null if not found
 */
export async function getValidationWithFreshness(
  projectPath: string,
  issueNumber: number
): Promise<{ validation: StoredValidation; isStale: boolean } | null> {
  const validation = await readValidation(projectPath, issueNumber);
  if (!validation) {
    return null;
  }

  return {
    validation,
    isStale: isValidationStale(validation),
  };
}

/**
 * Mark a validation as viewed by the user
 *
 * @param projectPath - Absolute path to project directory
 * @param issueNumber - GitHub issue number
 * @returns true if validation was marked as viewed, false if not found
 */
export async function markValidationViewed(
  projectPath: string,
  issueNumber: number
): Promise<boolean> {
  const validation = await readValidation(projectPath, issueNumber);
  if (!validation) {
    return false;
  }

  validation.viewedAt = new Date().toISOString();
  await writeValidation(projectPath, issueNumber, validation);
  return true;
}

/**
 * Get count of unviewed, non-stale validations for a project
 *
 * @param projectPath - Absolute path to project directory
 * @returns Number of unviewed validations
 */
export async function getUnviewedValidationsCount(projectPath: string): Promise<number> {
  const validations = await getAllValidations(projectPath);
  return validations.filter((v) => !v.viewedAt && !isValidationStale(v)).length;
}