@stonyx/utils 0.2.3-alpha.29 → 0.2.3-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/file.js +16 -4
- package/package.json +1 -1
package/dist/file.js
CHANGED
|
@@ -68,20 +68,32 @@ export async function updateFile(filePath, data, options = {}) {
|
|
|
68
68
|
// distinctness check collides about once in 8,600 runs. `wx` below makes
|
|
69
69
|
// any residual collision loud rather than corrupting.
|
|
70
70
|
const swapFile = `${filePath}.temp-${process.pid}-${randomBytes(6).toString('base64url')}`;
|
|
71
|
+
// Which stage failed, so the catch can tell "the open collided" from "the
|
|
72
|
+
// open succeeded and something later failed" — see the catch.
|
|
73
|
+
let wrote = false;
|
|
71
74
|
try {
|
|
72
75
|
// `wx` turns any residual name collision into a loud EEXIST rather than a
|
|
73
76
|
// silent overwrite of another caller's swap bytes. `mode` here is masked
|
|
74
77
|
// by the umask, so it only narrows — the chmod below sets the exact bits.
|
|
75
78
|
await fsp.writeFile(swapFile, options.json ? objToJson(data) : String(data), { encoding: 'utf8', flag: 'wx', mode: targetMode });
|
|
79
|
+
wrote = true;
|
|
76
80
|
await fsp.chmod(swapFile, targetMode);
|
|
77
81
|
await fsp.rename(swapFile, filePath);
|
|
78
82
|
}
|
|
79
83
|
catch (swapError) {
|
|
80
84
|
// Leave no orphan behind, and never let the cleanup mask the real error.
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// unlinking it would reintroduce
|
|
84
|
-
|
|
85
|
+
//
|
|
86
|
+
// Exactly one case is not ours to remove: the `wx` open lost a race, so
|
|
87
|
+
// the swap file is another writer's and unlinking it would reintroduce
|
|
88
|
+
// #44. That is EEXIST *from the write stage* — both halves matter.
|
|
89
|
+
// - Code alone is not enough: `rename` surfaces EEXIST on Windows and
|
|
90
|
+
// some network filesystems, and that file is one we created.
|
|
91
|
+
// - Stage alone is not enough: a write that fails after the open
|
|
92
|
+
// succeeded (ENOSPC, EDQUOT, EIO) also lands here with `wrote` false,
|
|
93
|
+
// and skipping it is the orphaned partial payload this PR exists to
|
|
94
|
+
// close.
|
|
95
|
+
const lostTheOpenRace = !wrote && isNodeError(swapError) && swapError.code === 'EEXIST';
|
|
96
|
+
if (!lostTheOpenRace) {
|
|
85
97
|
await fsp.unlink(swapFile).catch(() => { });
|
|
86
98
|
}
|
|
87
99
|
throw swapError;
|