@openclaw/fs-safe 0.7.1 → 0.8.0
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/CHANGELOG.md +71 -0
- package/README.md +5 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +9 -10
- package/dist/archive-merge.d.ts +18 -0
- package/dist/archive-merge.d.ts.map +1 -0
- package/dist/archive-merge.js +157 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +25 -5
- package/dist/archive-policy.d.ts +1 -1
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +3 -2
- package/dist/archive-staging.d.ts +13 -8
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +44 -192
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +9 -5
- package/dist/archive-tar-runtime.d.ts +1 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-zip-admission.d.ts +2 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +2 -2
- package/dist/archive-zip-directory.d.ts +8 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +6 -2
- package/dist/archive-zip-entry.d.ts +1 -1
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-names.d.ts +1 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +1 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +17 -25
- package/dist/directory-guard.d.ts +17 -5
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +8 -6
- package/dist/directory-mode-node.d.ts +8 -0
- package/dist/directory-mode-node.d.ts.map +1 -0
- package/dist/directory-mode-node.js +93 -0
- package/dist/directory-mode-owner.d.ts +21 -0
- package/dist/directory-mode-owner.d.ts.map +1 -0
- package/dist/directory-mode-owner.js +73 -0
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -0
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +13 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +8 -11
- package/dist/filename.d.ts +6 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +36 -0
- package/dist/guarded-mkdir.d.ts +2 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +2 -2
- package/dist/guarded-mutation.d.ts +3 -2
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +2 -0
- package/dist/json-document-store.d.ts +3 -0
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +2 -0
- package/dist/json-durable-queue-ownership.d.ts +3 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +7 -4
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +25 -28
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +18 -56
- package/dist/move-path-cleanup.d.ts +43 -0
- package/dist/move-path-cleanup.d.ts.map +1 -0
- package/dist/move-path-cleanup.js +159 -0
- package/dist/move-path.d.ts +2 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +47 -83
- package/dist/native-operations.d.ts +2 -4
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +7 -9
- package/dist/native-pinned-write-windows.d.ts +2 -2
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +5 -5
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +14 -4
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/opened-file-failure.d.ts +1 -0
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +4 -0
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +9 -3
- package/dist/pinned-write.d.ts +2 -2
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +32 -13
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +7 -3
- package/dist/regular-file.d.ts +1 -1
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -6
- package/dist/replace-file-descriptor.d.ts +9 -1
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +58 -10
- package/dist/replace-file-temp-owner.d.ts +7 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +29 -0
- package/dist/replace-file.d.ts +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +12 -38
- package/dist/root-context.d.ts +4 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +19 -0
- package/dist/root-impl.d.ts +43 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +13 -11
- package/dist/root-path-symlink.d.ts +6 -2
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +7 -6
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +6 -2
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +4 -0
- package/dist/root-write-verification.d.ts +3 -2
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +6 -0
- package/dist/secret-file.d.ts +9 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +136 -74
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +9 -3
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +18 -5
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +6 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +26 -0
- package/dist/write-open-flags.d.ts +5 -0
- package/dist/write-open-flags.d.ts.map +1 -0
- package/dist/write-open-flags.js +35 -0
- package/docs/advanced.md +1 -1
- package/docs/archive.md +56 -7
- package/docs/atomic.md +42 -2
- package/docs/contributing.md +13 -0
- package/docs/durability.md +3 -2
- package/docs/errors.md +3 -1
- package/docs/file-store.md +38 -8
- package/docs/local-roots.md +4 -3
- package/docs/output.md +4 -1
- package/docs/private-file-store.md +11 -2
- package/docs/reading.md +3 -1
- package/docs/regular-file.md +16 -2
- package/docs/root.md +4 -2
- package/docs/secret-file.md +37 -2
- package/docs/sidecar-lock.md +32 -5
- package/docs/store.md +5 -1
- package/docs/temp.md +19 -1
- package/docs/writing.md +24 -3
- package/package.json +14 -10
|
@@ -3,6 +3,40 @@ import fs, {} from "node:fs/promises";
|
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { sameFileIdentity } from "./file-identity.js";
|
|
5
5
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
|
+
import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
|
|
7
|
+
export async function syncDirectoryBestEffort(fsModule, dirPath) {
|
|
8
|
+
let handle;
|
|
9
|
+
try {
|
|
10
|
+
handle = await fsModule.open(dirPath, "r");
|
|
11
|
+
await handle.sync();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Best-effort on platforms/filesystems that do not support directory fsync.
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await handle?.close().catch(() => undefined);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function syncDirectoryBestEffortSync(fsModule, dirPath) {
|
|
21
|
+
let fd;
|
|
22
|
+
try {
|
|
23
|
+
fd = fsModule.openSync(dirPath, "r");
|
|
24
|
+
fsModule.fsyncSync(fd);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Best-effort on platforms/filesystems that do not support directory fsync.
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
if (fd !== undefined) {
|
|
31
|
+
try {
|
|
32
|
+
fsModule.closeSync(fd);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Best-effort close after directory fsync.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
6
40
|
function directoryOpenFlags() {
|
|
7
41
|
return (syncFs.constants.O_RDONLY |
|
|
8
42
|
syncFs.constants.O_DIRECTORY |
|
|
@@ -20,7 +54,7 @@ function assertSameDirectory(expected, opened, dirPath) {
|
|
|
20
54
|
throw new FsSafeError("path-mismatch", `Atomic replace parent changed before its mode could be applied: ${dirPath}`);
|
|
21
55
|
}
|
|
22
56
|
}
|
|
23
|
-
export async function
|
|
57
|
+
export async function pinDirectoryForMode(params) {
|
|
24
58
|
// Node does not enforce POSIX directory modes on Windows, and its directory
|
|
25
59
|
// descriptors are not consistently openable. mkdir(mode) remains the only
|
|
26
60
|
// bounded behavior there; never fall back to a pathname chmod.
|
|
@@ -31,17 +65,31 @@ export async function applyDirectoryMode(params) {
|
|
|
31
65
|
assertDirectory(expected, params.dirPath);
|
|
32
66
|
const handle = await params.fsModule.open(params.dirPath, directoryOpenFlags());
|
|
33
67
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
68
|
+
const owner = ownDirectoryMode({
|
|
69
|
+
async inspect() {
|
|
70
|
+
const opened = await handle.stat();
|
|
71
|
+
assertOwnedDirectory(expected, opened);
|
|
72
|
+
return opened.mode & 0o7777;
|
|
73
|
+
},
|
|
74
|
+
chmod: (mode) => handle.chmod(mode),
|
|
75
|
+
close: () => handle.close(),
|
|
76
|
+
ignoreChmodError: params.ignoreChmodError,
|
|
77
|
+
});
|
|
78
|
+
await owner.verify();
|
|
79
|
+
return owner;
|
|
42
80
|
}
|
|
43
|
-
|
|
81
|
+
catch (error) {
|
|
44
82
|
await handle.close();
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function applyDirectoryMode(params) {
|
|
87
|
+
const owner = await pinDirectoryForMode(params);
|
|
88
|
+
try {
|
|
89
|
+
await owner?.apply(params.mode);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await owner?.close();
|
|
45
93
|
}
|
|
46
94
|
}
|
|
47
95
|
export function applyDirectoryModeSync(params) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import syncFs, { type BigIntStats } from "node:fs";
|
|
2
2
|
import fs, { type FileHandle } from "node:fs/promises";
|
|
3
|
+
import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
|
|
3
4
|
type AsyncOwnerFileSystem = Pick<typeof fs, "lstat" | "open" | "unlink">;
|
|
4
5
|
type SyncOwnerFileSystem = Pick<typeof syncFs, "closeSync" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "unlinkSync">;
|
|
6
|
+
export declare function cleanupPinnedFilePath(params: {
|
|
7
|
+
pathname: string;
|
|
8
|
+
handle: FileHandle;
|
|
9
|
+
identity?: BigIntStats;
|
|
10
|
+
parentGuard: AnyAsyncDirectoryGuard;
|
|
11
|
+
}): Promise<void>;
|
|
5
12
|
export declare class AsyncAtomicTempOwner {
|
|
6
13
|
#private;
|
|
7
14
|
readonly pathname: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAM9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAqEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA0Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAgDN;IAED,WAAW,IAAI,IAAI,CAGlB;IAED,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,IAAI,CAgCP;CACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
2
|
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { assertAsyncDirectoryGuard } from "./directory-guard.js";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
5
|
import { sameFileIdentityForCleanup, sha256Hex } from "./file-identity.js";
|
|
5
6
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
@@ -56,6 +57,34 @@ async function cleanupOwnedPath(params) {
|
|
|
56
57
|
return false;
|
|
57
58
|
}
|
|
58
59
|
}
|
|
60
|
+
// Borrowed handle: the caller retains it until this best-effort cleanup finishes.
|
|
61
|
+
export async function cleanupPinnedFilePath(params) {
|
|
62
|
+
if (!params.identity)
|
|
63
|
+
return;
|
|
64
|
+
try {
|
|
65
|
+
const guard = params.parentGuard;
|
|
66
|
+
if ([guard.stat.dev, guard.stat.ino].some((value) => typeof value === "number" && !Number.isSafeInteger(value)))
|
|
67
|
+
return;
|
|
68
|
+
await assertAsyncDirectoryGuard(guard);
|
|
69
|
+
const parent = await fs.lstat(guard.dir, { bigint: true });
|
|
70
|
+
if (parent.isSymbolicLink() || !parent.isDirectory() ||
|
|
71
|
+
!sameFileIdentityForCleanup(parent, guard.stat))
|
|
72
|
+
return;
|
|
73
|
+
const opened = await params.handle.stat({ bigint: true });
|
|
74
|
+
if (!opened.isFile() || opened.nlink !== 1n ||
|
|
75
|
+
!sameFileIdentityForCleanup(opened, params.identity))
|
|
76
|
+
return;
|
|
77
|
+
await cleanupOwnedPath({
|
|
78
|
+
fsModule: fs,
|
|
79
|
+
pathname: params.pathname,
|
|
80
|
+
identity: params.identity,
|
|
81
|
+
throwOnCleanupError: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Unverifiable authority must preserve the path and the original write failure.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
59
88
|
function cleanupOwnedPathSync(params) {
|
|
60
89
|
if (!params.identity)
|
|
61
90
|
return true;
|
package/dist/replace-file.d.ts
CHANGED
|
@@ -53,5 +53,6 @@ export type ReplaceFileAtomicResult = {
|
|
|
53
53
|
method: "rename" | "copy-fallback";
|
|
54
54
|
};
|
|
55
55
|
export declare function replaceFileAtomic(options: ReplaceFileAtomicOptions): Promise<ReplaceFileAtomicResult>;
|
|
56
|
+
export declare function replaceFileAtomicWithDirectorySync(options: ReplaceFileAtomicOptions, syncParent?: (directoryPath: string) => Promise<unknown>): Promise<ReplaceFileAtomicResult>;
|
|
56
57
|
export declare function replaceFileAtomicSync(options: ReplaceFileAtomicSyncOptions): ReplaceFileAtomicResult;
|
|
57
58
|
//# sourceMappingURL=replace-file.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file.d.ts","sourceRoot":"","sources":["../src/replace-file.ts"],"names":[],"mappings":"AACA,OAAO,MAAwC,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,OAAO,EAKL,KAAK,+BAA+B,EACpC,KAAK,sCAAsC,EAC3C,KAAK,oCAAoC,EACzC,KAAK,oCAAoC,EAC1C,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file.d.ts","sourceRoot":"","sources":["../src/replace-file.ts"],"names":[],"mappings":"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"}
|
package/dist/replace-file.js
CHANGED
|
@@ -3,7 +3,7 @@ import syncFs, {} from "node:fs";
|
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { assertDestinationHardlinkPolicy, assertDestinationHardlinkPolicySync, copyFallbackReplace, copyFallbackReplaceSync, } from "./replace-file-copy-fallback.js";
|
|
6
|
-
import { applyDirectoryMode, applyDirectoryModeSync, writeTempFile, writeTempFileSync, } from "./replace-file-descriptor.js";
|
|
6
|
+
import { applyDirectoryMode, applyDirectoryModeSync, syncDirectoryBestEffort, syncDirectoryBestEffortSync, writeTempFile, writeTempFileSync, } from "./replace-file-descriptor.js";
|
|
7
7
|
import { atomicExpectedContentHash, validateRenameIdentity, withAtomicRenameIdentityLock, withAtomicRenameIdentityLockSync, } from "./replace-file-rename-policy.js";
|
|
8
8
|
import { AsyncAtomicTempOwner, SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
|
|
9
9
|
import { assertSafePathPrefix } from "./safe-path-segment.js";
|
|
@@ -130,56 +130,27 @@ function resolveModeSync(options) {
|
|
|
130
130
|
function missingFchmodSyncError() {
|
|
131
131
|
return new TypeError("fileSystem.fchmodSync is required when mode, dirMode, or preserveExistingMode is specified");
|
|
132
132
|
}
|
|
133
|
-
async function syncDirectoryBestEffort(fsModule, dirPath) {
|
|
134
|
-
let handle;
|
|
135
|
-
try {
|
|
136
|
-
handle = await fsModule.open(dirPath, "r");
|
|
137
|
-
await handle.sync();
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
// Best-effort on platforms/filesystems that do not support directory fsync.
|
|
141
|
-
}
|
|
142
|
-
finally {
|
|
143
|
-
await handle?.close().catch(() => undefined);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
function syncDirectoryBestEffortSync(fsModule, dirPath) {
|
|
147
|
-
let fd;
|
|
148
|
-
try {
|
|
149
|
-
fd = fsModule.openSync(dirPath, "r");
|
|
150
|
-
fsModule.fsyncSync(fd);
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Best-effort on platforms/filesystems that do not support directory fsync.
|
|
154
|
-
}
|
|
155
|
-
finally {
|
|
156
|
-
if (fd !== undefined) {
|
|
157
|
-
try {
|
|
158
|
-
fsModule.closeSync(fd);
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
// Best-effort close after directory fsync.
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
133
|
export async function replaceFileAtomic(options) {
|
|
134
|
+
return await replaceFileAtomicWithDirectorySync(options);
|
|
135
|
+
}
|
|
136
|
+
// Internal owner hook: keep directory durability inside publication verification and serialization.
|
|
137
|
+
export async function replaceFileAtomicWithDirectorySync(options, syncParent) {
|
|
167
138
|
const filePath = options.filePath;
|
|
168
139
|
validateReplaceFilePath(filePath);
|
|
169
140
|
validateRestoreOptions(options);
|
|
170
141
|
validateRenameIdentity(options.renameIdentity);
|
|
171
142
|
return await serializePathWrite(path.resolve(filePath), async () => {
|
|
172
143
|
if (options.renameIdentity !== "verify-content-with-lock") {
|
|
173
|
-
return await replaceFileAtomicUnserialized(options);
|
|
144
|
+
return await replaceFileAtomicUnserialized(options, syncParent);
|
|
174
145
|
}
|
|
175
146
|
await (options.fileSystem?.promises ?? fs).mkdir(path.dirname(filePath), {
|
|
176
147
|
recursive: true,
|
|
177
148
|
mode: options.dirMode ?? 0o700,
|
|
178
149
|
});
|
|
179
|
-
return await withAtomicRenameIdentityLock(filePath, async () => await replaceFileAtomicUnserialized(options));
|
|
150
|
+
return await withAtomicRenameIdentityLock(filePath, async () => await replaceFileAtomicUnserialized(options, syncParent));
|
|
180
151
|
});
|
|
181
152
|
}
|
|
182
|
-
async function replaceFileAtomicUnserialized(options) {
|
|
153
|
+
async function replaceFileAtomicUnserialized(options, syncParent) {
|
|
183
154
|
const filePath = options.filePath;
|
|
184
155
|
const fsModule = options.fileSystem?.promises ?? fs;
|
|
185
156
|
const dir = path.dirname(filePath);
|
|
@@ -228,7 +199,10 @@ async function replaceFileAtomicUnserialized(options) {
|
|
|
228
199
|
else {
|
|
229
200
|
await tempOwner.assertCurrent(fsModule);
|
|
230
201
|
}
|
|
231
|
-
if (
|
|
202
|
+
if (syncParent) {
|
|
203
|
+
await syncParent(dir);
|
|
204
|
+
}
|
|
205
|
+
else if (options.syncParentDir) {
|
|
232
206
|
await syncDirectoryBestEffort(fsModule, dir);
|
|
233
207
|
}
|
|
234
208
|
if (result.method === "rename") {
|
package/dist/root-context.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export type RootContext = {
|
|
|
3
3
|
rootIdentity: {
|
|
4
4
|
dev: number;
|
|
5
5
|
ino: number;
|
|
6
|
+
} | {
|
|
7
|
+
dev: bigint;
|
|
8
|
+
ino: bigint;
|
|
6
9
|
};
|
|
7
10
|
rootReal: string;
|
|
8
11
|
rootWithSep: string;
|
|
@@ -12,6 +15,7 @@ export declare function assertValidRootRelativePath(relativePath: string): void;
|
|
|
12
15
|
export declare function assertValidRootDestinationPath(relativePath: string): void;
|
|
13
16
|
export declare function expandRelativePathWithHome(relativePath: string): Promise<string>;
|
|
14
17
|
export declare function resolveRootContext(rootDir: string): Promise<RootContext>;
|
|
18
|
+
export declare function rootRelativeReadPath(root: RootContext, filePath: string): string;
|
|
15
19
|
export declare function assertRootIdentityCurrent(root: RootContext): Promise<void>;
|
|
16
20
|
export declare function resolvePathInRoot(root: RootContext, relativePath: string, options?: {
|
|
17
21
|
aliasErrorCode?: "outside-workspace" | "path-alias";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-context.d.ts","sourceRoot":"","sources":["../src/root-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-context.d.ts","sourceRoot":"","sources":["../src/root-context.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAAW,MAAM,WACM,CAAC;AAEtD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEtE;AAED,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAKzE;AAID,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYtF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA0B9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAehF;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBhF;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE;IACR,cAAc,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAAC;IACpD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0CtE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEvE"}
|
package/dist/root-context.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
4
5
|
import { FsSafeError } from "./errors.js";
|
|
5
6
|
import { sameFileIdentity } from "./file-identity.js";
|
|
6
7
|
import { expandHomePrefix } from "./home-dir.js";
|
|
@@ -61,9 +62,27 @@ export async function resolveRootContext(rootDir) {
|
|
|
61
62
|
rootWithSep: ensureTrailingSep(rootReal),
|
|
62
63
|
};
|
|
63
64
|
}
|
|
65
|
+
export function rootRelativeReadPath(root, filePath) {
|
|
66
|
+
const absoluteInput = path.isAbsolute(filePath);
|
|
67
|
+
const candidatePath = absoluteInput
|
|
68
|
+
? path.resolve(filePath)
|
|
69
|
+
: path.resolve(root.rootDir, filePath);
|
|
70
|
+
let relativeBase = root.rootDir;
|
|
71
|
+
if (absoluteInput &&
|
|
72
|
+
!isPathInside(root.rootDir, candidatePath) &&
|
|
73
|
+
isPathInside(root.rootReal, candidatePath)) {
|
|
74
|
+
// A Root created through an alias has two valid in-root absolute spellings.
|
|
75
|
+
relativeBase = root.rootReal;
|
|
76
|
+
}
|
|
77
|
+
return path.relative(relativeBase, candidatePath);
|
|
78
|
+
}
|
|
64
79
|
export async function assertRootIdentityCurrent(root) {
|
|
65
80
|
let current;
|
|
66
81
|
try {
|
|
82
|
+
if (typeof root.rootIdentity.dev === "bigint" && typeof root.rootIdentity.ino === "bigint") {
|
|
83
|
+
await inspectDirectoryIdentity(root.rootReal, { dev: root.rootIdentity.dev, ino: root.rootIdentity.ino });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
67
86
|
current = await fs.lstat(root.rootReal);
|
|
68
87
|
}
|
|
69
88
|
catch (error) {
|
package/dist/root-impl.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ContainmentGuarantee } from "./containment.js";
|
|
|
4
4
|
import { type DenyMutationPolicy } from "./deny-mutations.js";
|
|
5
5
|
import { type RenameIdentityPolicy } from "./pinned-write.js";
|
|
6
6
|
import { type ReadResult } from "./read-opened-file.js";
|
|
7
|
+
import { type RootContext } from "./root-context.js";
|
|
7
8
|
import type { DirEntry, PathStat } from "./types.js";
|
|
8
9
|
import { type RootWalkEntry, type RootWalkOptions } from "./root-walk.js";
|
|
9
10
|
export type { DenyMutationPolicy } from "./deny-mutations.js";
|
|
@@ -100,6 +101,48 @@ export interface Root {
|
|
|
100
101
|
move(fromRelative: string, toRelative: string, options?: RootMoveOptions): Promise<void>;
|
|
101
102
|
walk(relativePath: string, options: RootWalkOptions): AsyncIterableIterator<RootWalkEntry>;
|
|
102
103
|
}
|
|
104
|
+
export declare class RootHandle implements Root {
|
|
105
|
+
private readonly rootIdentity;
|
|
106
|
+
readonly rootDir: string;
|
|
107
|
+
readonly rootReal: string;
|
|
108
|
+
readonly rootWithSep: string;
|
|
109
|
+
readonly defaults: RootDefaults;
|
|
110
|
+
constructor(context: RootContext, defaults?: RootDefaults);
|
|
111
|
+
private get context();
|
|
112
|
+
private mutationOptions;
|
|
113
|
+
resolve(relativePath: string): Promise<string>;
|
|
114
|
+
open(relativePath: string, options?: RootOpenOptions): Promise<OpenResult>;
|
|
115
|
+
read(relativePath: string, options?: RootReadOptions): Promise<ReadResult>;
|
|
116
|
+
readBytes(relativePath: string, options?: RootReadOptions): Promise<Buffer>;
|
|
117
|
+
readText(relativePath: string, options?: RootReadOptions & {
|
|
118
|
+
encoding?: BufferEncoding;
|
|
119
|
+
}): Promise<string>;
|
|
120
|
+
readJson<T = unknown>(relativePath: string, options?: RootReadOptions & {
|
|
121
|
+
encoding?: BufferEncoding;
|
|
122
|
+
}): Promise<T>;
|
|
123
|
+
readAbsolute(filePath: string, options?: RootReadOptions): Promise<ReadResult>;
|
|
124
|
+
reader(options?: RootReadOptions): (filePath: string) => Promise<Buffer>;
|
|
125
|
+
openWritable(relativePath: string, options?: RootOpenWritableOptions): Promise<WritableOpenResult>;
|
|
126
|
+
append(relativePath: string, data: string | Buffer, options?: RootAppendOptions): Promise<void>;
|
|
127
|
+
remove(relativePath: string, options?: RootRemoveOptions): Promise<void>;
|
|
128
|
+
mkdir(relativePath: string, options?: RootMkdirOptions): Promise<void>;
|
|
129
|
+
ensureRoot(options?: RootMkdirOptions): Promise<void>;
|
|
130
|
+
write(relativePath: string, data: string | Buffer, options?: RootWriteOptions): Promise<void>;
|
|
131
|
+
create(relativePath: string, data: string | Buffer, options?: RootCreateOptions): Promise<void>;
|
|
132
|
+
writeJson(relativePath: string, data: unknown, options?: RootWriteJsonOptions): Promise<void>;
|
|
133
|
+
createJson(relativePath: string, data: unknown, options?: RootCreateJsonOptions): Promise<void>;
|
|
134
|
+
copyIn(relativePath: string, sourcePath: string, options?: RootCopyOptions): Promise<void>;
|
|
135
|
+
exists(relativePath: string): Promise<boolean>;
|
|
136
|
+
stat(relativePath: string): Promise<PathStat>;
|
|
137
|
+
list(relativePath: string, options?: {
|
|
138
|
+
withFileTypes?: false;
|
|
139
|
+
}): Promise<string[]>;
|
|
140
|
+
list(relativePath: string, options: {
|
|
141
|
+
withFileTypes: true;
|
|
142
|
+
}): Promise<DirEntry[]>;
|
|
143
|
+
move(fromRelative: string, toRelative: string, options?: RootMoveOptions): Promise<void>;
|
|
144
|
+
walk(relativePath: string, options: RootWalkOptions): AsyncIterableIterator<RootWalkEntry>;
|
|
145
|
+
}
|
|
103
146
|
export declare function root(rootDir: string, defaults?: RootDefaults): Promise<Root>;
|
|
104
147
|
export declare function readLocalFileSafely(params: {
|
|
105
148
|
filePath: string;
|
package/dist/root-impl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAW7D,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,KAAK,oBAAoB,EAG1B,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}
|
package/dist/root-impl.js
CHANGED
|
@@ -11,7 +11,7 @@ import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
|
|
|
11
11
|
import { withAsyncDirectoryGuards } from "./guarded-mutation.js";
|
|
12
12
|
import { assertMutationNotDenied, mergeDenyMutationPolicies, } from "./deny-mutations.js";
|
|
13
13
|
import { resolveOpenedFileRealPathForFd, resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
14
|
-
import { openedPathResolutionError, recordFileOpenFailure, recordOpenedFileFailure, recordPreOpenFileChange } from "./opened-file-failure.js";
|
|
14
|
+
import { openedPathResolutionError, recordExclusiveCreateFailure, recordFileOpenFailure, recordOpenedFileFailure, recordPreOpenFileChange } from "./opened-file-failure.js";
|
|
15
15
|
import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy, } from "./pinned-write.js";
|
|
16
16
|
import { getNativeBinding } from "./native.js";
|
|
17
17
|
import { validatePinnedOperationPayload } from "./pinned-operation.js";
|
|
@@ -19,8 +19,9 @@ import { assertNoPathAliasEscape, PATH_ALIAS_POLICIES } from "./path-policy.js";
|
|
|
19
19
|
import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, isSymlinkOpenError, } from "./path.js";
|
|
20
20
|
import { readOpenedFileSafely } from "./read-opened-file.js";
|
|
21
21
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
22
|
+
import { isNonRegularWriteOpenError, resolveNonblockingWriteFlag } from "./write-open-flags.js";
|
|
22
23
|
import { resolveRootPath } from "./root-path.js";
|
|
23
|
-
import { assertRootIdentityCurrent, assertValidRootDestinationPath, assertValidRootRelativePath, ensureTrailingSep, expandRelativePathWithHome, resolvePathInRoot, resolveRootContext, } from "./root-context.js";
|
|
24
|
+
import { assertRootIdentityCurrent, assertValidRootDestinationPath, assertValidRootRelativePath, ensureTrailingSep, expandRelativePathWithHome, resolvePathInRoot, resolveRootContext, rootRelativeReadPath, } from "./root-context.js";
|
|
24
25
|
import { fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, normalizePinnedWriteError, normalizeRemoveGuardError, normalizeRemovePathError, outsideWorkspaceError, } from "./root-errors.js";
|
|
25
26
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
26
27
|
import { stringifyJsonDocument } from "./json-stringify.js";
|
|
@@ -38,12 +39,14 @@ function logWarn(message) {
|
|
|
38
39
|
const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in fsConstants;
|
|
39
40
|
const OPEN_READ_FLAGS = resolveReadOpenFlags();
|
|
40
41
|
const OPEN_READ_FOLLOW_FLAGS = resolveReadOpenFlags({ followSymlinks: true });
|
|
41
|
-
const OPEN_WRITE_EXISTING_FLAGS = fsConstants.O_WRONLY | (SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0)
|
|
42
|
+
const OPEN_WRITE_EXISTING_FLAGS = fsConstants.O_WRONLY | (SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0) |
|
|
43
|
+
resolveNonblockingWriteFlag();
|
|
42
44
|
const OPEN_WRITE_CREATE_FLAGS = fsConstants.O_WRONLY |
|
|
43
45
|
fsConstants.O_CREAT |
|
|
44
46
|
fsConstants.O_EXCL |
|
|
45
47
|
(SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0);
|
|
46
|
-
const OPEN_APPEND_EXISTING_FLAGS = fsConstants.O_RDWR | fsConstants.O_APPEND | (SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0)
|
|
48
|
+
const OPEN_APPEND_EXISTING_FLAGS = fsConstants.O_RDWR | fsConstants.O_APPEND | (SUPPORTS_NOFOLLOW ? fsConstants.O_NOFOLLOW : 0) |
|
|
49
|
+
resolveNonblockingWriteFlag();
|
|
47
50
|
const OPEN_APPEND_CREATE_FLAGS = fsConstants.O_RDWR |
|
|
48
51
|
fsConstants.O_APPEND |
|
|
49
52
|
fsConstants.O_CREAT |
|
|
@@ -181,7 +184,7 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
181
184
|
throw err;
|
|
182
185
|
}
|
|
183
186
|
}
|
|
184
|
-
class RootHandle {
|
|
187
|
+
export class RootHandle {
|
|
185
188
|
rootIdentity;
|
|
186
189
|
rootDir;
|
|
187
190
|
rootReal;
|
|
@@ -430,11 +433,7 @@ async function readFileInRoot(root, params) {
|
|
|
430
433
|
}
|
|
431
434
|
}
|
|
432
435
|
async function readPathInRoot(root, params) {
|
|
433
|
-
const
|
|
434
|
-
const candidatePath = path.isAbsolute(params.filePath)
|
|
435
|
-
? path.resolve(params.filePath)
|
|
436
|
-
: path.resolve(rootDir, params.filePath);
|
|
437
|
-
const relativePath = path.relative(rootDir, candidatePath);
|
|
436
|
+
const relativePath = rootRelativeReadPath(root, params.filePath);
|
|
438
437
|
return await readFileInRoot(root, {
|
|
439
438
|
relativePath,
|
|
440
439
|
hardlinks: params.hardlinks,
|
|
@@ -549,6 +548,9 @@ async function openWritableFileInRoot(root, params) {
|
|
|
549
548
|
handle = await fs.open(ioPath, existingFlags, mode);
|
|
550
549
|
}
|
|
551
550
|
catch (err) {
|
|
551
|
+
if (await isNonRegularWriteOpenError(err, ioPath, existingFlags)) {
|
|
552
|
+
throw new FsSafeError("not-file", "path is not a regular file under root");
|
|
553
|
+
}
|
|
552
554
|
if (!isNotFoundPathError(err)) {
|
|
553
555
|
throw err;
|
|
554
556
|
}
|
|
@@ -1187,7 +1189,7 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1187
1189
|
let verifyingPublication = false;
|
|
1188
1190
|
try {
|
|
1189
1191
|
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
1190
|
-
const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
1192
|
+
const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600).catch((error) => recordExclusiveCreateFailure(error, targetPath));
|
|
1191
1193
|
created = true;
|
|
1192
1194
|
try {
|
|
1193
1195
|
createdIdentity = await handle.stat();
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
type ResolveSymlinkHopOptions = {
|
|
2
|
+
rejectUnresolved?: boolean;
|
|
3
|
+
};
|
|
4
|
+
export declare function resolveSymlinkHopPath(symlinkPath: string, options?: ResolveSymlinkHopOptions): Promise<string>;
|
|
5
|
+
export declare function resolveSymlinkHopPathSync(symlinkPath: string, options?: ResolveSymlinkHopOptions): string;
|
|
6
|
+
export {};
|
|
3
7
|
//# sourceMappingURL=root-path-symlink.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"AAUA,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAiBF,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAQR"}
|
|
@@ -4,8 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
import { FsSafeError } from "./errors.js";
|
|
5
5
|
import { isNotFoundPathError, isSymlinkOpenError } from "./path.js";
|
|
6
6
|
import { resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
|
|
7
|
-
function normalizeSymlinkResolutionError(error) {
|
|
8
|
-
if (isSymlinkOpenError(error)
|
|
7
|
+
function normalizeSymlinkResolutionError(error, options) {
|
|
8
|
+
if (isSymlinkOpenError(error) ||
|
|
9
|
+
(options.rejectUnresolved && isNotFoundPathError(error))) {
|
|
9
10
|
throw new FsSafeError("symlink", "symlink path could not be resolved", {
|
|
10
11
|
cause: error instanceof Error ? error : undefined,
|
|
11
12
|
});
|
|
@@ -13,22 +14,22 @@ function normalizeSymlinkResolutionError(error) {
|
|
|
13
14
|
if (!isNotFoundPathError(error))
|
|
14
15
|
throw error;
|
|
15
16
|
}
|
|
16
|
-
export async function resolveSymlinkHopPath(symlinkPath) {
|
|
17
|
+
export async function resolveSymlinkHopPath(symlinkPath, options = {}) {
|
|
17
18
|
try {
|
|
18
19
|
return path.resolve(await fsp.realpath(symlinkPath));
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
21
|
-
normalizeSymlinkResolutionError(error);
|
|
22
|
+
normalizeSymlinkResolutionError(error, options);
|
|
22
23
|
const linkTarget = await fsp.readlink(symlinkPath);
|
|
23
24
|
return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
|
-
export function resolveSymlinkHopPathSync(symlinkPath) {
|
|
27
|
+
export function resolveSymlinkHopPathSync(symlinkPath, options = {}) {
|
|
27
28
|
try {
|
|
28
29
|
return path.resolve(fs.realpathSync(symlinkPath));
|
|
29
30
|
}
|
|
30
31
|
catch (error) {
|
|
31
|
-
normalizeSymlinkResolutionError(error);
|
|
32
|
+
normalizeSymlinkResolutionError(error, options);
|
|
32
33
|
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
33
34
|
return resolvePathViaExistingAncestorSync(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
34
35
|
}
|
package/dist/root-path.d.ts
CHANGED
package/dist/root-path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yBAAyB,CAAC;AAE7E,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,wBAAwB;aACnC,MAAM;;;;aAIN,YAAY;;;;CAIJ,CAAC;AAEX,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
|
|
1
|
+
{"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yBAAyB,CAAC;AAE7E,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,wBAAwB;aACnC,MAAM;;;;aAIN,YAAY;;;;CAIJ,CAAC;AAEX,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
|
package/dist/root-path.js
CHANGED
|
@@ -297,7 +297,9 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
297
297
|
break;
|
|
298
298
|
}
|
|
299
299
|
await resolveAndApplySymlinkHop(context, {
|
|
300
|
-
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor
|
|
300
|
+
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor, {
|
|
301
|
+
rejectUnresolved: context.resolveParams.rejectUnresolvedSymlinks,
|
|
302
|
+
}),
|
|
301
303
|
});
|
|
302
304
|
if (context.resolveParams.rejectSymlinks === true) {
|
|
303
305
|
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
@@ -340,7 +342,9 @@ function resolveRootPathLexicalSync(params) {
|
|
|
340
342
|
break;
|
|
341
343
|
}
|
|
342
344
|
const maybeApplied = resolveAndApplySymlinkHop(context, {
|
|
343
|
-
resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor
|
|
345
|
+
resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor, {
|
|
346
|
+
rejectUnresolved: context.resolveParams.rejectUnresolvedSymlinks,
|
|
347
|
+
}),
|
|
344
348
|
});
|
|
345
349
|
if (isPromiseLike(maybeApplied)) {
|
|
346
350
|
throw new Error("Unexpected async symlink resolution");
|
package/dist/root-walk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-walk.d.ts","sourceRoot":"","sources":["../src/root-walk.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,oBAAoB,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,OAAO,CAAC;AACzD,MAAM,MAAM,8BAA8B,GAAG,OAAO,GAAG,iBAAiB,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,MAAM,GAAG,cAAc,CAAC;AAC5E,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,WAAW,CAAC;AAExF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GACpD;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,yBAAyB,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,qBAAqB,CAAC;IACrC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,gBAAgB,CAAC,EAAE,8BAA8B,CAAC;CACnD,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACnF,CAAC;AAqBF,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"root-walk.d.ts","sourceRoot":"","sources":["../src/root-walk.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,oBAAoB,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,OAAO,CAAC;AACzD,MAAM,MAAM,8BAA8B,GAAG,OAAO,GAAG,iBAAiB,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,MAAM,GAAG,cAAc,CAAC;AAC5E,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,WAAW,CAAC;AAExF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GACpD;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,yBAAyB,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,qBAAqB,CAAC;IACrC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,gBAAgB,CAAC,EAAE,8BAA8B,CAAC;CACnD,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACnF,CAAC;AAqBF,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,aAAa,CAAC,CAyH/B"}
|
package/dist/root-walk.js
CHANGED
|
@@ -37,10 +37,12 @@ export async function* walkRoot(root, relativePath, options) {
|
|
|
37
37
|
const maxEntries = validateBudget("maxEntries", options.maxEntries);
|
|
38
38
|
const visitedDirectories = new Set();
|
|
39
39
|
let examined = 0;
|
|
40
|
+
let truncated = false;
|
|
40
41
|
const onLimit = (atPath) => {
|
|
41
42
|
if ((options.limitBehavior ?? "truncate") === "throw") {
|
|
42
43
|
throw new FsSafeError("too-large", `root walk budget exceeded at ${atPath || "."}`);
|
|
43
44
|
}
|
|
45
|
+
truncated = true;
|
|
44
46
|
return limitEntry(atPath);
|
|
45
47
|
};
|
|
46
48
|
async function* visit(directory, depth) {
|
|
@@ -123,6 +125,8 @@ export async function* walkRoot(root, relativePath, options) {
|
|
|
123
125
|
return;
|
|
124
126
|
}
|
|
125
127
|
yield* visit(child, depth + 1);
|
|
128
|
+
if (truncated)
|
|
129
|
+
return;
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
yield* visit(relativePath, 0);
|