@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
|
@@ -90,7 +90,13 @@ export async function readSidecarLockRawSnapshot(lockPath, options = {}) {
|
|
|
90
90
|
await opened.handle.close().catch(() => undefined);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
|
|
93
|
+
let before;
|
|
94
|
+
try {
|
|
95
|
+
before = fsSync.lstatSync(lockPath);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
before = missingSnapshotPath(error);
|
|
99
|
+
}
|
|
94
100
|
if (!before)
|
|
95
101
|
return null;
|
|
96
102
|
if (!before.isFile() || before.isSymbolicLink()) {
|
|
@@ -119,7 +125,7 @@ export async function readSidecarLockRawSnapshot(lockPath, options = {}) {
|
|
|
119
125
|
options.onOpenFailure?.(error);
|
|
120
126
|
throw error;
|
|
121
127
|
}
|
|
122
|
-
const opened =
|
|
128
|
+
const opened = fsSync.fstatSync(handle.fd);
|
|
123
129
|
if (!opened.isFile()) {
|
|
124
130
|
if (options.rejectNonFile) {
|
|
125
131
|
throw new FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`);
|
|
@@ -129,7 +135,13 @@ export async function readSidecarLockRawSnapshot(lockPath, options = {}) {
|
|
|
129
135
|
if (!options.allowDescriptorIdentityDrift && !sameFileIdentity(before, opened))
|
|
130
136
|
return null;
|
|
131
137
|
const raw = (await readFileHandleBounded(handle, MAX_LOCK_PAYLOAD_BYTES)).toString("utf8");
|
|
132
|
-
|
|
138
|
+
let after;
|
|
139
|
+
try {
|
|
140
|
+
after = fsSync.lstatSync(lockPath);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
after = missingSnapshotPath(error);
|
|
144
|
+
}
|
|
133
145
|
if (!after || !after.isFile() || !sameFileIdentity(before, after))
|
|
134
146
|
return null;
|
|
135
147
|
return { raw, stat: after };
|
|
@@ -241,7 +253,7 @@ export async function sidecarLockSnapshotStillPresent(lockPath, observed, option
|
|
|
241
253
|
}
|
|
242
254
|
export async function sidecarReclaimGuardExists(pathname) {
|
|
243
255
|
try {
|
|
244
|
-
|
|
256
|
+
fsSync.lstatSync(pathname);
|
|
245
257
|
return true;
|
|
246
258
|
}
|
|
247
259
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAsIjC,0FAA0F;AAC1F,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAuFD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAU3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAkBL,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACxD,yBAAyB,CAAC,QAAQ,CAAC,MACxC,MAAM,OAAO,CAAC,CAAC,CAAC,KACnB,OAAO,CAAC,CAAC,CAAC;iBAqBW,OAAO,CAAC,IAAI,CAAC;iBAQnB,IAAI;uBAIE,oBAAoB,EAAE;EAW/C;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAMZ"}
|
package/dist/sidecar-lock.js
CHANGED
|
@@ -85,7 +85,8 @@ function snapshotMatchesSync(lockPath, observed) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
function releaseAllReclaimGuardsSync(state) {
|
|
88
|
-
|
|
88
|
+
// Exit cleanup also visits managers created by older copies and never reopened here.
|
|
89
|
+
for (const reclaimGuardPath of state.reclaimGuards ?? []) {
|
|
89
90
|
try {
|
|
90
91
|
fsSync.rmdirSync(reclaimGuardPath);
|
|
91
92
|
state.reclaimGuards.delete(reclaimGuardPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-cleanup.d.ts","sourceRoot":"","sources":["../src/temp-cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAChD,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/C,CAAC;
|
|
1
|
+
{"version":3,"file":"temp-cleanup.d.ts","sourceRoot":"","sources":["../src/temp-cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAChD,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/C,CAAC;AAmDF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CAC7B,GACA,oBAAoB,CA2BtB;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAUzE"}
|
package/dist/temp-cleanup.js
CHANGED
|
@@ -11,8 +11,9 @@ function pathStillMatchesReceipt(entry) {
|
|
|
11
11
|
return (!entry.singleLinkFile || (current.isFile() && current.nlink === 1n)) &&
|
|
12
12
|
sameFileIdentityForCleanup(current, entry.identity);
|
|
13
13
|
}
|
|
14
|
-
catch
|
|
15
|
-
|
|
14
|
+
catch {
|
|
15
|
+
// A missing pathname grants no authority over an entry created after this lookup.
|
|
16
|
+
return false;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
function removeRegisteredPathSync(entry) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AA8DF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAaT;AAyCD,wBAAsB,QAAQ,CAAC,MAAM,EAAE;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwBpB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE;IACN,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C,EACD,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
package/dist/temp-target.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import fsSync from "node:fs";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
@@ -81,12 +82,16 @@ function isNodeErrorWithCode(err, code) {
|
|
|
81
82
|
}
|
|
82
83
|
async function cleanupTempDir(dir, identity, onCleanupError) {
|
|
83
84
|
try {
|
|
84
|
-
|
|
85
|
+
let current;
|
|
86
|
+
try {
|
|
87
|
+
current = fsSync.lstatSync(dir, { bigint: true });
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
85
90
|
if (isNodeErrorWithCode(error, "ENOENT")) {
|
|
86
|
-
return
|
|
91
|
+
return;
|
|
87
92
|
}
|
|
88
93
|
throw error;
|
|
89
|
-
}
|
|
94
|
+
}
|
|
90
95
|
if (!current || !sameFileIdentityForCleanup(current, identity)) {
|
|
91
96
|
return;
|
|
92
97
|
}
|
|
@@ -107,7 +112,7 @@ export async function tempFile(params) {
|
|
|
107
112
|
const dir = await fs.mkdtemp(path.join(rootDir, prefix));
|
|
108
113
|
// Windows file indexes can exceed Number.MAX_SAFE_INTEGER. Cleanup receipts
|
|
109
114
|
// must retain the exact identity or adjacent directories can compare equal.
|
|
110
|
-
const identity =
|
|
115
|
+
const identity = fsSync.lstatSync(dir, { bigint: true });
|
|
111
116
|
const unregisterTempDir = registerTempPathForExit(dir, { recursive: true, identity });
|
|
112
117
|
const file = (fileName) => path.join(dir, sanitizeTempFileName(fileName ?? params.fileName ?? "download.bin"));
|
|
113
118
|
const cleanup = async () => {
|
package/dist/timing.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function scheduleTimeout(callback: () => void, ms: number): () => void;
|
|
1
2
|
export declare function sleep(ms: number): Promise<void>;
|
|
2
3
|
export declare function sleepSync(ms: number): void;
|
|
3
4
|
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, labelOrOptions?: string | {
|
package/dist/timing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../src/timing.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,IAAI,CAgB5E;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAK1C;AAED,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,GAAE,MAAM,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,KAAK,CAAA;CAErF,GACA,OAAO,CAAC,CAAC,CAAC,CA2BZ"}
|
package/dist/timing.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
+
const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
2
|
+
export function scheduleTimeout(callback, ms) {
|
|
3
|
+
let timer;
|
|
4
|
+
if (Number.isFinite(ms) && ms > MAX_TIMER_DELAY_MS) {
|
|
5
|
+
const startedAt = performance.now();
|
|
6
|
+
const tick = () => {
|
|
7
|
+
const remaining = ms - (performance.now() - startedAt);
|
|
8
|
+
if (remaining <= 0)
|
|
9
|
+
callback();
|
|
10
|
+
else
|
|
11
|
+
timer = setTimeout(tick, Math.min(remaining, MAX_TIMER_DELAY_MS));
|
|
12
|
+
};
|
|
13
|
+
// Node clamps overflowing delays to 1 ms. Rearm bounded timers against a
|
|
14
|
+
// monotonic clock so clock changes and event-loop stalls cannot shorten them.
|
|
15
|
+
timer = setTimeout(tick, MAX_TIMER_DELAY_MS);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
timer = setTimeout(callback, ms);
|
|
19
|
+
}
|
|
20
|
+
return () => clearTimeout(timer);
|
|
21
|
+
}
|
|
1
22
|
export function sleep(ms) {
|
|
2
|
-
return new Promise((resolve) =>
|
|
23
|
+
return new Promise((resolve) => { scheduleTimeout(resolve, ms); });
|
|
3
24
|
}
|
|
4
25
|
export function sleepSync(ms) {
|
|
5
26
|
if (ms <= 0) {
|
|
@@ -16,12 +37,12 @@ export async function withTimeout(promise, timeoutMs, labelOrOptions = {
|
|
|
16
37
|
const options = typeof labelOrOptions === "string" ? { label: labelOrOptions } : labelOrOptions;
|
|
17
38
|
const createError = options.createError ??
|
|
18
39
|
(() => new Error(options.message ?? `${options.label ?? "operation"} timed out after ${timeoutMs}ms`));
|
|
19
|
-
let
|
|
40
|
+
let cancelTimeout;
|
|
20
41
|
try {
|
|
21
42
|
return await Promise.race([
|
|
22
43
|
promise,
|
|
23
44
|
new Promise((_, reject) => {
|
|
24
|
-
|
|
45
|
+
cancelTimeout = scheduleTimeout(() => {
|
|
25
46
|
try {
|
|
26
47
|
reject(createError());
|
|
27
48
|
}
|
|
@@ -33,8 +54,6 @@ export async function withTimeout(promise, timeoutMs, labelOrOptions = {
|
|
|
33
54
|
]);
|
|
34
55
|
}
|
|
35
56
|
finally {
|
|
36
|
-
|
|
37
|
-
clearTimeout(timeoutId);
|
|
38
|
-
}
|
|
57
|
+
cancelTimeout?.();
|
|
39
58
|
}
|
|
40
59
|
}
|
package/dist/trash.js
CHANGED
|
@@ -18,7 +18,7 @@ function isTrashDestinationCollision(error) {
|
|
|
18
18
|
return Boolean(code && TRASH_DESTINATION_COLLISION_CODES.has(code));
|
|
19
19
|
}
|
|
20
20
|
function isSameOrChildPath(candidate, parent) {
|
|
21
|
-
return candidate === parent || candidate.startsWith(`${parent}${path.sep}`);
|
|
21
|
+
return candidate === parent || candidate.startsWith(parent.endsWith(path.sep) ? parent : `${parent}${path.sep}`);
|
|
22
22
|
}
|
|
23
23
|
function resolveAllowedTrashRoots(allowedRoots) {
|
|
24
24
|
const roots = [...(allowedRoots ?? [os.homedir(), os.tmpdir()])].flatMap((root) => {
|
|
@@ -107,7 +107,7 @@ function resolveContainedPath(root, leaf) {
|
|
|
107
107
|
return resolvedPath;
|
|
108
108
|
}
|
|
109
109
|
function reserveTrashDestination(trashDir, base, timestamp) {
|
|
110
|
-
const containerPrefix = resolveContainedPath(trashDir,
|
|
110
|
+
const containerPrefix = resolveContainedPath(trashDir, `.fs-safe-trash-${timestamp}-`);
|
|
111
111
|
const container = fs.mkdtempSync(containerPrefix);
|
|
112
112
|
const resolvedContainer = path.resolve(container);
|
|
113
113
|
const resolvedTrashDir = path.resolve(trashDir);
|
package/dist/walk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAI7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAI7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;AAgFF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,+BAA+B,CAyDjC;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,+BAA+B,CAAC,CAyD1C"}
|
package/dist/walk.js
CHANGED
|
@@ -27,7 +27,7 @@ function shouldStop(result, options) {
|
|
|
27
27
|
return options.maxEntries !== undefined && result.scannedEntryCount >= Math.max(0, options.maxEntries);
|
|
28
28
|
}
|
|
29
29
|
function buildEntry(params) {
|
|
30
|
-
const fullPath =
|
|
30
|
+
const fullPath = params.fullPath;
|
|
31
31
|
const relativePath = path.relative(params.rootDir, fullPath) || params.dirent.name;
|
|
32
32
|
return {
|
|
33
33
|
name: params.dirent.name,
|
|
@@ -47,7 +47,7 @@ function recordFailedDir(result, root, dir, depth, error) {
|
|
|
47
47
|
error,
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
function
|
|
50
|
+
function resolveKind(fullPath, dirent, symlinks) {
|
|
51
51
|
const kind = kindForDirent(dirent);
|
|
52
52
|
if (kind !== "symlink")
|
|
53
53
|
return kind;
|
|
@@ -67,26 +67,6 @@ function resolveSyncKind(fullPath, dirent, symlinks) {
|
|
|
67
67
|
}
|
|
68
68
|
return "other";
|
|
69
69
|
}
|
|
70
|
-
async function resolveAsyncKind(fullPath, dirent, symlinks) {
|
|
71
|
-
const kind = kindForDirent(dirent);
|
|
72
|
-
if (kind !== "symlink")
|
|
73
|
-
return kind;
|
|
74
|
-
if (symlinks === "skip")
|
|
75
|
-
return null;
|
|
76
|
-
if (symlinks === "include")
|
|
77
|
-
return "symlink";
|
|
78
|
-
try {
|
|
79
|
-
const stat = await fs.stat(fullPath);
|
|
80
|
-
if (stat.isDirectory())
|
|
81
|
-
return "directory";
|
|
82
|
-
if (stat.isFile())
|
|
83
|
-
return "file";
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
return "other";
|
|
89
|
-
}
|
|
90
70
|
export function walkDirectorySync(rootDir, options = {}) {
|
|
91
71
|
validateWalkOptions(options);
|
|
92
72
|
const root = path.resolve(rootDir);
|
|
@@ -127,10 +107,10 @@ export function walkDirectorySync(rootDir, options = {}) {
|
|
|
127
107
|
}
|
|
128
108
|
result.scannedEntryCount += 1;
|
|
129
109
|
const fullPath = path.join(dir, dirent.name);
|
|
130
|
-
const kind =
|
|
110
|
+
const kind = resolveKind(fullPath, dirent, symlinks);
|
|
131
111
|
if (!kind)
|
|
132
112
|
continue;
|
|
133
|
-
const entry = buildEntry({ rootDir: root,
|
|
113
|
+
const entry = buildEntry({ rootDir: root, fullPath, dirent, depth, kind });
|
|
134
114
|
if (options.include?.(entry) ?? true) {
|
|
135
115
|
result.entries.push(entry);
|
|
136
116
|
}
|
|
@@ -162,7 +142,7 @@ export async function walkDirectory(rootDir, options = {}) {
|
|
|
162
142
|
return;
|
|
163
143
|
let realDir;
|
|
164
144
|
try {
|
|
165
|
-
realDir =
|
|
145
|
+
realDir = fsSync.realpathSync.native(dir);
|
|
166
146
|
}
|
|
167
147
|
catch (error) {
|
|
168
148
|
recordFailedDir(result, root, dir, depth, error);
|
|
@@ -186,10 +166,10 @@ export async function walkDirectory(rootDir, options = {}) {
|
|
|
186
166
|
}
|
|
187
167
|
result.scannedEntryCount += 1;
|
|
188
168
|
const fullPath = path.join(dir, dirent.name);
|
|
189
|
-
const kind =
|
|
169
|
+
const kind = resolveKind(fullPath, dirent, symlinks);
|
|
190
170
|
if (!kind)
|
|
191
171
|
continue;
|
|
192
|
-
const entry = buildEntry({ rootDir: root,
|
|
172
|
+
const entry = buildEntry({ rootDir: root, fullPath, dirent, depth, kind });
|
|
193
173
|
if (options.include?.(entry) ?? true) {
|
|
194
174
|
result.entries.push(entry);
|
|
195
175
|
}
|
package/dist/windows-owner.d.ts
CHANGED
|
@@ -6,24 +6,21 @@ export type WindowsOwnerExec = (command: string, args: string[]) => Promise<{
|
|
|
6
6
|
export type WindowsOwnerSummary = {
|
|
7
7
|
sid?: string;
|
|
8
8
|
currentUserSid?: string;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
daclPresent?: boolean;
|
|
10
|
+
aces?: WindowsOwnerAce[];
|
|
11
|
+
aclError?: string;
|
|
11
12
|
remote?: boolean;
|
|
12
13
|
trusted?: boolean;
|
|
13
14
|
error?: string;
|
|
14
15
|
errorDetail?: PermissionCommandFailure;
|
|
15
16
|
errorCause?: unknown;
|
|
16
17
|
};
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
export declare function resolveWindowsCurrentUserSid(params: {
|
|
24
|
-
env?: NodeJS.ProcessEnv;
|
|
25
|
-
exec: WindowsOwnerExec;
|
|
26
|
-
}): Promise<string | null>;
|
|
18
|
+
export type WindowsOwnerAce = {
|
|
19
|
+
sid: string;
|
|
20
|
+
mask: number;
|
|
21
|
+
deny: boolean;
|
|
22
|
+
inheritOnly: boolean;
|
|
23
|
+
};
|
|
27
24
|
export declare function inspectWindowsOwner(params: {
|
|
28
25
|
targetPath: string;
|
|
29
26
|
env?: NodeJS.ProcessEnv;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"windows-owner.d.ts","sourceRoot":"","sources":["../src/windows-owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,
|
|
1
|
+
{"version":3,"file":"windows-owner.d.ts","sourceRoot":"","sources":["../src/windows-owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,wBAAwB,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAqDF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,IAAI,EAAE,gBAAgB,CAAC;CACxB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+C/B"}
|
package/dist/windows-owner.js
CHANGED
|
@@ -23,65 +23,28 @@ function windowsOwnerQueryCommand(targetPath) {
|
|
|
23
23
|
"$driveRoot=if($extendedDrive){$p.Substring(4,3)}else{$root}",
|
|
24
24
|
"$namespacePath=$p.StartsWith('\\\\')",
|
|
25
25
|
"$remote=($namespacePath -and -not $extendedDrive) -or ([IO.DriveInfo]::new($driveRoot).DriveType -eq [IO.DriveType]::Network)",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
26
|
+
// Emit only ASCII SID and numeric facts: console encodings can replace both
|
|
27
|
+
// Unicode pathnames and account names before JavaScript receives the bytes.
|
|
28
|
+
"$raw=[System.Security.AccessControl.RawSecurityDescriptor]::new($acl.GetSecurityDescriptorBinaryForm(),0)",
|
|
29
|
+
"$dacl=$raw.DiscretionaryAcl;$complete=$true",
|
|
30
|
+
"$aces=@(foreach($ace in $dacl){if($ace -isnot [System.Security.AccessControl.CommonAce] -or $ace.IsCallback -or [int]$ace.AceType -notin @(0,1)){$complete=$false;continue};@{sid=$ace.SecurityIdentifier.Value;mask=([long]$ace.AccessMask -band 4294967295);deny=([int]$ace.AceType -eq 1);inheritOnly=([int]$ace.AceFlags -band 8) -ne 0}})",
|
|
31
|
+
"@{ownerSid=$ownerSid;currentUserSid=$currentSid;daclPresent=($null -ne $dacl);aces=$aces;complete=$complete;remote=$remote}|ConvertTo-Json -Depth 4 -Compress",
|
|
29
32
|
].join(";");
|
|
30
33
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"$ErrorActionPreference='Stop'",
|
|
35
|
-
`$names=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encodedPrincipals}'))|ConvertFrom-Json`,
|
|
36
|
-
"$rows=@($names|ForEach-Object {@{name=$_;sid=(New-Object System.Security.Principal.NTAccount($_)).Translate([System.Security.Principal.SecurityIdentifier]).Value}})",
|
|
37
|
-
"ConvertTo-Json -InputObject $rows -Compress",
|
|
38
|
-
].join(";");
|
|
39
|
-
}
|
|
40
|
-
function parsePrincipalSidRows(value) {
|
|
41
|
-
const rows = Array.isArray(value) ? value : value ? [value] : [];
|
|
42
|
-
const result = {};
|
|
43
|
-
for (const row of rows) {
|
|
44
|
-
if (!row || typeof row !== "object") {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const name = "name" in row && typeof row.name === "string" ? row.name.trim() : "";
|
|
48
|
-
const sid = "sid" in row && typeof row.sid === "string" ? normalizeSid(row.sid) : "";
|
|
49
|
-
if (name && SID_RE.test(sid)) {
|
|
50
|
-
result[name.toLowerCase()] = sid;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
export async function resolveWindowsPrincipalSids(params) {
|
|
56
|
-
const principals = [...new Set(params.principals.map((value) => value.trim()).filter(Boolean))];
|
|
57
|
-
const known = Object.fromEntries(Object.entries(params.known ?? {}).map(([name, sid]) => [name.toLowerCase(), normalizeSid(sid)]));
|
|
58
|
-
const unresolved = principals.filter((principal) => !known[principal.toLowerCase()]);
|
|
59
|
-
if (unresolved.length === 0) {
|
|
60
|
-
return known;
|
|
34
|
+
function parseWindowsAclFacts(parsed) {
|
|
35
|
+
if (parsed.complete !== true || typeof parsed.daclPresent !== "boolean" || !Array.isArray(parsed.aces)) {
|
|
36
|
+
return { aclError: "Windows ACL query returned incomplete descriptor data" };
|
|
61
37
|
}
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const resolved = { ...known, ...parsePrincipalSidRows(JSON.parse(stdout.trim())) };
|
|
71
|
-
if (principals.some((principal) => !resolved[principal.toLowerCase()])) {
|
|
72
|
-
throw new Error("Windows ACL principal translation returned incomplete SID data");
|
|
73
|
-
}
|
|
74
|
-
return resolved;
|
|
75
|
-
}
|
|
76
|
-
export async function resolveWindowsCurrentUserSid(params) {
|
|
77
|
-
try {
|
|
78
|
-
const { stdout, stderr } = await params.exec(resolveWindowsSystemCommand("whoami.exe", params.env), ["/user", "/fo", "csv", "/nh"]);
|
|
79
|
-
const match = `${stdout}\n${stderr}`.match(/\*?S-\d+-\d+(?:-\d+)+/i);
|
|
80
|
-
return match ? normalizeSid(match[0]) : null;
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return null;
|
|
38
|
+
const aces = [];
|
|
39
|
+
for (const row of parsed.aces) {
|
|
40
|
+
if (!row || typeof row !== "object" || typeof row.sid !== "string" || !SID_RE.test(row.sid) ||
|
|
41
|
+
typeof row.mask !== "number" || !Number.isInteger(row.mask) || row.mask < 0 || row.mask > 0xffff_ffff ||
|
|
42
|
+
typeof row.deny !== "boolean" || typeof row.inheritOnly !== "boolean") {
|
|
43
|
+
return { aclError: "Windows ACL query returned invalid access-rule data" };
|
|
44
|
+
}
|
|
45
|
+
aces.push({ sid: normalizeSid(row.sid), mask: row.mask, deny: row.deny, inheritOnly: row.inheritOnly });
|
|
84
46
|
}
|
|
47
|
+
return { daclPresent: parsed.daclPresent, aces };
|
|
85
48
|
}
|
|
86
49
|
export async function inspectWindowsOwner(params) {
|
|
87
50
|
let command = "";
|
|
@@ -96,7 +59,11 @@ export async function inspectWindowsOwner(params) {
|
|
|
96
59
|
"-EncodedCommand",
|
|
97
60
|
encodePowerShellCommand(windowsOwnerQueryCommand(params.targetPath)),
|
|
98
61
|
]);
|
|
99
|
-
const
|
|
62
|
+
const value = JSON.parse(stdout.trim());
|
|
63
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
64
|
+
return { error: "Windows owner query returned invalid SID data" };
|
|
65
|
+
}
|
|
66
|
+
const parsed = value;
|
|
100
67
|
const ownerSid = typeof parsed.ownerSid === "string" && SID_RE.test(parsed.ownerSid)
|
|
101
68
|
? normalizeSid(parsed.ownerSid)
|
|
102
69
|
: undefined;
|
|
@@ -110,8 +77,7 @@ export async function inspectWindowsOwner(params) {
|
|
|
110
77
|
return {
|
|
111
78
|
sid: ownerSid,
|
|
112
79
|
currentUserSid,
|
|
113
|
-
|
|
114
|
-
principalTranslationFailed: parsed.principalTranslationFailed === true,
|
|
80
|
+
...parseWindowsAclFacts(parsed),
|
|
115
81
|
remote,
|
|
116
82
|
trusted: !remote && (ownerSid === currentUserSid || TRUSTED_OWNER_SIDS.has(ownerSid)),
|
|
117
83
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
export declare function writeAllToFile(target: FileHandle | number, data: string | Uint8Array, options?: {
|
|
3
|
+
encoding?: BufferEncoding;
|
|
4
|
+
assertBeforeMutation?: () => void;
|
|
5
|
+
}): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=write-file-handle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-file-handle.d.ts","sourceRoot":"","sources":["../src/write-file-handle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,IAAI,EAAE,MAAM,GAAG,UAAU,EACzB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GAC7E,OAAO,CAAC,IAAI,CAAC,CAmBf"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
const WRITE_CHUNK_BYTES = 512 * 1024;
|
|
4
|
+
export async function writeAllToFile(target, data, options = {}) {
|
|
5
|
+
const buffer = typeof data === "string" ? Buffer.from(data, options.encoding ?? "utf8") : data;
|
|
6
|
+
let offset = 0;
|
|
7
|
+
while (offset < buffer.byteLength) {
|
|
8
|
+
const length = Math.min(WRITE_CHUNK_BYTES, buffer.byteLength - offset);
|
|
9
|
+
options.assertBeforeMutation?.();
|
|
10
|
+
const written = typeof target === "number"
|
|
11
|
+
? await new Promise((resolve, reject) => {
|
|
12
|
+
fs.write(target, buffer, offset, length, null, (error, bytesWritten) => {
|
|
13
|
+
if (error)
|
|
14
|
+
reject(error);
|
|
15
|
+
else
|
|
16
|
+
resolve(bytesWritten);
|
|
17
|
+
});
|
|
18
|
+
})
|
|
19
|
+
: (await target.write(buffer, offset, length, null)).bytesWritten;
|
|
20
|
+
if (written <= 0) {
|
|
21
|
+
throw new FsSafeError("helper-failed", "file write made no progress");
|
|
22
|
+
}
|
|
23
|
+
offset += written;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-open-flags.d.ts","sourceRoot":"","sources":["../src/write-open-flags.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"write-open-flags.d.ts","sourceRoot":"","sources":["../src/write-open-flags.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAG7B,wBAAgB,2BAA2B,CACzC,SAAS,GAAE,OAAO,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAoB,GACjF,MAAM,CAIR;AAUD,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAOT"}
|
package/dist/write-open-flags.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
2
|
import { hasNodeErrorCode } from "./path.js";
|
|
4
3
|
export function resolveNonblockingWriteFlag(constants = fsSync.constants) {
|
|
5
4
|
return process.platform !== "win32" && typeof constants.O_NONBLOCK === "number"
|
|
@@ -17,7 +16,7 @@ export async function isNonRegularWriteOpenError(error, filePath, flags) {
|
|
|
17
16
|
if (!isNonblockingWriteEnxio(error, flags))
|
|
18
17
|
return false;
|
|
19
18
|
try {
|
|
20
|
-
return !
|
|
19
|
+
return !fsSync.lstatSync(filePath).isFile();
|
|
21
20
|
}
|
|
22
21
|
catch {
|
|
23
22
|
return false;
|
package/docs/advanced.md
CHANGED
|
@@ -65,7 +65,8 @@ Operational filesystem failures such as permissions or I/O errors are rethrown.
|
|
|
65
65
|
| Export | Page | Notes |
|
|
66
66
|
|---|---|---|
|
|
67
67
|
| `readFileDescriptorBounded`, `readFileDescriptorBoundedSync`, `readFileHandleBounded` | – | Incremental whole-file reads for already-open descriptors/handles. They consume at most `maxBytes + 1`, do not close the input, and throw `FsSafeError("too-large")` on overflow. |
|
|
68
|
-
| `
|
|
68
|
+
| `readFileWindowFully`, `readFileWindowFullySync`, `ReadFileWindowOptions` | [positional-read.md](positional-read.md) | Fill a caller-owned buffer at an explicit file position, completing short reads and returning the EOF count without moving or closing the descriptor. |
|
|
69
|
+
| `openRootFile`, `openRootFileSync`, `canUseRootFileOpen`, `matchRootFileOpenFailure`, related types | – | Low-level root-bounded open; rejects every symlink component by default, with `symlinks: "follow-parents-within-root"` for contained parent aliases or `"follow-within-root"` for final links too. |
|
|
69
70
|
| `appendRegularFile`, `appendRegularFileSync`, `readRegularFile`, `readRegularFileSync`, `statRegularFile`, `statRegularFileSync`, `resolveRegularFileAppendFlags`, `AppendRegularFileOptions`, `RegularFileStatResult` | [regular-file.md](regular-file.md) | Type-checked regular-file I/O. |
|
|
70
71
|
| `sameFileIdentity`, `FileIdentityStat` | – | Compare two stats for same-inode equality. |
|
|
71
72
|
| `pathExists`, `pathExistsSync` | – | Boolean existence check that does not throw on `ENOENT`. |
|
|
@@ -79,6 +80,19 @@ receipts remain numeric. Custom `ioFs` adapters must honor `{ bigint: true }` fo
|
|
|
79
80
|
Windows identities receive one re-inspection without reopening, then fail
|
|
80
81
|
validation if still unknown.
|
|
81
82
|
|
|
83
|
+
The explicit `symlinks` policy takes precedence over the existing `rejectSymlinks`
|
|
84
|
+
boolean. Without `symlinks`, `rejectSymlinks: false` retains its existing behavior
|
|
85
|
+
of following contained links, and omission still rejects all symlink components.
|
|
86
|
+
|
|
87
|
+
The bounded descriptor helpers cap their initial speculative allocation at
|
|
88
|
+
16 MiB plus the overflow-probe byte, even when a file reports a much larger
|
|
89
|
+
size. Regular files up to that size can return from one read without copying
|
|
90
|
+
chunks; larger files and short reads continue incrementally under the same
|
|
91
|
+
byte limit. Continuation buffers grow only after filling with actual bytes,
|
|
92
|
+
up to the byte budget plus its probe; file-size hints cannot force that growth.
|
|
93
|
+
Unknown-size inputs start with at most 64 KiB. This avoids per-chunk copies and
|
|
94
|
+
a final concatenation when a file exceeds the initial allocation.
|
|
95
|
+
|
|
82
96
|
The bounded descriptor helpers start at the descriptor's current offset and
|
|
83
97
|
leave ownership with the caller. They are intended for the second half of a
|
|
84
98
|
safe read: first open and validate the path using the boundary appropriate to
|
|
@@ -117,7 +131,7 @@ component is followed by another segment, both helpers throw
|
|
|
117
131
|
|---|---|---|
|
|
118
132
|
| `safeDirName`, `safePathSegmentHashed`, `resolveSafeInstallDir`, `assertCanonicalPathWithinBase` | [install-path.md](install-path.md) | Build install-target directories from caller-supplied identifiers. |
|
|
119
133
|
| `sanitizeUntrustedFileName` | [filename.md](filename.md) | Coerce an untrusted string into a safe filename. |
|
|
120
|
-
| `resolveHomeRelativePath` | – | Expand
|
|
134
|
+
| `resolveHomeRelativePath` | – | Expand a leading `~` before resolving `.` and `..`; tildes inside relative paths stay literal. |
|
|
121
135
|
|
|
122
136
|
### Temp targets and sibling-temp writes
|
|
123
137
|
|
|
@@ -139,7 +153,7 @@ component is followed by another segment, both helpers throw
|
|
|
139
153
|
| Export | Page | Notes |
|
|
140
154
|
|---|---|---|
|
|
141
155
|
| `createAsyncLock` | – | In-process async lock (separate from cross-process file locks). |
|
|
142
|
-
| `withTimeout` | [timing.md](timing.md) | Wrap a promise with a timeout that raises `
|
|
156
|
+
| `withTimeout` | [timing.md](timing.md) | Wrap a promise with a timeout that raises `Error` by default, or an error supplied by `createError`. |
|
|
143
157
|
| `movePathToTrash`, `MovePathToTrashOptions` | – | Best-effort move to the platform trash. |
|
|
144
158
|
|
|
145
159
|
## Stability
|
|
@@ -149,5 +163,5 @@ Items in this surface can change shape between minor versions if a higher-level
|
|
|
149
163
|
## Related pages
|
|
150
164
|
|
|
151
165
|
- [Root API](root.md) — built on top of these helpers.
|
|
152
|
-
- [Errors](errors.md) —
|
|
166
|
+
- [Errors](errors.md) — shared filesystem error codes; generic timing helpers retain their documented error types.
|
|
153
167
|
- [Security model](security-model.md) — what the underlying boundary checks promise.
|