mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-23 06:10:02 +08:00
* feat(fs): expose Stats disk metadata - Lower blocks, nlink, and atimeMs through the C and LLVM backends. - Populate path, promise, file-handle, and dynamic bridge stats across supported platforms. - Add differential, declaration, and npm bridge coverage. Co-authored-by: I Plan Websites <787729+iplanwebsites@users.noreply.github.com> * fix(fs): keep Windows stats snapshots coherent * fix(fs): preserve Windows lstat metadata * fix(fs): validate Windows mount point payloads --------- Co-authored-by: I Plan Websites <787729+iplanwebsites@users.noreply.github.com>
129 lines
5.6 KiB
JavaScript
129 lines
5.6 KiB
JavaScript
// The island's node:fs and node:fs/promises shims, differentially
|
|
// against Node on the real filesystem: both lanes run the same
|
|
// operations in a fresh mkdtemp sandbox (its random path normalized to
|
|
// <tmp> in the output), so every line must match byte-for-byte —
|
|
// read/write/append round trips in both encodings, mkdir -p, readdir
|
|
// plain and withFileTypes, Stats shapes, Node's exact error shapes with
|
|
// the errno-name code, access, copyFile/rename, realpath, the three
|
|
// call spellings (sync / callback / promises), and the memory-backed
|
|
// create*Stream pair.
|
|
import fs from "node:fs";
|
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
|
|
export async function report() {
|
|
const lines = [];
|
|
const dir = fs.mkdtempSync(os.tmpdir() + "/fszoo-");
|
|
const norm = (s) => String(s).split(dir).join("<tmp>");
|
|
const push = (label, v) => lines.push(label + " " + norm(v));
|
|
const errOf = (fn) => {
|
|
try {
|
|
fn();
|
|
return "no-throw";
|
|
} catch (e) {
|
|
return (e.code || "?") + ":" + norm(e.message);
|
|
}
|
|
};
|
|
|
|
try {
|
|
push("mkdtemp:", dir.includes("fszoo-") && fs.existsSync(dir));
|
|
|
|
// write/read round trips
|
|
fs.writeFileSync(dir + "/a.txt", "héllo ☃");
|
|
push("read-utf8:", fs.readFileSync(dir + "/a.txt", "utf8"));
|
|
const buf = fs.readFileSync(dir + "/a.txt");
|
|
push("read-buf:", Buffer.isBuffer(buf) + "|" + buf.toString("hex"));
|
|
fs.writeFileSync(dir + "/bin", Buffer.from([0, 255, 128]));
|
|
push("read-bin:", fs.readFileSync(dir + "/bin").toString("hex"));
|
|
fs.appendFileSync(dir + "/a.txt", "!");
|
|
push("append:", fs.readFileSync(dir + "/a.txt", "utf8"));
|
|
push("read-opts:", fs.readFileSync(dir + "/a.txt", { encoding: "utf8" }));
|
|
|
|
// errors: Node's shapes with codes
|
|
push("enoent:", errOf(() => fs.readFileSync(dir + "/missing.txt")));
|
|
push("stat-enoent:", errOf(() => fs.statSync(dir + "/missing.txt")));
|
|
push("stat-noentry:", String(fs.statSync(dir + "/missing.txt", { throwIfNoEntry: false })));
|
|
|
|
// mkdir / readdir / dirents
|
|
fs.mkdirSync(dir + "/sub/deep", { recursive: true });
|
|
fs.writeFileSync(dir + "/sub/f.txt", "x");
|
|
push("mkdir-exists:", fs.existsSync(dir + "/sub/deep"));
|
|
push("mkdir-eexist:", errOf(() => fs.mkdirSync(dir + "/sub")));
|
|
push("readdir:", fs.readdirSync(dir).sort().join(","));
|
|
const dirents = fs.readdirSync(dir + "/sub", { withFileTypes: true })
|
|
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
|
.map((d) => d.name + ":" + (d.isFile() ? "f" : d.isDirectory() ? "d" : "?"));
|
|
push("dirents:", dirents.join(","));
|
|
|
|
// stats
|
|
const st = fs.statSync(dir + "/a.txt");
|
|
push("stat:", [
|
|
st.isFile(), st.isDirectory(), st.size, st.mtimeMs > 0,
|
|
st.blocks >= 0, st.nlink >= 1, st.atimeMs > 0,
|
|
].join(","));
|
|
push("stat-dir:", fs.statSync(dir + "/sub").isDirectory());
|
|
const lst = fs.lstatSync(dir + "/sub");
|
|
push("lstat:", [lst.isDirectory(), lst.isSymbolicLink()].join(","));
|
|
|
|
// access / constants
|
|
push("access-ok:", String(fs.accessSync(dir + "/a.txt", fs.constants.R_OK)));
|
|
push("access-miss:", errOf(() => fs.accessSync(dir + "/missing", fs.constants.F_OK)));
|
|
push("consts:", [fs.constants.F_OK, fs.constants.R_OK, fs.constants.W_OK, fs.constants.X_OK].join(","));
|
|
|
|
// copy / rename / unlink / rm
|
|
fs.copyFileSync(dir + "/a.txt", dir + "/copy.txt");
|
|
push("copy:", fs.readFileSync(dir + "/copy.txt", "utf8"));
|
|
fs.renameSync(dir + "/copy.txt", dir + "/renamed.txt");
|
|
push("rename:", fs.existsSync(dir + "/copy.txt") + "|" + fs.readFileSync(dir + "/renamed.txt", "utf8"));
|
|
push("rename-miss:", errOf(() => fs.renameSync(dir + "/nope", dir + "/other")));
|
|
fs.unlinkSync(dir + "/renamed.txt");
|
|
push("unlink:", fs.existsSync(dir + "/renamed.txt"));
|
|
fs.rmSync(dir + "/sub", { recursive: true });
|
|
push("rm-recursive:", fs.existsSync(dir + "/sub"));
|
|
|
|
// realpath (the sandbox may itself live behind a symlink — normalize
|
|
// through Node-equivalent realpath on both lanes)
|
|
push("realpath-self:", fs.realpathSync(dir) === fs.realpathSync.native(dir));
|
|
|
|
// callback spelling
|
|
const cbRead = await new Promise((res) => {
|
|
fs.readFile(dir + "/a.txt", (err, data) => res(err + "|" + data.toString("utf8")));
|
|
});
|
|
push("cb-read:", cbRead);
|
|
const cbErr = await new Promise((res) => {
|
|
fs.readFile(dir + "/missing", (err) => res(err.code));
|
|
});
|
|
push("cb-err:", cbErr);
|
|
|
|
// promises spelling
|
|
await writeFile(dir + "/p.txt", "promised");
|
|
push("p-read:", await readFile(dir + "/p.txt", "utf8"));
|
|
await mkdir(dir + "/pdir");
|
|
push("p-readdir:", (await readdir(dir)).sort().join(","));
|
|
const pst = await stat(dir + "/p.txt");
|
|
push("p-stat:", [pst.size, pst.blocks >= 0, pst.nlink >= 1, pst.atimeMs > 0].join(","));
|
|
push("p-err:", await readFile(dir + "/missing").catch((e) => e.code + ":" + norm(e.message)));
|
|
await rm(dir + "/pdir", { recursive: true });
|
|
push("p-rm:", fs.existsSync(dir + "/pdir"));
|
|
|
|
// streams
|
|
fs.writeFileSync(dir + "/s.txt", "stream me");
|
|
const chunks = [];
|
|
const rs = fs.createReadStream(dir + "/s.txt", "utf8");
|
|
rs.on("data", (c) => chunks.push(c));
|
|
await new Promise((res) => rs.on("end", res));
|
|
push("readstream:", chunks.join(""));
|
|
const ws = fs.createWriteStream(dir + "/w.txt");
|
|
ws.write("part1 ");
|
|
ws.write(Buffer.from("part2"));
|
|
await new Promise((res) => {
|
|
ws.end(() => res());
|
|
});
|
|
push("writestream:", fs.readFileSync(dir + "/w.txt", "utf8"));
|
|
} finally {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
push("cleanup:", fs.existsSync(dir));
|
|
return lines.join("\n");
|
|
}
|