// src/transfer/fs-utils.ts
import { createHash } from "crypto";
import {
  lstat,
  mkdir,
  readdir,
  readFile,
  realpath,
  stat,
  writeFile
} from "fs/promises";
import path from "path";
async function sha256File(filePath) {
  const buf = await readFile(filePath);
  const sha256 = createHash("sha256").update(buf).digest("hex");
  return { sha256, bytes: buf.byteLength };
}
function sha256String(content) {
  const buf = Buffer.from(content, "utf-8");
  const sha256 = createHash("sha256").update(buf).digest("hex");
  return { sha256, bytes: buf.byteLength };
}
async function writeJsonFile(filePath, value) {
  await mkdir(path.dirname(filePath), { recursive: true });
  await writeFile(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
}
async function readJsonFile(filePath) {
  const raw = await readFile(filePath, "utf-8");
  return JSON.parse(raw);
}
async function listFilesRecursive(rootDir) {
  const out = [];
  async function walk(dir) {
    const entries = await readdir(dir, { withFileTypes: true });
    for (const ent of entries) {
      const fp = path.join(dir, ent.name);
      if (ent.isDirectory()) {
        await walk(fp);
      } else if (ent.isFile()) {
        out.push(fp);
      }
    }
  }
  await walk(rootDir);
  return out.sort();
}
async function ensureDirExists(dirPath) {
  await mkdir(dirPath, { recursive: true });
}
async function fileExists(filePath) {
  try {
    await stat(filePath);
    return true;
  } catch {
    return false;
  }
}
function toPosixRelPath(absPath, rootDir) {
  const rel = path.relative(rootDir, absPath);
  return rel.split(path.sep).join("/");
}
function fromPosixRelPath(relPath) {
  return relPath.split("/").join(path.sep);
}
function isPathInsideRoot(rootReal, absPath) {
  const rel = path.relative(rootReal, absPath);
  if (rel === "") return true;
  if (rel === "..") return false;
  if (rel.startsWith(`..${path.sep}`)) return false;
  if (path.isAbsolute(rel)) return false;
  return true;
}
async function assertIsDirectoryNotSymlink(absPath, errorPrefix, argName) {
  const st = await stat(absPath).catch(() => null);
  if (!st || !st.isDirectory()) {
    throw new Error(
      `${errorPrefix}: '${argName}' must be an existing directory: ${absPath}`
    );
  }
  const lst = await lstat(absPath).catch(() => null);
  if (lst && lst.isSymbolicLink()) {
    throw new Error(
      `${errorPrefix}: '${argName}' must not be a symlink \u2014 resolve it to its real path first: ${absPath}`
    );
  }
}
async function assertRealpathInsideRoot(rootReal, targetAbs, sourcePath, errorPrefix) {
  let existing = targetAbs;
  const suffix = [];
  while (existing !== path.dirname(existing)) {
    const st = await lstat(existing).catch(() => null);
    if (st !== null) break;
    suffix.unshift(path.basename(existing));
    existing = path.dirname(existing);
  }
  const existingReal = await realpath(existing).catch(() => existing);
  const targetReal = suffix.length > 0 ? path.join(existingReal, ...suffix) : existingReal;
  if (!isPathInsideRoot(rootReal, targetReal)) {
    throw new Error(
      `${errorPrefix}: record path escapes target root via symlink: ${sourcePath}`
    );
  }
}

export {
  sha256File,
  sha256String,
  writeJsonFile,
  readJsonFile,
  listFilesRecursive,
  ensureDirExists,
  fileExists,
  toPosixRelPath,
  fromPosixRelPath,
  isPathInsideRoot,
  assertIsDirectoryNotSymlink,
  assertRealpathInsideRoot
};
//# sourceMappingURL=chunk-457A4P3L.js.map