{
  "version": 3,
  "sources": ["../../../../src/packages/store.cafs/writeBufferToCafs.ts"],
  "sourcesContent": ["import fs from 'node:fs';\nimport path from 'node:path';\nimport workerThreads from 'node:worker_threads';\nimport util from 'node:util';\nimport renameOverwrite from 'rename-overwrite';\nimport type ssri from 'ssri';\nimport { verifyFileIntegrity } from './checkPkgFilesIntegrity.ts';\nimport { writeFile } from './writeFile.ts';\n\nexport function writeBufferToCafs(\n  locker: Map<string, number>,\n  storeDir: string,\n  buffer: Buffer,\n  fileDest: string,\n  mode: number | undefined,\n  integrity: ssri.IntegrityLike\n): { checkedAt: number; filePath: string } {\n  const newFileDest = path.join(storeDir, fileDest);\n\n  if (locker.has(newFileDest)) {\n    return {\n      checkedAt: locker.get(newFileDest) ?? 0,\n      filePath: newFileDest,\n    };\n  }\n\n  // This part is a bit redundant.\n  // When a file is already used by another package,\n  // we probably have validated its content already.\n  // However, there is no way to find which package index file references\n  // the given file. So we should revalidate the content of the file again.\n  if (existsSame(newFileDest, integrity)) {\n    return {\n      checkedAt: Date.now(),\n      filePath: newFileDest,\n    };\n  }\n\n  // This might be too cautious.\n  // The write is atomic, so in case pnpm crashes, no broken file\n  // will be added to the store.\n  // It might be a redundant step though, as we verify the contents of the\n  // files before linking\n  //\n  // If we don't allow --no-verify-store-integrity then we probably can write\n  // to the final file directly.\n  const temp = pathTemp(fileDest);\n\n  writeFile(temp, buffer, mode);\n\n  // Unfortunately, \"birth time\" (time of file creation) is available not on all filesystems.\n  // We log the creation time ourselves and save it in the package index file.\n  // Having this information allows us to skip content checks for files that were not modified since \"birth time\".\n  const birthtimeMs = Date.now();\n\n  optimisticRenameOverwrite(temp, fileDest);\n\n  locker.set(fileDest, birthtimeMs);\n\n  return {\n    checkedAt: birthtimeMs,\n    filePath: fileDest,\n  };\n}\n\nexport function optimisticRenameOverwrite(\n  temp: string,\n  fileDest: string\n): void {\n  try {\n    renameOverwrite.sync(temp, fileDest);\n  } catch (err: unknown) {\n    if (\n      !(\n        util.types.isNativeError(err) &&\n        'code' in err &&\n        err.code === 'ENOENT'\n      ) ||\n      !fs.existsSync(fileDest)\n    )\n      throw err;\n    // The temporary file path is created by appending the process ID to the target file name.\n    // This is done to avoid lots of random crypto number generations.\n    //   PR with related performance optimization: https://github.com/pnpm/pnpm/pull/6817\n    //\n    // Probably the only scenario in which the temp directory will disappear\n    // before being renamed is when two containers use the same mounted directory\n    // for their content-addressable store. In this case there's a chance that the process ID\n    // will be the same in both containers.\n    //\n    // As a workaround, if the temp file doesn't exist but the target file does,\n    // we just ignore the issue and assume that the target file is correct.\n  }\n}\n\n/**\n * Creates a unique temporary file path by appending both process ID and worker thread ID\n * to the original filename.\n *\n * The process ID prevents conflicts between different processes, while the worker thread ID\n * prevents race conditions between threads in the same process.\n *\n * If a process fails, its temporary file may remain. When the process is rerun, it will\n * safely overwrite any existing temporary file with the same name.\n *\n * @param file - The original file path\n * @returns A temporary file path in the format: {basename}{pid}{threadId}\n */\nexport function pathTemp(file: string): string {\n  const basename = removeSuffix(path.basename(file));\n  return path.join(\n    path.dirname(file),\n    `${basename}${process.pid}${workerThreads.threadId}`\n  );\n}\n\nfunction removeSuffix(filePath: string): string {\n  const dashPosition = filePath.indexOf('-');\n  if (dashPosition === -1) return filePath;\n  const withoutSuffix = filePath.substring(0, dashPosition);\n  if (filePath.substring(dashPosition) === '-exec') {\n    return `${withoutSuffix}x`;\n  }\n  return withoutSuffix;\n}\n\nfunction existsSame(filename: string, integrity: ssri.IntegrityLike): boolean {\n  const existingFile = fs.statSync(filename, { throwIfNoEntry: false });\n  if (!existingFile) return false;\n  return verifyFileIntegrity(filename, {\n    size: existingFile.size,\n    integrity,\n  }).passed;\n}\n"],
  "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,mBAAmB;AAC1B,OAAO,UAAU;AACjB,OAAO,qBAAqB;AAE5B,SAAS,2BAA2B;AACpC,SAAS,iBAAiB;AAEnB,SAAS,kBACd,QACA,UACA,QACA,UACA,MACA,WACyC;AACzC,QAAM,cAAc,KAAK,KAAK,UAAU,QAAQ;AAEhD,MAAI,OAAO,IAAI,WAAW,GAAG;AAC3B,WAAO;AAAA,MACL,WAAW,OAAO,IAAI,WAAW,KAAK;AAAA,MACtC,UAAU;AAAA,IACZ;AAAA,EACF;AAOA,MAAI,WAAW,aAAa,SAAS,GAAG;AACtC,WAAO;AAAA,MACL,WAAW,KAAK,IAAI;AAAA,MACpB,UAAU;AAAA,IACZ;AAAA,EACF;AAUA,QAAM,OAAO,SAAS,QAAQ;AAE9B,YAAU,MAAM,QAAQ,IAAI;AAK5B,QAAM,cAAc,KAAK,IAAI;AAE7B,4BAA0B,MAAM,QAAQ;AAExC,SAAO,IAAI,UAAU,WAAW;AAEhC,SAAO;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,0BACd,MACA,UACM;AACN,MAAI;AACF,oBAAgB,KAAK,MAAM,QAAQ;AAAA,EACrC,SAAS,KAAc;AACrB,QACE,EACE,KAAK,MAAM,cAAc,GAAG,KAC5B,UAAU,OACV,IAAI,SAAS,aAEf,CAAC,GAAG,WAAW,QAAQ;AAEvB,YAAM;AAAA,EAYV;AACF;AAeO,SAAS,SAAS,MAAsB;AAC7C,QAAM,WAAW,aAAa,KAAK,SAAS,IAAI,CAAC;AACjD,SAAO,KAAK;AAAA,IACV,KAAK,QAAQ,IAAI;AAAA,IACjB,GAAG,QAAQ,GAAG,QAAQ,GAAG,GAAG,cAAc,QAAQ;AAAA,EACpD;AACF;AAEA,SAAS,aAAa,UAA0B;AAC9C,QAAM,eAAe,SAAS,QAAQ,GAAG;AACzC,MAAI,iBAAiB,GAAI,QAAO;AAChC,QAAM,gBAAgB,SAAS,UAAU,GAAG,YAAY;AACxD,MAAI,SAAS,UAAU,YAAY,MAAM,SAAS;AAChD,WAAO,GAAG,aAAa;AAAA,EACzB;AACA,SAAO;AACT;AAEA,SAAS,WAAW,UAAkB,WAAwC;AAC5E,QAAM,eAAe,GAAG,SAAS,UAAU,EAAE,gBAAgB,MAAM,CAAC;AACpE,MAAI,CAAC,aAAc,QAAO;AAC1B,SAAO,oBAAoB,UAAU;AAAA,IACnC,MAAM,aAAa;AAAA,IACnB;AAAA,EACF,CAAC,EAAE;AACL;",
  "names": []
}
