{"version":3,"file":"dirs.mjs","names":["nodePath","path"],"sources":["../../../../../../../@warlock.js/fs/src/facade/dirs.ts"],"sourcesContent":["import { cp, readdir, rename, rm, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport nodePath from \"node:path\";\nimport { ensureDirectoryAsync } from \"../dirs\";\nimport { directoryExistsAsync, pathExistsAsync } from \"../exists\";\nimport { hashFileAsync, type HashAlgorithm } from \"../hash\";\nimport { listAsync, listDirectoriesAsync, listFilesAsync } from \"../list\";\nimport { removeDirectoryAsync } from \"../remove\";\nimport { statsAsync } from \"../stats\";\nimport {\n  normalizeStats,\n  type CopyOptions,\n  type FileStats,\n  type ListOptions,\n  type MoveOptions,\n  type WalkEntry,\n  type WalkOptions,\n} from \"./options\";\n\n/** Recursive (by default) directory walker — a constant-memory async iterator. */\nasync function* walk(root: string, options?: WalkOptions): AsyncGenerator<WalkEntry> {\n  const recursive = options?.recursive ?? true;\n  const followSymlinks = options?.followSymlinks ?? false;\n  const stack: string[] = [root];\n\n  while (stack.length > 0) {\n    const dir = stack.pop() as string;\n    const entries = await readdir(dir, { withFileTypes: true });\n\n    for (const entry of entries) {\n      const full = nodePath.join(dir, entry.name);\n      const isLink = entry.isSymbolicLink();\n      let isDirectory = entry.isDirectory();\n\n      if (isLink && followSymlinks) {\n        isDirectory = (await stat(full)).isDirectory();\n      }\n\n      yield { path: full, name: entry.name, type: isDirectory ? \"directory\" : \"file\" };\n\n      if (isDirectory && recursive && (!isLink || followSymlinks)) {\n        stack.push(full);\n      }\n    }\n  }\n}\n\nasync function collect(root: string, keep: (entry: WalkEntry) => boolean): Promise<string[]> {\n  const paths: string[] = [];\n  for await (const entry of walk(root, { recursive: true })) {\n    if (keep(entry)) paths.push(entry.path);\n  }\n  return paths;\n}\n\nfunction ensure(path: string): Promise<void> {\n  return ensureDirectoryAsync(path);\n}\n\nfunction remove(path: string): Promise<void> {\n  return removeDirectoryAsync(path);\n}\n\n/** Clear a directory's contents but keep the directory (created if missing). */\nasync function empty(path: string): Promise<void> {\n  await ensureDirectoryAsync(path);\n  const entries = await readdir(path);\n  await Promise.all(\n    entries.map((entry) => rm(nodePath.join(path, entry), { recursive: true, force: true })),\n  );\n}\n\nfunction exists(path: string): Promise<boolean> {\n  return directoryExistsAsync(path);\n}\n\nasync function copy(from: string, to: string, options?: CopyOptions): Promise<void> {\n  if ((options?.errorOnExist || options?.overwrite === false) && (await pathExistsAsync(to))) {\n    throw new Error(`fs.dirs.copy: destination \"${to}\" already exists`);\n  }\n\n  // Only set optional keys when defined — Node's cp rejects `undefined` values\n  // (they must be booleans when the property is present).\n  const cpOptions: Parameters<typeof cp>[2] = {\n    recursive: true,\n    force: options?.overwrite !== false,\n  };\n  if (options?.errorOnExist !== undefined) cpOptions.errorOnExist = options.errorOnExist;\n  if (options?.dereference !== undefined) cpOptions.dereference = options.dereference;\n\n  await cp(from, to, cpOptions);\n}\n\n/** Move a directory, EXDEV-safe: rename, falling back to copy+remove across devices. */\nasync function move(from: string, to: string, options?: MoveOptions): Promise<void> {\n  if (options?.overwrite === false && (await pathExistsAsync(to))) {\n    throw new Error(`fs.dirs.move: destination \"${to}\" already exists`);\n  }\n\n  if (options?.ensureDir !== false) {\n    await ensureDirectoryAsync(nodePath.dirname(to));\n  }\n\n  try {\n    await rename(from, to);\n  } catch (error) {\n    if ((error as { code?: string }).code === \"EXDEV\") {\n      await cp(from, to, { recursive: true });\n      await removeDirectoryAsync(from);\n      return;\n    }\n    throw error;\n  }\n}\n\nasync function stats(path: string): Promise<FileStats> {\n  return normalizeStats(path, await statsAsync(path));\n}\n\n/** Total byte size of a directory tree (sum of file sizes). */\nasync function size(path: string): Promise<number> {\n  let total = 0;\n  for await (const entry of walk(path, { recursive: true })) {\n    if (entry.type === \"file\") {\n      total += (await stat(entry.path)).size;\n    }\n  }\n  return total;\n}\n\n/** Number of immediate entries (files + subdirectories). */\nasync function count(path: string): Promise<number> {\n  return (await readdir(path)).length;\n}\n\nasync function isEmpty(path: string): Promise<boolean> {\n  return (await readdir(path)).length === 0;\n}\n\nfunction list(path: string, options?: ListOptions): Promise<string[]> {\n  return options?.recursive ? collect(path, () => true) : listAsync(path);\n}\n\nfunction listFiles(path: string, options?: ListOptions): Promise<string[]> {\n  return options?.recursive\n    ? collect(path, (entry) => entry.type === \"file\")\n    : listFilesAsync(path);\n}\n\nfunction listDirs(path: string, options?: ListOptions): Promise<string[]> {\n  return options?.recursive\n    ? collect(path, (entry) => entry.type === \"directory\")\n    : listDirectoriesAsync(path);\n}\n\n/**\n * Stable fingerprint of a directory tree: hashes every file (path-sorted) and\n * folds the relative path + per-file digest into one hash. Two trees with the\n * same relative layout + contents produce the same hash.\n */\nasync function hash(path: string, algorithm?: HashAlgorithm): Promise<string> {\n  const filePaths = (await collect(path, (entry) => entry.type === \"file\")).sort();\n  const digest = createHash(algorithm ?? \"sha256\");\n\n  for (const filePath of filePaths) {\n    const relative = nodePath.relative(path, filePath).split(nodePath.sep).join(\"/\");\n    digest.update(`${relative}\\0${await hashFileAsync(filePath, algorithm)}\\n`);\n  }\n\n  return digest.digest(\"hex\");\n}\n\n/**\n * The `fs.dirs.*` facade — an async, ergonomic surface over the directory\n * primitives, plus recursive `walk`/`list*`/`size` and a directory fingerprint.\n */\nexport const dirs = {\n  ensure,\n  remove,\n  empty,\n  exists,\n  copy,\n  move,\n  stats,\n  size,\n  count,\n  isEmpty,\n  list,\n  listFiles,\n  listDirs,\n  walk,\n  hash,\n};\n"],"mappings":";;;;;;;;;;;;;AAoBA,gBAAgB,KAAK,MAAc,SAAkD;CACnF,MAAM,YAAY,SAAS,aAAa;CACxC,MAAM,iBAAiB,SAAS,kBAAkB;CAClD,MAAM,QAAkB,CAAC,IAAI;CAE7B,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,IAAI;EACtB,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;EAE1D,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAOA,KAAS,KAAK,KAAK,MAAM,IAAI;GAC1C,MAAM,SAAS,MAAM,eAAe;GACpC,IAAI,cAAc,MAAM,YAAY;GAEpC,IAAI,UAAU,gBACZ,eAAe,MAAM,KAAK,IAAI,EAAC,CAAE,YAAY;GAG/C,MAAM;IAAE,MAAM;IAAM,MAAM,MAAM;IAAM,MAAM,cAAc,cAAc;GAAO;GAE/E,IAAI,eAAe,cAAc,CAAC,UAAU,iBAC1C,MAAM,KAAK,IAAI;EAEnB;CACF;AACF;AAEA,eAAe,QAAQ,MAAc,MAAwD;CAC3F,MAAM,QAAkB,CAAC;CACzB,WAAW,MAAM,SAAS,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC,GACtD,IAAI,KAAK,KAAK,GAAG,MAAM,KAAK,MAAM,IAAI;CAExC,OAAO;AACT;AAEA,SAAS,OAAO,MAA6B;CAC3C,OAAO,qBAAqB,IAAI;AAClC;AAEA,SAAS,OAAO,MAA6B;CAC3C,OAAO,qBAAqB,IAAI;AAClC;;AAGA,eAAe,MAAM,QAA6B;CAChD,MAAM,qBAAqBC,MAAI;CAC/B,MAAM,UAAU,MAAM,QAAQA,MAAI;CAClC,MAAM,QAAQ,IACZ,QAAQ,KAAK,UAAU,GAAGD,KAAS,KAAKC,QAAM,KAAK,GAAG;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,CAAC,CACzF;AACF;AAEA,SAAS,OAAO,MAAgC;CAC9C,OAAO,qBAAqB,IAAI;AAClC;AAEA,eAAe,KAAK,MAAc,IAAY,SAAsC;CAClF,KAAK,SAAS,gBAAgB,SAAS,cAAc,UAAW,MAAM,gBAAgB,EAAE,GACtF,MAAM,IAAI,MAAM,8BAA8B,GAAG,iBAAiB;CAKpE,MAAM,YAAsC;EAC1C,WAAW;EACX,OAAO,SAAS,cAAc;CAChC;CACA,IAAI,SAAS,iBAAiB,QAAW,UAAU,eAAe,QAAQ;CAC1E,IAAI,SAAS,gBAAgB,QAAW,UAAU,cAAc,QAAQ;CAExE,MAAM,GAAG,MAAM,IAAI,SAAS;AAC9B;;AAGA,eAAe,KAAK,MAAc,IAAY,SAAsC;CAClF,IAAI,SAAS,cAAc,SAAU,MAAM,gBAAgB,EAAE,GAC3D,MAAM,IAAI,MAAM,8BAA8B,GAAG,iBAAiB;CAGpE,IAAI,SAAS,cAAc,OACzB,MAAM,qBAAqBD,KAAS,QAAQ,EAAE,CAAC;CAGjD,IAAI;EACF,MAAM,OAAO,MAAM,EAAE;CACvB,SAAS,OAAO;EACd,IAAK,MAA4B,SAAS,SAAS;GACjD,MAAM,GAAG,MAAM,IAAI,EAAE,WAAW,KAAK,CAAC;GACtC,MAAM,qBAAqB,IAAI;GAC/B;EACF;EACA,MAAM;CACR;AACF;AAEA,eAAe,MAAM,MAAkC;CACrD,OAAO,eAAe,MAAM,MAAM,WAAW,IAAI,CAAC;AACpD;;AAGA,eAAe,KAAK,MAA+B;CACjD,IAAI,QAAQ;CACZ,WAAW,MAAM,SAAS,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC,GACtD,IAAI,MAAM,SAAS,QACjB,UAAU,MAAM,KAAK,MAAM,IAAI,EAAC,CAAE;CAGtC,OAAO;AACT;;AAGA,eAAe,MAAM,MAA+B;CAClD,QAAQ,MAAM,QAAQ,IAAI,EAAC,CAAE;AAC/B;AAEA,eAAe,QAAQ,MAAgC;CACrD,QAAQ,MAAM,QAAQ,IAAI,EAAC,CAAE,WAAW;AAC1C;AAEA,SAAS,KAAK,MAAc,SAA0C;CACpE,OAAO,SAAS,YAAY,QAAQ,YAAY,IAAI,IAAI,UAAU,IAAI;AACxE;AAEA,SAAS,UAAU,MAAc,SAA0C;CACzE,OAAO,SAAS,YACZ,QAAQ,OAAO,UAAU,MAAM,SAAS,MAAM,IAC9C,eAAe,IAAI;AACzB;AAEA,SAAS,SAAS,MAAc,SAA0C;CACxE,OAAO,SAAS,YACZ,QAAQ,OAAO,UAAU,MAAM,SAAS,WAAW,IACnD,qBAAqB,IAAI;AAC/B;;;;;;AAOA,eAAe,KAAK,QAAc,WAA4C;CAC5E,MAAM,aAAa,MAAM,QAAQC,SAAO,UAAU,MAAM,SAAS,MAAM,EAAC,CAAE,KAAK;CAC/E,MAAM,SAAS,WAAW,aAAa,QAAQ;CAE/C,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,WAAWD,KAAS,SAASC,QAAM,QAAQ,CAAC,CAAC,MAAMD,KAAS,GAAG,CAAC,CAAC,KAAK,GAAG;EAC/E,OAAO,OAAO,GAAG,SAAS,IAAI,MAAM,cAAc,UAAU,SAAS,EAAE,GAAG;CAC5E;CAEA,OAAO,OAAO,OAAO,KAAK;AAC5B;;;;;AAMA,MAAa,OAAO;CAClB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}