@volter/supercode-interchange 0.1.11 → 0.1.12
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/file-io.mjs +8 -2
- package/package.json +1 -1
package/file-io.mjs
CHANGED
|
@@ -3,6 +3,10 @@ import { parseNativeJson } from './native-json.mjs';
|
|
|
3
3
|
|
|
4
4
|
const stale = (path) => Object.assign(new Error(`${path} changed during its metadata read; previous index rows are retained`), { code: 'ESTALE' });
|
|
5
5
|
const same = (a, b) => a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.mtimeNs === b.mtimeNs;
|
|
6
|
+
// A file its writer appended to while it was read is the same file with more after the bytes read: the facts read are
|
|
7
|
+
// those of the open's size, and the next read takes the rest. A live transcript is written more often than a large
|
|
8
|
+
// read takes, so refusing an append would refuse every read of it.
|
|
9
|
+
const sameOrAppended = (before, now) => same(before, now) || before.dev === now.dev && before.ino === now.ino && now.size > before.size;
|
|
6
10
|
|
|
7
11
|
export function fileFingerprint(info) {
|
|
8
12
|
return { device: String(info.dev), inode: String(info.ino), size: String(info.size), mtime_ns: String(info.mtimeNs) };
|
|
@@ -19,7 +23,9 @@ export function appendableFingerprint(previous, current) {
|
|
|
19
23
|
|
|
20
24
|
/** Read one open identity; verify both the handle and its current path before
|
|
21
25
|
* returning facts for a transaction. A changed path never commits new offsets
|
|
22
|
-
* beside facts read from its predecessor
|
|
26
|
+
* beside facts read from its predecessor: a file replaced, truncated or rewritten
|
|
27
|
+
* in place is stale, while one only appended to answers for the size it was
|
|
28
|
+
* opened at. The caller owns failure retry. */
|
|
23
29
|
export async function withSessionFile(path, work, metrics = {}) {
|
|
24
30
|
const handle = await open(path, 'r');
|
|
25
31
|
metrics.files_opened = (metrics.files_opened ?? 0) + 1;
|
|
@@ -51,7 +57,7 @@ export async function withSessionFile(path, work, metrics = {}) {
|
|
|
51
57
|
const after = await handle.stat({ bigint: true });
|
|
52
58
|
let current;
|
|
53
59
|
try { current = await stat(path, { bigint: true }); } catch { throw stale(path); }
|
|
54
|
-
if (!
|
|
60
|
+
if (!sameOrAppended(before, after) || !sameOrAppended(before, current) || current.size < after.size) throw stale(path);
|
|
55
61
|
return { result, fingerprint: reader.fingerprint, size, modified_ms: reader.modified_ms };
|
|
56
62
|
} finally { await handle.close(); }
|
|
57
63
|
}
|
package/package.json
CHANGED