{"version":3,"file":"cacheState.js","sourceRoot":"","sources":["../../src/util/cacheState.ts"],"names":[],"mappings":"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","sourcesContent":["import { log } from \"builder-util\"\nimport * as fs from \"fs/promises\"\nimport * as path from \"path\"\n\nexport enum CacheState {\n  pending = \"pending\",\n  downloaded = \"downloaded\",\n  extracting = \"extracting\",\n  extracted = \"extracted\",\n  complete = \"complete\",\n  corrupted = \"corrupted\",\n}\n\nexport interface CacheStateFile {\n  version: number\n  state: CacheState\n  timestamp: number\n  fileCount: number\n  extractedSize: number\n}\n\nconst STATE_FILE_VERSION = 1\nconst STALE_EXTRACTING_TIMEOUT_MS = 120000 // 120 seconds\n\nfunction getStateFilePath(extractDir: string): string {\n  return `${extractDir}.state`\n}\n\nexport async function readCacheStateFile(extractDir: string): Promise<CacheStateFile | null> {\n  const stateFile = getStateFilePath(extractDir)\n  let content: string\n  try {\n    content = await fs.readFile(stateFile, \"utf-8\")\n  } catch (e: any) {\n    if (e.code !== \"ENOENT\") {\n      log.warn({ stateFile, error: e.message }, \"Failed to read cache state file\")\n    }\n    return null\n  }\n  try {\n    const data: CacheStateFile = JSON.parse(content)\n    if (data.version !== STATE_FILE_VERSION) {\n      log.warn({ stateFile, version: data.version, expected: STATE_FILE_VERSION }, \"Cache state file version mismatch, ignoring\")\n      return null\n    }\n    if (data.state === CacheState.extracting) {\n      const age = Date.now() - data.timestamp\n      if (age > STALE_EXTRACTING_TIMEOUT_MS) {\n        log.warn({ stateFile, age }, \"Detected stale extracting state, marking as corrupted\")\n        return { ...data, state: CacheState.corrupted }\n      }\n    }\n    return data\n  } catch (e: any) {\n    log.warn({ stateFile, error: e.message }, \"Failed to parse cache state file\")\n    return null\n  }\n}\n\nexport async function writeCacheState(extractDir: string, state: CacheState, metadata?: { fileCount?: number; extractedSize?: number }, throwOnError = false): Promise<void> {\n  const stateFile = getStateFilePath(extractDir)\n  const stateData: CacheStateFile = {\n    version: STATE_FILE_VERSION,\n    state,\n    timestamp: Date.now(),\n    fileCount: metadata?.fileCount ?? 0,\n    extractedSize: metadata?.extractedSize ?? 0,\n  }\n\n  try {\n    await fs.writeFile(stateFile, JSON.stringify(stateData, null, 2), \"utf-8\")\n  } catch (e: any) {\n    log.warn({ stateFile, error: e.message }, \"Failed to write cache state file\")\n    if (throwOnError) {\n      throw e\n    }\n  }\n}\n\nexport async function computeCacheMetadata(dir: string): Promise<{ fileCount: number; extractedSize: number }> {\n  let fileCount = 0\n  let extractedSize = 0\n  const stack: string[] = [dir]\n  while (stack.length > 0) {\n    const current = stack.pop()!\n    let entries\n    try {\n      entries = await fs.readdir(current, { withFileTypes: true })\n    } catch {\n      continue\n    }\n    for (const entry of entries) {\n      const fullPath = path.join(current, entry.name)\n      if (entry.isDirectory()) {\n        stack.push(fullPath)\n      } else {\n        fileCount++\n        if (entry.isFile()) {\n          try {\n            const stat = await fs.stat(fullPath)\n            extractedSize += stat.size\n          } catch {\n            // ignore stat errors for individual files\n          }\n        }\n      }\n    }\n  }\n  return { fileCount, extractedSize }\n}\n\nexport async function validateCacheDirectory(extractDir: string, expectedFileCount?: number): Promise<boolean> {\n  try {\n    const topLevel = await fs.readdir(extractDir)\n    if (topLevel.length === 0) {\n      return false\n    }\n\n    if (expectedFileCount != null && expectedFileCount > 0) {\n      const { fileCount: actual } = await computeCacheMetadata(extractDir)\n      if (actual < expectedFileCount) {\n        log.warn({ extractDir, expected: expectedFileCount, actual }, \"Cache file count mismatch (fewer files than expected), treating as invalid\")\n        return false\n      }\n    }\n\n    return true\n  } catch (e: any) {\n    log.warn({ extractDir, error: e.message }, \"Failed to validate cache directory\")\n    return false\n  }\n}\n\nexport async function cleanupCacheDirectory(extractDir: string, { skipLockFiles = false } = {}): Promise<void> {\n  const filesToClean = [extractDir, `${extractDir}.state`, `${extractDir}.tmp`, ...(!skipLockFiles ? [`${extractDir}.lock`, `${extractDir}.tmp.lock`] : [])]\n\n  for (const file of filesToClean) {\n    try {\n      await fs.rm(file, { recursive: true, force: true })\n    } catch (e: any) {\n      log.warn({ file, error: e.message }, \"Failed to clean up cache file\")\n    }\n  }\n}\n"]}