@openclaw/fs-safe 0.11.0 → 0.13.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 +119 -0
- package/README.md +15 -8
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +3 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +3 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +4 -3
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +13 -9
- 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 +55 -46
- 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 +50 -17
- 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/bounded-read.js +2 -2
- 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/device-path.d.ts.map +1 -1
- package/dist/device-path.js +5 -3
- 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 +12 -2
- 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-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +9 -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 +1 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +85 -23
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +10 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +217 -33
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +16 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts +6 -0
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +67 -19
- package/dist/json-durable-queue-ownership.d.ts +8 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +134 -43
- 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 +6 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -0
- package/dist/json-durable-queue-read.js +61 -0
- package/dist/json-durable-queue.d.ts +1 -1
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +83 -134
- 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 +21 -0
- package/dist/native-binding.d.ts.map +1 -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-parent-admission.d.ts +31 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +124 -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 +0 -3
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +309 -40
- 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 +14 -0
- package/dist/path-scope-lexical.d.ts.map +1 -0
- package/dist/path-scope-lexical.js +37 -0
- 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 +35 -6
- 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 +152 -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 +33 -29
- 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 +29 -0
- package/dist/root-boundary.d.ts.map +1 -0
- package/dist/root-boundary.js +182 -0
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +89 -16
- 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 +182 -33
- 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 +316 -325
- 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-move-preflight.d.ts +8 -0
- package/dist/root-move-preflight.d.ts.map +1 -0
- package/dist/root-move-preflight.js +16 -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 +2 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +63 -22
- 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 +13 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +263 -73
- 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 -29
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +89 -178
- 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-walk.d.ts.map +1 -1
- package/dist/root-walk.js +2 -1
- 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-mode.d.ts +2 -0
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +20 -7
- 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 +18 -7
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +3 -1
- 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-windows.d.ts +10 -0
- package/dist/secure-file-windows.d.ts.map +1 -0
- package/dist/secure-file-windows.js +186 -0
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +98 -10
- 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 -1
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +181 -50
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -12
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +45 -15
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -3
- 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 +133 -4
- 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 +20 -16
- 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 +28 -1
- package/docs/archive.md +29 -2
- package/docs/atomic.md +27 -3
- package/docs/contributing.md +4 -1
- package/docs/copy.md +4 -1
- package/docs/durability.md +18 -2
- package/docs/errors.md +14 -6
- package/docs/file-store.md +30 -3
- package/docs/filename.md +21 -7
- package/docs/guest.md +5 -0
- package/docs/install-path.md +61 -15
- package/docs/install.md +12 -8
- 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 +28 -9
- package/docs/native.md +46 -10
- 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 +12 -1
- package/docs/permissions.md +26 -1
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +14 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +21 -4
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +21 -16
- package/docs/security-model.md +76 -9
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +27 -2
- package/docs/temp.md +262 -34
- package/docs/test-hooks.md +14 -0
- package/docs/writing.md +38 -8
- package/package.json +10 -10
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
1
2
|
import type { DirectoryReceipt } from "./directory-durability.js";
|
|
2
3
|
import type { FileIdentityStat } from "./file-identity.js";
|
|
4
|
+
import { type MutationDirectoryObservation } from "./pinned-mutation-observation.js";
|
|
3
5
|
import type { StagedFileReceipt } from "./staged-file-types.js";
|
|
4
|
-
type
|
|
6
|
+
export type StagedDirectorySnapshot = StagedFileReceipt["directory"];
|
|
7
|
+
export type PolicyStagedDirectory = Readonly<{
|
|
8
|
+
directory: StagedDirectorySnapshot;
|
|
9
|
+
observation: MutationDirectoryObservation;
|
|
10
|
+
stat: BigIntStats;
|
|
11
|
+
}>;
|
|
5
12
|
export declare function exactIdentityMatches(expected: FileIdentityStat, actual: Readonly<{
|
|
6
13
|
dev: bigint;
|
|
7
14
|
ino: bigint;
|
|
8
15
|
}>): boolean;
|
|
9
|
-
export declare function describeStagedDirectory(fd: number, pathname: string):
|
|
10
|
-
export declare function assertStagedDirectoryCurrent(receipt:
|
|
16
|
+
export declare function describeStagedDirectory(fd: number, pathname: string): StagedDirectorySnapshot;
|
|
17
|
+
export declare function assertStagedDirectoryCurrent(receipt: StagedDirectorySnapshot): BigIntStats;
|
|
18
|
+
export declare function describePolicyStagedDirectory(fd: number, pathname: string): PolicyStagedDirectory;
|
|
19
|
+
export declare function assertPolicyStagedDirectoryCurrent(captured: PolicyStagedDirectory): BigIntStats;
|
|
20
|
+
export declare function refreshPolicyStagedDirectoryObservation(captured: PolicyStagedDirectory): MutationDirectoryObservation;
|
|
11
21
|
export declare function openStagedDirectory(directory: string | DirectoryReceipt): {
|
|
12
22
|
fd: number;
|
|
13
|
-
receipt:
|
|
23
|
+
receipt: StagedDirectorySnapshot;
|
|
14
24
|
};
|
|
15
|
-
export {};
|
|
16
25
|
//# sourceMappingURL=staged-directory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,uBAAuB,CAAC;IACnC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC,CAAC;AAEH,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,uBAAuB,CAY7F;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CAS1F;AASD,wBAAgB,6BAA6B,CAC3C,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACf,qBAAqB,CAwBvB;AAED,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,qBAAqB,GAC9B,WAAW,CAOb;AAED,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,qBAAqB,GAC9B,4BAA4B,CAU9B;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,uBAAuB,CAAC;CAClC,CAiCA"}
|
package/dist/staged-directory.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
|
+
import { checkedMutationDirectory, } from "./pinned-mutation-observation.js";
|
|
4
6
|
import { realpathSync } from "./realpath.js";
|
|
7
|
+
import { resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
|
|
5
8
|
export function exactIdentityMatches(expected, actual) {
|
|
6
9
|
return ["dev", "ino"].every((key) => {
|
|
7
10
|
const value = expected[key];
|
|
@@ -14,7 +17,7 @@ export function describeStagedDirectory(fd, pathname) {
|
|
|
14
17
|
throw new FsSafeError("not-file", "staging parent must be a directory");
|
|
15
18
|
}
|
|
16
19
|
const receipt = Object.freeze({
|
|
17
|
-
path:
|
|
20
|
+
path: resolvePathPreservingWindowsRoot(pathname),
|
|
18
21
|
realPath: realpathSync(pathname),
|
|
19
22
|
identity: Object.freeze({ dev: identity.dev, ino: identity.ino }),
|
|
20
23
|
});
|
|
@@ -27,10 +30,58 @@ export function assertStagedDirectoryCurrent(receipt) {
|
|
|
27
30
|
realpathSync(receipt.path) !== receipt.realPath) {
|
|
28
31
|
throw new FsSafeError("path-mismatch", "staging directory pathname changed");
|
|
29
32
|
}
|
|
33
|
+
return current;
|
|
34
|
+
}
|
|
35
|
+
function sameDirectoryMetadata(left, right) {
|
|
36
|
+
return left.dev === right.dev && left.ino === right.ino && left.mode === right.mode &&
|
|
37
|
+
left.nlink === right.nlink;
|
|
38
|
+
}
|
|
39
|
+
// The policy hot path is Node/POSIX-only. It deliberately uses native
|
|
40
|
+
// canonicalization so a successful capture contains no JS realpath work.
|
|
41
|
+
export function describePolicyStagedDirectory(fd, pathname) {
|
|
42
|
+
const resolved = path.resolve(pathname);
|
|
43
|
+
const descriptor = fs.fstatSync(fd, { bigint: true });
|
|
44
|
+
if (!descriptor.isDirectory()) {
|
|
45
|
+
throw new FsSafeError("not-file", "staging parent must be a directory");
|
|
46
|
+
}
|
|
47
|
+
const before = inspectDirectoryIdentitySync(resolved);
|
|
48
|
+
const canonicalPath = path.resolve(realpathSync.native(resolved));
|
|
49
|
+
const after = inspectDirectoryIdentitySync(resolved, descriptor);
|
|
50
|
+
if (canonicalPath !== resolved || !sameDirectoryMetadata(descriptor, before) ||
|
|
51
|
+
!sameDirectoryMetadata(descriptor, after) ||
|
|
52
|
+
path.resolve(realpathSync.native(resolved)) !== canonicalPath) {
|
|
53
|
+
throw new FsSafeError("path-mismatch", "staging directory pathname changed");
|
|
54
|
+
}
|
|
55
|
+
const directory = Object.freeze({
|
|
56
|
+
path: resolved,
|
|
57
|
+
realPath: canonicalPath,
|
|
58
|
+
identity: Object.freeze({ dev: descriptor.dev, ino: descriptor.ino }),
|
|
59
|
+
});
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
directory,
|
|
62
|
+
observation: checkedMutationDirectory(resolved, canonicalPath, after),
|
|
63
|
+
stat: after,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export function assertPolicyStagedDirectoryCurrent(captured) {
|
|
67
|
+
const current = inspectDirectoryIdentitySync(captured.directory.path, captured.directory.identity);
|
|
68
|
+
if (!sameDirectoryMetadata(captured.stat, current) ||
|
|
69
|
+
path.resolve(realpathSync.native(captured.directory.path)) !== captured.directory.realPath) {
|
|
70
|
+
throw new FsSafeError("path-mismatch", "staging directory pathname changed");
|
|
71
|
+
}
|
|
72
|
+
return current;
|
|
73
|
+
}
|
|
74
|
+
export function refreshPolicyStagedDirectoryObservation(captured) {
|
|
75
|
+
const current = inspectDirectoryIdentitySync(captured.directory.path, captured.directory.identity);
|
|
76
|
+
const canonicalPath = path.resolve(realpathSync.native(captured.directory.path));
|
|
77
|
+
if (canonicalPath !== captured.directory.realPath) {
|
|
78
|
+
throw new FsSafeError("path-mismatch", "staging directory pathname changed");
|
|
79
|
+
}
|
|
80
|
+
return checkedMutationDirectory(captured.directory.path, canonicalPath, current);
|
|
30
81
|
}
|
|
31
82
|
export function openStagedDirectory(directory) {
|
|
32
83
|
// Copy supplied facts before any asynchronous work; receipts are not authority.
|
|
33
|
-
const pathname =
|
|
84
|
+
const pathname = resolvePathPreservingWindowsRoot(typeof directory === "string" ? directory : directory.path);
|
|
34
85
|
const expected = typeof directory === "string" ? undefined : {
|
|
35
86
|
realPath: directory.realPath, dev: directory.identity.dev, ino: directory.identity.ino,
|
|
36
87
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone-publication-path.d.ts","sourceRoot":"","sources":["../src/standalone-publication-path.ts"],"names":[],"mappings":"AAKA,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAItF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { anchorWindowsDriveRelativePath, assertNoWindowsPathAlias, } from "./windows-path-alias.js";
|
|
2
|
+
export function admitStandalonePublicationPath(value, message) {
|
|
3
|
+
const admittedPath = anchorWindowsDriveRelativePath(value);
|
|
4
|
+
assertNoWindowsPathAlias(admittedPath, "filesystem", message);
|
|
5
|
+
return admittedPath;
|
|
6
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BigIntStats, Stats } from "node:fs";
|
|
2
|
+
export type ExactStatIdentity = Pick<BigIntStats, "dev" | "ino">;
|
|
3
|
+
export type StatObservationReceipt = {
|
|
4
|
+
stat: Stats | BigIntStats;
|
|
5
|
+
identity: ExactStatIdentity;
|
|
6
|
+
};
|
|
7
|
+
/** Operation-local metadata plus exact identity; never use projected PathStat IDs. */
|
|
8
|
+
export declare function inspectStatObservationSync(inspect: (bigint: boolean) => Stats | BigIntStats, expected?: ExactStatIdentity, initial?: Stats | BigIntStats, platform?: NodeJS.Platform): StatObservationReceipt;
|
|
9
|
+
/** Revalidate an exact identity without allocating a receipt that the caller will discard. */
|
|
10
|
+
export declare function assertStatObservationSync(inspect: (bigint: boolean) => Stats | BigIntStats, expected: ExactStatIdentity, initial?: Stats | BigIntStats, platform?: NodeJS.Platform): Stats | BigIntStats;
|
|
11
|
+
//# sourceMappingURL=stat-observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stat-observation.d.ts","sourceRoot":"","sources":["../src/stat-observation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKlD,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AACjE,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,KAAK,GAAG,WAAW,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAqEF,sFAAsF;AACtF,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,KAAK,GAAG,WAAW,EACjD,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,OAAO,CAAC,EAAE,KAAK,GAAG,WAAW,EAC7B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,CAExB;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,KAAK,GAAG,WAAW,EACjD,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,CAAC,EAAE,KAAK,GAAG,WAAW,EAC7B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,KAAK,GAAG,WAAW,CAErB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
import { recordFileObservationFailure } from "./file-observation.js";
|
|
3
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
4
|
+
function identityMismatch() {
|
|
5
|
+
const error = new FsSafeError("path-mismatch", "file identity changed or could not be verified");
|
|
6
|
+
recordFileObservationFailure(error, "identity");
|
|
7
|
+
throw error;
|
|
8
|
+
}
|
|
9
|
+
function safeNumber(value) {
|
|
10
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
11
|
+
}
|
|
12
|
+
function safeExpected(identity) {
|
|
13
|
+
return typeof identity.dev === "bigint" && typeof identity.ino === "bigint" &&
|
|
14
|
+
identity.dev >= 0n && identity.ino >= 0n &&
|
|
15
|
+
identity.dev <= 9007199254740991n && identity.ino <= 9007199254740991n;
|
|
16
|
+
}
|
|
17
|
+
function observeStatSync(inspect, expected, initial, platform = process.platform, captureIdentity) {
|
|
18
|
+
if (platform === "win32" || (!initial && expected && !safeExpected(expected))) {
|
|
19
|
+
// Preserve the strict Windows unknown-ID retry, including known components
|
|
20
|
+
// and validation of the expected identity before the first observation.
|
|
21
|
+
const stat = inspectFileIdentitySync(() => {
|
|
22
|
+
const current = initial;
|
|
23
|
+
initial = undefined;
|
|
24
|
+
return (current ?? inspect(true));
|
|
25
|
+
}, expected, platform);
|
|
26
|
+
return (captureIdentity
|
|
27
|
+
? { stat, identity: expected ?? { dev: stat.dev, ino: stat.ino } }
|
|
28
|
+
: stat);
|
|
29
|
+
}
|
|
30
|
+
const stat = initial ?? inspect(false);
|
|
31
|
+
const { dev: rawDev, ino: rawIno } = stat;
|
|
32
|
+
const numericDev = safeNumber(rawDev);
|
|
33
|
+
const numericIno = safeNumber(rawIno);
|
|
34
|
+
const dev = numericDev ? BigInt(rawDev) : typeof rawDev === "bigint" ? rawDev : undefined;
|
|
35
|
+
const ino = numericIno ? BigInt(rawIno) : typeof rawIno === "bigint" ? rawIno : undefined;
|
|
36
|
+
if (expected && ((dev !== undefined && dev !== expected.dev) ||
|
|
37
|
+
(ino !== undefined && ino !== expected.ino)))
|
|
38
|
+
identityMismatch();
|
|
39
|
+
if (numericDev && numericIno) {
|
|
40
|
+
return (captureIdentity
|
|
41
|
+
? { stat, identity: expected ?? { dev: dev, ino: ino } }
|
|
42
|
+
: stat);
|
|
43
|
+
}
|
|
44
|
+
// An unsafe numeric component is unknown, not a rounded identity. Retain
|
|
45
|
+
// every exact component from this observation while obtaining the full ID.
|
|
46
|
+
const exact = inspectFileIdentitySync(() => {
|
|
47
|
+
const current = inspect(true);
|
|
48
|
+
if ((dev !== undefined && current.dev !== dev) ||
|
|
49
|
+
(ino !== undefined && current.ino !== ino))
|
|
50
|
+
identityMismatch();
|
|
51
|
+
return current;
|
|
52
|
+
}, expected, platform);
|
|
53
|
+
return (captureIdentity
|
|
54
|
+
? { stat: exact, identity: expected ?? { dev: exact.dev, ino: exact.ino } }
|
|
55
|
+
: exact);
|
|
56
|
+
}
|
|
57
|
+
/** Operation-local metadata plus exact identity; never use projected PathStat IDs. */
|
|
58
|
+
export function inspectStatObservationSync(inspect, expected, initial, platform = process.platform) {
|
|
59
|
+
return observeStatSync(inspect, expected, initial, platform, true);
|
|
60
|
+
}
|
|
61
|
+
/** Revalidate an exact identity without allocating a receipt that the caller will discard. */
|
|
62
|
+
export function assertStatObservationSync(inspect, expected, initial, platform = process.platform) {
|
|
63
|
+
return observeStatSync(inspect, expected, initial, platform, false);
|
|
64
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,
|
|
1
|
+
{"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,CAmCH"}
|
|
@@ -41,10 +41,47 @@ export async function inspectFileIdentity(inspect, expected, platform = process.
|
|
|
41
41
|
throw identityMismatch();
|
|
42
42
|
}
|
|
43
43
|
export function inspectFileIdentitySync(inspect, expected, platform = process.platform) {
|
|
44
|
-
|
|
44
|
+
let knownDev;
|
|
45
|
+
let knownIno;
|
|
46
|
+
if (expected) {
|
|
47
|
+
knownDev = expected.dev;
|
|
48
|
+
if (typeof knownDev !== "bigint")
|
|
49
|
+
throw identityMismatch();
|
|
50
|
+
knownIno = expected.ino;
|
|
51
|
+
if (typeof knownIno !== "bigint")
|
|
52
|
+
throw identityMismatch();
|
|
53
|
+
// An unknown expected device must not bypass observation of its inode.
|
|
54
|
+
if (platform === "win32" && (knownDev === 0n || knownIno === 0n))
|
|
55
|
+
throw identityMismatch();
|
|
56
|
+
}
|
|
45
57
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
46
58
|
const stat = inspect();
|
|
47
|
-
|
|
59
|
+
let complete = true;
|
|
60
|
+
// Keep dev-before-ino access and early mismatch errors without allocating
|
|
61
|
+
// a checker closure, known-component object, or field array per inspection.
|
|
62
|
+
const dev = stat.dev;
|
|
63
|
+
if (typeof dev !== "bigint")
|
|
64
|
+
throw identityMismatch();
|
|
65
|
+
if (platform === "win32" && dev === 0n) {
|
|
66
|
+
complete = false;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (knownDev !== undefined && knownDev !== dev)
|
|
70
|
+
throw identityMismatch();
|
|
71
|
+
knownDev = dev;
|
|
72
|
+
}
|
|
73
|
+
const ino = stat.ino;
|
|
74
|
+
if (typeof ino !== "bigint")
|
|
75
|
+
throw identityMismatch();
|
|
76
|
+
if (platform === "win32" && ino === 0n) {
|
|
77
|
+
complete = false;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (knownIno !== undefined && knownIno !== ino)
|
|
81
|
+
throw identityMismatch();
|
|
82
|
+
knownIno = ino;
|
|
83
|
+
}
|
|
84
|
+
if (complete)
|
|
48
85
|
return stat;
|
|
49
86
|
}
|
|
50
87
|
throw identityMismatch();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAqCF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,6BAA6B,GACpC,IAAI,CA6BN"}
|
package/dist/symlink-parents.js
CHANGED
|
@@ -2,9 +2,14 @@ import fsSync from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { hasNodeErrorCode, isPathRelativeEscape } from "./path.js";
|
|
5
|
+
import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
5
6
|
function resolvePathWalk(params) {
|
|
6
|
-
const
|
|
7
|
-
|
|
7
|
+
const rawRootDir = params.rootDir;
|
|
8
|
+
assertNoWindowsPathAlias(rawRootDir, "filesystem", "root dir uses a Windows filesystem namespace alias");
|
|
9
|
+
const rawTargetPath = params.targetPath;
|
|
10
|
+
assertNoWindowsPathAlias(rawTargetPath, "filesystem", "target path uses a Windows filesystem namespace alias");
|
|
11
|
+
const root = resolvePathPreservingWindowsRoot(rawRootDir);
|
|
12
|
+
const target = resolvePathPreservingWindowsRoot(rawTargetPath);
|
|
8
13
|
const relative = path.relative(root, target);
|
|
9
14
|
if (isPathRelativeEscape(relative)) {
|
|
10
15
|
if (params.allowOutsideRoot) {
|
package/dist/temp-target.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
|
+
import type { TempWorkspaceCleanupSafety } from "./temp-workspace-owner.js";
|
|
2
3
|
export type TempFile = {
|
|
3
4
|
dir: string;
|
|
4
5
|
path: string;
|
|
@@ -11,6 +12,7 @@ type TempFileOptions = {
|
|
|
11
12
|
prefix: string;
|
|
12
13
|
fileName?: string;
|
|
13
14
|
onCleanupError?: (error: unknown) => void;
|
|
15
|
+
cleanupSafety?: TempWorkspaceCleanupSafety;
|
|
14
16
|
};
|
|
15
17
|
export declare function sanitizeTempFileName(fileName: string): string;
|
|
16
18
|
export declare function buildRandomTempFilePath(params: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,SAAS,CAAC;AAa7B,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,2BAA2B,CAAC;AAEnC,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;IAC1C,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C,CAAC;AA8DF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK7D;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,CAeT;AAwDD,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,CAmID;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
|
@@ -2,10 +2,12 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fsSync from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { suffixWindowsReservedDeviceName } from "./filename.js";
|
|
5
6
|
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
6
7
|
import { assertSafePathSegment, sanitizeSafePathSegment, trimHyphenEdges } from "./safe-path-segment.js";
|
|
7
8
|
import { resolveSecureTempRoot } from "./secure-temp-dir.js";
|
|
8
9
|
import { registerTempPathForExit } from "./temp-cleanup.js";
|
|
10
|
+
import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
9
11
|
const HYPHEN_CHAR_CODE = 0x2d;
|
|
10
12
|
const DOT_CHAR_CODE = 0x2e;
|
|
11
13
|
const NUMBER_ZERO_CHAR_CODE = 0x30;
|
|
@@ -57,9 +59,10 @@ function sanitizeExtension(extension) {
|
|
|
57
59
|
return token ? `.${token}` : "";
|
|
58
60
|
}
|
|
59
61
|
export function sanitizeTempFileName(fileName) {
|
|
60
|
-
|
|
62
|
+
const sanitized = sanitizeSafePathSegment(path.basename(fileName), "download.bin", {
|
|
61
63
|
allowDotPrefix: true,
|
|
62
64
|
});
|
|
65
|
+
return suffixWindowsReservedDeviceName(sanitized);
|
|
63
66
|
}
|
|
64
67
|
export function buildRandomTempFilePath(params) {
|
|
65
68
|
const rootDir = resolveTempRoot(params.rootDir);
|
|
@@ -72,7 +75,9 @@ export function buildRandomTempFilePath(params) {
|
|
|
72
75
|
const uuid = params.uuid
|
|
73
76
|
? assertSafePathSegment(params.uuid.trim(), { label: "temp uuid" })
|
|
74
77
|
: crypto.randomUUID();
|
|
75
|
-
|
|
78
|
+
const filePath = path.join(rootDir, `${prefix}-${now}-${uuid}${extension}`);
|
|
79
|
+
assertNoWindowsPathAlias(filePath, "filesystem", "temp file path uses a Windows filesystem namespace alias");
|
|
80
|
+
return filePath;
|
|
76
81
|
}
|
|
77
82
|
function isNodeErrorWithCode(err, code) {
|
|
78
83
|
return (typeof err === "object" &&
|
|
@@ -104,17 +109,141 @@ async function cleanupTempDir(dir, identity, onCleanupError) {
|
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
function resolveTempRoot(rootDir) {
|
|
107
|
-
|
|
112
|
+
if (rootDir !== undefined) {
|
|
113
|
+
assertNoWindowsPathAlias(rootDir, "filesystem", "temp root uses a Windows filesystem namespace alias");
|
|
114
|
+
}
|
|
115
|
+
const selectedRoot = rootDir ?? resolveSecureTempRoot({ fallbackPrefix: "fs-safe" });
|
|
116
|
+
assertNoWindowsPathAlias(selectedRoot, "filesystem", "temp root uses a Windows filesystem namespace alias");
|
|
117
|
+
const resolvedRoot = resolvePathPreservingWindowsRoot(selectedRoot);
|
|
118
|
+
assertNoWindowsPathAlias(resolvedRoot, "filesystem", "temp root uses a Windows filesystem namespace alias");
|
|
119
|
+
return resolvedRoot;
|
|
120
|
+
}
|
|
121
|
+
function resolveTempFileCleanupSafety(value) {
|
|
122
|
+
if (value === undefined || value === "compatible")
|
|
123
|
+
return "compatible";
|
|
124
|
+
if (value === "require-bounded")
|
|
125
|
+
return value;
|
|
126
|
+
throw new TypeError("cleanupSafety must be compatible or require-bounded");
|
|
108
127
|
}
|
|
109
128
|
export async function createOwnedTempFile(params) {
|
|
129
|
+
const cleanupSafety = resolveTempFileCleanupSafety(params.cleanupSafety);
|
|
110
130
|
const rootDir = resolveTempRoot(params.rootDir);
|
|
111
131
|
const prefix = `${sanitizePrefix(params.prefix)}-`;
|
|
132
|
+
if (cleanupSafety === "require-bounded") {
|
|
133
|
+
const initialFileName = sanitizeTempFileName(params.fileName ?? "download.bin");
|
|
134
|
+
const [{ TempWorkspaceCleanupCapability, TempWorkspaceCleanupOwner }, { admitExistingTempWorkspaceRoot }, { TempWorkspaceRetainedChild }, { validateInitialTempWorkspaceChild, admitRetainedTempWorkspaceChild }, { inspectDirectoryIdentitySync },] = await Promise.all([
|
|
135
|
+
import("./temp-workspace-owner.js"),
|
|
136
|
+
import("./temp-workspace-admission.js"),
|
|
137
|
+
import("./temp-workspace-descriptor.js"),
|
|
138
|
+
import("./temp-workspace-child-admission.js"),
|
|
139
|
+
import("./directory-guard.js"),
|
|
140
|
+
]);
|
|
141
|
+
const admission = admitExistingTempWorkspaceRoot(rootDir);
|
|
142
|
+
const childPrefix = path.join(admission.dir, prefix);
|
|
143
|
+
assertNoWindowsPathAlias(childPrefix, "filesystem", "temp directory uses a Windows filesystem namespace alias");
|
|
144
|
+
const capability = new TempWorkspaceCleanupCapability(admission.dir, cleanupSafety, admission, 0o700);
|
|
145
|
+
let dir;
|
|
146
|
+
let initialPath;
|
|
147
|
+
let identity;
|
|
148
|
+
let retainedChild;
|
|
149
|
+
let cleanupOwner;
|
|
150
|
+
let unregisterTempDir;
|
|
151
|
+
try {
|
|
152
|
+
capability.prepareChildCreation();
|
|
153
|
+
dir = await fs.mkdtemp(childPrefix);
|
|
154
|
+
assertNoWindowsPathAlias(dir, "filesystem", "temp directory uses a Windows filesystem namespace alias");
|
|
155
|
+
initialPath = path.join(dir, initialFileName);
|
|
156
|
+
assertNoWindowsPathAlias(initialPath, "filesystem", "temp file path uses a Windows filesystem namespace alias");
|
|
157
|
+
capability.assertCurrent();
|
|
158
|
+
const initial = inspectDirectoryIdentitySync(dir);
|
|
159
|
+
identity = Object.freeze({ dev: initial.dev, ino: initial.ino });
|
|
160
|
+
const needsModeInitialization = validateInitialTempWorkspaceChild(initial, admission.ownerUid, 0o700);
|
|
161
|
+
retainedChild = TempWorkspaceRetainedChild.retain(dir, identity);
|
|
162
|
+
const modeInitialization = admitRetainedTempWorkspaceChild(retainedChild, needsModeInitialization, admission, 0o700);
|
|
163
|
+
if (modeInitialization)
|
|
164
|
+
await modeInitialization;
|
|
165
|
+
const retainDescriptor = capability.admitChildDescriptor(retainedChild.ensureReadable());
|
|
166
|
+
capability.assertAncestryCurrent();
|
|
167
|
+
retainedChild.finalizeAdmission(admission.ownerUid, 0o700);
|
|
168
|
+
cleanupOwner = new TempWorkspaceCleanupOwner(retainedChild, capability, retainDescriptor);
|
|
169
|
+
retainedChild = undefined;
|
|
170
|
+
unregisterTempDir = registerTempPathForExit(dir, {
|
|
171
|
+
cleanupSync: () => cleanupOwner.cleanupSync(),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
try {
|
|
176
|
+
if (cleanupOwner)
|
|
177
|
+
cleanupOwner.cleanupSync();
|
|
178
|
+
else {
|
|
179
|
+
const closeErrors = [];
|
|
180
|
+
try {
|
|
181
|
+
retainedChild?.close();
|
|
182
|
+
}
|
|
183
|
+
catch (closeError) {
|
|
184
|
+
closeErrors.push(closeError);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
capability.close();
|
|
188
|
+
}
|
|
189
|
+
catch (closeError) {
|
|
190
|
+
closeErrors.push(closeError);
|
|
191
|
+
}
|
|
192
|
+
if (closeErrors.length === 1)
|
|
193
|
+
throw closeErrors[0];
|
|
194
|
+
if (closeErrors.length > 1) {
|
|
195
|
+
throw new AggregateError(closeErrors, "temp file admission descriptor close failed");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (cleanupError) {
|
|
200
|
+
throw new AggregateError([error, cleanupError], "temp file creation and cleanup both failed");
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
const owner = cleanupOwner;
|
|
205
|
+
const file = (fileName) => {
|
|
206
|
+
const filePath = path.join(dir, sanitizeTempFileName(fileName ?? params.fileName ?? "download.bin"));
|
|
207
|
+
assertNoWindowsPathAlias(filePath, "filesystem", "temp file path uses a Windows filesystem namespace alias");
|
|
208
|
+
return filePath;
|
|
209
|
+
};
|
|
210
|
+
const cleanup = async () => {
|
|
211
|
+
try {
|
|
212
|
+
try {
|
|
213
|
+
await owner.cleanup();
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
if (!isNodeErrorWithCode(err, "ENOENT")) {
|
|
217
|
+
params.onCleanupError?.(err);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
unregisterTempDir();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
target: {
|
|
227
|
+
dir,
|
|
228
|
+
path: initialPath,
|
|
229
|
+
file,
|
|
230
|
+
cleanup,
|
|
231
|
+
[Symbol.asyncDispose]: cleanup,
|
|
232
|
+
},
|
|
233
|
+
identity,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
112
236
|
const dir = await fs.mkdtemp(path.join(rootDir, prefix));
|
|
237
|
+
assertNoWindowsPathAlias(dir, "filesystem", "temp directory uses a Windows filesystem namespace alias");
|
|
113
238
|
// Windows file indexes can exceed Number.MAX_SAFE_INTEGER. Cleanup receipts
|
|
114
239
|
// must retain the exact identity or adjacent directories can compare equal.
|
|
115
240
|
const identity = fsSync.lstatSync(dir, { bigint: true });
|
|
116
241
|
const unregisterTempDir = registerTempPathForExit(dir, { recursive: true, identity });
|
|
117
|
-
const file = (fileName) =>
|
|
242
|
+
const file = (fileName) => {
|
|
243
|
+
const filePath = path.join(dir, sanitizeTempFileName(fileName ?? params.fileName ?? "download.bin"));
|
|
244
|
+
assertNoWindowsPathAlias(filePath, "filesystem", "temp file path uses a Windows filesystem namespace alias");
|
|
245
|
+
return filePath;
|
|
246
|
+
};
|
|
118
247
|
const cleanup = async () => {
|
|
119
248
|
try {
|
|
120
249
|
await cleanupTempDir(dir, identity, params.onCleanupError);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type ExactIdentity = Readonly<{
|
|
2
|
+
dev: bigint;
|
|
3
|
+
ino: bigint;
|
|
4
|
+
}>;
|
|
5
|
+
export type TempWorkspaceRootAdmission = {
|
|
6
|
+
dir: string;
|
|
7
|
+
identity: ExactIdentity;
|
|
8
|
+
ownerUid: number | undefined;
|
|
9
|
+
realPath: string;
|
|
10
|
+
retainCleanupParent(descriptorFd: number): void;
|
|
11
|
+
prepareCleanupProbe(descriptorFd: number): void;
|
|
12
|
+
prepareChildCreation(descriptorFd?: number): void;
|
|
13
|
+
assertCurrent(): void;
|
|
14
|
+
assertAncestry(): void;
|
|
15
|
+
associateCurrent(descriptorFd: number): void;
|
|
16
|
+
associateAncestry(descriptorFd: number): void;
|
|
17
|
+
};
|
|
18
|
+
export declare function admitExistingTempWorkspaceRoot(rootDir: string): TempWorkspaceRootAdmission;
|
|
19
|
+
export declare function admitTempWorkspaceRoot(rootDir: string): Promise<TempWorkspaceRootAdmission>;
|
|
20
|
+
export declare function admitTempWorkspaceRootSync(rootDir: string): TempWorkspaceRootAdmission;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=temp-workspace-admission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-workspace-admission.d.ts","sourceRoot":"","sources":["../src/temp-workspace-admission.ts"],"names":[],"mappings":"AA6BA,KAAK,aAAa,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAiB5D,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,oBAAoB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,aAAa,IAAI,IAAI,CAAC;IACtB,cAAc,IAAI,IAAI,CAAC;IACvB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C,CAAC;AA0UF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,CAM1F;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAyBjG;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,CAsBtF"}
|