← Files CARBON LiteARCHIVED FILE
src/report.mjs
1.58 KB · Nov 3, 2026 · 19:00 UTC
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
export function exportWorkspace(store, plugin) {
const snapshot = store.snapshot();
const template = fs.readFileSync(
path.join(plugin, "dist/workspace-template.html"),
"utf8",
);
const data = JSON.stringify(snapshot)
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e")
.replace(/&/g, "\\u0026");
const html = template.replace("/*CARBON_SNAPSHOT*/null", () => data);
for (const [name, contents] of [
["workspace.json", JSON.stringify(snapshot, null, 2)],
["index.html", html],
]) {
// Outputs are separate from the authoritative store, so a portable export never includes roots.
const dest = path.join(store.dir, "reports");
if (fs.existsSync(dest) && fs.lstatSync(dest).isSymbolicLink())
throw Error("Report directory cannot be a symlink.");
fs.mkdirSync(dest, { recursive: true, mode: 0o700 });
const target = path.join(dest, name),
temp = path.join(dest, crypto.randomUUID() + ".tmp");
if (fs.existsSync(target) && fs.lstatSync(target).isSymbolicLink())
throw Error("Report files cannot be symlinks.");
fs.writeFileSync(temp, contents, { flag: "wx", mode: 0o600 });
fs.renameSync(temp, target);
}
return {
html: path.join(store.dir, "reports/index.html"),
snapshot: path.join(store.dir, "reports/workspace.json"),
revision: snapshot.revision,
note: "Local portable snapshot. Reload after updates, or select workspace.json in the report to watch it. Feedback must be exported and explicitly imported.",
};
}
SHA-256: 6c1e0442a1ecc0d41c87e303eee950f3791acd5c7c6558497220fbb8c1044016