← Files VeraARCHIVED FILE

modules/browser-automation/scripts/download_directory.mjs

4.78 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

/** Local file identity and hashes are mechanically verifiable; no invoice interpretation. */
import { createHash } from "node:crypto";
import { constants } from "node:fs";
import { lstat, mkdir, open, readdir, realpath, rmdir } from "node:fs/promises";
import { homedir, tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";

export const DEFAULT_DOWNLOAD_DIRECTORY = join(homedir(), "Downloads");
const PARTIAL = /\.(crdownload|part|partial|tmp)$/i;
const identity = (s) => `${s.dev}:${s.ino}:${s.size}:${s.mtimeMs}:${s.ctimeMs}`;

export class DownloadDirectoryError extends Error {
  constructor(code) {
    super(code);
    this.evidenceCode = code;
  }
}

/** Snapshot names only; never read pre-existing documents. Lock cooperating runners. */
export async function observeDownloadDirectory(directory) {
  let root;
  try {
    root = await realpath(directory);
  } catch {
    throw new DownloadDirectoryError("download-directory-unavailable");
  }
  const key = createHash("sha256").update(root).digest("hex");
  const lock = join(tmpdir(), `vera-download-${key}.lock`);
  try {
    await mkdir(lock, { mode: 0o700 });
  } catch {
    throw new DownloadDirectoryError("download-directory-busy");
  }
  const close = () => rmdir(lock);
  try {
    const baseline = new Set(await readdir(root));
    const existingPartials = [...baseline].filter((name) => PARTIAL.test(name));
    const newArrivals = async () => {
      const names = new Set(await readdir(root));
      // An old partial may have completed under a new name. Names alone cannot
      // attribute that file to this run; unchanged old partials are irrelevant.
      if (existingPartials.some((name) => !names.has(name))) {
        throw new DownloadDirectoryError("download-directory-ambiguous");
      }
      return [...names].filter((name) => !baseline.has(name));
    };
    return {
      close,
      async wait({ timeoutMs = 10000, stableMs = 1000, pollMs = 100 } = {}) {
        const deadline = performance.now() + timeoutMs;
        let previous = null;
        let stableSince = 0;
        const observedFinalNames = new Set();
        while (performance.now() < deadline) {
          const arrivals = await newArrivals();
          const finished = arrivals.filter((name) => !PARTIAL.test(name));
          for (const name of finished) observedFinalNames.add(name);
          if (observedFinalNames.size > 1) {
            throw new DownloadDirectoryError("download-directory-ambiguous");
          }
          if (finished.length === 1 && !arrivals.some((name) => PARTIAL.test(name))) {
            const path = join(root, finished[0]);
            let before;
            try {
              before = await lstat(path);
            } catch (error) {
              if (error.code !== "ENOENT") throw error;
            }
            if (before) {
              if (!before.isFile() || before.isSymbolicLink()) {
                throw new DownloadDirectoryError("download-path-not-regular-file");
              }
              const signature = `${finished[0]}:${identity(before)}`;
              if (signature !== previous) {
                previous = signature;
                stableSince = performance.now();
              } else if (performance.now() - stableSince >= stableMs) {
                const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
                try {
                  if (identity(await handle.stat()) !== identity(before)) {
                    throw new DownloadDirectoryError("download-file-changed");
                  }
                  const hash = createHash("sha256");
                  let bytes = 0;
                  for await (const chunk of handle.createReadStream({ autoClose: false })) {
                    bytes += chunk.length;
                    hash.update(chunk);
                  }
                  if (bytes !== before.size || identity(await handle.stat()) !== identity(before)
                      || identity(await lstat(path)) !== identity(before)) {
                    throw new DownloadDirectoryError("download-file-changed");
                  }
                  const finalArrivals = await newArrivals();
                  if (finalArrivals.length !== 1 || finalArrivals[0] !== finished[0]) {
                    throw new DownloadDirectoryError("download-directory-ambiguous");
                  }
                  return { path, byte_length: bytes, sha256: hash.digest("hex") };
                } finally {
                  await handle.close();
                }
              }
            }
          } else {
            previous = null;
          }
          await delay(pollMs);
        }
        throw new DownloadDirectoryError("download-file-not-completed");
      },
    };
  } catch (error) {
    await close();
    throw error;
  }
}

SHA-256: e515c5c8985f861ed063c14de40aa8868cb0c1e6073338415ec16b305cd37f47