@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,9 +1,21 @@
|
|
|
1
|
-
import type { Stats } from "node:fs";
|
|
1
|
+
import type { BigIntStats, Stats } from "node:fs";
|
|
2
|
+
import { type AsyncDirectoryGuard } from "./directory-guard.js";
|
|
2
3
|
import { type RootContext } from "./root-context.js";
|
|
4
|
+
import type { RootPathDirectoryObservationGuard, RootPathObservationReceipt, RootPathParentObservationReceipt } from "./root-path.js";
|
|
3
5
|
import type { DirEntry, PathStat } from "./types.js";
|
|
4
|
-
export declare function pathStatFromStats(stat: Stats): PathStat;
|
|
5
|
-
export
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function pathStatFromStats(stat: Stats | BigIntStats): PathStat;
|
|
7
|
+
export type RootDirectoryObservationGuard = AsyncDirectoryGuard<BigIntStats>;
|
|
8
|
+
export declare function createRootDirectoryObservationGuard(root: RootContext, directory: string): Promise<RootDirectoryObservationGuard>;
|
|
9
|
+
export declare function assertRootDirectoryObservationGuard(root: RootContext, guard: RootDirectoryObservationGuard | RootPathDirectoryObservationGuard): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Close an operation-local traversal receipt with fresh exact observations.
|
|
12
|
+
* Descendant canonical paths are admitted while the receipt is created. The
|
|
13
|
+
* final directory check repeats canonical admission; the Root-only case does
|
|
14
|
+
* both its identity and canonical check here.
|
|
15
|
+
*/
|
|
16
|
+
export declare function assertRootPathObservationReceiptCurrent(root: RootContext, receipt: RootPathObservationReceipt | RootPathParentObservationReceipt, finalTarget?: Stats | BigIntStats): void;
|
|
17
|
+
export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: true, receipt?: RootPathObservationReceipt): Promise<DirEntry[]>;
|
|
18
|
+
export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: boolean, receipt?: RootPathObservationReceipt): Promise<string[] | DirEntry[]>;
|
|
7
19
|
export type RootDirectoryListing = {
|
|
8
20
|
next(): Promise<{
|
|
9
21
|
kind: "entry";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-directory-list.d.ts","sourceRoot":"","sources":["../src/root-directory-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"root-directory-list.d.ts","sourceRoot":"","sources":["../src/root-directory-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAKvD,OAAO,EAKL,KAAK,mBAAmB,EAEzB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EACV,iCAAiC,EACjC,0BAA0B,EAC1B,gCAAgC,EACjC,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,KAAK,GAAG,WAAW,GAAG,QAAQ,CAmBrE;AAED,MAAM,MAAM,6BAA6B,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;AAE7E,wBAAsB,mCAAmC,CACvD,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,6BAA6B,CAAC,CAaxC;AASD,wBAAsB,mCAAmC,CACvD,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,6BAA6B,GAAG,iCAAiC,GACvE,OAAO,CAAC,IAAI,CAAC,CAuBf;AAiCD;;;;;GAKG;AACH,wBAAgB,uCAAuC,CACrD,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,0BAA0B,GAAG,gCAAgC,EACtE,WAAW,CAAC,EAAE,KAAK,GAAG,WAAW,GAChC,IAAI,CAgCN;AAkBD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,IAAI,EACnB,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AACvB,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,OAAO,EACtB,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;AAoElC,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAClG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,IAAI,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAuI/B"}
|
|
@@ -2,14 +2,23 @@ import fsSync from "node:fs";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
5
|
-
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard } from "./directory-guard.js";
|
|
5
|
+
import { assertAsyncDirectoryGuard, assertDirectoryObservationGuardSync, assertDirectoryObservationSync, createAsyncDirectoryGuard, } from "./directory-guard.js";
|
|
6
6
|
import { FsSafeError } from "./errors.js";
|
|
7
|
-
import {
|
|
7
|
+
import { inspectNativeDirectoryObservation, isNativeDirectoryObservationGuard, } from "./native-directory-observation.js";
|
|
8
|
+
import { isNotFoundPathError } from "./path.js";
|
|
8
9
|
import { assertRootIdentityCurrent } from "./root-context.js";
|
|
9
10
|
import { rootPathChangedError } from "./root-errors.js";
|
|
11
|
+
import { admitPathInsideRoot } from "./root-boundary.js";
|
|
10
12
|
import { createSuppressedError } from "./suppressed-error.js";
|
|
13
|
+
import { realpathSync } from "./realpath.js";
|
|
14
|
+
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
11
15
|
const METADATA_BATCH_SIZE = 32;
|
|
12
16
|
export function pathStatFromStats(stat) {
|
|
17
|
+
const mtimeMs = typeof stat.mtimeMs === "bigint"
|
|
18
|
+
? "mtimeNs" in stat && typeof stat.mtimeNs === "bigint"
|
|
19
|
+
? Number(stat.mtimeNs) / 1_000_000
|
|
20
|
+
: stat.mtime.getTime()
|
|
21
|
+
: stat.mtimeMs;
|
|
13
22
|
return {
|
|
14
23
|
dev: Number(stat.dev),
|
|
15
24
|
gid: Number(stat.gid),
|
|
@@ -17,13 +26,129 @@ export function pathStatFromStats(stat) {
|
|
|
17
26
|
isDirectory: stat.isDirectory(),
|
|
18
27
|
isFile: stat.isFile(),
|
|
19
28
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
20
|
-
mode: stat.mode,
|
|
21
|
-
mtimeMs
|
|
22
|
-
nlink: stat.nlink,
|
|
23
|
-
size: stat.size,
|
|
24
|
-
uid: stat.uid,
|
|
29
|
+
mode: Number(stat.mode),
|
|
30
|
+
mtimeMs,
|
|
31
|
+
nlink: Number(stat.nlink),
|
|
32
|
+
size: Number(stat.size),
|
|
33
|
+
uid: Number(stat.uid),
|
|
25
34
|
};
|
|
26
35
|
}
|
|
36
|
+
export async function createRootDirectoryObservationGuard(root, directory) {
|
|
37
|
+
const guard = await createAsyncDirectoryGuard(directory, { bigint: true });
|
|
38
|
+
const admittedRealPath = admitPathInsideRoot({
|
|
39
|
+
rootPath: root.rootReal,
|
|
40
|
+
candidatePath: guard.realPath,
|
|
41
|
+
rootIdentity: root.rootIdentity,
|
|
42
|
+
});
|
|
43
|
+
if (!admittedRealPath) {
|
|
44
|
+
throw new FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
45
|
+
}
|
|
46
|
+
guard.dir = admittedRealPath.path;
|
|
47
|
+
guard.realPath = admittedRealPath.path;
|
|
48
|
+
return guard;
|
|
49
|
+
}
|
|
50
|
+
function directoryChangedError(error) {
|
|
51
|
+
if (error instanceof FsSafeError && error.code === "path-mismatch")
|
|
52
|
+
return error;
|
|
53
|
+
return new FsSafeError("path-mismatch", "directory changed during operation", {
|
|
54
|
+
cause: error instanceof Error ? error : undefined,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export async function assertRootDirectoryObservationGuard(root, guard) {
|
|
58
|
+
if (isNativeDirectoryObservationGuard(guard)) {
|
|
59
|
+
throw new FsSafeError("path-mismatch", "native directory observation cannot be revalidated");
|
|
60
|
+
}
|
|
61
|
+
const identity = "identity" in guard ? guard.identity : guard.stat;
|
|
62
|
+
const guardPinsRoot = guard.dir === root.rootReal &&
|
|
63
|
+
identity.dev === root.rootIdentity.dev && identity.ino === root.rootIdentity.ino;
|
|
64
|
+
if (guardPinsRoot) {
|
|
65
|
+
try {
|
|
66
|
+
if ("identity" in guard)
|
|
67
|
+
assertDirectoryObservationGuardSync(guard);
|
|
68
|
+
else
|
|
69
|
+
await assertAsyncDirectoryGuard(guard);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
await assertRootIdentityCurrent(root);
|
|
77
|
+
try {
|
|
78
|
+
if ("identity" in guard)
|
|
79
|
+
assertDirectoryObservationGuardSync(guard);
|
|
80
|
+
else
|
|
81
|
+
await assertAsyncDirectoryGuard(guard);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw directoryChangedError(error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function sameObservationDirectory(left, right) {
|
|
88
|
+
return left.dir === right.dir && left.realPath === right.realPath &&
|
|
89
|
+
left.identity.dev === right.identity.dev && left.identity.ino === right.identity.ino;
|
|
90
|
+
}
|
|
91
|
+
function assertReceiptDirectoryGuardSync(root, guard, backend) {
|
|
92
|
+
if (backend) {
|
|
93
|
+
const observed = inspectNativeDirectoryObservation(backend, guard.dir, guard.identity);
|
|
94
|
+
const admitted = admitPathInsideRoot({
|
|
95
|
+
rootPath: root.rootReal,
|
|
96
|
+
candidatePath: observed.realPath,
|
|
97
|
+
rootIdentity: root.rootIdentity,
|
|
98
|
+
});
|
|
99
|
+
if (!admitted || admitted.path !== guard.realPath) {
|
|
100
|
+
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (isNativeDirectoryObservationGuard(guard)) {
|
|
105
|
+
throw new FsSafeError("path-mismatch", "native directory observation cannot be revalidated");
|
|
106
|
+
}
|
|
107
|
+
assertDirectoryObservationGuardSync(guard);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Close an operation-local traversal receipt with fresh exact observations.
|
|
111
|
+
* Descendant canonical paths are admitted while the receipt is created. The
|
|
112
|
+
* final directory check repeats canonical admission; the Root-only case does
|
|
113
|
+
* both its identity and canonical check here.
|
|
114
|
+
*/
|
|
115
|
+
export function assertRootPathObservationReceiptCurrent(root, receipt, finalTarget) {
|
|
116
|
+
const { rootGuard, directoryGuard } = receipt;
|
|
117
|
+
if (rootGuard.dir !== root.rootReal || rootGuard.realPath !== root.rootReal ||
|
|
118
|
+
rootGuard.identity.dev !== root.rootIdentity.dev || rootGuard.identity.ino !== root.rootIdentity.ino) {
|
|
119
|
+
throw rootPathChangedError();
|
|
120
|
+
}
|
|
121
|
+
if (sameObservationDirectory(rootGuard, directoryGuard)) {
|
|
122
|
+
try {
|
|
123
|
+
if (receipt.kind === "stat" && receipt.target === rootGuard && finalTarget &&
|
|
124
|
+
!receipt.directoryObserver) {
|
|
125
|
+
if (finalTarget.isSymbolicLink() || !finalTarget.isDirectory() ||
|
|
126
|
+
realpathSync.native(rootGuard.dir) !== rootGuard.realPath) {
|
|
127
|
+
throw new FsSafeError("path-mismatch", "root path changed during operation");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
assertReceiptDirectoryGuardSync(root, directoryGuard, receipt.directoryObserver);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
assertDirectoryObservationSync(rootGuard.dir, rootGuard.identity);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
assertReceiptDirectoryGuardSync(root, directoryGuard, receipt.directoryObserver);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw directoryChangedError(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
27
152
|
function normalizeDirectoryError(error) {
|
|
28
153
|
if (isNotFoundPathError(error)) {
|
|
29
154
|
return new FsSafeError("not-found", "directory not found", {
|
|
@@ -32,46 +157,70 @@ function normalizeDirectoryError(error) {
|
|
|
32
157
|
}
|
|
33
158
|
return error;
|
|
34
159
|
}
|
|
35
|
-
|
|
160
|
+
function normalizeInitialDirectoryError(error) {
|
|
161
|
+
if (error instanceof FsSafeError && error.code === "not-file") {
|
|
162
|
+
return new FsSafeError("not-found", "directory not found", { cause: error });
|
|
163
|
+
}
|
|
164
|
+
return normalizeDirectoryError(error);
|
|
165
|
+
}
|
|
166
|
+
export async function listDirectoryPath(root, directory, withFileTypes, receipt) {
|
|
167
|
+
let guard;
|
|
168
|
+
if (receipt) {
|
|
169
|
+
if (receipt.kind !== "directory" || receipt.targetPath !== directory ||
|
|
170
|
+
receipt.directoryGuard.dir !== directory ||
|
|
171
|
+
receipt.target !== receipt.directoryGuard ||
|
|
172
|
+
(!isNativeDirectoryObservationGuard(receipt.directoryGuard) &&
|
|
173
|
+
!receipt.directoryGuard.stat.isDirectory())) {
|
|
174
|
+
throw new FsSafeError("path-mismatch", "directory observation receipt does not match target");
|
|
175
|
+
}
|
|
176
|
+
guard = receipt.directoryGuard;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
try {
|
|
180
|
+
guard = await createRootDirectoryObservationGuard(root, directory);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw normalizeInitialDirectoryError(error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return await listGuardedDirectoryPath(root, guard, withFileTypes, receipt);
|
|
187
|
+
}
|
|
188
|
+
async function listGuardedDirectoryPath(root, guard, withFileTypes, receipt) {
|
|
189
|
+
let entries;
|
|
36
190
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
191
|
+
const beforeObservation = getFsSafeTestHooks()?.beforeRootListObservation;
|
|
192
|
+
if (beforeObservation)
|
|
193
|
+
await beforeObservation(guard.realPath, withFileTypes);
|
|
194
|
+
const names = (await fs.readdir(guard.realPath)).sort();
|
|
195
|
+
entries = withFileTypes
|
|
39
196
|
? names.map(name => ({
|
|
40
197
|
name,
|
|
41
|
-
...pathStatFromStats(fsSync.lstatSync(path.join(
|
|
198
|
+
...pathStatFromStats(fsSync.lstatSync(path.join(guard.realPath, name))),
|
|
42
199
|
}))
|
|
43
200
|
: names;
|
|
44
|
-
await assertRootIdentityCurrent(root);
|
|
45
|
-
return entries;
|
|
46
201
|
}
|
|
47
202
|
catch (error) {
|
|
203
|
+
// Preserve ordinary observation errors only while the admitted directory is
|
|
204
|
+
// still current. A post-admission replacement is an identity failure.
|
|
205
|
+
if (receipt)
|
|
206
|
+
assertRootPathObservationReceiptCurrent(root, receipt);
|
|
207
|
+
else
|
|
208
|
+
await assertRootDirectoryObservationGuard(root, guard);
|
|
48
209
|
throw normalizeDirectoryError(error);
|
|
49
210
|
}
|
|
211
|
+
if (receipt)
|
|
212
|
+
assertRootPathObservationReceiptCurrent(root, receipt);
|
|
213
|
+
else
|
|
214
|
+
await assertRootDirectoryObservationGuard(root, guard);
|
|
215
|
+
return entries;
|
|
50
216
|
}
|
|
51
217
|
export async function openRootDirectoryListing(root, directory, options) {
|
|
52
|
-
const guard = await
|
|
218
|
+
const guard = await createRootDirectoryObservationGuard(root, directory).catch((error) => {
|
|
53
219
|
throw normalizeDirectoryError(error);
|
|
54
220
|
});
|
|
55
|
-
if (!isPathInside(root.rootReal, guard.realPath)) {
|
|
56
|
-
throw new FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
57
|
-
}
|
|
58
|
-
const guardPinsRoot = guard.dir === root.rootReal &&
|
|
59
|
-
guard.stat.dev === root.rootIdentity.dev && guard.stat.ino === root.rootIdentity.ino;
|
|
60
221
|
const assertCurrent = async () => {
|
|
61
222
|
options.signal?.throwIfAborted();
|
|
62
|
-
|
|
63
|
-
// One exact receipt proves both identities without dropping the canonical-path check.
|
|
64
|
-
try {
|
|
65
|
-
await assertAsyncDirectoryGuard(guard);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
await assertRootIdentityCurrent(root);
|
|
73
|
-
await assertAsyncDirectoryGuard(guard);
|
|
74
|
-
}
|
|
223
|
+
await assertRootDirectoryObservationGuard(root, guard);
|
|
75
224
|
options.signal?.throwIfAborted();
|
|
76
225
|
};
|
|
77
226
|
let handle;
|
|
@@ -95,7 +244,7 @@ export async function openRootDirectoryListing(root, directory, options) {
|
|
|
95
244
|
handle = await fs.opendir(guard.realPath, { bufferSize: 1 });
|
|
96
245
|
}
|
|
97
246
|
else if (options.snapshot) {
|
|
98
|
-
snapshot = await
|
|
247
|
+
snapshot = await listGuardedDirectoryPath(root, guard, true);
|
|
99
248
|
}
|
|
100
249
|
else if (options.maxNames !== undefined) {
|
|
101
250
|
names = [];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
export type DirectoryResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
path: string;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
error: string;
|
|
8
|
+
diagnostic?: FsSafeError;
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveNearestExistingPath(targetPath: string): string;
|
|
11
|
+
export declare function assertNoSymlinkSegments(params: {
|
|
12
|
+
rootDir: string;
|
|
13
|
+
targetPath: string;
|
|
14
|
+
scopeLabel: string;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
export declare function ensureDirectoryWithinRoot(params: {
|
|
17
|
+
rootDir: string;
|
|
18
|
+
requestedPath: string;
|
|
19
|
+
scopeLabel: string;
|
|
20
|
+
defaultDirName?: string;
|
|
21
|
+
mode?: number;
|
|
22
|
+
}): Promise<DirectoryResult>;
|
|
23
|
+
//# sourceMappingURL=root-directory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-directory.d.ts","sourceRoot":"","sources":["../src/root-directory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB1C,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAM3D,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBrE;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BhB;AAED,wBAAsB,yBAAyB,CAAC,MAAM,EAAE;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,eAAe,CAAC,CAiF3B"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { formatErrorDetail } from "./error-detail.js";
|
|
5
|
+
import { FsSafeError } from "./errors.js";
|
|
6
|
+
import { assertNoNulPathInput, hasNodeErrorCode, isNodeError, isNotFoundPathError, isPathInside, isPathRelativeEscape, } from "./path.js";
|
|
7
|
+
import { realpathSync } from "./realpath.js";
|
|
8
|
+
import { resolvePathWithinRoot } from "./root-paths-lexical.js";
|
|
9
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
10
|
+
function invalidPath(scopeLabel) {
|
|
11
|
+
return { ok: false, error: `Invalid path: must stay within ${scopeLabel}` };
|
|
12
|
+
}
|
|
13
|
+
export function resolveNearestExistingPath(targetPath) {
|
|
14
|
+
assertNoWindowsPathAlias(targetPath);
|
|
15
|
+
let current = resolvePathPreservingWindowsRoot(targetPath);
|
|
16
|
+
assertNoWindowsPathAlias(current);
|
|
17
|
+
while (true) {
|
|
18
|
+
try {
|
|
19
|
+
fsSync.lstatSync(pathForWindowsFilesystem(current));
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (!isNotFoundPathError(err))
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
const parent = path.dirname(current);
|
|
27
|
+
if (parent === current) {
|
|
28
|
+
throw new Error(`failed to resolve existing path for ${targetPath}`);
|
|
29
|
+
}
|
|
30
|
+
current = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function assertNoSymlinkSegments(params) {
|
|
34
|
+
const relative = path.relative(params.rootDir, params.targetPath);
|
|
35
|
+
if (isPathRelativeEscape(relative)) {
|
|
36
|
+
throw new FsSafeError("outside-workspace", `Invalid path: must stay within ${params.scopeLabel}`);
|
|
37
|
+
}
|
|
38
|
+
let current = params.rootDir;
|
|
39
|
+
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
40
|
+
current = path.join(current, segment);
|
|
41
|
+
try {
|
|
42
|
+
const stat = fsSync.lstatSync(current);
|
|
43
|
+
if (stat.isSymbolicLink()) {
|
|
44
|
+
throw new FsSafeError("symlink", `Invalid path: must not traverse symlinks within ${params.scopeLabel}`);
|
|
45
|
+
}
|
|
46
|
+
if (!stat.isDirectory()) {
|
|
47
|
+
throw new FsSafeError("not-file", `Invalid path: existing segment must be a directory within ${params.scopeLabel}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
if (isNotFoundPathError(err))
|
|
52
|
+
return;
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function ensureDirectoryWithinRoot(params) {
|
|
58
|
+
const scopeLabel = formatErrorDetail(params.scopeLabel.slice(0, 80));
|
|
59
|
+
try {
|
|
60
|
+
const rootDirInput = params.rootDir;
|
|
61
|
+
const requestedPathInput = params.requestedPath;
|
|
62
|
+
const useDefault = !requestedPathInput.trim();
|
|
63
|
+
const defaultDirName = useDefault ? params.defaultDirName : undefined;
|
|
64
|
+
const mode = params.mode;
|
|
65
|
+
assertNoNulPathInput(rootDirInput);
|
|
66
|
+
assertNoNulPathInput(requestedPathInput);
|
|
67
|
+
if (useDefault)
|
|
68
|
+
assertNoNulPathInput(defaultDirName ?? "");
|
|
69
|
+
assertNoWindowsPathAlias(rootDirInput);
|
|
70
|
+
assertNoWindowsPathAlias(requestedPathInput);
|
|
71
|
+
if (useDefault)
|
|
72
|
+
assertNoWindowsPathAlias(defaultDirName ?? "");
|
|
73
|
+
const lexical = resolvePathWithinRoot({
|
|
74
|
+
rootDir: rootDirInput,
|
|
75
|
+
requestedPath: requestedPathInput,
|
|
76
|
+
scopeLabel,
|
|
77
|
+
defaultFileName: defaultDirName,
|
|
78
|
+
});
|
|
79
|
+
if (!lexical.ok)
|
|
80
|
+
return lexical;
|
|
81
|
+
const rootDir = resolvePathPreservingWindowsRoot(rootDirInput);
|
|
82
|
+
const targetPath = lexical.path;
|
|
83
|
+
const rootStat = fsSync.lstatSync(pathForWindowsFilesystem(rootDir));
|
|
84
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
85
|
+
return invalidPath(scopeLabel);
|
|
86
|
+
await assertNoSymlinkSegments({ rootDir, targetPath, scopeLabel });
|
|
87
|
+
const rootReal = realpathSync.native(pathForWindowsFilesystem(rootDir));
|
|
88
|
+
assertNoWindowsPathAlias(rootReal);
|
|
89
|
+
const nearestExistingPath = resolveNearestExistingPath(targetPath);
|
|
90
|
+
const nearestExistingReal = realpathSync.native(pathForWindowsFilesystem(nearestExistingPath));
|
|
91
|
+
assertNoWindowsPathAlias(nearestExistingReal);
|
|
92
|
+
if (!isPathInside(rootReal, nearestExistingReal))
|
|
93
|
+
return invalidPath(scopeLabel);
|
|
94
|
+
const relative = path.relative(rootDir, targetPath);
|
|
95
|
+
let current = rootDir;
|
|
96
|
+
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
97
|
+
current = path.join(current, segment);
|
|
98
|
+
while (true) {
|
|
99
|
+
try {
|
|
100
|
+
const stat = fsSync.lstatSync(current);
|
|
101
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
102
|
+
return invalidPath(scopeLabel);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (!isNotFoundPathError(err))
|
|
107
|
+
throw err;
|
|
108
|
+
try {
|
|
109
|
+
await fs.mkdir(current, { mode });
|
|
110
|
+
}
|
|
111
|
+
catch (mkdirErr) {
|
|
112
|
+
if (hasNodeErrorCode(mkdirErr, "EEXIST"))
|
|
113
|
+
continue;
|
|
114
|
+
throw mkdirErr;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const currentReal = realpathSync.native(current);
|
|
119
|
+
assertNoWindowsPathAlias(currentReal);
|
|
120
|
+
if (!isPathInside(rootReal, currentReal))
|
|
121
|
+
return invalidPath(scopeLabel);
|
|
122
|
+
}
|
|
123
|
+
const targetReal = realpathSync.native(pathForWindowsFilesystem(targetPath));
|
|
124
|
+
assertNoWindowsPathAlias(targetReal);
|
|
125
|
+
if (!isPathInside(rootReal, targetReal))
|
|
126
|
+
return invalidPath(scopeLabel);
|
|
127
|
+
return { ok: true, path: targetPath };
|
|
128
|
+
}
|
|
129
|
+
catch (cause) {
|
|
130
|
+
if ((cause instanceof FsSafeError && cause.category === "policy") ||
|
|
131
|
+
hasNodeErrorCode(cause, "ENOTDIR")) {
|
|
132
|
+
return invalidPath(scopeLabel);
|
|
133
|
+
}
|
|
134
|
+
const code = isNodeError(cause) && typeof cause.code === "string"
|
|
135
|
+
? formatErrorDetail(cause.code.slice(0, 40)) : "filesystem operation failed";
|
|
136
|
+
const syscall = isNodeError(cause) && typeof cause.syscall === "string"
|
|
137
|
+
? ` during ${formatErrorDetail(cause.syscall.slice(0, 40))}` : "";
|
|
138
|
+
const diagnostic = new FsSafeError("helper-failed", `Could not prepare ${scopeLabel}: ${code}${syscall}`, { cause });
|
|
139
|
+
return { ok: false, error: diagnostic.message, diagnostic };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAWnE,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAI5F;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,WAAW,CAE/D;AAED,wBAAgB,mCAAmC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIhF;AAED,wBAAgB,6BAA6B,IAAI,WAAW,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAe/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAQ5F;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAQ7F;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAe5F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQzE"}
|
package/dist/root-errors.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
import { hasNodeErrorCode, isNodeError, isNotFoundPathError } from "./path.js";
|
|
3
3
|
const REMOVE_NOT_EMPTY_CODES = new Set(["ENOTEMPTY", "EEXIST"]);
|
|
4
|
+
const PINNED_WRITE_ERRNO_MESSAGES = new Map([
|
|
5
|
+
["EACCES", "permission denied"],
|
|
6
|
+
["EPERM", "permission denied"],
|
|
7
|
+
["EROFS", "read-only filesystem"],
|
|
8
|
+
["ENOSPC", "no space left on device"],
|
|
9
|
+
]);
|
|
4
10
|
export function fileNotFoundError(cause, details) {
|
|
5
11
|
return cause === undefined
|
|
6
12
|
? new FsSafeError("not-found", "file not found", { details })
|
|
@@ -30,7 +36,12 @@ export function normalizePinnedWriteError(error) {
|
|
|
30
36
|
if (isNotFoundPathError(error)) {
|
|
31
37
|
return fileNotFoundError(error instanceof Error ? error : undefined);
|
|
32
38
|
}
|
|
33
|
-
|
|
39
|
+
const code = isNodeError(error) && typeof error.code === "string" && /^E[A-Z0-9_]+$/.test(error.code)
|
|
40
|
+
? error.code : undefined;
|
|
41
|
+
const message = code
|
|
42
|
+
? `${PINNED_WRITE_ERRNO_MESSAGES.get(code) ?? "filesystem write failed"} (${code})`
|
|
43
|
+
: "path is not a regular file under root";
|
|
44
|
+
return new FsSafeError("invalid-path", message, {
|
|
34
45
|
cause: error instanceof Error ? error : undefined,
|
|
35
46
|
});
|
|
36
47
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import type { PinnedOpenSyncFinalAdmission } from "./pinned-open.js";
|
|
3
|
+
export type RootFileAdmissionFs = Pick<typeof fs, "lstatSync" | "realpathSync">;
|
|
4
|
+
type ExactIdentity = Readonly<{
|
|
5
|
+
dev: bigint;
|
|
6
|
+
ino: bigint;
|
|
7
|
+
}>;
|
|
8
|
+
export type CanonicalRootObservation = {
|
|
9
|
+
ok: true;
|
|
10
|
+
path: string;
|
|
11
|
+
identity: ExactIdentity;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: unknown;
|
|
15
|
+
};
|
|
16
|
+
export declare function observeCanonicalRoot(ioFs: RootFileAdmissionFs, rootPath: string): CanonicalRootObservation;
|
|
17
|
+
export declare function createRootFileFinalAdmission(ioFs: RootFileAdmissionFs, root: Extract<CanonicalRootObservation, {
|
|
18
|
+
ok: true;
|
|
19
|
+
}>, boundaryLabel: string): PinnedOpenSyncFinalAdmission;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=root-file-final-admission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-file-final-admission.d.ts","sourceRoot":"","sources":["../src/root-file-final-admission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AAMrE,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,GAAG,cAAc,CAAC,CAAC;AAEhF,KAAK,aAAa,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5D,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GACnD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAoClC,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,mBAAmB,EACzB,QAAQ,EAAE,MAAM,GACf,wBAAwB,CAe1B;AAMD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,mBAAmB,EACzB,IAAI,EAAE,OAAO,CAAC,wBAAwB,EAAE;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,EACrD,aAAa,EAAE,MAAM,GACpB,4BAA4B,CAsC9B"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
import { realpathSync } from "./realpath.js";
|
|
4
|
+
import { admitPathInsideRoot } from "./root-boundary.js";
|
|
5
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
|
+
import { assertNoWindowsPathAlias, pathForWindowsFilesystem } from "./windows-path-alias.js";
|
|
7
|
+
function rootIdentityMismatch(cause) {
|
|
8
|
+
return new FsSafeError("path-mismatch", "canonical root directory identity changed or could not be verified", cause === undefined ? {} : { cause });
|
|
9
|
+
}
|
|
10
|
+
function isExpectedPathError(error) {
|
|
11
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
12
|
+
? String(error.code)
|
|
13
|
+
: "";
|
|
14
|
+
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
15
|
+
}
|
|
16
|
+
function inspectCanonicalRoot(ioFs, rootPath, expected) {
|
|
17
|
+
try {
|
|
18
|
+
assertNoWindowsPathAlias(rootPath);
|
|
19
|
+
const operationPath = ioFs === fs ? pathForWindowsFilesystem(rootPath) : rootPath;
|
|
20
|
+
return inspectFileIdentitySync(() => {
|
|
21
|
+
const stat = ioFs.lstatSync(operationPath, { bigint: true });
|
|
22
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
23
|
+
throw rootIdentityMismatch();
|
|
24
|
+
return stat;
|
|
25
|
+
}, expected);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (expected && isExpectedPathError(error))
|
|
29
|
+
throw rootIdentityMismatch(error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function observeCanonicalRoot(ioFs, rootPath) {
|
|
34
|
+
try {
|
|
35
|
+
const stat = inspectCanonicalRoot(ioFs, rootPath);
|
|
36
|
+
const identity = Object.freeze({ dev: stat.dev, ino: stat.ino });
|
|
37
|
+
let observedPath = rootPath;
|
|
38
|
+
if (process.platform === "win32" && ioFs === fs) {
|
|
39
|
+
// Native leaf resolution expands Windows short names. Bind that root
|
|
40
|
+
// spelling before traversal without replacing the observed identity.
|
|
41
|
+
observedPath = realpathSync.native(pathForWindowsFilesystem(rootPath));
|
|
42
|
+
if (observedPath !== rootPath)
|
|
43
|
+
inspectCanonicalRoot(ioFs, observedPath, identity);
|
|
44
|
+
}
|
|
45
|
+
return { ok: true, path: observedPath, identity };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return { ok: false, error };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function resolveConsumedPath(ioFs, filePath) {
|
|
52
|
+
return ioFs === fs ? realpathSync.native(filePath) : ioFs.realpathSync(filePath);
|
|
53
|
+
}
|
|
54
|
+
export function createRootFileFinalAdmission(ioFs, root, boundaryLabel) {
|
|
55
|
+
const inspectRoot = (candidateRootPath, expected) => {
|
|
56
|
+
inspectCanonicalRoot(ioFs, candidateRootPath, expected);
|
|
57
|
+
};
|
|
58
|
+
return ({ path: consumedPath, descriptorIdentity }) => {
|
|
59
|
+
inspectCanonicalRoot(ioFs, root.path, root.identity);
|
|
60
|
+
const canonicalPath = resolveConsumedPath(ioFs, consumedPath);
|
|
61
|
+
assertNoWindowsPathAlias(canonicalPath, "filesystem", "resolved file path uses a Windows filesystem namespace alias");
|
|
62
|
+
const admitted = admitPathInsideRoot({
|
|
63
|
+
rootPath: root.path,
|
|
64
|
+
candidatePath: canonicalPath,
|
|
65
|
+
rootIdentity: root.identity,
|
|
66
|
+
inspectCandidateRoot: inspectRoot,
|
|
67
|
+
});
|
|
68
|
+
if (!admitted) {
|
|
69
|
+
throw new FsSafeError("outside-workspace", `resolved path escapes ${boundaryLabel}`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
inspectFileIdentitySync(() => ioFs.lstatSync(admitted.path, { bigint: true }), descriptorIdentity);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (isExpectedPathError(error)) {
|
|
76
|
+
throw new FsSafeError("path-mismatch", "canonical file identity changed or could not be verified", { cause: error });
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
inspectCanonicalRoot(ioFs, root.path, root.identity);
|
|
81
|
+
return admitted.path;
|
|
82
|
+
};
|
|
83
|
+
}
|
package/dist/root-file.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;AAS1B,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,GACd,cAAc,CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,GAAG,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAqBF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAW3D;AASD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAwCnF;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE;IACR,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACjD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;CAC/C,GACA,CAAC,CAUH;AA6DD,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAmD7B"}
|