* Crash-durable atomic write: write to tmp → fsync file → rename → fsync parent dir. * On power loss, the target file either has the old content or the full new content — * never zero-byte or partially-written.
(filePath: string, content: string, mode: number)
| 32 | * never zero-byte or partially-written. |
| 33 | */ |
| 34 | async function writeFileDurable(filePath: string, content: string, mode: number): Promise<void> { |
| 35 | const tmp = filePath + '.tmp'; |
| 36 | const handle = await open(tmp, 'w', mode); |
| 37 | try { |
| 38 | await handle.writeFile(content, 'utf8'); |
| 39 | await handle.sync(); |
| 40 | } finally { |
| 41 | await handle.close(); |
| 42 | } |
| 43 | await rename(tmp, filePath); |
| 44 | // fsync the parent directory so the rename itself is durable. |
| 45 | const parent = path.dirname(filePath); |
| 46 | try { |
| 47 | const dirHandle = await open(parent, 'r'); |
| 48 | try { |
| 49 | await dirHandle.sync(); |
| 50 | } finally { |
| 51 | await dirHandle.close(); |
| 52 | } |
| 53 | } catch { |
| 54 | // Some platforms (Windows) can't open a dir for fsync. The file fsync |
| 55 | // above is still the critical durability guarantee. |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | export async function writeJson(filePath: string, value: unknown, options: WriteOptions = {}): Promise<void> { |
| 60 | await writeFileDurable(filePath, JSON.stringify(value, null, 2), options.mode ?? 0o600); |
no test coverage detected