@openclaw/fs-safe 0.9.0 → 0.11.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 +134 -0
- package/LICENSE +1 -0
- package/README.md +52 -4
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +3 -2
- package/dist/advanced.d.ts +5 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +5 -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 +6 -6
- package/dist/archive-durability.d.ts.map +1 -1
- package/dist/archive-durability.js +1 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +4 -5
- package/dist/archive-gzip-tail.d.ts +3 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +23 -3
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +4 -2
- package/dist/archive-merge.d.ts +5 -1
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +16 -13
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -6
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +83 -74
- package/dist/archive-staging.d.ts +6 -3
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +42 -22
- 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 +23 -10
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +18 -14
- package/dist/archive-zip-admission.d.ts +1 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +48 -12
- package/dist/archive-zip-loader.d.ts +6 -0
- package/dist/archive-zip-loader.d.ts.map +1 -0
- package/dist/archive-zip-loader.js +38 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +13 -8
- package/dist/archive-zip-preflight.d.ts +2 -3
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +2 -34
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +12 -10
- package/dist/bounded-read.d.ts +12 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +82 -45
- 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 +22 -0
- package/dist/copy-file-input.d.ts.map +1 -0
- package/dist/copy-file-input.js +69 -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 +10 -1
- package/dist/copy-publication.d.ts.map +1 -1
- package/dist/copy-publication.js +27 -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 +222 -0
- package/dist/copy.d.ts +16 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +125 -0
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +5 -4
- package/dist/directory-guard.d.ts +11 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +53 -11
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +1 -1
- package/dist/error-detail.d.ts.map +1 -1
- package/dist/error-detail.js +4 -1
- package/dist/file-handle-transfer.d.ts +14 -0
- package/dist/file-handle-transfer.d.ts.map +1 -0
- package/dist/file-handle-transfer.js +64 -0
- package/dist/file-hash.d.ts +9 -2
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +135 -39
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -4
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +7 -5
- package/dist/file-store-path.d.ts +3 -0
- package/dist/file-store-path.d.ts.map +1 -0
- package/dist/file-store-path.js +27 -0
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +6 -4
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +56 -44
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +2 -18
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +4 -13
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +4 -2
- package/dist/guarded-mutation.d.ts +2 -0
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +8 -4
- package/dist/guest-dispatch-python.d.ts +2 -0
- package/dist/guest-dispatch-python.d.ts.map +1 -0
- package/dist/guest-dispatch-python.js +117 -0
- package/dist/guest-native-python.d.ts +4 -0
- package/dist/guest-native-python.d.ts.map +1 -0
- package/dist/guest-native-python.js +135 -0
- package/dist/guest.d.ts +9 -0
- package/dist/guest.d.ts.map +1 -0
- package/dist/guest.js +413 -0
- 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 +5 -8
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +19 -18
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +2 -1
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +24 -25
- 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 +56 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +30 -34
- package/dist/mutation-authority.d.ts +9 -0
- package/dist/mutation-authority.d.ts.map +1 -0
- package/dist/mutation-authority.js +36 -0
- package/dist/native-binding.d.ts +29 -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 +19 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +3 -1
- 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-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +5 -4
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +2 -0
- package/dist/overwrite-file-handle.d.ts +8 -0
- package/dist/overwrite-file-handle.d.ts.map +1 -0
- package/dist/overwrite-file-handle.js +42 -0
- package/dist/path-case.d.ts +7 -0
- package/dist/path-case.d.ts.map +1 -0
- package/dist/path-case.js +136 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +5 -2
- package/dist/permissions-windows.d.ts +1 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +85 -53
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +3 -1
- package/dist/pinned-operation.js +1 -1
- package/dist/pinned-write.d.ts +7 -3
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +51 -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 +6 -4
- 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 +6 -5
- package/dist/realpath.d.ts +4 -0
- package/dist/realpath.d.ts.map +1 -0
- package/dist/realpath.js +43 -0
- package/dist/recursive-mkdir-path.d.ts +3 -0
- package/dist/recursive-mkdir-path.d.ts.map +1 -0
- package/dist/recursive-mkdir-path.js +8 -0
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +2 -1
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +23 -31
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +7 -12
- package/dist/replace-file-mode.d.ts +3 -0
- package/dist/replace-file-mode.d.ts.map +1 -0
- package/dist/replace-file-mode.js +10 -0
- 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 +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +14 -8
- package/dist/root-context.d.ts +3 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +53 -28
- package/dist/root-create-input.d.ts +10 -0
- package/dist/root-create-input.d.ts.map +1 -0
- package/dist/root-create-input.js +80 -0
- package/dist/root-directory-list.d.ts +26 -0
- package/dist/root-directory-list.d.ts.map +1 -0
- package/dist/root-directory-list.js +219 -0
- package/dist/root-entries.d.ts +11 -0
- package/dist/root-entries.d.ts.map +1 -0
- package/dist/root-entries.js +61 -0
- package/dist/root-errors.d.ts +6 -5
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +16 -12
- 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 +17 -51
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +358 -234
- package/dist/root-options.d.ts +77 -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 +59 -3
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +3 -2
- 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-paths.d.ts.map +1 -1
- package/dist/root-paths.js +13 -9
- package/dist/root-remove.d.ts +5 -0
- package/dist/root-remove.d.ts.map +1 -0
- package/dist/root-remove.js +286 -0
- package/dist/root-symlink-policy.d.ts +14 -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 +3 -5
- package/dist/root.d.ts +4 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -1
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +2 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +25 -8
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +2 -1
- package/dist/sibling-staged-file.d.ts +1 -0
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +42 -8
- package/dist/sibling-temp.d.ts +2 -0
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +6 -4
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +17 -5
- package/dist/sidecar-lock-policy.d.ts +2 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +20 -1
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +4 -3
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +3 -2
- package/dist/temp-target.d.ts +14 -12
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +12 -6
- package/dist/timing.d.ts +1 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +25 -6
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +9 -7
- package/dist/unicode-path.d.ts +3 -0
- package/dist/unicode-path.d.ts.map +1 -0
- package/dist/unicode-path.js +13 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +9 -28
- 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 +7 -0
- package/dist/write-file-handle.d.ts.map +1 -0
- package/dist/write-file-handle.js +26 -0
- package/docs/advanced.md +22 -4
- package/docs/archive.md +44 -9
- package/docs/atomic.md +24 -1
- package/docs/config.md +1 -0
- package/docs/contributing.md +36 -1
- package/docs/copy.md +155 -0
- package/docs/directory-identity.md +85 -0
- package/docs/durability.md +53 -3
- package/docs/entries.md +109 -0
- package/docs/errors.md +4 -4
- package/docs/file-store.md +15 -0
- package/docs/guest.md +141 -0
- package/docs/in-place-write.md +81 -0
- package/docs/index.md +2 -0
- package/docs/install.md +31 -0
- package/docs/local-roots.md +8 -1
- package/docs/native-helper.md +10 -3
- package/docs/native.md +18 -1
- package/docs/output.md +32 -6
- package/docs/path-case.md +64 -0
- package/docs/path-scope.md +1 -1
- package/docs/permissions.md +29 -10
- package/docs/positional-read.md +63 -0
- package/docs/public-api.md +31 -2
- package/docs/root.md +196 -6
- package/docs/secure-file.md +2 -0
- package/docs/security-model.md +14 -0
- package/docs/sidecar-lock.md +12 -3
- package/docs/temp.md +35 -6
- package/docs/timing.md +2 -0
- package/docs/types.md +19 -12
- package/docs/walk.md +54 -5
- package/docs/writing.md +173 -5
- package/package.json +19 -8
|
@@ -5,11 +5,14 @@ import { canonicalPathFromExistingAncestor } from "./absolute-path.js";
|
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { fileObservation } from "./file-observation.js";
|
|
7
7
|
import { readFileHandleBounded } from "./bounded-read.js";
|
|
8
|
+
import { realpathSync } from "./realpath.js";
|
|
9
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
8
10
|
import { openSidecarRoot } from "./sidecar-lock-root.js";
|
|
9
11
|
import { createNativeExclusiveFile } from "./native-operations.js";
|
|
10
|
-
import { computeSidecarLockDelayMs, defaultSidecarLockShouldReclaim, isTransientLockFileDenial, maxTransientLockDenials, validateSidecarLockRetryOptions, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
|
|
12
|
+
import { computeSidecarLockDelayMs, defaultSidecarLockShouldReclaim, isTransientLockFileDenial, maxTransientLockDenials, validateSidecarLockStaleMs, validateSidecarLockRetryOptions, validateSidecarLockCompromiseCheckIntervalMs, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
|
|
11
13
|
import { readSidecarLockRawSnapshot, parseSidecarLockSnapshot, relativeSidecarLockPath, releaseSidecarReclaimGuard, removeSidecarLockIfUnchanged, removeStaleSidecarLockIfAllowed, serializeSidecarLockPayload, sidecarLockSnapshotStillPresent, sidecarReclaimGuardExists, tryAcquireSidecarReclaimGuard, } from "./sidecar-lock-reclaim.js";
|
|
12
14
|
import { createSuppressedError } from "./suppressed-error.js";
|
|
15
|
+
import { sleep } from "./timing.js";
|
|
13
16
|
async function resolveNormalizedTargetPath(targetPath, lockRoot) {
|
|
14
17
|
const resolved = path.resolve(targetPath);
|
|
15
18
|
const dir = path.dirname(resolved);
|
|
@@ -21,9 +24,9 @@ async function resolveNormalizedTargetPath(targetPath, lockRoot) {
|
|
|
21
24
|
await lockRoot.resolve(".");
|
|
22
25
|
return path.join(parent, path.basename(resolved));
|
|
23
26
|
}
|
|
24
|
-
await fs.mkdir(dir, { recursive: true });
|
|
27
|
+
await fs.mkdir(recursiveMkdirPath(dir), { recursive: true });
|
|
25
28
|
try {
|
|
26
|
-
return path.join(
|
|
29
|
+
return path.join(realpathSync.native(dir), path.basename(resolved));
|
|
27
30
|
}
|
|
28
31
|
catch {
|
|
29
32
|
return resolved;
|
|
@@ -33,6 +36,8 @@ export async function acquireSidecarLock(options, context) {
|
|
|
33
36
|
const retry = options.retry ?? {};
|
|
34
37
|
validateSidecarLockRetryOptions(retry);
|
|
35
38
|
validateSidecarLockTimeoutMs(options.timeoutMs);
|
|
39
|
+
validateSidecarLockStaleMs(options.staleMs);
|
|
40
|
+
validateSidecarLockCompromiseCheckIntervalMs(options.compromiseCheckIntervalMs);
|
|
36
41
|
context.ensureExitCleanupRegistered();
|
|
37
42
|
const normalizedTargetPath = await resolveNormalizedTargetPath(options.targetPath, options.lockRoot);
|
|
38
43
|
const lockPath = options.lockPath ?? `${normalizedTargetPath}.lock`;
|
|
@@ -86,7 +91,7 @@ export async function acquireSidecarLock(options, context) {
|
|
|
86
91
|
: Math.max(0, options.timeoutMs - elapsed);
|
|
87
92
|
const delay = Math.min(computeSidecarLockDelayMs(retry, attempt), remaining);
|
|
88
93
|
attempt += 1;
|
|
89
|
-
await
|
|
94
|
+
await sleep(delay);
|
|
90
95
|
};
|
|
91
96
|
// Waiting can fail on the caller's own retry or deadline limits. Classifying
|
|
92
97
|
// a denial as contention must not cost them the original diagnosis, so hand
|
|
@@ -152,7 +157,7 @@ export async function acquireSidecarLock(options, context) {
|
|
|
152
157
|
try {
|
|
153
158
|
handle =
|
|
154
159
|
(await createNativeExclusiveFile(lockPath, 0o600)) ??
|
|
155
|
-
(await fs.open(lockPath, "wx"));
|
|
160
|
+
(await fs.open(lockPath, "wx", 0o600));
|
|
156
161
|
}
|
|
157
162
|
catch (createError) {
|
|
158
163
|
lockFileCreateDenied = isTransientLockFileDenial(createError, lockPath);
|
|
@@ -193,7 +198,11 @@ export async function acquireSidecarLock(options, context) {
|
|
|
193
198
|
const returnedHandle = context.handleForHeldLock(normalizedTargetPath, createdHeld);
|
|
194
199
|
const interval = options.compromiseCheckIntervalMs;
|
|
195
200
|
if (options.onCompromised && interval !== undefined && interval > 0) {
|
|
201
|
+
let compromiseCheckInFlight = false;
|
|
196
202
|
createdHeld.compromiseTimer = setInterval(() => {
|
|
203
|
+
if (compromiseCheckInFlight)
|
|
204
|
+
return;
|
|
205
|
+
compromiseCheckInFlight = true;
|
|
197
206
|
void returnedHandle
|
|
198
207
|
.verifyStillHeld()
|
|
199
208
|
.catch(() => false)
|
|
@@ -203,6 +212,9 @@ export async function acquireSidecarLock(options, context) {
|
|
|
203
212
|
createdHeld.compromiseTimer = undefined;
|
|
204
213
|
options.onCompromised?.({ lockPath, normalizedTargetPath });
|
|
205
214
|
}
|
|
215
|
+
})
|
|
216
|
+
.finally(() => {
|
|
217
|
+
compromiseCheckInFlight = false;
|
|
206
218
|
});
|
|
207
219
|
}, interval);
|
|
208
220
|
createdHeld.compromiseTimer.unref();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SidecarLockRetryOptions } from "./sidecar-lock-types.js";
|
|
2
2
|
export declare function validateSidecarLockRetryOptions(retry: SidecarLockRetryOptions): void;
|
|
3
3
|
export declare function validateSidecarLockTimeoutMs(timeoutMs: number | undefined): void;
|
|
4
|
+
export declare function validateSidecarLockStaleMs(staleMs: number | undefined): void;
|
|
5
|
+
export declare function validateSidecarLockCompromiseCheckIntervalMs(intervalMs: number | undefined): void;
|
|
4
6
|
export declare function computeSidecarLockDelayMs(retry: SidecarLockRetryOptions, attempt: number): number;
|
|
5
7
|
export declare const maxTransientLockDenials = 8;
|
|
6
8
|
export declare function isTransientLockFileDenial(error: unknown, lockPath: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar-lock-policy.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"sidecar-lock-policy.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAUvE,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAkBpF;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAO5E;AAED,wBAAgB,4CAA4C,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAOjG;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAUjG;AASD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGnF;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED,wBAAsB,+BAA+B,CAAC,MAAM,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
|
+
const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
2
3
|
function assertFiniteNonNegative(value, label) {
|
|
3
4
|
if (!Number.isFinite(value) || value < 0) {
|
|
4
5
|
throw new RangeError(`${label} must be a finite non-negative number`);
|
|
@@ -27,12 +28,30 @@ export function validateSidecarLockTimeoutMs(timeoutMs) {
|
|
|
27
28
|
return;
|
|
28
29
|
assertFiniteNonNegative(timeoutMs, "lock timeoutMs");
|
|
29
30
|
}
|
|
31
|
+
export function validateSidecarLockStaleMs(staleMs) {
|
|
32
|
+
// The lower-level sidecar manager historically treats an omitted threshold
|
|
33
|
+
// as disabling age-based reclamation. Its public type requires a number, but
|
|
34
|
+
// retain that fail-closed runtime behavior for JavaScript callers and older
|
|
35
|
+
// consumers.
|
|
36
|
+
if (staleMs === undefined || staleMs === Number.POSITIVE_INFINITY)
|
|
37
|
+
return;
|
|
38
|
+
assertFiniteNonNegative(staleMs, "lock staleMs");
|
|
39
|
+
}
|
|
40
|
+
export function validateSidecarLockCompromiseCheckIntervalMs(intervalMs) {
|
|
41
|
+
if (intervalMs === undefined || intervalMs === 0)
|
|
42
|
+
return;
|
|
43
|
+
if (!Number.isFinite(intervalMs) || intervalMs < 1 || intervalMs > MAX_TIMER_DELAY_MS) {
|
|
44
|
+
throw new RangeError(`lock compromiseCheckIntervalMs must be 0 or a finite number between 1 and ${MAX_TIMER_DELAY_MS}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
30
47
|
export function computeSidecarLockDelayMs(retry, attempt) {
|
|
31
48
|
validateSidecarLockRetryOptions(retry);
|
|
32
49
|
const minTimeout = retry.minTimeout ?? 50;
|
|
33
50
|
const maxTimeout = retry.maxTimeout ?? 1000;
|
|
34
51
|
const factor = retry.factor ?? 1;
|
|
35
|
-
|
|
52
|
+
// Zero times an overflowing power is NaN, which makes Atomics.wait unbounded.
|
|
53
|
+
const scaled = minTimeout === 0 ? 0 : minTimeout * factor ** attempt;
|
|
54
|
+
const base = Math.min(maxTimeout, Math.max(minTimeout, scaled));
|
|
36
55
|
const jitter = retry.randomize ? 1 + Math.random() : 1;
|
|
37
56
|
return Math.min(maxTimeout, Math.round(base * jitter));
|
|
38
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,KAAK,iBAAiB,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAExD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7C,OAAO,CAKT;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYvF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAQ7E;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,iBAAiB,CAAC;CAC5B,CA+BA"}
|
package/dist/staged-directory.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { realpathSync } from "./realpath.js";
|
|
4
5
|
export function exactIdentityMatches(expected, actual) {
|
|
5
6
|
return ["dev", "ino"].every((key) => {
|
|
6
7
|
const value = expected[key];
|
|
@@ -14,7 +15,7 @@ export function describeStagedDirectory(fd, pathname) {
|
|
|
14
15
|
}
|
|
15
16
|
const receipt = Object.freeze({
|
|
16
17
|
path: path.resolve(pathname),
|
|
17
|
-
realPath:
|
|
18
|
+
realPath: realpathSync(pathname),
|
|
18
19
|
identity: Object.freeze({ dev: identity.dev, ino: identity.ino }),
|
|
19
20
|
});
|
|
20
21
|
assertStagedDirectoryCurrent(receipt);
|
|
@@ -23,7 +24,7 @@ export function describeStagedDirectory(fd, pathname) {
|
|
|
23
24
|
export function assertStagedDirectoryCurrent(receipt) {
|
|
24
25
|
const current = fs.lstatSync(receipt.path, { bigint: true });
|
|
25
26
|
if (!current.isDirectory() || !exactIdentityMatches(receipt.identity, current) ||
|
|
26
|
-
|
|
27
|
+
realpathSync(receipt.path) !== receipt.realPath) {
|
|
27
28
|
throw new FsSafeError("path-mismatch", "staging directory pathname changed");
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -37,7 +38,7 @@ export function openStagedDirectory(directory) {
|
|
|
37
38
|
if (!before.isDirectory()) {
|
|
38
39
|
throw new FsSafeError("not-file", "staging parent must be a real directory");
|
|
39
40
|
}
|
|
40
|
-
if (expected && (!exactIdentityMatches(expected, before) ||
|
|
41
|
+
if (expected && (!exactIdentityMatches(expected, before) || realpathSync(pathname) !== expected.realPath)) {
|
|
41
42
|
throw new FsSafeError("path-mismatch", "stale staging directory receipt");
|
|
42
43
|
}
|
|
43
44
|
const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
@@ -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) {
|
package/dist/temp-target.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
1
2
|
export type TempFile = {
|
|
2
3
|
dir: string;
|
|
3
4
|
path: string;
|
|
@@ -5,6 +6,12 @@ export type TempFile = {
|
|
|
5
6
|
cleanup: () => Promise<void>;
|
|
6
7
|
[Symbol.asyncDispose](): Promise<void>;
|
|
7
8
|
};
|
|
9
|
+
type TempFileOptions = {
|
|
10
|
+
rootDir?: string;
|
|
11
|
+
prefix: string;
|
|
12
|
+
fileName?: string;
|
|
13
|
+
onCleanupError?: (error: unknown) => void;
|
|
14
|
+
};
|
|
8
15
|
export declare function sanitizeTempFileName(fileName: string): string;
|
|
9
16
|
export declare function buildRandomTempFilePath(params: {
|
|
10
17
|
rootDir?: string;
|
|
@@ -13,16 +20,11 @@ export declare function buildRandomTempFilePath(params: {
|
|
|
13
20
|
now?: number;
|
|
14
21
|
uuid?: string;
|
|
15
22
|
}): string;
|
|
16
|
-
export declare function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
rootDir?: string;
|
|
24
|
-
prefix: string;
|
|
25
|
-
fileName?: string;
|
|
26
|
-
onCleanupError?: (error: unknown) => void;
|
|
27
|
-
}, fn: (tmpPath: string) => Promise<T>): Promise<T>;
|
|
23
|
+
export declare function createOwnedTempFile(params: TempFileOptions): Promise<{
|
|
24
|
+
target: TempFile;
|
|
25
|
+
identity: Readonly<Pick<fsSync.BigIntStats, "dev" | "ino">>;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function tempFile(params: TempFileOptions): Promise<TempFile>;
|
|
28
|
+
export declare function withTempFile<T>(params: TempFileOptions, fn: (tmpPath: string) => Promise<T>): Promise<T>;
|
|
29
|
+
export {};
|
|
28
30
|
//# sourceMappingURL=temp-target.d.ts.map
|
|
@@ -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":"AACA,OAAO,MAAM,MAAM,SAAS,CAAC;AAQ7B,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;AAEF,KAAK,eAAe,GAAG;IACrB,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,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,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC;IAC1E,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;CAC7D,CAAC,CA2BD;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEzE;AAED,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE,eAAe,EACvB,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
package/dist/temp-target.js
CHANGED
|
@@ -106,7 +106,7 @@ async function cleanupTempDir(dir, identity, onCleanupError) {
|
|
|
106
106
|
function resolveTempRoot(rootDir) {
|
|
107
107
|
return path.resolve(rootDir ?? resolveSecureTempRoot({ fallbackPrefix: "fs-safe" }));
|
|
108
108
|
}
|
|
109
|
-
export async function
|
|
109
|
+
export async function createOwnedTempFile(params) {
|
|
110
110
|
const rootDir = resolveTempRoot(params.rootDir);
|
|
111
111
|
const prefix = `${sanitizePrefix(params.prefix)}-`;
|
|
112
112
|
const dir = await fs.mkdtemp(path.join(rootDir, prefix));
|
|
@@ -124,13 +124,19 @@ export async function tempFile(params) {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
return {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
target: {
|
|
128
|
+
dir,
|
|
129
|
+
path: file(),
|
|
130
|
+
file,
|
|
131
|
+
cleanup,
|
|
132
|
+
[Symbol.asyncDispose]: cleanup,
|
|
133
|
+
},
|
|
134
|
+
identity: Object.freeze({ dev: identity.dev, ino: identity.ino }),
|
|
132
135
|
};
|
|
133
136
|
}
|
|
137
|
+
export async function tempFile(params) {
|
|
138
|
+
return (await createOwnedTempFile(params)).target;
|
|
139
|
+
}
|
|
134
140
|
export async function withTempFile(params, fn) {
|
|
135
141
|
const target = await tempFile(params);
|
|
136
142
|
try {
|
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.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trash.d.ts","sourceRoot":"","sources":["../src/trash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trash.d.ts","sourceRoot":"","sources":["../src/trash.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC;AAkLF,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
package/dist/trash.js
CHANGED
|
@@ -3,6 +3,8 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { sameFileIdentity } from "./file-identity.js";
|
|
5
5
|
import { guardedRenameSync, guardedRmSync } from "./guarded-mutation.js";
|
|
6
|
+
import { realpathSync } from "./realpath.js";
|
|
7
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
6
8
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
7
9
|
const TRASH_DESTINATION_COLLISION_CODES = new Set(["EEXIST", "ENOTEMPTY", "ERR_FS_CP_EEXIST"]);
|
|
8
10
|
const TRASH_DESTINATION_RETRY_LIMIT = 4;
|
|
@@ -18,7 +20,7 @@ function isTrashDestinationCollision(error) {
|
|
|
18
20
|
return Boolean(code && TRASH_DESTINATION_COLLISION_CODES.has(code));
|
|
19
21
|
}
|
|
20
22
|
function isSameOrChildPath(candidate, parent) {
|
|
21
|
-
return candidate === parent || candidate.startsWith(`${parent}${path.sep}`);
|
|
23
|
+
return candidate === parent || candidate.startsWith(parent.endsWith(path.sep) ? parent : `${parent}${path.sep}`);
|
|
22
24
|
}
|
|
23
25
|
function resolveAllowedTrashRoots(allowedRoots) {
|
|
24
26
|
const roots = [...(allowedRoots ?? [os.homedir(), os.tmpdir()])].flatMap((root) => {
|
|
@@ -26,7 +28,7 @@ function resolveAllowedTrashRoots(allowedRoots) {
|
|
|
26
28
|
try {
|
|
27
29
|
// Keep both spellings: broken symlink targets cannot be realpathed and
|
|
28
30
|
// may only compare equal to the caller's lexical allowed root.
|
|
29
|
-
return [path.resolve(
|
|
31
|
+
return [path.resolve(realpathSync.native(root)), lexicalRoot];
|
|
30
32
|
}
|
|
31
33
|
catch {
|
|
32
34
|
return [lexicalRoot];
|
|
@@ -36,7 +38,7 @@ function resolveAllowedTrashRoots(allowedRoots) {
|
|
|
36
38
|
}
|
|
37
39
|
function resolveTrashTargetPath(targetPath) {
|
|
38
40
|
try {
|
|
39
|
-
return { path: path.resolve(
|
|
41
|
+
return { path: path.resolve(realpathSync.native(targetPath)), resolved: true };
|
|
40
42
|
}
|
|
41
43
|
catch {
|
|
42
44
|
// Broken symlinks are valid trash targets. Fall back to the lexical path,
|
|
@@ -75,13 +77,13 @@ function assertTrashTargetGuard(guard) {
|
|
|
75
77
|
function resolveTrashDir() {
|
|
76
78
|
const homeDir = os.homedir();
|
|
77
79
|
const trashDir = path.join(homeDir, ".Trash");
|
|
78
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
80
|
+
fs.mkdirSync(recursiveMkdirPath(trashDir), { recursive: true, mode: 0o700 });
|
|
79
81
|
const trashDirStat = fs.lstatSync(trashDir);
|
|
80
82
|
if (!trashDirStat.isDirectory() || trashDirStat.isSymbolicLink()) {
|
|
81
83
|
throw new Error(`Refusing to use non-directory/symlink trash directory: ${trashDir}`);
|
|
82
84
|
}
|
|
83
|
-
const realHome = path.resolve(
|
|
84
|
-
const resolvedTrashDir = path.resolve(
|
|
85
|
+
const realHome = path.resolve(realpathSync.native(homeDir));
|
|
86
|
+
const resolvedTrashDir = path.resolve(realpathSync.native(trashDir));
|
|
85
87
|
if (resolvedTrashDir === realHome || !isSameOrChildPath(resolvedTrashDir, realHome)) {
|
|
86
88
|
throw new Error(`Trash directory escaped home directory: ${trashDir}`);
|
|
87
89
|
}
|
|
@@ -107,7 +109,7 @@ function resolveContainedPath(root, leaf) {
|
|
|
107
109
|
return resolvedPath;
|
|
108
110
|
}
|
|
109
111
|
function reserveTrashDestination(trashDir, base, timestamp) {
|
|
110
|
-
const containerPrefix = resolveContainedPath(trashDir,
|
|
112
|
+
const containerPrefix = resolveContainedPath(trashDir, `.fs-safe-trash-${timestamp}-`);
|
|
111
113
|
const container = fs.mkdtempSync(containerPrefix);
|
|
112
114
|
const resolvedContainer = path.resolve(container);
|
|
113
115
|
const resolvedTrashDir = path.resolve(trashDir);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unicode-path.d.ts","sourceRoot":"","sources":["../src/unicode-path.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAUhF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const NON_ASCII = /[^\x00-\x7f]/;
|
|
2
|
+
export function lowerCaseNfc(value) {
|
|
3
|
+
return NON_ASCII.test(value) ? value.normalize("NFC").toLowerCase().normalize("NFC") : value.toLowerCase();
|
|
4
|
+
}
|
|
5
|
+
export function maxNormalizedUtf8Bytes(value, includeRaw = false) {
|
|
6
|
+
const nfc = value.normalize("NFC");
|
|
7
|
+
const bytes = Buffer.byteLength(nfc, "utf8");
|
|
8
|
+
const maximum = includeRaw && nfc !== value ? Math.max(bytes, Buffer.byteLength(value, "utf8")) : bytes;
|
|
9
|
+
// ASCII NFC output also has identical NFD and one UTF-8 byte per code unit.
|
|
10
|
+
if (bytes === nfc.length)
|
|
11
|
+
return maximum;
|
|
12
|
+
return Math.max(maximum, Buffer.byteLength(value.normalize("NFD"), "utf8"));
|
|
13
|
+
}
|
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;
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAK7B,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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { realpathSync } from "./realpath.js";
|
|
4
5
|
function validateWalkBudget(name, value) {
|
|
5
6
|
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
|
|
6
7
|
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
@@ -27,7 +28,7 @@ function shouldStop(result, options) {
|
|
|
27
28
|
return options.maxEntries !== undefined && result.scannedEntryCount >= Math.max(0, options.maxEntries);
|
|
28
29
|
}
|
|
29
30
|
function buildEntry(params) {
|
|
30
|
-
const fullPath =
|
|
31
|
+
const fullPath = params.fullPath;
|
|
31
32
|
const relativePath = path.relative(params.rootDir, fullPath) || params.dirent.name;
|
|
32
33
|
return {
|
|
33
34
|
name: params.dirent.name,
|
|
@@ -47,27 +48,7 @@ function recordFailedDir(result, root, dir, depth, error) {
|
|
|
47
48
|
error,
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
|
-
function
|
|
51
|
-
const kind = kindForDirent(dirent);
|
|
52
|
-
if (kind !== "symlink")
|
|
53
|
-
return kind;
|
|
54
|
-
if (symlinks === "skip")
|
|
55
|
-
return null;
|
|
56
|
-
if (symlinks === "include")
|
|
57
|
-
return "symlink";
|
|
58
|
-
try {
|
|
59
|
-
const stat = fsSync.statSync(fullPath);
|
|
60
|
-
if (stat.isDirectory())
|
|
61
|
-
return "directory";
|
|
62
|
-
if (stat.isFile())
|
|
63
|
-
return "file";
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
return "other";
|
|
69
|
-
}
|
|
70
|
-
async function resolveAsyncKind(fullPath, dirent, symlinks) {
|
|
51
|
+
function resolveKind(fullPath, dirent, symlinks) {
|
|
71
52
|
const kind = kindForDirent(dirent);
|
|
72
53
|
if (kind !== "symlink")
|
|
73
54
|
return kind;
|
|
@@ -103,7 +84,7 @@ export function walkDirectorySync(rootDir, options = {}) {
|
|
|
103
84
|
return;
|
|
104
85
|
let realDir;
|
|
105
86
|
try {
|
|
106
|
-
realDir =
|
|
87
|
+
realDir = realpathSync(dir);
|
|
107
88
|
}
|
|
108
89
|
catch (error) {
|
|
109
90
|
recordFailedDir(result, root, dir, depth, error);
|
|
@@ -127,10 +108,10 @@ export function walkDirectorySync(rootDir, options = {}) {
|
|
|
127
108
|
}
|
|
128
109
|
result.scannedEntryCount += 1;
|
|
129
110
|
const fullPath = path.join(dir, dirent.name);
|
|
130
|
-
const kind =
|
|
111
|
+
const kind = resolveKind(fullPath, dirent, symlinks);
|
|
131
112
|
if (!kind)
|
|
132
113
|
continue;
|
|
133
|
-
const entry = buildEntry({ rootDir: root,
|
|
114
|
+
const entry = buildEntry({ rootDir: root, fullPath, dirent, depth, kind });
|
|
134
115
|
if (options.include?.(entry) ?? true) {
|
|
135
116
|
result.entries.push(entry);
|
|
136
117
|
}
|
|
@@ -162,7 +143,7 @@ export async function walkDirectory(rootDir, options = {}) {
|
|
|
162
143
|
return;
|
|
163
144
|
let realDir;
|
|
164
145
|
try {
|
|
165
|
-
realDir =
|
|
146
|
+
realDir = realpathSync.native(dir);
|
|
166
147
|
}
|
|
167
148
|
catch (error) {
|
|
168
149
|
recordFailedDir(result, root, dir, depth, error);
|
|
@@ -186,10 +167,10 @@ export async function walkDirectory(rootDir, options = {}) {
|
|
|
186
167
|
}
|
|
187
168
|
result.scannedEntryCount += 1;
|
|
188
169
|
const fullPath = path.join(dir, dirent.name);
|
|
189
|
-
const kind =
|
|
170
|
+
const kind = resolveKind(fullPath, dirent, symlinks);
|
|
190
171
|
if (!kind)
|
|
191
172
|
continue;
|
|
192
|
-
const entry = buildEntry({ rootDir: root,
|
|
173
|
+
const entry = buildEntry({ rootDir: root, fullPath, dirent, depth, kind });
|
|
193
174
|
if (options.include?.(entry) ?? true) {
|
|
194
175
|
result.entries.push(entry);
|
|
195
176
|
}
|
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"}
|