{
  "version": 3,
  "sources": ["../../../../src/packages/store.cafs/checkPkgFilesIntegrity.ts"],
  "sourcesContent": ["import fs from 'node:fs';\nimport util from 'node:util';\nimport type {\n  PackageFiles,\n  PackageFileInfo,\n  SideEffects,\n} from '../cafs-types/index.ts';\nimport gfs from '../graceful-fs/index.ts';\nimport type { DependencyManifest } from '../types/index.ts';\nimport rimraf from '@zkochan/rimraf';\nimport ssri from 'ssri';\nimport { getFilePathByModeInCafs } from './getFilePathInCafs.ts';\nimport { parseJsonBufferSync } from './parseJson.ts';\n\n// We track how many files were checked during installation.\n// It should be rare that a files content should be checked.\n// If it happens too frequently, something is wrong.\n// Checking a file's integrity is an expensive operation!\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-expect-error\nglobal['verifiedFileIntegrity'] = 0;\n\nexport type VerifyResult = {\n  passed: boolean;\n  manifest?: DependencyManifest | undefined;\n};\n\nexport type PackageFilesIndex = {\n  // name and version are nullable for backward compatibility\n  // the initial specs of pnpm store v3 did not require these fields.\n  // However, it might be possible that some types of dependencies don't\n  // have the name/version fields, like the local tarball dependencies.\n  name?: string | undefined;\n  version?: string | undefined;\n  requiresBuild?: boolean | undefined;\n\n  files: PackageFiles;\n  sideEffects?: SideEffects | undefined;\n};\n\nexport function checkPkgFilesIntegrity(\n  storeDir: string,\n  pkgIndex: PackageFilesIndex,\n  readManifest?: boolean | undefined\n): VerifyResult {\n  // It might make sense to use this cache for all files in the store\n  // but there's a smaller chance that the same file will be checked twice\n  // so it's probably not worth the memory (this assumption should be verified)\n  const verifiedFilesCache = new Set<string>();\n\n  const _checkFilesIntegrity = checkFilesIntegrity.bind(\n    null,\n    verifiedFilesCache,\n    storeDir\n  );\n\n  const verified = _checkFilesIntegrity(pkgIndex.files, readManifest);\n\n  if (!verified.passed) {\n    return { passed: false };\n  }\n\n  if (pkgIndex.sideEffects) {\n    // We verify all side effects cache. We could optimize it to verify only the side effects cache\n    // that satisfies the current os/arch/platform.\n    // However, it likely won't make a big difference.\n    for (const [sideEffectName, { added }] of Object.entries(\n      pkgIndex.sideEffects\n    )) {\n      if (added) {\n        const { passed } = _checkFilesIntegrity(added);\n\n        if (!passed) {\n          delete pkgIndex.sideEffects[sideEffectName];\n        }\n      }\n    }\n  }\n\n  return verified;\n}\n\nfunction checkFilesIntegrity(\n  verifiedFilesCache: Set<string>,\n  storeDir: string,\n  files: PackageFiles,\n  readManifest?: boolean | undefined\n): VerifyResult {\n  let allVerified = true;\n\n  let manifest: DependencyManifest | undefined;\n\n  for (const [f, fstat] of Object.entries(files)) {\n    if (!fstat.integrity) {\n      throw new Error(`Integrity checksum is missing for ${f}`);\n    }\n\n    const filename = getFilePathByModeInCafs(\n      storeDir,\n      fstat.integrity,\n      fstat.mode\n    );\n\n    const readFile = readManifest === true && f === 'package.json';\n\n    if (!readFile && verifiedFilesCache.has(filename)) {\n      continue;\n    }\n\n    const verifyResult = verifyFile(filename, fstat, readFile);\n\n    if (readFile) {\n      manifest = verifyResult.manifest;\n    }\n\n    if (verifyResult.passed) {\n      verifiedFilesCache.add(filename);\n    } else {\n      allVerified = false;\n    }\n  }\n\n  return {\n    passed: allVerified,\n    manifest,\n  };\n}\n\ntype FileInfo = Pick<PackageFileInfo, 'size' | 'checkedAt'> & {\n  integrity: string | ssri.IntegrityLike;\n};\n\nfunction verifyFile(\n  filename: string,\n  fstat: FileInfo,\n  readManifest?: boolean | undefined\n): VerifyResult {\n  const currentFile = checkFile(filename, fstat.checkedAt);\n\n  if (currentFile == null) return { passed: false };\n\n  if (currentFile.isModified) {\n    if (currentFile.size !== fstat.size) {\n      rimraf.sync(filename);\n\n      return { passed: false };\n    }\n\n    return verifyFileIntegrity(filename, fstat, readManifest);\n  }\n\n  if (readManifest === true) {\n    return {\n      passed: true,\n      manifest: parseJsonBufferSync(\n        gfs.readFileSync(filename)\n      ) as DependencyManifest,\n    };\n  }\n  // If a file was not edited, we are skipping integrity check.\n  // We assume that nobody will manually remove a file in the store and create a new one.\n  return { passed: true };\n}\n\nexport function verifyFileIntegrity(\n  filename: string,\n  expectedFile: FileInfo,\n  readManifest?: boolean\n): VerifyResult {\n  // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n  // @ts-expect-error\n  global['verifiedFileIntegrity']++;\n\n  try {\n    const data = gfs.readFileSync(filename);\n\n    const passed = Boolean(ssri.checkData(data, expectedFile.integrity));\n\n    if (!passed) {\n      gfs.unlinkSync(filename);\n      return { passed };\n    }\n\n    if (readManifest === true) {\n      return {\n        passed,\n        manifest: parseJsonBufferSync(data) as DependencyManifest,\n      };\n    }\n\n    return { passed };\n  } catch (err: unknown) {\n    switch (util.types.isNativeError(err) && 'code' in err && err.code) {\n      case 'ENOENT': {\n        return { passed: false };\n      }\n\n      case 'EINTEGRITY': {\n        // Broken files are removed from the store\n        gfs.unlinkSync(filename);\n        return { passed: false };\n      }\n    }\n\n    throw err;\n  }\n}\n\nfunction checkFile(\n  filename: string,\n  checkedAt?: number | undefined\n): { isModified: boolean; size: number } | null {\n  try {\n    const { mtimeMs, size } = fs.statSync(filename);\n\n    return {\n      isModified: mtimeMs - (checkedAt ?? 0) > 100,\n      size,\n    };\n  } catch (err: unknown) {\n    if (\n      util.types.isNativeError(err) &&\n      'code' in err &&\n      err.code === 'ENOENT'\n    ) {\n      return null;\n    }\n\n    throw err;\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
