@openclaw/fs-safe 0.8.6 → 0.10.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 +93 -0
- package/README.md +17 -0
- package/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-crc32.d.ts.map +1 -1
- package/dist/archive-crc32.js +6 -1
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +3 -4
- package/dist/archive-durability.d.ts +24 -0
- package/dist/archive-durability.d.ts.map +1 -0
- package/dist/archive-durability.js +180 -0
- package/dist/archive-gzip-tail.d.ts +2 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +22 -4
- package/dist/archive-input.js +4 -4
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +3 -2
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +42 -9
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +4 -2
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +72 -70
- package/dist/archive-staging.d.ts +1 -1
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +24 -19
- package/dist/archive-tar-stream.d.ts +11 -4
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +20 -8
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +2 -1
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/bounded-read.d.ts +7 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +73 -43
- package/dist/clone-metadata.d.ts +19 -0
- package/dist/clone-metadata.d.ts.map +1 -0
- package/dist/clone-metadata.js +32 -0
- package/dist/copy-file-input.d.ts +23 -0
- package/dist/copy-file-input.d.ts.map +1 -0
- package/dist/copy-file-input.js +91 -0
- package/dist/copy-policy.d.ts +3 -0
- package/dist/copy-policy.d.ts.map +1 -0
- package/dist/copy-policy.js +8 -0
- package/dist/copy-publication.d.ts +15 -0
- package/dist/copy-publication.d.ts.map +1 -0
- package/dist/copy-publication.js +30 -0
- package/dist/copy-tree-portable.d.ts +9 -0
- package/dist/copy-tree-portable.d.ts.map +1 -0
- package/dist/copy-tree-portable.js +191 -0
- package/dist/copy.d.ts +16 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +123 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +4 -4
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/error-detail.d.ts.map +1 -1
- package/dist/error-detail.js +4 -1
- package/dist/file-hash.d.ts +6 -2
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +49 -12
- package/dist/file-store-sync-write.d.ts +1 -0
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +9 -6
- package/dist/file-store.d.ts +4 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +10 -1
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +2 -12
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +1 -2
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +1 -0
- package/dist/guarded-mutation.d.ts +2 -0
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +9 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +9 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +9 -13
- 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 +1 -0
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue-ownership.js +3 -2
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +31 -27
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +22 -24
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +4 -3
- package/dist/move-path-stage.d.ts +8 -0
- package/dist/move-path-stage.d.ts.map +1 -0
- package/dist/move-path-stage.js +55 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +55 -47
- package/dist/mutation-authority.d.ts +8 -0
- package/dist/mutation-authority.d.ts.map +1 -0
- package/dist/mutation-authority.js +36 -0
- package/dist/native-binding.d.ts +24 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +11 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +18 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +1 -0
- package/dist/native-staged-file.d.ts +3 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +35 -8
- package/dist/opened-file-failure.d.ts +1 -1
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +3 -2
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +3 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +38 -48
- package/dist/permissions.js +3 -3
- package/dist/pinned-operation.js +1 -1
- package/dist/pinned-write.d.ts +5 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +50 -30
- package/dist/positional-read.d.ts +9 -0
- package/dist/positional-read.d.ts.map +1 -0
- package/dist/positional-read.js +36 -0
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +9 -3
- package/dist/publish-copy-stage.d.ts +13 -0
- package/dist/publish-copy-stage.d.ts.map +1 -0
- package/dist/publish-copy-stage.js +47 -0
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +26 -25
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +30 -17
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -5
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +6 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +8 -3
- package/dist/root-context.d.ts +3 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +51 -26
- package/dist/root-directory-list.d.ts +24 -0
- package/dist/root-directory-list.d.ts.map +1 -0
- package/dist/root-directory-list.js +201 -0
- package/dist/root-errors.d.ts +1 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +3 -0
- package/dist/root-file.d.ts +2 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +12 -4
- package/dist/root-impl.d.ts +6 -50
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +342 -208
- package/dist/root-options.d.ts +66 -0
- package/dist/root-options.d.ts.map +1 -0
- package/dist/root-options.js +18 -0
- package/dist/root-path-existing.d.ts +5 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +56 -1
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +74 -162
- package/dist/root-symlink-policy.d.ts +13 -0
- package/dist/root-symlink-policy.d.ts.map +1 -0
- package/dist/root-symlink-policy.js +34 -0
- package/dist/root-walk.d.ts +5 -4
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +99 -50
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +1 -4
- package/dist/root.d.ts +3 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts +1 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secret-read-async.js +5 -5
- package/dist/secure-file.d.ts +1 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +23 -13
- package/dist/sibling-staged-file.js +7 -7
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +16 -7
- package/dist/sidecar-lock-acquire.d.ts +1 -1
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +6 -4
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +5 -3
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +16 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -1
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +3 -2
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +9 -4
- package/dist/timing.d.ts +1 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +25 -6
- package/dist/trash.js +2 -2
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +7 -27
- package/dist/windows-owner.d.ts +9 -12
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +24 -58
- package/dist/write-file-handle.d.ts +6 -0
- package/dist/write-file-handle.d.ts.map +1 -0
- package/dist/write-file-handle.js +25 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -2
- package/docs/advanced.md +18 -4
- package/docs/archive.md +57 -9
- package/docs/atomic.md +7 -0
- package/docs/contributing.md +7 -0
- package/docs/copy.md +86 -0
- package/docs/durability.md +17 -2
- package/docs/errors.md +1 -1
- package/docs/file-store.md +48 -3
- package/docs/json-store.md +10 -0
- package/docs/local-roots.md +8 -1
- package/docs/native.md +14 -1
- package/docs/permissions.md +20 -10
- package/docs/positional-read.md +63 -0
- package/docs/reading.md +6 -0
- package/docs/root.md +168 -6
- package/docs/secret-file.md +6 -0
- package/docs/security-model.md +14 -0
- package/docs/sidecar-lock.md +2 -0
- package/docs/timing.md +2 -0
- package/docs/types.md +18 -11
- package/docs/walk.md +54 -5
- package/docs/writing.md +25 -5
- package/package.json +16 -11
|
@@ -319,7 +319,9 @@ export class SyncAtomicTempOwner {
|
|
|
319
319
|
if (sha256Hex(fsModule.readFileSync(publishedFd)) !== expectedHash) {
|
|
320
320
|
throw new FsSafeError("path-mismatch", `Atomic replace published content changed: ${pathname}`);
|
|
321
321
|
}
|
|
322
|
-
|
|
322
|
+
const previousFd = this.#fd;
|
|
323
|
+
this.#fd = undefined;
|
|
324
|
+
fsModule.closeSync(previousFd);
|
|
323
325
|
this.#fd = publishedFd;
|
|
324
326
|
this.#identity = identity;
|
|
325
327
|
publishedFd = undefined;
|
|
@@ -359,8 +361,10 @@ export class SyncAtomicTempOwner {
|
|
|
359
361
|
if (cleanupComplete)
|
|
360
362
|
this.#unregister();
|
|
361
363
|
if (this.#fd !== undefined) {
|
|
364
|
+
const fd = this.#fd;
|
|
365
|
+
this.#fd = undefined;
|
|
362
366
|
try {
|
|
363
|
-
params.fsModule.closeSync(
|
|
367
|
+
params.fsModule.closeSync(fd);
|
|
364
368
|
}
|
|
365
369
|
catch (closeError) {
|
|
366
370
|
deferredError = deferredError
|
|
@@ -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;AAUzC,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,iCAAiC,CAAC;AAMzC,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,IAAI,CACZ,OAAO,EAAE,EACP,OAAO,GACP,WAAW,GACX,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,IAAI,GACJ,MAAM,GACN,MAAM,GACN,OAAO,CACV,GAAG;QACF,uEAAuE;QACvE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC;KACzB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,OAAO,MAAM,EACX,WAAW,GACX,cAAc,GACd,eAAe,GACf,YAAY,GACZ,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,WAAW,GACX,eAAe,GACf,UAAU,GACV,WAAW,CACd,GAAG;IACF,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IACpC,UAAU,CAAC,EAAE,OAAO,MAAM,CAAC,UAAU,CAAC;CACvC,CAAC;AAEF,YAAY,EACV,oBAAoB,EACpB,+BAA+B,EAC/B,sCAAsC,EACtC,oCAAoC,EACpC,oCAAoC,GACrC,CAAC;AAEF,KAAK,4BAA4B,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,mBAAmB,CAAC,EAAE,oCAAoC,CAAC;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,+FAA+F;IAC/F,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,4BAA4B,GAAG;IACpE,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,6FAA6F;IAC7F,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClF,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,4BAA4B,GAAG;IACxE,UAAU,CAAC,EAAE,+BAA+B,CAAC;IAC7C,6FAA6F;IAC7F,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,QAAQ,GAAG,eAAe,CAAC;CACpC,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
|
@@ -104,12 +104,17 @@ async function resolveMode(options) {
|
|
|
104
104
|
if (!options.preserveExistingMode) {
|
|
105
105
|
return defaultMode;
|
|
106
106
|
}
|
|
107
|
-
const
|
|
107
|
+
const fsModule = options.fileSystem?.promises ?? fs;
|
|
108
|
+
let stat;
|
|
109
|
+
try {
|
|
110
|
+
stat = fsModule === fs ? syncFs.statSync(options.filePath) : await fsModule.stat(options.filePath);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
108
113
|
if (error.code === "ENOENT") {
|
|
109
|
-
return
|
|
114
|
+
return defaultMode;
|
|
110
115
|
}
|
|
111
116
|
throw error;
|
|
112
|
-
}
|
|
117
|
+
}
|
|
113
118
|
return stat ? stat.mode : defaultMode;
|
|
114
119
|
}
|
|
115
120
|
function resolveModeSync(options) {
|
package/dist/root-context.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare function resolvePathInRoot(root: RootContext, relativePath: strin
|
|
|
22
22
|
allowFinalSymlink?: boolean;
|
|
23
23
|
rejectUnsafeDeviceReads?: boolean;
|
|
24
24
|
rejectSymlinks?: boolean;
|
|
25
|
+
rejectFinalSymlink?: boolean;
|
|
26
|
+
resolveCanonical?: boolean;
|
|
27
|
+
rejectAmbiguousParents?: boolean;
|
|
25
28
|
}): Promise<{
|
|
26
29
|
rootReal: string;
|
|
27
30
|
rootWithSep: string;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAMD,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBtF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4B9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBhF;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhF;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;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDtE;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
|
@@ -4,11 +4,11 @@ import path from "node:path";
|
|
|
4
4
|
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { sameFileIdentity } from "./file-identity.js";
|
|
7
|
-
import { expandHomePrefix } from "./home-dir.js";
|
|
8
7
|
import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, } from "./path.js";
|
|
9
8
|
import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath } from "./root-path.js";
|
|
10
|
-
import { outsideWorkspaceError } from "./root-errors.js";
|
|
9
|
+
import { outsideWorkspaceError, rootPathChangedError } from "./root-errors.js";
|
|
11
10
|
import { isDriveRelativePath } from "./safe-path-segment.js";
|
|
11
|
+
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
12
12
|
export const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
|
|
13
13
|
export function assertValidRootRelativePath(relativePath) {
|
|
14
14
|
assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
@@ -20,6 +20,8 @@ export function assertValidRootDestinationPath(relativePath) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
let cachedHomePath;
|
|
23
|
+
const POSIX_PARENT_COMPONENT = /(?:^|\/)\.\.(?:\/|$)/;
|
|
24
|
+
const WINDOWS_PARENT_COMPONENT = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
|
|
23
25
|
export async function expandRelativePathWithHome(relativePath) {
|
|
24
26
|
const rawHome = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
25
27
|
if (cachedHomePath?.raw !== rawHome) {
|
|
@@ -32,18 +34,26 @@ export async function expandRelativePathWithHome(relativePath) {
|
|
|
32
34
|
}
|
|
33
35
|
cachedHomePath = { raw: rawHome, real: realHome };
|
|
34
36
|
}
|
|
35
|
-
|
|
37
|
+
if (relativePath === "~")
|
|
38
|
+
return cachedHomePath.real;
|
|
39
|
+
if (relativePath.startsWith("~/") || (path.sep === "\\" && relativePath.startsWith("~\\"))) {
|
|
40
|
+
return `${ensureTrailingSep(cachedHomePath.real)}${relativePath.slice(2)}`;
|
|
41
|
+
}
|
|
42
|
+
return relativePath;
|
|
36
43
|
}
|
|
37
44
|
export async function resolveRootContext(rootDir) {
|
|
38
45
|
assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
|
|
46
|
+
const lexicalRoot = path.resolve(rootDir);
|
|
39
47
|
let rootReal;
|
|
40
48
|
let rootIdentity;
|
|
41
49
|
try {
|
|
42
50
|
rootReal = fs.realpathSync.native(rootDir);
|
|
43
|
-
const rootStat =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
const rootStat = await inspectFileIdentity(() => {
|
|
52
|
+
const stat = fs.statSync(rootReal, { bigint: true });
|
|
53
|
+
if (!stat.isDirectory())
|
|
54
|
+
throw new FsSafeError("invalid-path", "root dir is not a directory");
|
|
55
|
+
return stat;
|
|
56
|
+
});
|
|
47
57
|
rootIdentity = { dev: rootStat.dev, ino: rootStat.ino };
|
|
48
58
|
}
|
|
49
59
|
catch (err) {
|
|
@@ -56,7 +66,7 @@ export async function resolveRootContext(rootDir) {
|
|
|
56
66
|
throw err;
|
|
57
67
|
}
|
|
58
68
|
return {
|
|
59
|
-
rootDir:
|
|
69
|
+
rootDir: lexicalRoot,
|
|
60
70
|
rootIdentity,
|
|
61
71
|
rootReal,
|
|
62
72
|
rootWithSep: ensureTrailingSep(rootReal),
|
|
@@ -64,17 +74,22 @@ export async function resolveRootContext(rootDir) {
|
|
|
64
74
|
}
|
|
65
75
|
export function rootRelativeReadPath(root, filePath) {
|
|
66
76
|
const absoluteInput = path.isAbsolute(filePath);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
if (!absoluteInput)
|
|
78
|
+
return filePath;
|
|
79
|
+
const raw = process.platform === "win32" ? filePath.replaceAll("/", path.sep) : filePath;
|
|
80
|
+
for (const base of [root.rootDir, root.rootReal]) {
|
|
81
|
+
const prefix = ensureTrailingSep(base);
|
|
82
|
+
const matches = process.platform === "win32"
|
|
83
|
+
? raw.slice(0, prefix.length).toLowerCase() === prefix.toLowerCase()
|
|
84
|
+
: raw.startsWith(prefix);
|
|
85
|
+
if (matches) {
|
|
86
|
+
let start = prefix.length;
|
|
87
|
+
while (raw[start] === path.sep)
|
|
88
|
+
start += 1;
|
|
89
|
+
return raw.slice(start);
|
|
90
|
+
}
|
|
76
91
|
}
|
|
77
|
-
return
|
|
92
|
+
return raw;
|
|
78
93
|
}
|
|
79
94
|
export async function assertRootIdentityCurrent(root) {
|
|
80
95
|
let current;
|
|
@@ -86,22 +101,20 @@ export async function assertRootIdentityCurrent(root) {
|
|
|
86
101
|
current = fs.lstatSync(root.rootReal);
|
|
87
102
|
}
|
|
88
103
|
catch (error) {
|
|
89
|
-
throw
|
|
90
|
-
cause: error instanceof Error ? error : undefined,
|
|
91
|
-
});
|
|
104
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
92
105
|
}
|
|
93
106
|
if (current.isSymbolicLink() ||
|
|
94
107
|
!current.isDirectory() ||
|
|
95
108
|
!sameFileIdentity(current, root.rootIdentity)) {
|
|
96
|
-
throw
|
|
109
|
+
throw rootPathChangedError();
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
export async function resolvePathInRoot(root, relativePath, options) {
|
|
100
113
|
assertValidRootRelativePath(relativePath);
|
|
101
114
|
await assertRootIdentityCurrent(root);
|
|
102
115
|
const expanded = await expandRelativePathWithHome(relativePath);
|
|
103
|
-
|
|
104
|
-
if (!isPathInside(root.rootWithSep, resolved)) {
|
|
116
|
+
let resolved = path.resolve(root.rootWithSep, expanded);
|
|
117
|
+
if (!options?.resolveCanonical && !isPathInside(root.rootWithSep, resolved)) {
|
|
105
118
|
throw outsideWorkspaceError();
|
|
106
119
|
}
|
|
107
120
|
if (options?.rejectUnsafeDeviceReads === true) {
|
|
@@ -111,14 +124,26 @@ export async function resolvePathInRoot(root, relativePath, options) {
|
|
|
111
124
|
? expanded
|
|
112
125
|
: `${root.rootWithSep}${expanded}`;
|
|
113
126
|
try {
|
|
114
|
-
|
|
127
|
+
const resolution = {
|
|
115
128
|
absolutePath: rawAbsolutePath,
|
|
116
129
|
rootPath: root.rootReal,
|
|
117
130
|
rootCanonicalPath: root.rootReal,
|
|
118
131
|
boundaryLabel: "root",
|
|
119
132
|
policy: options?.allowFinalSymlink ? ROOT_PATH_ALIAS_POLICIES.unlinkTarget : undefined,
|
|
120
133
|
rejectSymlinks: options?.rejectSymlinks,
|
|
121
|
-
|
|
134
|
+
rejectFinalSymlink: options?.rejectFinalSymlink,
|
|
135
|
+
};
|
|
136
|
+
const checked = await resolveRootPath(resolution);
|
|
137
|
+
const parentComponent = process.platform === "win32" ? WINDOWS_PARENT_COMPONENT : POSIX_PARENT_COMPONENT;
|
|
138
|
+
if (options?.rejectAmbiguousParents && parentComponent.test(expanded) &&
|
|
139
|
+
path.relative(checked.canonicalPath, resolved) !== "") {
|
|
140
|
+
const normalized = await resolveRootPath({ ...resolution, absolutePath: resolved });
|
|
141
|
+
if (path.relative(checked.canonicalPath, normalized.canonicalPath) !== "") {
|
|
142
|
+
throw new FsSafeError("path-alias", "parent traversal resolves differently through a symlink");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (options?.resolveCanonical)
|
|
146
|
+
resolved = checked.canonicalPath;
|
|
122
147
|
}
|
|
123
148
|
catch (error) {
|
|
124
149
|
if (error instanceof FsSafeError && error.code === "symlink") {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
import { type RootContext } from "./root-context.js";
|
|
3
|
+
import type { DirEntry, PathStat } from "./types.js";
|
|
4
|
+
export declare function pathStatFromStats(stat: Stats): PathStat;
|
|
5
|
+
export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: true): Promise<DirEntry[]>;
|
|
6
|
+
export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: boolean): Promise<string[] | DirEntry[]>;
|
|
7
|
+
export type RootDirectoryListing = {
|
|
8
|
+
next(): Promise<{
|
|
9
|
+
kind: "entry";
|
|
10
|
+
entry: DirEntry;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "limit";
|
|
13
|
+
name: string;
|
|
14
|
+
} | undefined>;
|
|
15
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
export type RootDirectoryListingOptions = {
|
|
18
|
+
order: "sorted" | "filesystem";
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
snapshot: boolean;
|
|
21
|
+
admitEntry(): boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function listDirectoryForWalk(root: RootContext, directory: string, options: RootDirectoryListingOptions): Promise<RootDirectoryListing>;
|
|
24
|
+
//# sourceMappingURL=root-directory-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-directory-list.d.ts","sourceRoot":"","sources":["../src/root-directory-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAQ1C,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAcvD;AAWD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAClH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;AAqBhI,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAClG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,IAAI,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAqI/B"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
5
|
+
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard } from "./directory-guard.js";
|
|
6
|
+
import { FsSafeError } from "./errors.js";
|
|
7
|
+
import { isNotFoundPathError, isPathInside } from "./path.js";
|
|
8
|
+
import { assertRootIdentityCurrent } from "./root-context.js";
|
|
9
|
+
import { rootPathChangedError } from "./root-errors.js";
|
|
10
|
+
import { createSuppressedError } from "./suppressed-error.js";
|
|
11
|
+
const METADATA_BATCH_SIZE = 32;
|
|
12
|
+
export function pathStatFromStats(stat) {
|
|
13
|
+
return {
|
|
14
|
+
dev: Number(stat.dev),
|
|
15
|
+
gid: Number(stat.gid),
|
|
16
|
+
ino: Number(stat.ino),
|
|
17
|
+
isDirectory: stat.isDirectory(),
|
|
18
|
+
isFile: stat.isFile(),
|
|
19
|
+
isSymbolicLink: stat.isSymbolicLink(),
|
|
20
|
+
mode: stat.mode,
|
|
21
|
+
mtimeMs: stat.mtimeMs,
|
|
22
|
+
nlink: stat.nlink,
|
|
23
|
+
size: stat.size,
|
|
24
|
+
uid: stat.uid,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function normalizeDirectoryError(error) {
|
|
28
|
+
if (isNotFoundPathError(error)) {
|
|
29
|
+
return new FsSafeError("not-found", "directory not found", {
|
|
30
|
+
cause: error instanceof Error ? error : undefined,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
35
|
+
export async function listDirectoryPath(root, directory, withFileTypes) {
|
|
36
|
+
try {
|
|
37
|
+
const names = (await fs.readdir(directory)).sort();
|
|
38
|
+
const entries = withFileTypes
|
|
39
|
+
? names.map(name => ({
|
|
40
|
+
name,
|
|
41
|
+
...pathStatFromStats(fsSync.lstatSync(path.join(directory, name))),
|
|
42
|
+
}))
|
|
43
|
+
: names;
|
|
44
|
+
await assertRootIdentityCurrent(root);
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
throw normalizeDirectoryError(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function listDirectoryForWalk(root, directory, options) {
|
|
52
|
+
const guard = await createAsyncDirectoryGuard(directory, { bigint: true }).catch((error) => {
|
|
53
|
+
throw normalizeDirectoryError(error);
|
|
54
|
+
});
|
|
55
|
+
if (!isPathInside(root.rootReal, guard.realPath)) {
|
|
56
|
+
throw new FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
57
|
+
}
|
|
58
|
+
const guardPinsRoot = guard.dir === root.rootReal &&
|
|
59
|
+
guard.stat.dev === root.rootIdentity.dev && guard.stat.ino === root.rootIdentity.ino;
|
|
60
|
+
const assertCurrent = async () => {
|
|
61
|
+
options.signal?.throwIfAborted();
|
|
62
|
+
if (guardPinsRoot) {
|
|
63
|
+
// One exact receipt proves both identities without dropping the canonical-path check.
|
|
64
|
+
try {
|
|
65
|
+
await assertAsyncDirectoryGuard(guard);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
await assertRootIdentityCurrent(root);
|
|
73
|
+
await assertAsyncDirectoryGuard(guard);
|
|
74
|
+
}
|
|
75
|
+
options.signal?.throwIfAborted();
|
|
76
|
+
};
|
|
77
|
+
let handle;
|
|
78
|
+
let names;
|
|
79
|
+
let snapshot;
|
|
80
|
+
let index = 0;
|
|
81
|
+
let prepared = [];
|
|
82
|
+
let preparedIndex = 0;
|
|
83
|
+
let pendingFailure;
|
|
84
|
+
let pendingLimit;
|
|
85
|
+
let preparedBatch = false;
|
|
86
|
+
const close = async () => {
|
|
87
|
+
const owned = handle;
|
|
88
|
+
handle = undefined;
|
|
89
|
+
await owned?.close();
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
await assertCurrent();
|
|
93
|
+
if (options.order === "filesystem") {
|
|
94
|
+
// A one-entry buffer keeps the truncation lookahead independent of width.
|
|
95
|
+
handle = await fs.opendir(guard.realPath, { bufferSize: 1 });
|
|
96
|
+
}
|
|
97
|
+
else if (options.snapshot) {
|
|
98
|
+
snapshot = await listDirectoryPath(root, guard.realPath, true);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
names = await fs.readdir(guard.realPath);
|
|
102
|
+
}
|
|
103
|
+
await assertCurrent();
|
|
104
|
+
names?.sort();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const operationError = normalizeDirectoryError(error);
|
|
108
|
+
try {
|
|
109
|
+
await close();
|
|
110
|
+
}
|
|
111
|
+
catch (closeError) {
|
|
112
|
+
throw createSuppressedError(closeError, operationError, "directory setup and close both failed");
|
|
113
|
+
}
|
|
114
|
+
throw operationError;
|
|
115
|
+
}
|
|
116
|
+
const prepareBatch = async (sortedNames) => {
|
|
117
|
+
let name = sortedNames[index++];
|
|
118
|
+
if (name === undefined)
|
|
119
|
+
return;
|
|
120
|
+
if (!options.admitEntry()) {
|
|
121
|
+
pendingLimit = name;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (preparedBatch)
|
|
125
|
+
await yieldToEventLoop();
|
|
126
|
+
await assertCurrent();
|
|
127
|
+
preparedBatch = true;
|
|
128
|
+
prepared = [];
|
|
129
|
+
preparedIndex = 0;
|
|
130
|
+
while (true) {
|
|
131
|
+
try {
|
|
132
|
+
const entry = { name, ...pathStatFromStats(fsSync.lstatSync(path.join(guard.realPath, name))) };
|
|
133
|
+
prepared.push(entry);
|
|
134
|
+
// No later sibling can be observed before a possible recursive descent.
|
|
135
|
+
if (entry.isDirectory || entry.isSymbolicLink || prepared.length >= METADATA_BATCH_SIZE)
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
pendingFailure = { error: normalizeDirectoryError(error) };
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
name = sortedNames[index++];
|
|
143
|
+
if (name === undefined)
|
|
144
|
+
break;
|
|
145
|
+
if (!options.admitEntry()) {
|
|
146
|
+
pendingLimit = name;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await assertCurrent();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (pendingFailure) {
|
|
155
|
+
throw createSuppressedError(error, pendingFailure.error, "directory observation and identity checks both failed");
|
|
156
|
+
}
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
async next() {
|
|
162
|
+
try {
|
|
163
|
+
options.signal?.throwIfAborted();
|
|
164
|
+
if (snapshot) {
|
|
165
|
+
const entry = snapshot[index++];
|
|
166
|
+
if (!entry)
|
|
167
|
+
return;
|
|
168
|
+
return options.admitEntry() ? { kind: "entry", entry } : { kind: "limit", name: entry.name };
|
|
169
|
+
}
|
|
170
|
+
if (names) {
|
|
171
|
+
if (preparedIndex >= prepared.length && !pendingFailure && pendingLimit === undefined) {
|
|
172
|
+
await prepareBatch(names);
|
|
173
|
+
}
|
|
174
|
+
const entry = prepared[preparedIndex++];
|
|
175
|
+
if (entry)
|
|
176
|
+
return { kind: "entry", entry };
|
|
177
|
+
if (pendingFailure)
|
|
178
|
+
throw pendingFailure.error;
|
|
179
|
+
if (pendingLimit !== undefined)
|
|
180
|
+
return { kind: "limit", name: pendingLimit };
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
await assertCurrent();
|
|
184
|
+
const name = (await handle.read())?.name;
|
|
185
|
+
await assertCurrent();
|
|
186
|
+
if (name === undefined)
|
|
187
|
+
return;
|
|
188
|
+
if (!options.admitEntry())
|
|
189
|
+
return { kind: "limit", name };
|
|
190
|
+
// The stream's post-read fence is also the pre-stat fence in this owned operation.
|
|
191
|
+
const stat = fsSync.lstatSync(path.join(guard.realPath, name));
|
|
192
|
+
await assertCurrent();
|
|
193
|
+
return { kind: "entry", entry: { name, ...pathStatFromStats(stat) } };
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
throw normalizeDirectoryError(error);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
[Symbol.asyncDispose]: close,
|
|
200
|
+
};
|
|
201
|
+
}
|
package/dist/root-errors.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
export declare function fileNotFoundError(cause?: unknown): FsSafeError;
|
|
3
3
|
export declare function outsideWorkspaceError(): FsSafeError;
|
|
4
|
+
export declare function rootPathChangedError(cause?: Error): FsSafeError;
|
|
4
5
|
export declare function directoryComponentNotDirectoryError(cause?: unknown): FsSafeError;
|
|
5
6
|
export declare function hardlinkedPathNotAllowedError(): FsSafeError;
|
|
6
7
|
export declare function isAlreadyExistsError(error: unknown): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAI9D;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED,wBAAgB,mCAAmC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIhF;AAED,wBAAgB,6BAA6B,IAAI,WAAW,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAU/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO9D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQ/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAe9D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQzE"}
|
|
1
|
+
{"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAI9D;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,WAAW,CAE/D;AAED,wBAAgB,mCAAmC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIhF;AAED,wBAAgB,6BAA6B,IAAI,WAAW,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAU/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO9D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQ/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAe9D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQzE"}
|
package/dist/root-errors.js
CHANGED
|
@@ -9,6 +9,9 @@ export function fileNotFoundError(cause) {
|
|
|
9
9
|
export function outsideWorkspaceError() {
|
|
10
10
|
return new FsSafeError("outside-workspace", "file is outside workspace root");
|
|
11
11
|
}
|
|
12
|
+
export function rootPathChangedError(cause) {
|
|
13
|
+
return new FsSafeError("path-mismatch", "root path changed during operation", { cause });
|
|
14
|
+
}
|
|
12
15
|
export function directoryComponentNotDirectoryError(cause) {
|
|
13
16
|
return cause === undefined
|
|
14
17
|
? new FsSafeError("not-file", "directory component must be a directory")
|
package/dist/root-file.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import type { PathAliasPolicy } from "./path-policy.js";
|
|
3
|
+
import { type SymlinkPolicy } from "./root-symlink-policy.js";
|
|
3
4
|
import { type PinnedOpenSyncAllowedType, type PinnedOpenSyncFailureReason } from "./pinned-open.js";
|
|
4
5
|
type BoundaryReadFs = Pick<typeof fs, "closeSync" | "constants" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "realpathSync">;
|
|
5
6
|
export type RootFileOpenFailureReason = PinnedOpenSyncFailureReason | "validation";
|
|
@@ -25,6 +26,7 @@ export type OpenRootFileSyncParams = {
|
|
|
25
26
|
maxBytes?: number;
|
|
26
27
|
rejectHardlinks?: boolean;
|
|
27
28
|
rejectSymlinks?: boolean;
|
|
29
|
+
symlinks?: SymlinkPolicy;
|
|
28
30
|
allowedType?: PinnedOpenSyncAllowedType;
|
|
29
31
|
skipLexicalRootCheck?: boolean;
|
|
30
32
|
ioFs?: BoundaryReadFs;
|
package/dist/root-file.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;AAE1B,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,GACd,cAAc,CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,GAAG,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAW3D;
|
|
1
|
+
{"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;AAE1B,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,GACd,cAAc,CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,GAAG,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAW3D;AASD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAuBnF;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE;IACR,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACjD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;CAC/C,GACA,CAAC,CAUH;AAoDD,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
|
package/dist/root-file.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { resolveRootPath, resolveRootPathSync, } from "./root-path.js";
|
|
4
|
+
import { readSymlinkResolution } from "./root-symlink-policy.js";
|
|
4
5
|
import { openPinnedFileSync, } from "./pinned-open.js";
|
|
5
6
|
export function canUseRootFileOpen(ioFs) {
|
|
6
7
|
return (typeof ioFs.openSync === "function" &&
|
|
@@ -12,9 +13,16 @@ export function canUseRootFileOpen(ioFs) {
|
|
|
12
13
|
typeof ioFs.constants === "object" &&
|
|
13
14
|
ioFs.constants !== null);
|
|
14
15
|
}
|
|
16
|
+
function absoluteRootFilePath(filePath) {
|
|
17
|
+
if (path.isAbsolute(filePath))
|
|
18
|
+
return filePath;
|
|
19
|
+
const drive = path.parse(filePath).root;
|
|
20
|
+
const base = drive ? path.resolve(drive) : process.cwd();
|
|
21
|
+
return `${base}${path.sep}${filePath.slice(drive.length)}`;
|
|
22
|
+
}
|
|
15
23
|
export function openRootFileSync(params) {
|
|
16
24
|
const ioFs = params.ioFs ?? fs;
|
|
17
|
-
const absolutePath =
|
|
25
|
+
const absolutePath = absoluteRootFilePath(params.absolutePath);
|
|
18
26
|
let resolved;
|
|
19
27
|
try {
|
|
20
28
|
resolved = mapResolvedRootPath(absolutePath, resolveRootPathSync({
|
|
@@ -22,7 +30,7 @@ export function openRootFileSync(params) {
|
|
|
22
30
|
rootPath: params.rootPath,
|
|
23
31
|
rootCanonicalPath: params.rootRealPath,
|
|
24
32
|
boundaryLabel: params.boundaryLabel,
|
|
25
|
-
|
|
33
|
+
...readSymlinkResolution(params.symlinks ?? (params.rejectSymlinks === false ? "follow-within-root" : "reject")),
|
|
26
34
|
skipLexicalRootCheck: params.skipLexicalRootCheck,
|
|
27
35
|
}));
|
|
28
36
|
}
|
|
@@ -84,7 +92,7 @@ function finalizeRootFileOpen(params) {
|
|
|
84
92
|
}
|
|
85
93
|
export async function openRootFile(params) {
|
|
86
94
|
const ioFs = params.ioFs ?? fs;
|
|
87
|
-
const absolutePath =
|
|
95
|
+
const absolutePath = absoluteRootFilePath(params.absolutePath);
|
|
88
96
|
let resolved;
|
|
89
97
|
try {
|
|
90
98
|
resolved = mapResolvedRootPath(absolutePath, await resolveRootPath({
|
|
@@ -93,7 +101,7 @@ export async function openRootFile(params) {
|
|
|
93
101
|
rootCanonicalPath: params.rootRealPath,
|
|
94
102
|
boundaryLabel: params.boundaryLabel,
|
|
95
103
|
policy: params.aliasPolicy,
|
|
96
|
-
|
|
104
|
+
...readSymlinkResolution(params.symlinks ?? (params.rejectSymlinks === false ? "follow-within-root" : "reject")),
|
|
97
105
|
skipLexicalRootCheck: params.skipLexicalRootCheck,
|
|
98
106
|
}));
|
|
99
107
|
}
|