@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.
Files changed (2) hide show
  1. package/dist/file.js +16 -4
  2. 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
- // EEXIST is the one code that must NOT be cleaned up: it means this call
82
- // did not create the path, so the file belongs to another writer and
83
- // unlinking it would reintroduce #44.
84
- if (!(isNodeError(swapError) && swapError.code === 'EEXIST')) {
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;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "keywords": [
4
4
  "stonyx-module"
5
5
  ],
6
- "version": "0.2.3-alpha.29",
6
+ "version": "0.2.3-alpha.30",
7
7
  "description": "Utils module for Stonyx Framework",
8
8
  "repository": {
9
9
  "type": "git",