// JS-target atomic-write primitive. Opens a .mem_*.tmp next to the
// target, writes, fsyncs, closes, and renames onto the target. On any
// failure the tmp is unlinked before rethrowing so memories/ doesn't
// accumulate `.mem_*.tmp` droppings.
///|
extern "js" fn ffi_atomic_write_impl(path : String, content : String) -> Unit =
#| (path, content) => {
#| const fs = require("node:fs");
#| const crypto = require("node:crypto");
#| const nodePath = require("node:path");
#| const dir = nodePath.dirname(path);
#| const tmp = nodePath.join(dir, ".mem_" + crypto.randomBytes(8).toString("hex") + ".tmp");
#| const cleanup = () => { try { fs.unlinkSync(tmp); } catch {} };
#| const fd = fs.openSync(tmp, "w");
#| try {
#| try {
#| fs.writeSync(fd, content);
#| fs.fsyncSync(fd);
#| } finally { fs.closeSync(fd); }
#| } catch (e) { cleanup(); throw e; }
#| try { fs.renameSync(tmp, path); }
#| catch (e) { cleanup(); throw e; }
#| }
///| Test-only helper: mirror of ffi_atomic_write_impl that returns Bool
///| instead of throwing, so the inline failure-path test can assert
///| without propagating a panic through the async test driver.
extern "js" fn ffi_ms_try_write(path : String, content : String) -> Bool =
#| (path, content) => {
#| try {
#| const fs = require("node:fs");
#| const crypto = require("node:crypto");
#| const nodePath = require("node:path");
#| const dir = nodePath.dirname(path);
#| const tmp = nodePath.join(dir, ".mem_" + crypto.randomBytes(8).toString("hex") + ".tmp");
#| const cleanup = () => { try { fs.unlinkSync(tmp); } catch {} };
#| const fd = fs.openSync(tmp, "w");
#| try {
#| try { fs.writeSync(fd, content); fs.fsyncSync(fd); }
#| finally { fs.closeSync(fd); }
#| } catch (e) { cleanup(); return false; }
#| try { fs.renameSync(tmp, path); return true; }
#| catch (e) { cleanup(); return false; }
#| } catch (e) { return false; }
#| }