@hyperframes/core 0.8.98 → 0.8.99

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.
@@ -0,0 +1,10 @@
1
+ import * as fs from "node:fs";
2
+ type SiblingFileSystem = Pick<typeof fs, "writeFileSync" | "chmodSync" | "unlinkSync">;
3
+ /** Replace a file only after the complete sibling temp file is written. No mode: the default one. */
4
+ export declare function replaceFileAtomically(filePath: string, content: string | Uint8Array, mode?: number, operations?: SiblingFileSystem & Pick<typeof fs, "renameSync">): void;
5
+ /** Create a file that must not exist yet (EEXIST otherwise); readers never see it empty or partial. */
6
+ export declare function createFileAtomically(filePath: string, content: string | Uint8Array, operations?: SiblingFileSystem & Pick<typeof fs, "linkSync">): void;
7
+ /** The file a write to `filePath` lands on: folder links and file links followed as the system does. */
8
+ export declare function resolveWritePath(filePath: string): string;
9
+ export {};
10
+ //# sourceMappingURL=atomicFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomicFile.d.ts","sourceRoot":"","sources":["../src/atomicFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAK9B,KAAK,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,GAAG,WAAW,GAAG,YAAY,CAAC,CAAC;AAWvF,qGAAqG;AACrG,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,GAAE,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAM,GACjE,IAAI,CAKN;AAED,uGAAuG;AACvG,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,UAAU,GAAE,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAM,GAC/D,IAAI,CAeN;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBzD"}
@@ -0,0 +1,121 @@
1
+ import * as fs from "node:fs";
2
+ import { randomBytes } from "node:crypto";
3
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
4
+ import { realpath } from "./safePath.js";
5
+ // Codes a volume without hard links (FAT, exFAT, some network shares) answers link() with.
6
+ // EISDIR: libuv maps Windows ERROR_INVALID_FUNCTION (FAT/exFAT refusing a link) to it; nodejs/node#65817.
7
+ const NO_HARD_LINKS = new Set(["EPERM", "ENOTSUP", "EOPNOTSUPP", "EXDEV", "EISDIR"]);
8
+ // Windows refuses a rename while another process briefly holds the target open.
9
+ const BUSY_RENAME = new Set(["EPERM", "EBUSY", "EACCES"]);
10
+ const RENAME_RETRY_DELAYS_MS = [10, 20, 30, 40];
11
+ const MAX_LINK_HOPS = 40;
12
+ const TEMP_NAME_TRIES = 3;
13
+ /** Replace a file only after the complete sibling temp file is written. No mode: the default one. */
14
+ export function replaceFileAtomically(filePath, content, mode, operations = fs) {
15
+ // Node fs.rename uses libuv uv_fs_rename; win32 calls MoveFileExW with MOVEFILE_REPLACE_EXISTING.
16
+ publishSibling(filePath, content, mode, operations, (tempPath) => renameWithRetry(operations, tempPath, filePath));
17
+ }
18
+ /** Create a file that must not exist yet (EEXIST otherwise); readers never see it empty or partial. */
19
+ export function createFileAtomically(filePath, content, operations = fs) {
20
+ publishSibling(filePath, content, undefined, operations, (tempPath) => {
21
+ try {
22
+ operations.linkSync(tempPath, filePath);
23
+ }
24
+ catch (error) {
25
+ if (!NO_HARD_LINKS.has(errorCode(error)))
26
+ throw error;
27
+ // Without hard links, keep today's direct exclusive write.
28
+ operations.writeFileSync(filePath, content, { flag: "wx" });
29
+ }
30
+ try {
31
+ operations.unlinkSync(tempPath);
32
+ }
33
+ catch (error) {
34
+ console.warn(`[hyperframes] created ${filePath} but could not remove ${tempPath}: ${error}`);
35
+ }
36
+ });
37
+ }
38
+ /** The file a write to `filePath` lands on: folder links and file links followed as the system does. */
39
+ export function resolveWritePath(filePath) {
40
+ let path = resolve(filePath);
41
+ for (let hop = 0; hop < MAX_LINK_HOPS; hop++) {
42
+ try {
43
+ return realpath(path);
44
+ }
45
+ catch (error) {
46
+ if (errorCode(error) !== "ENOENT")
47
+ throw error;
48
+ }
49
+ // Missing: a new file, or a dangling link to the file a write through it creates.
50
+ const parent = realpath(dirname(path));
51
+ const leaf = join(parent, basename(path));
52
+ if (!fs.lstatSync(leaf, { throwIfNoEntry: false })?.isSymbolicLink())
53
+ return leaf;
54
+ // Not normalized: `x/..` must climb out of where link `x` points, as the system reads it.
55
+ const target = fs.readlinkSync(leaf);
56
+ path = isAbsolute(target) ? target : `${parent}${sep}${target}`;
57
+ }
58
+ throw Object.assign(new Error(`ELOOP: too many symbolic links, '${filePath}'`), {
59
+ code: "ELOOP",
60
+ });
61
+ }
62
+ function publishSibling(filePath, content, mode, operations, publish) {
63
+ const tempPath = writeTempSibling(filePath, content, mode, operations);
64
+ try {
65
+ if (mode !== undefined)
66
+ operations.chmodSync(tempPath, mode);
67
+ publish(tempPath);
68
+ }
69
+ catch (error) {
70
+ try {
71
+ operations.unlinkSync(tempPath);
72
+ }
73
+ catch {
74
+ // Preserve the write error; cleanup is best effort.
75
+ }
76
+ throw error;
77
+ }
78
+ }
79
+ /** A new temp file beside `filePath`; a name another writer already holds is never touched. */
80
+ function writeTempSibling(filePath, content, mode, operations) {
81
+ for (let attempt = 1;; attempt++) {
82
+ // Short, so a name near the filesystem's limit still fits.
83
+ const tempPath = `${filePath}.${randomBytes(4).toString("hex")}.tmp`;
84
+ try {
85
+ operations.writeFileSync(tempPath, content, { encoding: "utf-8", mode, flag: "wx" });
86
+ return tempPath;
87
+ }
88
+ catch (error) {
89
+ if (errorCode(error) === "EEXIST") {
90
+ if (attempt < TEMP_NAME_TRIES)
91
+ continue;
92
+ throw error;
93
+ }
94
+ try {
95
+ operations.unlinkSync(tempPath);
96
+ }
97
+ catch {
98
+ // Preserve the write error; cleanup is best effort.
99
+ }
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ function renameWithRetry(operations, from, to) {
105
+ for (let attempt = 0;; attempt++) {
106
+ try {
107
+ operations.renameSync(from, to);
108
+ return;
109
+ }
110
+ catch (error) {
111
+ const delay = RENAME_RETRY_DELAYS_MS[attempt];
112
+ if (delay === undefined || !BUSY_RENAME.has(errorCode(error)))
113
+ throw error;
114
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
115
+ }
116
+ }
117
+ }
118
+ function errorCode(error) {
119
+ return String(error?.code);
120
+ }
121
+ //# sourceMappingURL=atomicFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomicFile.js","sourceRoot":"","sources":["../src/atomicFile.ts"],"names":[],"mappings":"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"}