@openclaw/fs-safe 0.12.0 → 0.13.1
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 +73 -0
- package/README.md +12 -7
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +6 -2
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -19
- package/dist/archive-plan.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +18 -13
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +43 -10
- package/dist/archive-tar-inspect.d.ts.map +1 -1
- package/dist/archive-tar-inspect.js +4 -1
- 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 +2 -2
- package/dist/archive-zip-directory.d.ts +3 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +13 -2
- package/dist/archive-zip-loader.d.ts +3 -2
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +30 -4
- package/dist/archive-zip-manifest.d.ts +5 -0
- package/dist/archive-zip-manifest.d.ts.map +1 -0
- package/dist/archive-zip-manifest.js +22 -0
- package/dist/archive-zip-names.d.ts +6 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +35 -14
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +3 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/copy-file-input.d.ts +1 -1
- package/dist/copy-file-input.d.ts.map +1 -1
- package/dist/copy-file-input.js +2 -0
- package/dist/darwin-acl.d.ts +4 -0
- package/dist/darwin-acl.d.ts.map +1 -0
- package/dist/darwin-acl.js +24 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +8 -2
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +67 -23
- package/dist/directory-entry-path.d.ts +3 -0
- package/dist/directory-entry-path.d.ts.map +1 -0
- package/dist/directory-entry-path.js +21 -0
- package/dist/directory-guard.d.ts +17 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +134 -48
- package/dist/directory-mode-node.d.ts +12 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +102 -4
- package/dist/effective-uid.d.ts +2 -0
- package/dist/effective-uid.d.ts.map +1 -0
- package/dist/effective-uid.js +25 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +98 -27
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +3 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +36 -6
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +29 -9
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +6 -2
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -65
- package/dist/file-store-copy-source.d.ts +5 -0
- package/dist/file-store-copy-source.d.ts.map +1 -0
- package/dist/file-store-copy-source.js +31 -0
- package/dist/file-store-path.d.ts.map +1 -1
- package/dist/file-store-path.js +4 -1
- package/dist/file-store-sync-directory.d.ts +16 -0
- package/dist/file-store-sync-directory.d.ts.map +1 -0
- package/dist/file-store-sync-directory.js +349 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +11 -28
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +65 -20
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +8 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +176 -27
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +4 -1
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +54 -19
- package/dist/json-durable-queue-ownership.d.ts +6 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +89 -42
- package/dist/json-durable-queue-paths.d.ts +11 -0
- package/dist/json-durable-queue-paths.d.ts.map +1 -0
- package/dist/json-durable-queue-paths.js +42 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -1
- package/dist/json-durable-queue-read.js +2 -0
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +50 -58
- package/dist/json-store.d.ts.map +1 -1
- package/dist/json-store.js +5 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +54 -22
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +4 -0
- package/dist/local-file-descriptor.d.ts +17 -0
- package/dist/local-file-descriptor.d.ts.map +1 -0
- package/dist/local-file-descriptor.js +84 -0
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -7
- package/dist/move-path-cleanup.d.ts +2 -0
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +44 -18
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +31 -6
- package/dist/native-binding.d.ts +17 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-binding.js +8 -1
- package/dist/native-directory-observation.d.ts +17 -0
- package/dist/native-directory-observation.d.ts.map +1 -0
- package/dist/native-directory-observation.js +37 -0
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +6 -4
- package/dist/native-parent-admission.d.ts +32 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +127 -0
- package/dist/native-pinned-write-windows.d.ts +0 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +8 -9
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +314 -42
- package/dist/native-staged-file.d.ts +4 -4
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +20 -10
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +7 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +15 -5
- package/dist/overwrite-file-handle.d.ts.map +1 -1
- package/dist/overwrite-file-handle.js +5 -1
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -0
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +7 -2
- package/dist/path-prefix.d.ts +7 -0
- package/dist/path-prefix.d.ts.map +1 -0
- package/dist/path-prefix.js +82 -0
- package/dist/path-scope-lexical.d.ts.map +1 -1
- package/dist/path-scope-lexical.js +18 -8
- package/dist/path-segment-route.d.ts +7 -0
- package/dist/path-segment-route.d.ts.map +1 -0
- package/dist/path-segment-route.js +24 -0
- package/dist/path-suffix-aliases.d.ts +10 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -0
- package/dist/path-suffix-aliases.js +386 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +16 -5
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +14 -3
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +37 -8
- package/dist/pinned-mutation-admission.d.ts +25 -0
- package/dist/pinned-mutation-admission.d.ts.map +1 -0
- package/dist/pinned-mutation-admission.js +425 -0
- package/dist/pinned-mutation-observation.d.ts +34 -0
- package/dist/pinned-mutation-observation.d.ts.map +1 -0
- package/dist/pinned-mutation-observation.js +142 -0
- package/dist/pinned-mutation-shared-route.d.ts +24 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
- package/dist/pinned-mutation-shared-route.js +70 -0
- package/dist/pinned-open.d.ts +6 -0
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +28 -10
- package/dist/pinned-write-types.d.ts +75 -0
- package/dist/pinned-write-types.d.ts.map +1 -0
- package/dist/pinned-write-types.js +1 -0
- package/dist/pinned-write.d.ts +7 -33
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +151 -17
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +2 -0
- package/dist/private-producer-handoff.d.ts +16 -0
- package/dist/private-producer-handoff.d.ts.map +1 -0
- package/dist/private-producer-handoff.js +272 -0
- package/dist/private-temp-workspace.d.ts +2 -39
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +183 -77
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +38 -32
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +56 -45
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +12 -5
- package/dist/replace-file-temp-owner.d.ts +1 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +12 -1
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +24 -24
- package/dist/root-boundary.d.ts +4 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +6 -1
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +57 -11
- package/dist/root-directory-creation.d.ts +13 -0
- package/dist/root-directory-creation.d.ts.map +1 -0
- package/dist/root-directory-creation.js +212 -0
- package/dist/root-directory-list.d.ts +16 -4
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +180 -39
- package/dist/root-directory.d.ts +23 -0
- package/dist/root-directory.d.ts.map +1 -0
- package/dist/root-directory.js +141 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +12 -1
- package/dist/root-file-final-admission.d.ts +21 -0
- package/dist/root-file-final-admission.d.ts.map +1 -0
- package/dist/root-file-final-admission.js +83 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +103 -24
- package/dist/root-impl.d.ts +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +289 -317
- package/dist/root-move-noreplace.d.ts +14 -0
- package/dist/root-move-noreplace.d.ts.map +1 -0
- package/dist/root-move-noreplace.js +202 -0
- package/dist/root-observed-path.d.ts +9 -0
- package/dist/root-observed-path.d.ts.map +1 -0
- package/dist/root-observed-path.js +95 -0
- package/dist/root-path-errors.d.ts +12 -0
- package/dist/root-path-errors.d.ts.map +1 -0
- package/dist/root-path-errors.js +13 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +53 -20
- package/dist/root-path-observation.d.ts +63 -0
- package/dist/root-path-observation.d.ts.map +1 -0
- package/dist/root-path-observation.js +180 -0
- package/dist/root-path-stat.d.ts +5 -0
- package/dist/root-path-stat.d.ts.map +1 -0
- package/dist/root-path-stat.js +101 -0
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +23 -4
- package/dist/root-path.d.ts +11 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +215 -53
- package/dist/root-paths-lexical.d.ts +9 -0
- package/dist/root-paths-lexical.d.ts.map +1 -0
- package/dist/root-paths-lexical.js +22 -0
- package/dist/root-paths.d.ts +3 -25
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +77 -154
- package/dist/root-read-admission.d.ts +26 -0
- package/dist/root-read-admission.d.ts.map +1 -0
- package/dist/root-read-admission.js +94 -0
- package/dist/root-remove-identity.d.ts +15 -0
- package/dist/root-remove-identity.d.ts.map +1 -0
- package/dist/root-remove-identity.js +89 -0
- package/dist/root-remove-receipt.d.ts +17 -0
- package/dist/root-remove-receipt.d.ts.map +1 -0
- package/dist/root-remove-receipt.js +37 -0
- package/dist/root-remove.d.ts +2 -1
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +172 -10
- package/dist/root-write-admission.d.ts +68 -0
- package/dist/root-write-admission.d.ts.map +1 -0
- package/dist/root-write-admission.js +322 -0
- package/dist/root-write-compatibility.d.ts +13 -0
- package/dist/root-write-compatibility.d.ts.map +1 -0
- package/dist/root-write-compatibility.js +74 -0
- package/dist/root-write-complete-parent.d.ts +42 -0
- package/dist/root-write-complete-parent.d.ts.map +1 -0
- package/dist/root-write-complete-parent.js +195 -0
- package/dist/root-write-publication.d.ts +24 -0
- package/dist/root-write-publication.d.ts.map +1 -0
- package/dist/root-write-publication.js +85 -0
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +6 -4
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +62 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +18 -13
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +5 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +92 -7
- package/dist/secure-temp-dir.d.ts +6 -3
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +121 -101
- package/dist/secure-temp-repair.d.ts +35 -0
- package/dist/secure-temp-repair.d.ts.map +1 -0
- package/dist/secure-temp-repair.js +106 -0
- package/dist/sibling-staged-file.d.ts +3 -2
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +179 -55
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -13
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +44 -14
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- package/dist/staged-directory.d.ts +14 -5
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +54 -3
- package/dist/standalone-publication-path.d.ts +2 -0
- package/dist/standalone-publication-path.d.ts.map +1 -0
- package/dist/standalone-publication-path.js +6 -0
- package/dist/stat-observation.d.ts +11 -0
- package/dist/stat-observation.d.ts.map +1 -0
- package/dist/stat-observation.js +64 -0
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +39 -2
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +7 -2
- package/dist/temp-target.d.ts +2 -0
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +130 -3
- package/dist/temp-workspace-admission.d.ts +22 -0
- package/dist/temp-workspace-admission.d.ts.map +1 -0
- package/dist/temp-workspace-admission.js +374 -0
- package/dist/temp-workspace-child-admission.d.ts +13 -0
- package/dist/temp-workspace-child-admission.d.ts.map +1 -0
- package/dist/temp-workspace-child-admission.js +95 -0
- package/dist/temp-workspace-descriptor.d.ts +48 -0
- package/dist/temp-workspace-descriptor.d.ts.map +1 -0
- package/dist/temp-workspace-descriptor.js +363 -0
- package/dist/temp-workspace-identity.d.ts +15 -0
- package/dist/temp-workspace-identity.d.ts.map +1 -0
- package/dist/temp-workspace-identity.js +41 -0
- package/dist/temp-workspace-owner.d.ts +7 -6
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +121 -61
- package/dist/temp-workspace-permissions.d.ts +4 -0
- package/dist/temp-workspace-permissions.d.ts.map +1 -0
- package/dist/temp-workspace-permissions.js +32 -0
- package/dist/temp-workspace-types.d.ts +40 -0
- package/dist/temp-workspace-types.d.ts.map +1 -0
- package/dist/temp-workspace-types.js +1 -0
- package/dist/temp.d.ts +1 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/temp.js +1 -1
- package/dist/test-hooks.d.ts +7 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/text-atomic.d.ts.map +1 -1
- package/dist/text-atomic.js +3 -1
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +54 -8
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +9 -6
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +5 -0
- package/dist/windows-path-alias.d.ts +39 -0
- package/dist/windows-path-alias.d.ts.map +1 -0
- package/dist/windows-path-alias.js +153 -0
- package/docs/advanced.md +25 -0
- package/docs/archive.md +23 -2
- package/docs/atomic.md +27 -3
- package/docs/copy.md +3 -0
- package/docs/durability.md +14 -0
- package/docs/errors.md +14 -6
- package/docs/file-store.md +24 -3
- package/docs/filename.md +14 -7
- package/docs/install-path.md +2 -2
- package/docs/install.md +8 -7
- package/docs/json-store.md +5 -1
- package/docs/json.md +11 -0
- package/docs/mutation-policy-proof.md +65 -0
- package/docs/native-helper.md +41 -11
- package/docs/native.md +32 -8
- package/docs/output.md +33 -11
- package/docs/path-prefix.md +64 -0
- package/docs/path-suffix-aliases.md +159 -0
- package/docs/path.md +11 -0
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +9 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +18 -3
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +6 -2
- package/docs/security-model.md +75 -8
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +5 -1
- package/docs/temp.md +260 -36
- package/docs/test-hooks.md +14 -0
- package/docs/writing.md +38 -8
- package/package.json +10 -10
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import fsSync, {} from "node:fs";
|
|
2
|
+
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
5
|
+
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
6
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
|
+
import { registerTempPathForExit, } from "./temp-cleanup.js";
|
|
8
|
+
function pathMismatch(message) {
|
|
9
|
+
return new FsSafeError("path-mismatch", message);
|
|
10
|
+
}
|
|
11
|
+
function assertInitialSource(stat) {
|
|
12
|
+
if (stat.isSymbolicLink()) {
|
|
13
|
+
throw new FsSafeError("path-alias", "isolated producer output must not be a symlink");
|
|
14
|
+
}
|
|
15
|
+
if (!stat.isFile()) {
|
|
16
|
+
throw new FsSafeError("not-file", "isolated producer output must be a regular file");
|
|
17
|
+
}
|
|
18
|
+
if (stat.nlink !== 1n) {
|
|
19
|
+
throw new FsSafeError("hardlink", "isolated producer output must have exactly one link");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function inspectLinkedFile(inspect, expected, links, label) {
|
|
23
|
+
return inspectFileIdentitySync(() => {
|
|
24
|
+
const stat = inspect();
|
|
25
|
+
if (stat.isSymbolicLink() || !stat.isFile() || stat.nlink !== links) {
|
|
26
|
+
throw pathMismatch(`${label} changed during isolated producer handoff`);
|
|
27
|
+
}
|
|
28
|
+
return stat;
|
|
29
|
+
}, expected);
|
|
30
|
+
}
|
|
31
|
+
function assertParents(params) {
|
|
32
|
+
params.assertTargetParent();
|
|
33
|
+
params.assertSourceParent();
|
|
34
|
+
}
|
|
35
|
+
function normalizeLinkError(error) {
|
|
36
|
+
const code = error?.code;
|
|
37
|
+
if (code === "EEXIST") {
|
|
38
|
+
return new FsSafeError("already-exists", "isolated producer sibling already exists", {
|
|
39
|
+
cause: error instanceof Error ? error : undefined,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (code === "EXDEV" ||
|
|
43
|
+
code === "ENOSYS" ||
|
|
44
|
+
code === "ENOTSUP" ||
|
|
45
|
+
code === "EOPNOTSUPP" ||
|
|
46
|
+
code === "EPERM") {
|
|
47
|
+
return new FsSafeError("helper-unavailable", "atomic isolated producer handoff is unavailable", {
|
|
48
|
+
cause: error instanceof Error ? error : undefined,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
function removeSiblingIfOwned(params) {
|
|
54
|
+
if (!params.state.siblingOwned)
|
|
55
|
+
return;
|
|
56
|
+
params.assertTargetParent();
|
|
57
|
+
let current;
|
|
58
|
+
try {
|
|
59
|
+
current = fsSync.lstatSync(params.targetPath, { bigint: true });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error?.code === "ENOENT") {
|
|
63
|
+
params.state.siblingOwned = false;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
if (current.isSymbolicLink() ||
|
|
69
|
+
!current.isFile() ||
|
|
70
|
+
(current.nlink !== 1n && current.nlink !== 2n) ||
|
|
71
|
+
!sameFileIdentityForCleanup(current, params.identity)) {
|
|
72
|
+
throw pathMismatch("isolated producer sibling changed before cleanup");
|
|
73
|
+
}
|
|
74
|
+
fsSync.unlinkSync(params.targetPath);
|
|
75
|
+
params.state.siblingOwned = false;
|
|
76
|
+
params.assertTargetParent();
|
|
77
|
+
}
|
|
78
|
+
function combinedFailure(primary, settlement) {
|
|
79
|
+
if (settlement.length === 0)
|
|
80
|
+
return primary;
|
|
81
|
+
return new AggregateError([primary, ...settlement], "isolated producer handoff and settlement failed");
|
|
82
|
+
}
|
|
83
|
+
function hasCode(error, codes) {
|
|
84
|
+
return !(error instanceof FsSafeError) &&
|
|
85
|
+
codes.includes(error?.code ?? "");
|
|
86
|
+
}
|
|
87
|
+
async function throwAfterClosing(primary, handles) {
|
|
88
|
+
const settlement = [];
|
|
89
|
+
const closed = new Set();
|
|
90
|
+
for (const handle of handles) {
|
|
91
|
+
if (!handle || closed.has(handle))
|
|
92
|
+
continue;
|
|
93
|
+
closed.add(handle);
|
|
94
|
+
try {
|
|
95
|
+
await handle.close();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
settlement.push(error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
throw combinedFailure(primary, settlement);
|
|
102
|
+
}
|
|
103
|
+
function inspectSourceDescriptor(handle, identity, links = 1n) {
|
|
104
|
+
inspectLinkedFile(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity, links, "isolated producer descriptor");
|
|
105
|
+
}
|
|
106
|
+
function historicalOpenFlags(readWrite) {
|
|
107
|
+
const access = readWrite ? fsSync.constants.O_RDWR : fsSync.constants.O_RDONLY;
|
|
108
|
+
return access | resolveReadOpenFlags();
|
|
109
|
+
}
|
|
110
|
+
async function openHistoricalSource(sourcePath, identity, readWrite, links) {
|
|
111
|
+
let handle;
|
|
112
|
+
try {
|
|
113
|
+
handle = await fs.open(sourcePath, historicalOpenFlags(readWrite));
|
|
114
|
+
inspectSourceDescriptor(handle, identity, links);
|
|
115
|
+
return handle;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (handle)
|
|
119
|
+
return await throwAfterClosing(error, [handle]);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Windows data-read access can synchronously inspect a completed file through
|
|
124
|
+
// filesystem filters. Prefer write-only admission, which still supports the
|
|
125
|
+
// descriptor metadata, chmod and sync operations used below. Providers or
|
|
126
|
+
// read-only files that reject that access retain the historical open contract.
|
|
127
|
+
async function openProducerSource(params, identity, links = 1n) {
|
|
128
|
+
if (process.platform !== "win32") {
|
|
129
|
+
return await openHistoricalSource(params.sourcePath, identity, params.readWrite, links);
|
|
130
|
+
}
|
|
131
|
+
let provisional;
|
|
132
|
+
try {
|
|
133
|
+
provisional = await fs.open(params.sourcePath, fsSync.constants.O_WRONLY);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (!hasCode(error, ["EACCES", "EPERM", "EBUSY"]))
|
|
137
|
+
throw error;
|
|
138
|
+
return await openHistoricalSource(params.sourcePath, identity, params.readWrite, links);
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
inspectSourceDescriptor(provisional, identity, links);
|
|
142
|
+
return provisional;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (!hasCode(error, ["EACCES", "EPERM"])) {
|
|
146
|
+
return await throwAfterClosing(error, [provisional]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
let fallback;
|
|
150
|
+
try {
|
|
151
|
+
fallback = await fs.open(params.sourcePath, historicalOpenFlags(params.readWrite));
|
|
152
|
+
inspectSourceDescriptor(fallback, identity, links);
|
|
153
|
+
inspectLinkedFile(() => fsSync.lstatSync(params.sourcePath, { bigint: true }), identity, links, "isolated producer path");
|
|
154
|
+
assertParents(params);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return await throwAfterClosing(error, [fallback, provisional]);
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await provisional.close();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return await throwAfterClosing(error, [fallback]);
|
|
164
|
+
}
|
|
165
|
+
return fallback;
|
|
166
|
+
}
|
|
167
|
+
function assertLinkedHandoff(params, handle, identity) {
|
|
168
|
+
assertParents(params);
|
|
169
|
+
inspectSourceDescriptor(handle, identity, 2n);
|
|
170
|
+
inspectLinkedFile(() => fsSync.lstatSync(params.sourcePath, { bigint: true }), identity, 2n, "isolated producer path");
|
|
171
|
+
inspectLinkedFile(() => fsSync.lstatSync(params.targetPath, { bigint: true }), identity, 2n, "isolated producer sibling");
|
|
172
|
+
}
|
|
173
|
+
export async function handoffPrivateProducerFile(params) {
|
|
174
|
+
assertParents(params);
|
|
175
|
+
const identity = inspectFileIdentitySync(() => {
|
|
176
|
+
const stat = fsSync.lstatSync(params.sourcePath, { bigint: true });
|
|
177
|
+
assertInitialSource(stat);
|
|
178
|
+
return stat;
|
|
179
|
+
});
|
|
180
|
+
let handle;
|
|
181
|
+
let siblingHandle;
|
|
182
|
+
let unregister;
|
|
183
|
+
const state = { siblingOwned: false };
|
|
184
|
+
try {
|
|
185
|
+
handle = await openProducerSource(params, identity);
|
|
186
|
+
inspectLinkedFile(() => fsSync.lstatSync(params.sourcePath, { bigint: true }), identity, 1n, "isolated producer path");
|
|
187
|
+
assertParents(params);
|
|
188
|
+
unregister = registerTempPathForExit(params.targetPath, {
|
|
189
|
+
cleanupSync: () => removeSiblingIfOwned({
|
|
190
|
+
assertTargetParent: params.assertTargetParent,
|
|
191
|
+
identity,
|
|
192
|
+
state,
|
|
193
|
+
targetPath: params.targetPath,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
assertParents(params);
|
|
197
|
+
try {
|
|
198
|
+
fsSync.linkSync(params.sourcePath, params.targetPath);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
throw normalizeLinkError(error);
|
|
202
|
+
}
|
|
203
|
+
state.siblingOwned = true;
|
|
204
|
+
assertLinkedHandoff(params, handle, identity);
|
|
205
|
+
if (process.platform === "win32") {
|
|
206
|
+
// Legacy Windows unlink retains a delete-pending name until handles
|
|
207
|
+
// opened through that name close. Transfer the pin before retiring it.
|
|
208
|
+
siblingHandle = await openProducerSource({ ...params, sourcePath: params.targetPath }, identity, 2n);
|
|
209
|
+
assertLinkedHandoff(params, siblingHandle, identity);
|
|
210
|
+
inspectSourceDescriptor(handle, identity, 2n);
|
|
211
|
+
await handle.close();
|
|
212
|
+
handle = siblingHandle;
|
|
213
|
+
siblingHandle = undefined;
|
|
214
|
+
// The old handle's close yielded; both names must still be ours.
|
|
215
|
+
assertLinkedHandoff(params, handle, identity);
|
|
216
|
+
}
|
|
217
|
+
assertParents(params);
|
|
218
|
+
fsSync.unlinkSync(params.sourcePath);
|
|
219
|
+
assertParents(params);
|
|
220
|
+
const opened = inspectLinkedFile(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity, 1n, "isolated producer descriptor");
|
|
221
|
+
const sibling = inspectLinkedFile(() => fsSync.lstatSync(params.targetPath, { bigint: true }), identity, 1n, "isolated producer sibling");
|
|
222
|
+
const originalMode = identity.mode & 511n;
|
|
223
|
+
if (process.platform === "win32" && (originalMode & 128n) === 0n &&
|
|
224
|
+
((opened.mode & 511n) !== originalMode || (sibling.mode & 511n) !== originalMode)) {
|
|
225
|
+
// Legacy unlink clears the shared read-only attribute. Repair only the
|
|
226
|
+
// admitted sibling inode, without reopening or chmodding its pathname.
|
|
227
|
+
assertParents(params);
|
|
228
|
+
fsSync.fchmodSync(handle.fd, Number(originalMode));
|
|
229
|
+
assertParents(params);
|
|
230
|
+
const restored = inspectLinkedFile(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity, 1n, "isolated producer descriptor");
|
|
231
|
+
const restoredSibling = inspectLinkedFile(() => fsSync.lstatSync(params.targetPath, { bigint: true }), identity, 1n, "isolated producer sibling");
|
|
232
|
+
if ((restored.mode & 511n) !== originalMode || (restoredSibling.mode & 511n) !== originalMode) {
|
|
233
|
+
throw pathMismatch("isolated producer read-only mode could not be restored");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const registered = unregister;
|
|
237
|
+
const release = (() => {
|
|
238
|
+
state.siblingOwned = false;
|
|
239
|
+
registered();
|
|
240
|
+
});
|
|
241
|
+
release.setIdentity = registered.setIdentity;
|
|
242
|
+
return { handle, identity, unregister: release };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
const settlement = [];
|
|
246
|
+
if (unregister) {
|
|
247
|
+
try {
|
|
248
|
+
removeSiblingIfOwned({
|
|
249
|
+
assertTargetParent: params.assertTargetParent,
|
|
250
|
+
identity,
|
|
251
|
+
state,
|
|
252
|
+
targetPath: params.targetPath,
|
|
253
|
+
});
|
|
254
|
+
unregister();
|
|
255
|
+
}
|
|
256
|
+
catch (cleanupError) {
|
|
257
|
+
settlement.push(cleanupError);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const retained of new Set([siblingHandle, handle])) {
|
|
261
|
+
if (!retained)
|
|
262
|
+
continue;
|
|
263
|
+
try {
|
|
264
|
+
await retained.close();
|
|
265
|
+
}
|
|
266
|
+
catch (closeError) {
|
|
267
|
+
settlement.push(closeError);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
throw combinedFailure(error, settlement);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -1,43 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type TempPathIdentityReceipt } from "./temp-cleanup.js";
|
|
3
|
-
import { type TempWorkspaceCleanupResult, type TempWorkspaceCleanupSafety } from "./temp-workspace-owner.js";
|
|
1
|
+
import type { TempWorkspaceOptions, TempWorkspace, TempWorkspaceSync } from "./temp-workspace-types.js";
|
|
4
2
|
export type { TempWorkspaceCleanupResult, TempWorkspaceCleanupSafety, } from "./temp-workspace-owner.js";
|
|
5
|
-
export type TempWorkspaceOptions
|
|
6
|
-
rootDir: string;
|
|
7
|
-
prefix: string;
|
|
8
|
-
dirMode?: number;
|
|
9
|
-
mode?: number;
|
|
10
|
-
cleanupSafety?: TempWorkspaceCleanupSafety;
|
|
11
|
-
};
|
|
12
|
-
export type TempWorkspace = {
|
|
13
|
-
dir: string;
|
|
14
|
-
identity: TempPathIdentityReceipt;
|
|
15
|
-
store: FileStore;
|
|
16
|
-
path(fileName: string): string;
|
|
17
|
-
write(fileName: string, data: string | Uint8Array): Promise<string>;
|
|
18
|
-
writeText(fileName: string, data: string): Promise<string>;
|
|
19
|
-
writeJson(fileName: string, data: unknown, options?: {
|
|
20
|
-
trailingNewline?: boolean;
|
|
21
|
-
}): Promise<string>;
|
|
22
|
-
copyIn(fileName: string, sourcePath: string): Promise<string>;
|
|
23
|
-
read(fileName: string): Promise<Buffer>;
|
|
24
|
-
cleanup(): Promise<TempWorkspaceCleanupResult>;
|
|
25
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
26
|
-
};
|
|
27
|
-
export type TempWorkspaceSync = {
|
|
28
|
-
dir: string;
|
|
29
|
-
identity: TempPathIdentityReceipt;
|
|
30
|
-
store: FileStoreSync;
|
|
31
|
-
path(fileName: string): string;
|
|
32
|
-
write(fileName: string, data: string | Uint8Array): string;
|
|
33
|
-
writeText(fileName: string, data: string): string;
|
|
34
|
-
writeJson(fileName: string, data: unknown, options?: {
|
|
35
|
-
trailingNewline?: boolean;
|
|
36
|
-
}): string;
|
|
37
|
-
read(fileName: string): Buffer;
|
|
38
|
-
cleanup(): TempWorkspaceCleanupResult;
|
|
39
|
-
[Symbol.dispose](): void;
|
|
40
|
-
};
|
|
3
|
+
export type { TempWorkspaceOptions, TempWorkspace, TempWorkspaceSync } from "./temp-workspace-types.js";
|
|
41
4
|
export declare function tempWorkspace(options: TempWorkspaceOptions): Promise<TempWorkspace>;
|
|
42
5
|
export declare function withTempWorkspace<T>(options: TempWorkspaceOptions, run: (workspace: TempWorkspace) => Promise<T>): Promise<T>;
|
|
43
6
|
export declare function tempWorkspaceSync(options: TempWorkspaceOptions): TempWorkspaceSync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAExG,YAAY,EACV,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA8OxG,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAExB;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAOZ;AA6JD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,GAC5B,iBAAiB,CAEnB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,CAAC,GACvC,CAAC,CAOH"}
|