@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
|
@@ -2,12 +2,11 @@ import { randomUUID } 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 { createAsyncDirectoryGuard, createSyncDirectoryGuard } from "./directory-guard.js";
|
|
6
5
|
import { FsSafeError } from "./errors.js";
|
|
6
|
+
import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
|
|
7
7
|
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
8
|
-
import { withAsyncDirectoryGuards, withSyncDirectoryGuards } from "./guarded-mutation.js";
|
|
9
8
|
import { getNativeBinding } from "./native.js";
|
|
10
|
-
import {
|
|
9
|
+
import { openTempWorkspaceCleanupParent, TempWorkspaceRetainedChild, } from "./temp-workspace-descriptor.js";
|
|
11
10
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
12
11
|
function isNativeCleanupBinding(binding) {
|
|
13
12
|
return typeof binding?.renameNoReplace === "function" &&
|
|
@@ -25,9 +24,27 @@ function nativeRemovalError(result) {
|
|
|
25
24
|
export class TempWorkspaceCleanupCapability {
|
|
26
25
|
binding;
|
|
27
26
|
parent;
|
|
27
|
+
#admission;
|
|
28
|
+
#safety;
|
|
28
29
|
#ownedTreeRemovalAvailable;
|
|
29
|
-
#
|
|
30
|
-
constructor(root, safety) {
|
|
30
|
+
#phase = "new";
|
|
31
|
+
constructor(root, safety, admission, dirMode) {
|
|
32
|
+
assertNoWindowsPathAlias(root, "filesystem");
|
|
33
|
+
const admittedRoot = admission.dir;
|
|
34
|
+
assertNoWindowsPathAlias(admittedRoot, "filesystem");
|
|
35
|
+
const resolvedRoot = resolvePathPreservingWindowsRoot(root);
|
|
36
|
+
assertNoWindowsPathAlias(resolvedRoot, "filesystem");
|
|
37
|
+
if (resolvedRoot !== resolvePathPreservingWindowsRoot(admittedRoot)) {
|
|
38
|
+
throw new FsSafeError("path-mismatch", "temp workspace cleanup parent differs from admitted root");
|
|
39
|
+
}
|
|
40
|
+
this.#admission = admission;
|
|
41
|
+
this.#safety = safety;
|
|
42
|
+
// POSIX enumeration reopens fd-relative ".", so retaining O_RDONLY before
|
|
43
|
+
// chmod cannot supply read/search authority that the final mode removes.
|
|
44
|
+
const childModeAllowsRemoval = process.platform === "win32" || (dirMode & 0o500) === 0o500;
|
|
45
|
+
if (safety === "require-bounded" && !childModeAllowsRemoval) {
|
|
46
|
+
throw new FsSafeError("helper-unavailable", "temp workspace owned-tree cleanup requires owner read and search in dirMode");
|
|
47
|
+
}
|
|
31
48
|
let binding;
|
|
32
49
|
try {
|
|
33
50
|
binding = getNativeBinding();
|
|
@@ -39,47 +56,104 @@ export class TempWorkspaceCleanupCapability {
|
|
|
39
56
|
this.binding = binding;
|
|
40
57
|
let parent;
|
|
41
58
|
try {
|
|
42
|
-
parent =
|
|
43
|
-
assertStagedDirectoryCurrent(parent.receipt);
|
|
59
|
+
parent = openTempWorkspaceCleanupParent(root, admission);
|
|
44
60
|
}
|
|
45
61
|
catch {
|
|
46
62
|
if (parent)
|
|
47
63
|
fsSync.closeSync(parent.fd);
|
|
48
64
|
parent = undefined;
|
|
49
65
|
}
|
|
50
|
-
this.parent = parent;
|
|
51
66
|
let available = false;
|
|
52
|
-
if (parent && isNativeCleanupBinding(binding)) {
|
|
67
|
+
if (childModeAllowsRemoval && parent?.access === "read" && isNativeCleanupBinding(binding)) {
|
|
68
|
+
let probeReady = false;
|
|
69
|
+
try {
|
|
70
|
+
admission.prepareCleanupProbe(parent.fd);
|
|
71
|
+
probeReady = true;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// A descriptor that cannot be associated even provisionally must not
|
|
75
|
+
// reach native code or remain available to compatible cleanup.
|
|
76
|
+
try {
|
|
77
|
+
fsSync.closeSync(parent.fd);
|
|
78
|
+
}
|
|
79
|
+
catch (closeError) {
|
|
80
|
+
throw new AggregateError([error, closeError], "temp workspace cleanup parent probe admission and close failed");
|
|
81
|
+
}
|
|
82
|
+
parent = undefined;
|
|
83
|
+
}
|
|
53
84
|
try {
|
|
54
|
-
|
|
85
|
+
if (probeReady && parent) {
|
|
86
|
+
available = binding.ownedTreeRemovalAvailable(parent.fd) === true;
|
|
87
|
+
}
|
|
55
88
|
}
|
|
56
89
|
catch {
|
|
57
90
|
// Runtime denial must select fallback or reject before child creation.
|
|
58
91
|
}
|
|
59
92
|
}
|
|
93
|
+
this.parent = parent;
|
|
60
94
|
this.#ownedTreeRemovalAvailable = available;
|
|
61
|
-
if (safety === "require-bounded" && !this
|
|
95
|
+
if (safety === "require-bounded" && !this.#ownedTreeRemovalAvailable) {
|
|
62
96
|
this.close();
|
|
63
97
|
throw new FsSafeError("helper-unavailable", "temp workspace owned-tree cleanup is unavailable");
|
|
64
98
|
}
|
|
65
99
|
}
|
|
66
100
|
get canRemoveOwnedTree() {
|
|
67
|
-
return
|
|
101
|
+
return (this.#phase === "ready" || this.#phase === "sealed") &&
|
|
102
|
+
this.#ownedTreeRemovalAvailable;
|
|
68
103
|
}
|
|
69
|
-
|
|
70
|
-
|
|
104
|
+
prepareChildCreation() {
|
|
105
|
+
const replay = this.#phase === "ready";
|
|
106
|
+
if (this.#phase !== "new" && !replay) {
|
|
107
|
+
throw new FsSafeError("path-mismatch", "temp workspace cleanup parent is unavailable");
|
|
108
|
+
}
|
|
109
|
+
// A failed initial admission or receipt replay is terminal: no earlier
|
|
110
|
+
// authority may survive a partial revalidation.
|
|
111
|
+
this.#phase = "failed";
|
|
112
|
+
if (replay) {
|
|
113
|
+
if (this.parent)
|
|
114
|
+
this.#admission.associateAncestry(this.parent.fd);
|
|
115
|
+
else
|
|
116
|
+
this.#admission.assertAncestry();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.#admission.prepareChildCreation(this.parent?.fd);
|
|
120
|
+
}
|
|
121
|
+
// The retained descriptor cannot authorize cleanup until the complete
|
|
122
|
+
// ancestry and its exact descriptor association succeeded together.
|
|
123
|
+
this.#phase = "ready";
|
|
124
|
+
}
|
|
125
|
+
#assertCurrent(ancestry) {
|
|
126
|
+
if ((this.#phase !== "ready" && this.#phase !== "sealed") || !this.parent) {
|
|
127
|
+
throw new FsSafeError("path-mismatch", "temp workspace cleanup parent is unavailable");
|
|
128
|
+
}
|
|
129
|
+
if (ancestry)
|
|
130
|
+
this.#admission.associateAncestry(this.parent.fd);
|
|
131
|
+
else
|
|
132
|
+
this.#admission.associateCurrent(this.parent.fd);
|
|
133
|
+
}
|
|
134
|
+
admitChildDescriptor(canEnumerate) {
|
|
135
|
+
if (this.#phase !== "ready") {
|
|
71
136
|
throw new FsSafeError("path-mismatch", "temp workspace cleanup parent is unavailable");
|
|
72
137
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
138
|
+
// From this point the capability belongs to this successfully created
|
|
139
|
+
// child. Collision retries and further preparation must remain impossible.
|
|
140
|
+
this.#phase = "sealed";
|
|
141
|
+
const bounded = this.canRemoveOwnedTree && canEnumerate;
|
|
142
|
+
if (this.#safety === "require-bounded" && !bounded) {
|
|
143
|
+
throw new FsSafeError("helper-unavailable", "temp workspace owned-tree cleanup requires a readable child descriptor");
|
|
77
144
|
}
|
|
145
|
+
return bounded;
|
|
146
|
+
}
|
|
147
|
+
assertCurrent() {
|
|
148
|
+
this.#assertCurrent(false);
|
|
149
|
+
}
|
|
150
|
+
assertAncestryCurrent() {
|
|
151
|
+
this.#assertCurrent(true);
|
|
78
152
|
}
|
|
79
153
|
close() {
|
|
80
|
-
if (this.#closed)
|
|
154
|
+
if (this.#phase === "closed")
|
|
81
155
|
return;
|
|
82
|
-
this.#
|
|
156
|
+
this.#phase = "closed";
|
|
83
157
|
if (this.parent)
|
|
84
158
|
fsSync.closeSync(this.parent.fd);
|
|
85
159
|
}
|
|
@@ -94,21 +168,12 @@ export class TempWorkspaceCleanupOwner {
|
|
|
94
168
|
#exitInterrupted = false;
|
|
95
169
|
#result;
|
|
96
170
|
#pending;
|
|
97
|
-
constructor(
|
|
98
|
-
|
|
99
|
-
this.#
|
|
171
|
+
constructor(retained, capability, retainDescriptor) {
|
|
172
|
+
const child = retained.transfer(retainDescriptor);
|
|
173
|
+
this.#dir = child.dir;
|
|
174
|
+
this.#identity = child.identity;
|
|
100
175
|
this.#capability = capability;
|
|
101
|
-
|
|
102
|
-
if (capability.canRemoveOwnedTree) {
|
|
103
|
-
directory = openStagedDirectory(dir);
|
|
104
|
-
const current = fsSync.fstatSync(directory.fd, { bigint: true });
|
|
105
|
-
if (!sameFileIdentityForCleanup(current, this.#identity)) {
|
|
106
|
-
fsSync.closeSync(directory.fd);
|
|
107
|
-
capability.close();
|
|
108
|
-
throw new FsSafeError("path-mismatch", "temp workspace changed while retaining cleanup authority");
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
this.#directory = directory;
|
|
176
|
+
this.#directory = child.directory;
|
|
112
177
|
}
|
|
113
178
|
#repeat() {
|
|
114
179
|
return this.#result === "removed" ? "missing" : this.#result;
|
|
@@ -186,11 +251,10 @@ export class TempWorkspaceCleanupOwner {
|
|
|
186
251
|
this.#capability.binding.renameNoReplace(parent.fd, path.basename(this.#dir), parent.fd, name);
|
|
187
252
|
}
|
|
188
253
|
else {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
254
|
+
// The admitted receipt is exact and descriptor-associated. Reuse it as
|
|
255
|
+
// the pre/post parent fence instead of layering a numeric guard over it.
|
|
256
|
+
this.#capability.assertCurrent();
|
|
257
|
+
fsSync.renameSync(this.#dir, quarantinePath);
|
|
194
258
|
}
|
|
195
259
|
this.#capability.assertCurrent();
|
|
196
260
|
const quarantined = fsSync.lstatSync(quarantinePath, { bigint: true });
|
|
@@ -232,17 +296,15 @@ export class TempWorkspaceCleanupOwner {
|
|
|
232
296
|
}
|
|
233
297
|
let removalError;
|
|
234
298
|
try {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
});
|
|
299
|
+
this.#assertQuarantine(quarantine);
|
|
300
|
+
try {
|
|
301
|
+
await fs.rm(quarantine.path, { recursive: true, force: true });
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
removalError = error;
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
this.#capability.assertCurrent();
|
|
246
308
|
return "removed";
|
|
247
309
|
}
|
|
248
310
|
catch (error) {
|
|
@@ -258,17 +320,15 @@ export class TempWorkspaceCleanupOwner {
|
|
|
258
320
|
}
|
|
259
321
|
let removalError;
|
|
260
322
|
try {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
});
|
|
323
|
+
this.#assertQuarantine(quarantine);
|
|
324
|
+
try {
|
|
325
|
+
fsSync.rmSync(quarantine.path, { recursive: true, force: true });
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
removalError = error;
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
this.#capability.assertCurrent();
|
|
272
332
|
return "removed";
|
|
273
333
|
}
|
|
274
334
|
catch (error) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BigIntStats, Stats } from "node:fs";
|
|
2
|
+
export declare function validateTempWorkspaceDirMode(mode: number): void;
|
|
3
|
+
export declare function assertTrustedTempWorkspaceDirectory(stat: Pick<BigIntStats, "uid" | "mode"> | Pick<Stats, "uid" | "mode">, uid: number | undefined, child?: boolean): void;
|
|
4
|
+
//# sourceMappingURL=temp-workspace-permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-workspace-permissions.d.ts","sourceRoot":"","sources":["../src/temp-workspace-permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGlD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO/D;AAED,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,EACrE,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,KAAK,UAAQ,GACZ,IAAI,CAwBN"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
export function validateTempWorkspaceDirMode(mode) {
|
|
3
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
|
|
4
|
+
throw new FsSafeError("insecure-permissions", "temp workspace dirMode must be permission bits");
|
|
5
|
+
}
|
|
6
|
+
if (process.platform !== "win32" && (mode & 0o022) !== 0) {
|
|
7
|
+
throw new FsSafeError("insecure-permissions", "temp workspace must not be group/world writable");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function assertTrustedTempWorkspaceDirectory(stat, uid, child = false) {
|
|
11
|
+
if (uid === undefined)
|
|
12
|
+
return;
|
|
13
|
+
const ownedByUser = typeof stat.uid === "bigint" ? stat.uid === BigInt(uid) : stat.uid === uid;
|
|
14
|
+
const ownedByRoot = typeof stat.uid === "bigint" ? stat.uid === 0n : stat.uid === 0;
|
|
15
|
+
if (!ownedByUser && (child || !ownedByRoot)) {
|
|
16
|
+
throw new FsSafeError("not-owned", "temp workspace directory has an untrusted owner");
|
|
17
|
+
}
|
|
18
|
+
// A root/current-user-owned sticky directory protects children owned by us,
|
|
19
|
+
// including the usual shared system temp directory. Never chmod that parent.
|
|
20
|
+
const writable = typeof stat.mode === "bigint"
|
|
21
|
+
? (stat.mode & 18n) !== 0n
|
|
22
|
+
: Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o022) !== 0;
|
|
23
|
+
const sticky = typeof stat.mode === "bigint"
|
|
24
|
+
? (stat.mode & 512n) !== 0n
|
|
25
|
+
: Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o1000) !== 0;
|
|
26
|
+
if (typeof stat.mode !== "bigint" && (!Number.isSafeInteger(stat.mode) || stat.mode < 0)) {
|
|
27
|
+
throw new FsSafeError("insecure-permissions", "temp workspace directory permissions are invalid");
|
|
28
|
+
}
|
|
29
|
+
if (writable && (child || !sticky)) {
|
|
30
|
+
throw new FsSafeError("insecure-permissions", "temp workspace directory is group/world writable without sticky protection");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FileStore, FileStoreSync } from "./file-store.js";
|
|
2
|
+
import type { TempPathIdentityReceipt } from "./temp-cleanup.js";
|
|
3
|
+
import type { TempWorkspaceCleanupResult, TempWorkspaceCleanupSafety } from "./temp-workspace-owner.js";
|
|
4
|
+
export type TempWorkspaceOptions = {
|
|
5
|
+
rootDir: string;
|
|
6
|
+
prefix: string;
|
|
7
|
+
dirMode?: number;
|
|
8
|
+
mode?: number;
|
|
9
|
+
cleanupSafety?: TempWorkspaceCleanupSafety;
|
|
10
|
+
};
|
|
11
|
+
export type TempWorkspace = {
|
|
12
|
+
dir: string;
|
|
13
|
+
identity: TempPathIdentityReceipt;
|
|
14
|
+
store: FileStore;
|
|
15
|
+
path(fileName: string): string;
|
|
16
|
+
write(fileName: string, data: string | Uint8Array): Promise<string>;
|
|
17
|
+
writeText(fileName: string, data: string): Promise<string>;
|
|
18
|
+
writeJson(fileName: string, data: unknown, options?: {
|
|
19
|
+
trailingNewline?: boolean;
|
|
20
|
+
}): Promise<string>;
|
|
21
|
+
copyIn(fileName: string, sourcePath: string): Promise<string>;
|
|
22
|
+
read(fileName: string): Promise<Buffer>;
|
|
23
|
+
cleanup(): Promise<TempWorkspaceCleanupResult>;
|
|
24
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
export type TempWorkspaceSync = {
|
|
27
|
+
dir: string;
|
|
28
|
+
identity: TempPathIdentityReceipt;
|
|
29
|
+
store: FileStoreSync;
|
|
30
|
+
path(fileName: string): string;
|
|
31
|
+
write(fileName: string, data: string | Uint8Array): string;
|
|
32
|
+
writeText(fileName: string, data: string): string;
|
|
33
|
+
writeJson(fileName: string, data: unknown, options?: {
|
|
34
|
+
trailingNewline?: boolean;
|
|
35
|
+
}): string;
|
|
36
|
+
read(fileName: string): Buffer;
|
|
37
|
+
cleanup(): TempWorkspaceCleanupResult;
|
|
38
|
+
[Symbol.dispose](): void;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=temp-workspace-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-workspace-types.d.ts","sourceRoot":"","sources":["../src/temp-workspace-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAExG,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,SAAS,CACP,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC/C,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,uBAAuB,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAC3D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CAAC;IAC5F,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,IAAI,0BAA0B,CAAC;IACtC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/temp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { tempWorkspace, type TempWorkspace, type TempWorkspaceOptions, type TempWorkspaceCleanupResult, type TempWorkspaceCleanupSafety, tempWorkspaceSync, type TempWorkspaceSync, withTempWorkspace, withTempWorkspaceSync, } from "./private-temp-workspace.js";
|
|
2
2
|
export type { TempPathIdentityReceipt } from "./temp-cleanup.js";
|
|
3
|
-
export { resolveSecureTempRoot, type ResolveSecureTempRootOptions } from "./secure-temp-dir.js";
|
|
3
|
+
export { resolveSecureTempRoot, type ResolveSecureTempRootOptions, type SecureTempRootDescriptorAdapter, } from "./secure-temp-dir.js";
|
|
4
4
|
//# sourceMappingURL=temp.d.ts.map
|
package/dist/temp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp.d.ts","sourceRoot":"","sources":["../src/temp.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,
|
|
1
|
+
{"version":3,"file":"temp.d.ts","sourceRoot":"","sources":["../src/temp.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,GACrC,MAAM,sBAAsB,CAAC"}
|
package/dist/temp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { tempWorkspace, tempWorkspaceSync, withTempWorkspace, withTempWorkspaceSync, } from "./private-temp-workspace.js";
|
|
2
|
-
export { resolveSecureTempRoot } from "./secure-temp-dir.js";
|
|
2
|
+
export { resolveSecureTempRoot, } from "./secure-temp-dir.js";
|
package/dist/test-hooks.d.ts
CHANGED
|
@@ -5,10 +5,17 @@ export type FsSafeTestHooks = {
|
|
|
5
5
|
beforeOpen?: (filePath: string, flags: number) => Promise<void> | void;
|
|
6
6
|
afterOpen?: (filePath: string, handle: FileHandle) => Promise<void> | void;
|
|
7
7
|
afterOpenedPathIdentityCheck?: (filePath: string, handle: FileHandle) => Promise<void> | void;
|
|
8
|
+
afterRootReadPathResolution?: (filePath: string) => Promise<void> | void;
|
|
9
|
+
beforeRootReadFinalFence?: (filePath: string, handle: FileHandle) => Promise<void> | void;
|
|
10
|
+
afterRootReadFinalPathIdentityCheck?: (filePath: string, handle: FileHandle) => void;
|
|
8
11
|
beforeArchiveOutputMutation?: (operation: "mkdir" | "chmod", targetPath: string) => Promise<void> | void;
|
|
9
12
|
beforeFileStorePruneDescend?: (dirPath: string) => Promise<void> | void;
|
|
10
13
|
beforeFileStoreSyncPrivateWrite?: (filePath: string) => void;
|
|
11
14
|
beforeRootFallbackMutation?: (operation: "mkdir" | "move" | "remove", targetPath: string) => Promise<void> | void;
|
|
15
|
+
beforePinnedWriteParentAdmission?: (targetPath: string) => Promise<void> | void;
|
|
16
|
+
beforeRootStatObservation?: (targetPath: string) => Promise<void> | void;
|
|
17
|
+
beforeRootStatInitialObservation?: (targetPath: string) => Promise<void> | void;
|
|
18
|
+
beforeRootListObservation?: (directoryPath: string, withFileTypes: boolean) => Promise<void> | void;
|
|
12
19
|
afterPinnedWriteFallbackRename?: (targetPath: string) => Promise<void> | void;
|
|
13
20
|
beforeSiblingTempWrite?: (tempPath: string) => Promise<void> | void;
|
|
14
21
|
beforeSidecarLockSnapshotOpen?: (lockPath: string) => Promise<void> | void;
|
package/dist/test-hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-hooks.d.ts","sourceRoot":"","sources":["../src/test-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG;IAC5B,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/D,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3E,4BAA4B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9F,2BAA2B,CAAC,EAAE,CAC5B,SAAS,EAAE,OAAO,GAAG,OAAO,EAC5B,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,2BAA2B,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxE,+BAA+B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,0BAA0B,CAAC,EAAE,CAC3B,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,EACtC,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,8BAA8B,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9E,sBAAsB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpE,6BAA6B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3E,2BAA2B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzE,+BAA+B,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,gCAAgC,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpF,oCAAoC,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,yBAAyB,CAAC,EAAE,CAC1B,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,EAC1D,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,gBAAgB,KACvB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,0BAA0B,CAAC,EAAE,CAC3B,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,EAC1D,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,gBAAgB,KACvB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC3B,CAAC;AAQF,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAEhE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,CAKzE"}
|
|
1
|
+
{"version":3,"file":"test-hooks.d.ts","sourceRoot":"","sources":["../src/test-hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-atomic.d.ts","sourceRoot":"","sources":["../src/text-atomic.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"text-atomic.d.ts","sourceRoot":"","sources":["../src/text-atomic.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAaf"}
|
package/dist/text-atomic.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { replaceFileAtomic } from "./replace-file.js";
|
|
2
|
+
import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
|
|
2
3
|
export async function writeTextAtomic(filePath, content, options) {
|
|
4
|
+
const admittedPath = admitStandalonePublicationPath(filePath);
|
|
3
5
|
const payload = options?.trailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
|
|
4
6
|
const durable = options?.durable ?? true;
|
|
5
7
|
await replaceFileAtomic({
|
|
6
|
-
filePath,
|
|
8
|
+
filePath: admittedPath,
|
|
7
9
|
content: payload,
|
|
8
10
|
mode: options?.mode ?? 0o600,
|
|
9
11
|
dirMode: options?.dirMode ?? (0o777 & ~process.umask()),
|
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":"AAaA,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC;AA8NF,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
|
package/dist/trash.js
CHANGED
|
@@ -6,6 +6,7 @@ import { guardedRenameSync, guardedRmSync } from "./guarded-mutation.js";
|
|
|
6
6
|
import { realpathSync } from "./realpath.js";
|
|
7
7
|
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
8
8
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
9
|
+
import { hasWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
9
10
|
const TRASH_DESTINATION_COLLISION_CODES = new Set(["EEXIST", "ENOTEMPTY", "ERR_FS_CP_EEXIST"]);
|
|
10
11
|
const TRASH_DESTINATION_RETRY_LIMIT = 4;
|
|
11
12
|
function getFsErrorCode(error) {
|
|
@@ -22,32 +23,60 @@ function isTrashDestinationCollision(error) {
|
|
|
22
23
|
function isSameOrChildPath(candidate, parent) {
|
|
23
24
|
return candidate === parent || candidate.startsWith(parent.endsWith(path.sep) ? parent : `${parent}${path.sep}`);
|
|
24
25
|
}
|
|
26
|
+
function assertNoTrashPathAlias(value, label) {
|
|
27
|
+
if (hasWindowsPathAlias(value, "filesystem")) {
|
|
28
|
+
throw new Error(`Refusing to trash ${label} using a Windows filesystem namespace alias: ${value}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function collectAllowedTrashRoots(allowedRoots) {
|
|
32
|
+
const roots = [...(allowedRoots ?? [os.homedir(), os.tmpdir()])];
|
|
33
|
+
for (const root of roots)
|
|
34
|
+
assertNoTrashPathAlias(root, "allowed root");
|
|
35
|
+
return roots;
|
|
36
|
+
}
|
|
25
37
|
function resolveAllowedTrashRoots(allowedRoots) {
|
|
26
|
-
const roots =
|
|
27
|
-
|
|
38
|
+
const roots = allowedRoots.flatMap((root) => {
|
|
39
|
+
assertNoTrashPathAlias(root, "allowed root");
|
|
40
|
+
const lexicalRoot = resolvePathPreservingWindowsRoot(root);
|
|
41
|
+
assertNoTrashPathAlias(lexicalRoot, "allowed root");
|
|
42
|
+
let realRoot;
|
|
28
43
|
try {
|
|
29
44
|
// Keep both spellings: broken symlink targets cannot be realpathed and
|
|
30
45
|
// may only compare equal to the caller's lexical allowed root.
|
|
31
|
-
|
|
46
|
+
realRoot = realpathSync.native(root);
|
|
32
47
|
}
|
|
33
48
|
catch {
|
|
34
49
|
return [lexicalRoot];
|
|
35
50
|
}
|
|
51
|
+
assertNoTrashPathAlias(realRoot, "allowed root");
|
|
52
|
+
const resolvedRealRoot = resolvePathPreservingWindowsRoot(realRoot);
|
|
53
|
+
assertNoTrashPathAlias(resolvedRealRoot, "allowed root");
|
|
54
|
+
return [resolvedRealRoot, lexicalRoot];
|
|
36
55
|
});
|
|
37
56
|
return [...new Set(roots)];
|
|
38
57
|
}
|
|
39
58
|
function resolveTrashTargetPath(targetPath) {
|
|
59
|
+
assertNoTrashPathAlias(targetPath, "target path");
|
|
60
|
+
let realPath;
|
|
40
61
|
try {
|
|
41
|
-
|
|
62
|
+
realPath = realpathSync.native(targetPath);
|
|
42
63
|
}
|
|
43
64
|
catch {
|
|
44
65
|
// Broken symlinks are valid trash targets. Fall back to the lexical path,
|
|
45
66
|
// then rely on lstat identity so the move renames the symlink itself.
|
|
46
|
-
|
|
67
|
+
const lexicalPath = path.resolve(targetPath);
|
|
68
|
+
assertNoTrashPathAlias(lexicalPath, "target path");
|
|
69
|
+
return { path: lexicalPath, resolved: false };
|
|
47
70
|
}
|
|
71
|
+
const resolvedPath = path.resolve(realPath);
|
|
72
|
+
assertNoTrashPathAlias(resolvedPath, "target path");
|
|
73
|
+
return { path: resolvedPath, resolved: true };
|
|
48
74
|
}
|
|
49
75
|
function assertAllowedTrashTarget(targetPath, allowedRoots) {
|
|
50
|
-
|
|
76
|
+
assertNoTrashPathAlias(targetPath, "target path");
|
|
77
|
+
const lexicalTarget = path.resolve(targetPath);
|
|
78
|
+
assertNoTrashPathAlias(lexicalTarget, "target path");
|
|
79
|
+
const stat = fs.lstatSync(lexicalTarget);
|
|
51
80
|
const resolvedTarget = resolveTrashTargetPath(targetPath);
|
|
52
81
|
const resolvedTargetPath = resolvedTarget.path;
|
|
53
82
|
const isAllowed = resolveAllowedTrashRoots(allowedRoots).some((root) => resolvedTargetPath !== root && isSameOrChildPath(resolvedTargetPath, root));
|
|
@@ -55,7 +84,7 @@ function assertAllowedTrashTarget(targetPath, allowedRoots) {
|
|
|
55
84
|
throw new Error(`Refusing to trash path outside allowed roots: ${targetPath}`);
|
|
56
85
|
}
|
|
57
86
|
return {
|
|
58
|
-
path:
|
|
87
|
+
path: lexicalTarget,
|
|
59
88
|
realPath: resolvedTargetPath,
|
|
60
89
|
realPathResolved: resolvedTarget.resolved,
|
|
61
90
|
stat,
|
|
@@ -76,7 +105,9 @@ function assertTrashTargetGuard(guard) {
|
|
|
76
105
|
}
|
|
77
106
|
function resolveTrashDir() {
|
|
78
107
|
const homeDir = os.homedir();
|
|
108
|
+
assertNoTrashPathAlias(homeDir, "home directory");
|
|
79
109
|
const trashDir = path.join(homeDir, ".Trash");
|
|
110
|
+
assertNoTrashPathAlias(trashDir, "trash directory");
|
|
80
111
|
fs.mkdirSync(recursiveMkdirPath(trashDir), { recursive: true, mode: 0o700 });
|
|
81
112
|
const trashDirStat = fs.lstatSync(trashDir);
|
|
82
113
|
if (!trashDirStat.isDirectory() || trashDirStat.isSymbolicLink()) {
|
|
@@ -84,13 +115,17 @@ function resolveTrashDir() {
|
|
|
84
115
|
}
|
|
85
116
|
const realHome = path.resolve(realpathSync.native(homeDir));
|
|
86
117
|
const resolvedTrashDir = path.resolve(realpathSync.native(trashDir));
|
|
118
|
+
assertNoTrashPathAlias(realHome, "home directory");
|
|
119
|
+
assertNoTrashPathAlias(resolvedTrashDir, "trash directory");
|
|
87
120
|
if (resolvedTrashDir === realHome || !isSameOrChildPath(resolvedTrashDir, realHome)) {
|
|
88
121
|
throw new Error(`Trash directory escaped home directory: ${trashDir}`);
|
|
89
122
|
}
|
|
90
123
|
return resolvedTrashDir;
|
|
91
124
|
}
|
|
92
125
|
function trashBaseName(targetPath) {
|
|
126
|
+
assertNoTrashPathAlias(targetPath, "target path");
|
|
93
127
|
const resolvedTargetPath = path.resolve(targetPath);
|
|
128
|
+
assertNoTrashPathAlias(resolvedTargetPath, "target path");
|
|
94
129
|
if (resolvedTargetPath === path.parse(resolvedTargetPath).root) {
|
|
95
130
|
throw new Error(`Refusing to trash root path: ${targetPath}`);
|
|
96
131
|
}
|
|
@@ -101,18 +136,26 @@ function trashBaseName(targetPath) {
|
|
|
101
136
|
return base;
|
|
102
137
|
}
|
|
103
138
|
function resolveContainedPath(root, leaf) {
|
|
139
|
+
assertNoTrashPathAlias(root, "destination root");
|
|
140
|
+
assertNoTrashPathAlias(leaf, "destination name");
|
|
104
141
|
const resolvedRoot = path.resolve(root);
|
|
105
142
|
const resolvedPath = path.resolve(resolvedRoot, leaf);
|
|
143
|
+
assertNoTrashPathAlias(resolvedRoot, "destination root");
|
|
144
|
+
assertNoTrashPathAlias(resolvedPath, "destination path");
|
|
106
145
|
if (!isSameOrChildPath(resolvedPath, resolvedRoot) || resolvedPath === resolvedRoot) {
|
|
107
146
|
throw new Error(`Trash destination escaped trash directory: ${resolvedPath}`);
|
|
108
147
|
}
|
|
109
148
|
return resolvedPath;
|
|
110
149
|
}
|
|
111
150
|
function reserveTrashDestination(trashDir, base, timestamp) {
|
|
151
|
+
assertNoTrashPathAlias(trashDir, "trash directory");
|
|
152
|
+
assertNoTrashPathAlias(base, "destination name");
|
|
112
153
|
const containerPrefix = resolveContainedPath(trashDir, `.fs-safe-trash-${timestamp}-`);
|
|
113
154
|
const container = fs.mkdtempSync(containerPrefix);
|
|
114
155
|
const resolvedContainer = path.resolve(container);
|
|
115
156
|
const resolvedTrashDir = path.resolve(trashDir);
|
|
157
|
+
assertNoTrashPathAlias(resolvedContainer, "destination container");
|
|
158
|
+
assertNoTrashPathAlias(resolvedTrashDir, "trash directory");
|
|
116
159
|
if (resolvedContainer === resolvedTrashDir ||
|
|
117
160
|
!isSameOrChildPath(resolvedContainer, resolvedTrashDir)) {
|
|
118
161
|
throw new Error(`Trash destination escaped trash directory: ${container}`);
|
|
@@ -122,6 +165,7 @@ function reserveTrashDestination(trashDir, base, timestamp) {
|
|
|
122
165
|
function copyTrashTargetSync(target, dest) {
|
|
123
166
|
if (target.stat.isSymbolicLink()) {
|
|
124
167
|
const linkTarget = fs.readlinkSync(target.path);
|
|
168
|
+
assertNoTrashPathAlias(linkTarget, "symlink target");
|
|
125
169
|
assertTrashTargetGuard(target);
|
|
126
170
|
fs.symlinkSync(linkTarget, dest);
|
|
127
171
|
return;
|
|
@@ -159,8 +203,10 @@ function movePathToDestination(target, dest) {
|
|
|
159
203
|
}
|
|
160
204
|
export async function movePathToTrash(targetPath, options = {}) {
|
|
161
205
|
// Avoid resolving external trash helpers through the service PATH during cleanup.
|
|
206
|
+
assertNoTrashPathAlias(targetPath, "target path");
|
|
207
|
+
const allowedRoots = collectAllowedTrashRoots(options.allowedRoots);
|
|
162
208
|
const base = trashBaseName(targetPath);
|
|
163
|
-
const target = assertAllowedTrashTarget(targetPath,
|
|
209
|
+
const target = assertAllowedTrashTarget(targetPath, allowedRoots);
|
|
164
210
|
const trashDir = resolveTrashDir();
|
|
165
211
|
const timestamp = Date.now();
|
|
166
212
|
for (let attempt = 0; attempt < TRASH_DESTINATION_RETRY_LIMIT; attempt += 1) {
|
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;AAS7B,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;AA+EF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,+BAA+B,CA2DjC;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,+BAA+B,CAAC,CA2D1C"}
|