{"version":3,"file":"files.mjs","names":["path","nodePath"],"sources":["../../../../../../../@warlock.js/fs/src/facade/files.ts"],"sourcesContent":["import { appendFile, readFile, rename, utimes, writeFile } from \"node:fs/promises\";\nimport { createReadStream } from \"node:fs\";\nimport readline from \"node:readline\";\nimport nodePath from \"node:path\";\nimport { atomicWriteAsync } from \"../atomic\";\nimport { copyFileAsync } from \"../copy\";\nimport { ensureDirectoryAsync } from \"../dirs\";\nimport { fileExistsAsync, pathExistsAsync } from \"../exists\";\nimport { hashFileAsync, type HashAlgorithm } from \"../hash\";\nimport { getFileAsync } from \"../read\";\nimport { unlinkAsync } from \"../remove\";\nimport { lastModifiedAsync, statsAsync } from \"../stats\";\nimport { putFileAsync } from \"../write\";\nimport {\n  normalizeStats,\n  type CopyOptions,\n  type FileStats,\n  type MergeJsonOptions,\n  type MoveOptions,\n  type ReadJsonOptions,\n  type ReadOptions,\n  type WriteJsonOptions,\n  type WriteOptions,\n} from \"./options\";\nimport { validateAgainstSchema } from \"./standard-schema\";\n\nfunction isENOENT(error: unknown): boolean {\n  return (error as { code?: string } | null)?.code === \"ENOENT\";\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction deepMerge(target: unknown, source: unknown): unknown {\n  if (!isPlainObject(target) || !isPlainObject(source)) return source;\n\n  const output: Record<string, unknown> = { ...target };\n\n  for (const [key, value] of Object.entries(source)) {\n    output[key] = isPlainObject(value) && isPlainObject(output[key])\n      ? deepMerge(output[key], value)\n      : value;\n  }\n\n  return output;\n}\n\n/** Read a file as text (utf-8) or, when `encoding: null`, as a `Buffer`. */\nfunction get(path: string): Promise<string>;\nfunction get(path: string, options: ReadOptions & { encoding: null }): Promise<Buffer>;\nfunction get(path: string, options: ReadOptions): Promise<string | Buffer>;\nasync function get(path: string, options?: ReadOptions): Promise<string | Buffer> {\n  if (options && options.encoding === null) {\n    return readFile(path);\n  }\n\n  return readFile(path, options?.encoding ?? \"utf-8\");\n}\n\n/**\n * Read + parse JSON. `options.schema` validates the parsed value against any\n * Standard Schema; `options.default` is returned when the file is missing.\n */\nasync function getJson<T = unknown>(path: string, options?: ReadJsonOptions<T>): Promise<T> {\n  let raw: string;\n\n  try {\n    raw = await getFileAsync(path);\n  } catch (error) {\n    if (options && \"default\" in options && isENOENT(error)) {\n      return options.default as T;\n    }\n    throw error;\n  }\n\n  const value = JSON.parse(raw) as T;\n\n  if (options?.schema) {\n    return validateAgainstSchema(path, options.schema, value);\n  }\n\n  return value;\n}\n\n/** Write a file. Routes Buffers / `{ atomic }` through the atomic writer. */\nasync function put(\n  path: string,\n  content: string | Buffer,\n  options?: WriteOptions,\n): Promise<void> {\n  if (options?.overwrite === false && (await pathExistsAsync(path))) {\n    throw new Error(`fs.files: refusing to overwrite existing path \"${path}\" (overwrite:false)`);\n  }\n\n  if (typeof content !== \"string\" || options?.atomic) {\n    await atomicWriteAsync(path, content);\n    return;\n  }\n\n  const encoding = options?.encoding ?? \"utf-8\";\n\n  if (options?.ensureDir === false) {\n    await writeFile(path, content, encoding);\n    return;\n  }\n\n  if (encoding === \"utf-8\") {\n    await putFileAsync(path, content);\n    return;\n  }\n\n  await ensureDirectoryAsync(nodePath.dirname(path));\n  await writeFile(path, content, encoding);\n}\n\nasync function putJson(path: string, value: unknown, options?: WriteJsonOptions): Promise<void> {\n  await put(path, JSON.stringify(value, null, options?.indent ?? 2), options);\n}\n\n/** Create a file, refusing to overwrite an existing one (`put` with `overwrite:false`). */\nasync function create(path: string, content: string | Buffer, options?: WriteOptions): Promise<void> {\n  await put(path, content, { ...options, overwrite: false });\n}\n\nasync function createJson(path: string, value: unknown, options?: WriteJsonOptions): Promise<void> {\n  await putJson(path, value, { ...options, overwrite: false });\n}\n\nfunction exists(path: string): Promise<boolean> {\n  return fileExistsAsync(path);\n}\n\nfunction remove(path: string): Promise<void> {\n  return unlinkAsync(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.files.copy: destination \"${to}\" already exists`);\n  }\n\n  await copyFileAsync(from, to);\n}\n\n/** Move a file, EXDEV-safe: rename, falling back to copy+unlink 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.files.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 copyFileAsync(from, to);\n      await unlinkAsync(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\nfunction lastModified(path: string): Promise<Date> {\n  return lastModifiedAsync(path);\n}\n\nfunction hash(path: string, algorithm?: HashAlgorithm): Promise<string> {\n  return hashFileAsync(path, algorithm);\n}\n\nasync function append(path: string, content: string | Buffer, options?: WriteOptions): Promise<void> {\n  if (options?.ensureDir !== false) {\n    await ensureDirectoryAsync(nodePath.dirname(path));\n  }\n  await appendFile(path, content, typeof content === \"string\" ? (options?.encoding ?? \"utf-8\") : undefined);\n}\n\nasync function prepend(path: string, content: string | Buffer, options?: WriteOptions): Promise<void> {\n  let existing = \"\";\n  try {\n    existing = await getFileAsync(path);\n  } catch (error) {\n    if (!isENOENT(error)) throw error;\n  }\n  await put(path, `${content.toString()}${existing}`, options);\n}\n\nfunction appendLine(path: string, line: string): Promise<void> {\n  return append(path, `${line}\\n`);\n}\n\nfunction appendJsonLine(path: string, value: unknown): Promise<void> {\n  return append(path, `${JSON.stringify(value)}\\n`);\n}\n\nasync function size(path: string): Promise<number> {\n  return (await statsAsync(path)).size;\n}\n\nasync function isEmpty(path: string): Promise<boolean> {\n  return (await statsAsync(path)).size === 0;\n}\n\n/** Create an empty file if missing (and its parents). Never truncates an existing file. */\nasync function ensure(path: string): Promise<void> {\n  if (!(await pathExistsAsync(path))) {\n    await putFileAsync(path, \"\");\n  }\n}\n\n/** Create the file if missing, otherwise bump its modified time. */\nasync function touch(path: string): Promise<void> {\n  if (await pathExistsAsync(path)) {\n    const now = new Date();\n    await utimes(path, now, now);\n    return;\n  }\n  await putFileAsync(path, \"\");\n}\n\n/** Read → transform → write (text). */\nasync function edit(\n  path: string,\n  editor: (content: string) => string | Promise<string>,\n): Promise<void> {\n  const next = await editor(await getFileAsync(path));\n  await put(path, next);\n}\n\n/** Read → transform → write (JSON value). */\nasync function editJson<T = unknown>(\n  path: string,\n  editor: (value: T) => T | Promise<T>,\n  options?: WriteJsonOptions,\n): Promise<void> {\n  const next = await editor(await getJson<T>(path));\n  await putJson(path, next, options);\n}\n\n/** Read → merge a partial into the JSON object → write. Shallow unless `{ deep:true }`. */\nasync function mergeJson<T = Record<string, unknown>>(\n  path: string,\n  partial: Partial<T>,\n  options?: MergeJsonOptions,\n): Promise<void> {\n  const current = await getJson<T>(path);\n  const merged = options?.deep\n    ? (deepMerge(current, partial) as T)\n    : ({ ...current, ...partial } as T);\n  await putJson(path, merged, options);\n}\n\n/** Return the parsed JSON, or create the file with `fallback` and return it. */\nasync function ensureJson<T = unknown>(\n  path: string,\n  fallback: T,\n  options?: WriteJsonOptions,\n): Promise<T> {\n  if (await pathExistsAsync(path)) {\n    return getJson<T>(path);\n  }\n  await putJson(path, fallback, options);\n  return fallback;\n}\n\n/** Compare a file's digest against an expected hash (case-insensitive). */\nasync function checksumMatches(\n  path: string,\n  expected: string,\n  algorithm?: HashAlgorithm,\n): Promise<boolean> {\n  const actual = await hashFileAsync(path, algorithm);\n  return actual.toLowerCase() === expected.toLowerCase();\n}\n\n/** Stream a text file line by line (constant memory). */\nfunction readLines(path: string): AsyncIterable<string> {\n  return {\n    async *[Symbol.asyncIterator]() {\n      const rl = readline.createInterface({\n        input: createReadStream(path, { encoding: \"utf-8\" }),\n        crlfDelay: Infinity,\n      });\n      try {\n        for await (const line of rl) {\n          yield line;\n        }\n      } finally {\n        rl.close();\n      }\n    },\n  };\n}\n\n/**\n * The `fs.files.*` facade — an async, ergonomic surface over the file\n * primitives. Sync callers stay on the bare primitives (`getFile`/`putFile`/…).\n */\nexport const files = {\n  get,\n  getJson,\n  put,\n  putJson,\n  create,\n  createJson,\n  exists,\n  remove,\n  copy,\n  move,\n  stats,\n  lastModified,\n  hash,\n  append,\n  prepend,\n  appendLine,\n  appendJsonLine,\n  size,\n  isEmpty,\n  ensure,\n  touch,\n  edit,\n  editJson,\n  mergeJson,\n  ensureJson,\n  checksumMatches,\n  readLines,\n};\n"],"mappings":"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"}