'use strict'; const fs = require('fs').promises; const path = require('path'); const crypto = require('crypto'); async function writeJsonAtomic(file, value, io = fs) { const folder = path.dirname(file), name = path.basename(file); const temp = path.join(folder, `.${name}.${crypto.randomUUID()}.tmp`); await io.writeFile(temp, JSON.stringify(value, null, 2) + '\n'); try { try { await io.rename(temp, file); return; } catch (error) { if (!['EPERM', 'EACCES', 'EBUSY', 'EEXIST'].includes(error.code)) throw error; } // Windows may refuse rename-over-existing. Keep the old JSON recoverable // until the replacement is safely in place; never delete it first. const backup = path.join(folder, `.old-${name}-${crypto.randomUUID()}`); await io.rename(file, backup); try { await io.rename(temp, file); } catch (error) { try { await io.rename(backup, file); } catch (restoreError) { error.message += `; previous JSON remains at ${backup}: ${restoreError.message}`; } throw error; } await io.rm(backup, { force: true }); } finally { await io.rm(temp, { force: true }); } } module.exports = { writeJsonAtomic };