@stonyx/utils 0.2.3-alpha.27 → 0.2.3-alpha.29
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/README.md +1 -1
- package/dist/file.js +9 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ Updates a file atomically by writing to a sibling swap file first, then renaming
|
|
|
83
83
|
|
|
84
84
|
**File mode is preserved; other inode metadata is not.** The swap file is created with the target's permission bits and `chmod`ed to them before the rename, so a `0600` file stays `0600`. ACLs, extended attributes, hard links and ownership are *not* carried across — the target necessarily gets a new inode.
|
|
85
85
|
|
|
86
|
-
**Swap files after abnormal termination.** Cleanup runs on every failure path `updateFile` controls, but a process killed between the write and the rename leaves a `<path>.temp-<pid>-<token>` sibling behind, and no module ships a sweeper. This is a deliberate trade against the previous whole-second swap name, which
|
|
86
|
+
**Swap files after abnormal termination.** Cleanup runs on every failure path `updateFile` controls, but a process killed between the write and the rename leaves a `<path>.temp-<pid>-<token>` sibling behind, and no module ships a sweeper. This is a deliberate trade against the previous whole-second swap name, which was reused — and reuse is exactly the collision that caused #44, so uniqueness has to win. Be precise about what that costs: the old name only ever collapsed two orphans that were abandoned within the *same whole second*, so at any ordinary crash cadence it reclaimed nothing either (measured: five crashes 1200 ms apart leave five orphans under both names). The accumulation uniqueness genuinely adds is confined to a sub-second crash loop. Consumers that persist into a directory they scan, sync or commit should sweep or ignore `*.temp-*` siblings of their targets; an app that runs `git add` over its data directory will otherwise commit one.
|
|
87
87
|
|
|
88
88
|
#### `copyFile(sourcePath, targetPath, options={})`
|
|
89
89
|
|
package/dist/file.js
CHANGED
|
@@ -3,7 +3,7 @@ import { objToJson } from './object.js';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { promises as fsp } from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import {
|
|
6
|
+
import { randomBytes } from 'crypto';
|
|
7
7
|
function isNodeError(error) {
|
|
8
8
|
return error instanceof Error && 'code' in error;
|
|
9
9
|
}
|
|
@@ -60,11 +60,14 @@ export async function updateFile(filePath, data, options = {}) {
|
|
|
60
60
|
const targetMode = mode & 0o7777;
|
|
61
61
|
// pid + random token, never a timestamp: the swap name is a uniqueness
|
|
62
62
|
// token, and whole seconds cannot discriminate between concurrent callers.
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// ENAMETOOLONG on a 255-byte NAME_MAX.
|
|
66
|
-
//
|
|
67
|
-
|
|
63
|
+
// The token is deliberately short — a full 36-char UUID pushed the name
|
|
64
|
+
// overhead to 48 characters and made ~210-character basenames fail
|
|
65
|
+
// ENAMETOOLONG on a 255-byte NAME_MAX. 6 CSPRNG bytes as base64url is 8
|
|
66
|
+
// characters carrying 48 bits, where the first 8 hex characters of a UUID
|
|
67
|
+
// would be the same width but only 32 bits; at 32 bits a 1000-sample
|
|
68
|
+
// distinctness check collides about once in 8,600 runs. `wx` below makes
|
|
69
|
+
// any residual collision loud rather than corrupting.
|
|
70
|
+
const swapFile = `${filePath}.temp-${process.pid}-${randomBytes(6).toString('base64url')}`;
|
|
68
71
|
try {
|
|
69
72
|
// `wx` turns any residual name collision into a loud EEXIST rather than a
|
|
70
73
|
// silent overwrite of another caller's swap bytes. `mode` here is masked
|