toolcraft 0.0.749 → 0.0.751
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/composition.json +23 -3
- package/dist/composition.json +23 -3
- package/dist/human-in-loop/approvals-commands.d.ts +1 -1
- package/dist/index.d.ts +1 -262
- package/dist/index.js +1 -573
- package/dist/runtime-platform.d.ts +2 -0
- package/dist/runtime-platform.js +2 -0
- package/dist/runtime-platform.workerd.d.ts +3 -0
- package/dist/runtime-platform.workerd.js +10 -0
- package/dist/runtime.d.ts +262 -0
- package/dist/runtime.js +576 -0
- package/node_modules/@noble/hashes/LICENSE +21 -0
- package/node_modules/@noble/hashes/README.md +478 -0
- package/node_modules/@noble/hashes/_blake.d.ts +20 -0
- package/node_modules/@noble/hashes/_blake.js +59 -0
- package/node_modules/@noble/hashes/_md.d.ts +95 -0
- package/node_modules/@noble/hashes/_md.js +209 -0
- package/node_modules/@noble/hashes/_u64.d.ts +36 -0
- package/node_modules/@noble/hashes/_u64.js +77 -0
- package/node_modules/@noble/hashes/argon2.d.ts +148 -0
- package/node_modules/@noble/hashes/argon2.js +577 -0
- package/node_modules/@noble/hashes/blake1.d.ts +162 -0
- package/node_modules/@noble/hashes/blake1.js +630 -0
- package/node_modules/@noble/hashes/blake2.d.ts +203 -0
- package/node_modules/@noble/hashes/blake2.js +573 -0
- package/node_modules/@noble/hashes/blake3.d.ts +68 -0
- package/node_modules/@noble/hashes/blake3.js +310 -0
- package/node_modules/@noble/hashes/eskdf.d.ts +91 -0
- package/node_modules/@noble/hashes/eskdf.js +225 -0
- package/node_modules/@noble/hashes/hkdf.d.ts +70 -0
- package/node_modules/@noble/hashes/hkdf.js +152 -0
- package/node_modules/@noble/hashes/hmac.d.ts +50 -0
- package/node_modules/@noble/hashes/hmac.js +93 -0
- package/node_modules/@noble/hashes/index.d.ts +1 -0
- package/node_modules/@noble/hashes/index.js +34 -0
- package/node_modules/@noble/hashes/legacy.d.ts +107 -0
- package/node_modules/@noble/hashes/legacy.js +348 -0
- package/node_modules/@noble/hashes/package.json +91 -0
- package/node_modules/@noble/hashes/pbkdf2.d.ts +66 -0
- package/node_modules/@noble/hashes/pbkdf2.js +176 -0
- package/node_modules/@noble/hashes/scrypt.d.ts +98 -0
- package/node_modules/@noble/hashes/scrypt.js +304 -0
- package/node_modules/@noble/hashes/sha2.d.ts +176 -0
- package/node_modules/@noble/hashes/sha2.js +446 -0
- package/node_modules/@noble/hashes/sha3-addons.d.ts +533 -0
- package/node_modules/@noble/hashes/sha3-addons.js +973 -0
- package/node_modules/@noble/hashes/sha3.d.ts +208 -0
- package/node_modules/@noble/hashes/sha3.js +475 -0
- package/node_modules/@noble/hashes/src/_blake.ts +67 -0
- package/node_modules/@noble/hashes/src/_md.ts +227 -0
- package/node_modules/@noble/hashes/src/_u64.ts +111 -0
- package/node_modules/@noble/hashes/src/argon2.ts +693 -0
- package/node_modules/@noble/hashes/src/blake1.ts +677 -0
- package/node_modules/@noble/hashes/src/blake2.ts +648 -0
- package/node_modules/@noble/hashes/src/blake3.ts +344 -0
- package/node_modules/@noble/hashes/src/eskdf.ts +265 -0
- package/node_modules/@noble/hashes/src/hkdf.ts +161 -0
- package/node_modules/@noble/hashes/src/hmac.ts +130 -0
- package/node_modules/@noble/hashes/src/index.ts +33 -0
- package/node_modules/@noble/hashes/src/legacy.ts +361 -0
- package/node_modules/@noble/hashes/src/pbkdf2.ts +219 -0
- package/node_modules/@noble/hashes/src/scrypt.ts +375 -0
- package/node_modules/@noble/hashes/src/sha2.ts +486 -0
- package/node_modules/@noble/hashes/src/sha3-addons.ts +1157 -0
- package/node_modules/@noble/hashes/src/sha3.ts +525 -0
- package/node_modules/@noble/hashes/src/utils.ts +978 -0
- package/node_modules/@noble/hashes/src/webcrypto.ts +283 -0
- package/node_modules/@noble/hashes/utils.d.ts +560 -0
- package/node_modules/@noble/hashes/utils.js +687 -0
- package/node_modules/@noble/hashes/webcrypto.d.ts +121 -0
- package/node_modules/@noble/hashes/webcrypto.js +228 -0
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +23 -9
- package/node_modules/@poe-code/config-mutations/README.md +2 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +4 -5
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.d.ts +3 -4
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +3 -3
- package/node_modules/@poe-code/config-mutations/dist/template/render.js +1 -1
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.d.ts +2 -2
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +3 -6
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +4 -2
- package/node_modules/@poe-code/process-runner/README.md +2 -1
- package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.workerd.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.workerd.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +1 -1
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +6 -5
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +67 -29
- package/node_modules/@poe-code/process-runner/package.json +13 -0
- package/node_modules/@poe-code/safe-fs/LICENSE +21 -0
- package/node_modules/@poe-code/safe-fs/PROVENANCE.json +428 -0
- package/node_modules/@poe-code/safe-fs/README.md +411 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/confinement.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/confinement.js +171 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/filesystem.d.ts +93 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/filesystem.js +453 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/index.d.ts +7 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/index.js +19 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/stats.d.ts +4 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/stats.js +37 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/types.d.ts +81 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/types.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/values.d.ts +6 -0
- package/node_modules/@poe-code/safe-fs/dist/bridge/values.js +65 -0
- package/node_modules/@poe-code/safe-fs/dist/config/memory.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/config/memory.js +7 -0
- package/node_modules/@poe-code/safe-fs/dist/config/real.d.ts +5 -0
- package/node_modules/@poe-code/safe-fs/dist/config/real.js +16 -0
- package/node_modules/@poe-code/safe-fs/dist/config/registry.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/config/registry.js +15 -0
- package/node_modules/@poe-code/safe-fs/dist/config.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/config.js +46 -0
- package/node_modules/@poe-code/safe-fs/dist/config.node.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/config.node.js +11 -0
- package/node_modules/@poe-code/safe-fs/dist/config.portable.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/config.portable.js +9 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/abort.d.ts +5 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/abort.js +34 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/cleanup.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/cleanup.js +9 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/descriptor.d.ts +38 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/descriptor.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/errors.d.ts +53 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/errors.js +99 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/filesystem.d.ts +379 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/filesystem.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/index.d.ts +6 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/index.js +6 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/io.d.ts +12 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/io.js +321 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/object.d.ts +134 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/object.js +97 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/path.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/path.js +2 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/portable-path.d.ts +21 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/portable-path.js +78 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/virtual-path.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/contracts/virtual-path.js +162 -0
- package/node_modules/@poe-code/safe-fs/dist/core.d.ts +19 -0
- package/node_modules/@poe-code/safe-fs/dist/core.js +17 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/capabilities.d.ts +10 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/capabilities.js +216 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/conditional-chmod.d.ts +7 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/conditional-chmod.js +19 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/descriptor.d.ts +21 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/descriptor.js +279 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/index.d.ts +59 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/index.js +875 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/path.d.ts +7 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/path.js +250 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/stream.d.ts +6 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/devices/stream.js +136 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/directory-admission.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/directory-admission.js +14 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/atomic-view.d.ts +11 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/atomic-view.js +13 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/index.d.ts +186 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/index.js +3771 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/ledger.d.ts +26 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/ledger.js +96 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/limits.d.ts +13 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/limits.js +26 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/missing-target.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/memory/missing-target.js +144 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/comparison.d.ts +19 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/comparison.js +118 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/identity.d.ts +6 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/identity.js +29 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/index.d.ts +74 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/mount/index.js +1291 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/object-authority.d.ts +27 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/object-authority.js +287 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/object-publication/index.d.ts +45 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/object-publication/index.js +531 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/overlay/index.d.ts +87 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/overlay/index.js +1185 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/overlay/memory-publication.d.ts +42 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/overlay/memory-publication.js +558 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/path-namespace.d.ts +4 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/path-namespace.js +63 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/quota/index.d.ts +12 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/quota/index.js +520 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/readonly/index.d.ts +37 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/readonly/index.js +208 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/real/allocation.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/real/allocation.js +8 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/real/index.d.ts +105 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/real/index.js +1259 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/authority.d.ts +7 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/authority.js +38 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.d.ts +130 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.js +1137 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/bytes.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/bytes.js +9 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/index.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/index.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-fetch.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-fetch.js +74 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-node.d.ts +4 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-node.js +111 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request.d.ts +17 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request.js +89 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/signature.d.ts +20 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/signature.js +76 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/transport.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/transport.js +478 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/types.d.ts +30 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/types.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/xml.d.ts +14 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/xml.js +192 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/index.d.ts +8 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/index.js +4 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/key-order.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/key-order.js +11 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/manifest-admission.d.ts +8 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/manifest-admission.js +167 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/mock.d.ts +48 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/mock.js +249 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/namespace.d.ts +13 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/namespace.js +400 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/registry.d.ts +10 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/registry.js +73 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/transport.d.ts +103 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/s3/transport.js +28 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/scoped.d.ts +19 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/scoped.js +663 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/staging-ancestry.d.ts +8 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/staging-ancestry.js +115 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/staging-cleanup.d.ts +5 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/staging-cleanup.js +63 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/index.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/index.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/resource-id.d.ts +18 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/resource-id.js +109 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/webdav.d.ts +141 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/webdav.js +1377 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/xml.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/fs/webdav/xml.js +59 -0
- package/node_modules/@poe-code/safe-fs/dist/index.d.ts +25 -0
- package/node_modules/@poe-code/safe-fs/dist/index.js +21 -0
- package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/linux-x64-glibc.node +0 -0
- package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/loader.d.ts +5 -0
- package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/loader.mjs +120 -0
- package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/manifest.json +46 -0
- package/node_modules/@poe-code/safe-fs/dist/node/filesystem.d.ts +19 -0
- package/node_modules/@poe-code/safe-fs/dist/node/filesystem.js +30 -0
- package/node_modules/@poe-code/safe-fs/dist/node/host.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/node/host.js +78 -0
- package/node_modules/@poe-code/safe-fs/dist/node/index.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/node/index.js +2 -0
- package/node_modules/@poe-code/safe-fs/dist/node/native-seek-unavailable.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node/native-seek-unavailable.js +5 -0
- package/node_modules/@poe-code/safe-fs/dist/node/native-seek.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/node/native-seek.js +54 -0
- package/node_modules/@poe-code/safe-fs/dist/node/stats.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node/stats.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node/values.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node/values.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node-host.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node-host.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node-unavailable.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/node-unavailable.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/package.json +29 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/browser-path.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/browser-path.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/browser.d.ts +26 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/browser.js +58 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/node-path.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/node-path.js +1 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/node.d.ts +26 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/node.js +64 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/transport-budget.d.ts +9 -0
- package/node_modules/@poe-code/safe-fs/dist/platform/transport-budget.js +17 -0
- package/node_modules/@poe-code/safe-fs/dist/python/emscripten.d.ts +15 -0
- package/node_modules/@poe-code/safe-fs/dist/python/emscripten.js +357 -0
- package/node_modules/@poe-code/safe-fs/dist/python/filesystem.d.ts +51 -0
- package/node_modules/@poe-code/safe-fs/dist/python/filesystem.js +243 -0
- package/node_modules/@poe-code/safe-fs/dist/python/flags.d.ts +3 -0
- package/node_modules/@poe-code/safe-fs/dist/python/flags.js +14 -0
- package/node_modules/@poe-code/safe-fs/dist/python/index.d.ts +9 -0
- package/node_modules/@poe-code/safe-fs/dist/python/index.js +5 -0
- package/node_modules/@poe-code/safe-fs/dist/python/native.d.ts +18 -0
- package/node_modules/@poe-code/safe-fs/dist/python/native.js +446 -0
- package/node_modules/@poe-code/safe-fs/dist/python/request.d.ts +2 -0
- package/node_modules/@poe-code/safe-fs/dist/python/request.js +61 -0
- package/node_modules/@poe-code/safe-fs/dist/python/stat.d.ts +8 -0
- package/node_modules/@poe-code/safe-fs/dist/python/stat.js +34 -0
- package/node_modules/@poe-code/safe-fs/dist/runtime-core.d.ts +27 -0
- package/node_modules/@poe-code/safe-fs/dist/runtime-core.js +57 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/atomic-filesystem.d.ts +16 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/atomic-filesystem.js +496 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-io-control.d.ts +23 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-io-control.js +39 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-io-metrics.d.ts +31 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-io-metrics.js +143 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-publication.d.ts +19 -0
- package/node_modules/@poe-code/safe-fs/dist/testing/object-publication.js +209 -0
- package/node_modules/@poe-code/safe-fs/dist/xml.d.ts +1 -0
- package/node_modules/@poe-code/safe-fs/dist/xml.js +1 -0
- package/node_modules/@poe-code/safe-fs/package.json +189 -0
- package/node_modules/@poe-code/task-list/README.md +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.d.ts +1 -1
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +11 -4
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +2 -2
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +5 -7
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +1 -1
- package/node_modules/@poe-code/task-list/dist/default-host.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/default-host.js +2 -0
- package/node_modules/@poe-code/task-list/dist/default-host.workerd.d.ts +4 -0
- package/node_modules/@poe-code/task-list/dist/default-host.workerd.js +6 -0
- package/node_modules/@poe-code/task-list/dist/filesystem.d.ts +3 -0
- package/node_modules/@poe-code/task-list/dist/filesystem.js +32 -0
- package/node_modules/@poe-code/task-list/dist/move.js +3 -3
- package/node_modules/@poe-code/task-list/dist/open.js +2 -5
- package/node_modules/@poe-code/task-list/dist/paths.d.ts +12 -0
- package/node_modules/@poe-code/task-list/dist/paths.js +7 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +6 -5
- package/node_modules/@poe-code/task-list/package.json +9 -0
- package/node_modules/@poe-code/terminal-text/LICENSE +21 -0
- package/node_modules/@poe-code/terminal-text/README.md +12 -0
- package/node_modules/@poe-code/terminal-text/dist/index.d.ts +2 -0
- package/node_modules/@poe-code/terminal-text/dist/index.js +20 -0
- package/node_modules/@poe-code/terminal-text/package.json +22 -0
- package/node_modules/@poe-code/xml-ast/LICENSE +21 -0
- package/node_modules/@poe-code/xml-ast/README.md +28 -0
- package/node_modules/@poe-code/xml-ast/dist/index.d.ts +47 -0
- package/node_modules/@poe-code/xml-ast/dist/index.js +635 -0
- package/node_modules/@poe-code/xml-ast/package.json +25 -0
- package/node_modules/auth-store/README.md +35 -0
- package/node_modules/auth-store/dist/index.browser.d.ts +4 -0
- package/node_modules/auth-store/dist/index.browser.js +2 -0
- package/node_modules/auth-store/dist/provider-store.d.ts +1 -1
- package/node_modules/auth-store/dist/safe-fs-secret-store.d.ts +21 -0
- package/node_modules/auth-store/dist/safe-fs-secret-store.js +119 -0
- package/node_modules/auth-store/dist/secret-store.d.ts +12 -0
- package/node_modules/auth-store/dist/secret-store.js +1 -0
- package/node_modules/auth-store/dist/transaction-lock.d.ts +2 -4
- package/node_modules/auth-store/dist/types.d.ts +2 -9
- package/node_modules/auth-store/package.json +16 -2
- package/node_modules/tiny-http-mcp-server/dist/composition.json +16 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +25 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/authorization-state.js +4 -4
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +6 -40
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/oauth-form.d.ts +10 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/oauth-form.js +20 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/pkce.js +4 -3
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/register-client.d.ts +14 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/register-client.js +53 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resumable.d.ts +16 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resumable.js +29 -9
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/revoke-token.d.ts +12 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/revoke-token.js +23 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -19
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.js +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.d.ts +6 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.js +5 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +5 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +4 -1
- package/node_modules/tiny-http-mcp-server/package.json +4 -4
- package/node_modules/tiny-mcp-client/README.md +2 -0
- package/node_modules/tiny-mcp-client/dist/index.browser.d.ts +25 -23
- package/node_modules/tiny-mcp-client/dist/index.browser.js +116 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +25 -23
- package/node_modules/tiny-mcp-client/dist/index.js +1034 -561
- package/node_modules/tiny-mcp-client/package.json +15 -0
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-design/dist/components/table.js +6 -0
- package/node_modules/toolcraft-design/dist/escape-terminal-text.d.ts +1 -2
- package/node_modules/toolcraft-design/dist/escape-terminal-text.js +1 -20
- package/node_modules/toolcraft-design/dist/terminal-markdown/parser/frontmatter.js +2 -1
- package/node_modules/toolcraft-design/dist/terminal-markdown/parser.js +2 -1
- package/node_modules/toolcraft-schema/dist/json-schema/utils.js +3 -0
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +32 -7
|
@@ -0,0 +1,1259 @@
|
|
|
1
|
+
import { platform } from "#safe-fs-platform";
|
|
2
|
+
import { snapshotStagingCreation } from "../staging-cleanup.js";
|
|
3
|
+
import { snapshotConditionalChmod } from "../conditional-chmod.js";
|
|
4
|
+
import { runStagingGuard, snapshotDirectoryAncestry } from "../staging-ancestry.js";
|
|
5
|
+
import { constants } from "node:fs";
|
|
6
|
+
import * as immediate from "node:fs";
|
|
7
|
+
import * as native from "node:fs/promises";
|
|
8
|
+
import { dirname, basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
|
+
import { nativeAllocatedBytes } from "./allocation.js";
|
|
10
|
+
import { openFileDescriptor } from "../descriptor.js";
|
|
11
|
+
import { compareIdentity } from "../mount/identity.js";
|
|
12
|
+
import { finishCleanup } from "../../contracts/cleanup.js";
|
|
13
|
+
import { callNativeSeekEnd, loadNativeSeekBinding } from "../../node/native-seek.js";
|
|
14
|
+
import { admitDirectoryEntries, directoryEntryLimit } from "../directory-admission.js";
|
|
15
|
+
import { FsError, collectBytes, isErrnoCode, toByteSource, toFsError, validatePath, } from "../../contracts/index.js";
|
|
16
|
+
function fileType(stats) {
|
|
17
|
+
if (stats.isFile())
|
|
18
|
+
return "file";
|
|
19
|
+
if (stats.isDirectory())
|
|
20
|
+
return "directory";
|
|
21
|
+
if (stats.isSymbolicLink())
|
|
22
|
+
return "symlink";
|
|
23
|
+
throw new FsError("ENOTSUP", { message: "special filesystem nodes are not supported" });
|
|
24
|
+
}
|
|
25
|
+
function fileStat(stats) {
|
|
26
|
+
const allocatedBytes = nativeAllocatedBytes(typeof stats.size === "bigint" && typeof stats.blocks === "bigint" ? Number(stats.blocks) : stats.blocks, process.platform);
|
|
27
|
+
const blockSize = typeof stats.size === "bigint" && typeof stats.blksize === "bigint" ? Number(stats.blksize) : stats.blksize;
|
|
28
|
+
const dev = Number(stats.dev), ino = Number(stats.ino);
|
|
29
|
+
const snapshot = {
|
|
30
|
+
type: fileType(stats), size: Number(stats.size), mode: Number(stats.mode),
|
|
31
|
+
...(allocatedBytes === undefined ? {} : { allocatedBytes }),
|
|
32
|
+
...(typeof blockSize === "number" && Number.isSafeInteger(blockSize) && blockSize > 0 ? { ioBlockSize: blockSize, preferredIoBlockSize: blockSize } : {}),
|
|
33
|
+
atimeMs: "atimeNs" in stats ? Number(stats.atimeNs / 1000000n) + Number(stats.atimeNs % 1000000n) / 1e6 : stats.atimeMs,
|
|
34
|
+
mtimeMs: "mtimeNs" in stats ? Number(stats.mtimeNs / 1000000n) + Number(stats.mtimeNs % 1000000n) / 1e6 : stats.mtimeMs,
|
|
35
|
+
ctimeMs: "ctimeNs" in stats ? Number(stats.ctimeNs / 1000000n) + Number(stats.ctimeNs % 1000000n) / 1e6 : stats.ctimeMs,
|
|
36
|
+
birthtimeMs: "birthtimeNs" in stats ? Number(stats.birthtimeNs / 1000000n) + Number(stats.birthtimeNs % 1000000n) / 1e6 : stats.birthtimeMs, ino, dev,
|
|
37
|
+
...(Number.isSafeInteger(dev) && dev >= 0 && Number.isSafeInteger(ino) && ino > 0
|
|
38
|
+
? { identityScope: Symbol.for("virtual-bash.fs.native") } : {}),
|
|
39
|
+
nlink: Number(stats.nlink), uid: Number(stats.uid), gid: Number(stats.gid),
|
|
40
|
+
};
|
|
41
|
+
if (snapshot.identityScope !== undefined && "ctimeNs" in stats && (stats.ctimeNs !== 0n || stats.mtimeNs !== 0n) && !stats.isDirectory())
|
|
42
|
+
Object.defineProperty(snapshot, "opaqueVersion", { value: `${stats.ctimeNs}:${stats.mtimeNs}`, enumerable: true });
|
|
43
|
+
return snapshot;
|
|
44
|
+
}
|
|
45
|
+
function integer(value, minimum = 0) {
|
|
46
|
+
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
47
|
+
throw new FsError("EINVAL", { message: "expected a nonnegative safe integer" });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function nativeError(error) {
|
|
51
|
+
if (typeof error === "object" && error !== null && "code" in error
|
|
52
|
+
&& (error.code === "ERR_INVALID_ARG_TYPE" || error.code === "ERR_INVALID_ARG_VALUE" || error.code === "ERR_OUT_OF_RANGE")) {
|
|
53
|
+
return new FsError("EINVAL", { cause: error });
|
|
54
|
+
}
|
|
55
|
+
if (typeof error === "object" && error !== null && "info" in error) {
|
|
56
|
+
const info = error.info;
|
|
57
|
+
if (typeof info === "object" && info !== null && "code" in info && isErrnoCode(info.code)) {
|
|
58
|
+
return new FsError(info.code, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return toFsError(error);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Async byte filesystem for trusted POSIX hosts (Windows is unsupported).
|
|
65
|
+
* Both relative and absolute input paths are virtual POSIX paths rooted at `/`;
|
|
66
|
+
* components resolve in order, following symlinks before subsequent `..` and
|
|
67
|
+
* preserving trailing directory requirements. Empty paths are ENOENT. Excess
|
|
68
|
+
* input `..` clamps at the virtual root; symlink targets may never cross it.
|
|
69
|
+
* There is no process cwd dependency. The configured host root must already
|
|
70
|
+
* exist; its canonical location is pinned on first use or by the async factory.
|
|
71
|
+
*
|
|
72
|
+
* Absolute symlink targets created here are virtual paths, stored as rooted
|
|
73
|
+
* host targets without lexical normalization. Relative targets retain their
|
|
74
|
+
* text; safe dangling, non-directory, and looping targets may be created.
|
|
75
|
+
* Existing host symlinks are
|
|
76
|
+
* followed only when every traversed component remains beneath the root, with
|
|
77
|
+
* a 40-link traversal limit. Existing absolute host targets must start with the
|
|
78
|
+
* canonical root, not a different host alias of that root. lstat, removal, and
|
|
79
|
+
* rename inspect or modify a final symlink itself rather than its target. Absolute readlink results are
|
|
80
|
+
* translated back into virtual paths; external absolute targets are refused.
|
|
81
|
+
*
|
|
82
|
+
* SECURITY LIMIT: containment checks and subsequent Node path operations are
|
|
83
|
+
* not atomic. O_NOFOLLOW narrows final-file open races, but ancestor swaps,
|
|
84
|
+
* concurrent renames, mount changes, and preexisting hardlinks cannot be made
|
|
85
|
+
* safe by these APIs. This is NOT a race-proof sandbox or an isolation boundary
|
|
86
|
+
* against another process modifying the tree. Use an OS sandbox for that.
|
|
87
|
+
* Conditional chmod and synchronous ancestry validation use the same trusted,
|
|
88
|
+
* externally isolated tree boundary as owned staging. Their final checks and
|
|
89
|
+
* metadata commit do not yield to JavaScript; they do not prevent OS races.
|
|
90
|
+
*
|
|
91
|
+
* Only regular files, directories, and symlinks are represented. Permissions,
|
|
92
|
+
* ownership, timestamp precision, case sensitivity, umask, and rename behavior
|
|
93
|
+
* are those of the host filesystem. Copy is not atomic; rename can fail EXDEV.
|
|
94
|
+
* Cancellation is cooperative between operations/chunks, not rollback: failed
|
|
95
|
+
* or canceled writes/copies may leave partial data. No native commands execute.
|
|
96
|
+
* Destructive rm/rename operands ending in `.` or `..` are refused with EINVAL,
|
|
97
|
+
* including native recursive-rm edge cases that could otherwise delete content.
|
|
98
|
+
* Public filesystem errors carry only virtual operands; native causes are
|
|
99
|
+
* intentionally omitted so host paths cannot escape through nested errors.
|
|
100
|
+
*/
|
|
101
|
+
export class RealFileSystem {
|
|
102
|
+
capabilities = Object.freeze({
|
|
103
|
+
read: true, stat: true, readdir: true, realpath: true, access: true,
|
|
104
|
+
write: true, append: true, exclusiveCreate: true, explicitDirectories: true, implicitDirectories: false,
|
|
105
|
+
mkdir: true, recursiveMkdir: true, remove: true, removeDirectory: true, recursiveRemove: true,
|
|
106
|
+
rename: true, atomicRenameNoReplace: false, copy: true, exclusiveCopy: true, readlink: true, truncate: true,
|
|
107
|
+
streamingAppend: true, randomAccessWrite: true,
|
|
108
|
+
readOnly: false, symlinks: true, hardlinks: true, streamingRead: true, streamingWrite: true, retainedRead: true, retainedResize: true,
|
|
109
|
+
...platform.nativeFileSystem,
|
|
110
|
+
});
|
|
111
|
+
configuredRoot;
|
|
112
|
+
renameNoReplace;
|
|
113
|
+
rootPromise;
|
|
114
|
+
constructor(options) {
|
|
115
|
+
const root = typeof options === "string" ? options : options.root;
|
|
116
|
+
validatePath(root);
|
|
117
|
+
if (!isAbsolute(root)) {
|
|
118
|
+
throw new FsError("EINVAL", { syscall: "root", path: root, message: "root must be an absolute host path" });
|
|
119
|
+
}
|
|
120
|
+
if (sep !== "/") {
|
|
121
|
+
throw new FsError("ENOTSUP", { syscall: "root", message: "this backend requires a POSIX host" });
|
|
122
|
+
}
|
|
123
|
+
this.configuredRoot = root;
|
|
124
|
+
this.renameNoReplace = typeof options === "string" ? undefined : options.renameNoReplace;
|
|
125
|
+
if (this.renameNoReplace !== undefined) {
|
|
126
|
+
if (!platform.nativeFileSystem.atomicRename)
|
|
127
|
+
throw new FsError("ENOTSUP", { syscall: "root", message: "atomic rename is unavailable on this host" });
|
|
128
|
+
if (typeof this.renameNoReplace !== "function")
|
|
129
|
+
throw new FsError("EINVAL", { syscall: "root", message: "renameNoReplace must be a function" });
|
|
130
|
+
this.capabilities = Object.freeze({ ...this.capabilities, atomicRenameNoReplace: true });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async root(options = {}) {
|
|
134
|
+
options.signal?.throwIfAborted();
|
|
135
|
+
this.rootPromise ??= (async () => {
|
|
136
|
+
const root = await native.realpath(this.configuredRoot);
|
|
137
|
+
if (!(await native.stat(root)).isDirectory())
|
|
138
|
+
throw new FsError("ENOTDIR");
|
|
139
|
+
return root;
|
|
140
|
+
})();
|
|
141
|
+
const root = await this.rootPromise;
|
|
142
|
+
options.signal?.throwIfAborted();
|
|
143
|
+
if (await native.realpath(root) !== root)
|
|
144
|
+
throw new FsError("EACCES");
|
|
145
|
+
options.signal?.throwIfAborted();
|
|
146
|
+
if (!(await native.stat(root)).isDirectory())
|
|
147
|
+
throw new FsError("ENOTDIR");
|
|
148
|
+
options.signal?.throwIfAborted();
|
|
149
|
+
return root;
|
|
150
|
+
}
|
|
151
|
+
absoluteTarget(root, target) {
|
|
152
|
+
if (target === root)
|
|
153
|
+
return [];
|
|
154
|
+
const prefix = root === "/" ? "/" : `${root}/`;
|
|
155
|
+
if (!target.startsWith(prefix)) {
|
|
156
|
+
throw new FsError("EACCES", { message: "symlink target escapes the configured root" });
|
|
157
|
+
}
|
|
158
|
+
return target.slice(prefix.length).split("/");
|
|
159
|
+
}
|
|
160
|
+
async walk(root, components, options) {
|
|
161
|
+
let current = root;
|
|
162
|
+
let links = 0;
|
|
163
|
+
const pending = [...components];
|
|
164
|
+
while (pending.length > 0) {
|
|
165
|
+
options.signal?.throwIfAborted();
|
|
166
|
+
const { name: component, fromLink } = pending.shift();
|
|
167
|
+
if (component === "")
|
|
168
|
+
continue;
|
|
169
|
+
if ((component === "." || component === "..") && !options.checkTarget) {
|
|
170
|
+
await native.lstat(`${current}/.`);
|
|
171
|
+
options.signal?.throwIfAborted();
|
|
172
|
+
}
|
|
173
|
+
if (component === ".")
|
|
174
|
+
continue;
|
|
175
|
+
if (component === "..") {
|
|
176
|
+
if (current === root) {
|
|
177
|
+
if (fromLink)
|
|
178
|
+
throw new FsError("EACCES", { message: "symlink target escapes the configured root" });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
current = resolve(current, "..");
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (options.createFile && pending.length > 0 && pending.every((part) => part.name === "")) {
|
|
186
|
+
await native.lstat(`${current}/.`);
|
|
187
|
+
options.signal?.throwIfAborted();
|
|
188
|
+
throw new FsError("EISDIR");
|
|
189
|
+
}
|
|
190
|
+
const candidate = join(current, component);
|
|
191
|
+
let stats;
|
|
192
|
+
try {
|
|
193
|
+
stats = await native.lstat(candidate);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
options.signal?.throwIfAborted();
|
|
197
|
+
const code = error.code;
|
|
198
|
+
if (options.checkTarget && (code === "ENOENT" || code === "ENOTDIR")) {
|
|
199
|
+
current = candidate;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (code !== "ENOENT")
|
|
203
|
+
throw error;
|
|
204
|
+
if (options.createDirectories && !fromLink) {
|
|
205
|
+
try {
|
|
206
|
+
options.signal?.throwIfAborted();
|
|
207
|
+
await native.mkdir(candidate, { mode: options.createDirectories.mode });
|
|
208
|
+
}
|
|
209
|
+
catch (creationError) {
|
|
210
|
+
if (nativeError(creationError).code !== "EEXIST")
|
|
211
|
+
throw creationError;
|
|
212
|
+
}
|
|
213
|
+
options.signal?.throwIfAborted();
|
|
214
|
+
stats = await native.lstat(candidate);
|
|
215
|
+
}
|
|
216
|
+
else if (!options.createDirectories && options.missing === "final" && pending.every((part) => part.name === "")) {
|
|
217
|
+
return pending.length > 0 ? `${candidate}/` : candidate;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
options.signal?.throwIfAborted();
|
|
224
|
+
if (stats.isSymbolicLink() && (pending.length > 0 || options.followFinal !== false)) {
|
|
225
|
+
if (++links > 40)
|
|
226
|
+
throw new FsError("ELOOP");
|
|
227
|
+
const target = await native.readlink(candidate);
|
|
228
|
+
if (isAbsolute(target)) {
|
|
229
|
+
pending.unshift(...this.absoluteTarget(root, target).map((name) => ({ name, fromLink: true })));
|
|
230
|
+
current = root;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
pending.unshift(...target.split("/").map((name) => ({ name, fromLink: true })));
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (pending.length > 0 && !stats.isDirectory() && !options.checkTarget) {
|
|
238
|
+
if (options.deferTrailingSeparator && stats.isFile() && pending.every((part) => part.name === "")) {
|
|
239
|
+
return `${candidate}/`;
|
|
240
|
+
}
|
|
241
|
+
throw new FsError("ENOTDIR");
|
|
242
|
+
}
|
|
243
|
+
if (!options.checkTarget)
|
|
244
|
+
fileType(stats);
|
|
245
|
+
current = candidate;
|
|
246
|
+
}
|
|
247
|
+
options.signal?.throwIfAborted();
|
|
248
|
+
return components.at(-1)?.name === "" && current !== root ? `${current}/` : current;
|
|
249
|
+
}
|
|
250
|
+
async path(path, options = {}) {
|
|
251
|
+
options.signal?.throwIfAborted();
|
|
252
|
+
validatePath(path);
|
|
253
|
+
if (path === "")
|
|
254
|
+
throw new FsError("ENOENT");
|
|
255
|
+
return this.walk(await this.root(options), path.split("/").map((name) => ({ name, fromLink: false })), options);
|
|
256
|
+
}
|
|
257
|
+
async operation(syscall, path, options, action, dest, preserveReceipt = false) {
|
|
258
|
+
options.signal?.throwIfAborted();
|
|
259
|
+
try {
|
|
260
|
+
validatePath(path);
|
|
261
|
+
if (dest !== undefined)
|
|
262
|
+
validatePath(dest);
|
|
263
|
+
const result = await action();
|
|
264
|
+
if (!preserveReceipt)
|
|
265
|
+
options.signal?.throwIfAborted();
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (!preserveReceipt)
|
|
270
|
+
options.signal?.throwIfAborted();
|
|
271
|
+
else if (options.signal?.aborted && Object.is(error, options.signal.reason))
|
|
272
|
+
throw error;
|
|
273
|
+
const converted = nativeError(error);
|
|
274
|
+
throw new FsError(converted.code, {
|
|
275
|
+
syscall, path, ...(dest === undefined ? {} : { dest }),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
protectRoot(path, root) {
|
|
280
|
+
if (resolve(path) === root)
|
|
281
|
+
throw new FsError("EBUSY", { message: "the filesystem root cannot be removed or replaced" });
|
|
282
|
+
}
|
|
283
|
+
// These critical sections contain no await. They serialize with all JavaScript
|
|
284
|
+
// callers, under this adapter's existing trusted-host (externally isolated tree)
|
|
285
|
+
// boundary. They deliberately do not advertise the stronger atomic capabilities.
|
|
286
|
+
stagingSnapshot(path) {
|
|
287
|
+
try {
|
|
288
|
+
return fileStat(immediate.lstatSync(path, { bigint: true }));
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (nativeError(error).code === "ENOENT")
|
|
292
|
+
return null;
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
expectStaging(path, expected, identityOnly = false) {
|
|
297
|
+
if (!platform.nativeFileSystem.trustedOwnedStaging)
|
|
298
|
+
throw new FsError("ENOTSUP");
|
|
299
|
+
if (expected && (expected.identityScope !== Symbol.for("virtual-bash.fs.native")
|
|
300
|
+
|| expected.dev === undefined || expected.ino === undefined || !identityOnly && expected.opaqueVersion === undefined))
|
|
301
|
+
throw new FsError("ENOTSUP");
|
|
302
|
+
const current = this.stagingSnapshot(path);
|
|
303
|
+
if (expected && current && (current.identityScope !== Symbol.for("virtual-bash.fs.native")
|
|
304
|
+
|| current.dev === undefined || current.ino === undefined || !identityOnly && current.opaqueVersion === undefined))
|
|
305
|
+
throw new FsError("ENOTSUP");
|
|
306
|
+
if (expected === null ? current !== null : !current || current.dev !== expected.dev || current.ino !== expected.ino || current.type !== expected.type
|
|
307
|
+
|| !identityOnly && (current.opaqueVersion !== expected.opaqueVersion || current.size !== expected.size || current.mode !== expected.mode || current.nlink !== expected.nlink))
|
|
308
|
+
throw new FsError("EAGAIN");
|
|
309
|
+
if (immediate.realpathSync(dirname(path)) !== dirname(path))
|
|
310
|
+
throw new FsError("EAGAIN");
|
|
311
|
+
return current;
|
|
312
|
+
}
|
|
313
|
+
stagingMetadata(options) {
|
|
314
|
+
if (options.mode !== undefined)
|
|
315
|
+
integer(options.mode);
|
|
316
|
+
for (const time of [options.atimeMs, options.mtimeMs])
|
|
317
|
+
if (time !== undefined && (!Number.isFinite(time) || Math.abs(time) > 8.64e15))
|
|
318
|
+
throw new FsError("EINVAL");
|
|
319
|
+
}
|
|
320
|
+
async prepareDirectoryAncestry(ancestors, options = {}) {
|
|
321
|
+
const controls = options.signal === undefined ? {} : { signal: options.signal };
|
|
322
|
+
controls.signal?.throwIfAborted();
|
|
323
|
+
const entries = snapshotDirectoryAncestry(ancestors);
|
|
324
|
+
const path = entries.at(-1).path;
|
|
325
|
+
return this.operation("prepareDirectoryAncestry", path, controls, async () => {
|
|
326
|
+
const root = await this.root(controls);
|
|
327
|
+
const validate = () => {
|
|
328
|
+
controls.signal?.throwIfAborted();
|
|
329
|
+
try {
|
|
330
|
+
for (const entry of entries)
|
|
331
|
+
this.expectStaging(join(root, entry.path.slice(1)), entry.stat, true);
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
controls.signal?.throwIfAborted();
|
|
336
|
+
throw new FsError(nativeError(error).code, { syscall: "prepareDirectoryAncestry", path });
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
validate();
|
|
340
|
+
return validate;
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
async createStagedFile(directoryPath, name, content, options) {
|
|
344
|
+
options = snapshotStagingCreation(options, directoryPath);
|
|
345
|
+
// Unlike operation(), this must return its receipt after commit even if the
|
|
346
|
+
// signal is aborted before the promise settles.
|
|
347
|
+
options.signal?.throwIfAborted();
|
|
348
|
+
try {
|
|
349
|
+
if (options.retainCleanup === true)
|
|
350
|
+
throw new FsError("ENOTSUP");
|
|
351
|
+
if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\0"))
|
|
352
|
+
throw new FsError("EINVAL");
|
|
353
|
+
this.stagingMetadata(options);
|
|
354
|
+
if (content.type === "file" && !(content.data instanceof Uint8Array))
|
|
355
|
+
throw new FsError("EINVAL");
|
|
356
|
+
const directory = await this.path(directoryPath, { ...options, followFinal: false, missing: "final" });
|
|
357
|
+
const root = await this.root(options);
|
|
358
|
+
const target = content.type === "symlink" ? (validatePath(content.target), isAbsolute(content.target) ? `${root}/${content.target.slice(1)}` : content.target) : undefined;
|
|
359
|
+
if (content.type === "symlink")
|
|
360
|
+
await this.walk(root, (isAbsolute(content.target) ? content.target.slice(1).split("/") : relative(root, directory).split("/").concat(content.target.split("/"))).map(name => ({ name, fromLink: true })), { ...options, checkTarget: true });
|
|
361
|
+
options.signal?.throwIfAborted();
|
|
362
|
+
this.protectTerminal(directoryPath);
|
|
363
|
+
this.expectStaging(dirname(directory), options.parent, true);
|
|
364
|
+
immediate.mkdirSync(directory, { mode: 0o700 });
|
|
365
|
+
const file = join(directory, name);
|
|
366
|
+
try {
|
|
367
|
+
if (content.type === "file") {
|
|
368
|
+
const fd = immediate.openSync(file, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, options.mode ?? 0o666);
|
|
369
|
+
try {
|
|
370
|
+
immediate.writeFileSync(fd, content.data);
|
|
371
|
+
if (options.mode !== undefined)
|
|
372
|
+
immediate.fchmodSync(fd, options.mode);
|
|
373
|
+
if (options.atimeMs !== undefined || options.mtimeMs !== undefined) {
|
|
374
|
+
const stat = immediate.fstatSync(fd);
|
|
375
|
+
immediate.futimesSync(fd, (options.atimeMs ?? stat.atimeMs) / 1000, (options.mtimeMs ?? stat.mtimeMs) / 1000);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
finally {
|
|
379
|
+
immediate.closeSync(fd);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else
|
|
383
|
+
immediate.symlinkSync(target, file);
|
|
384
|
+
const virtualDirectory = `/${relative(root, directory)}`;
|
|
385
|
+
const receipt = (path, host) => Object.freeze({ path, stat: Object.freeze(this.stagingSnapshot(host)) });
|
|
386
|
+
return Object.freeze({ parent: receipt(dirname(virtualDirectory), dirname(directory)), directory: receipt(virtualDirectory, directory), file: receipt(`${virtualDirectory}/${name}`, file) });
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
// Only entries just acquired by this synchronous section can be removed.
|
|
390
|
+
if (immediate.existsSync(file))
|
|
391
|
+
immediate.unlinkSync(file);
|
|
392
|
+
immediate.rmdirSync(directory);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
options.signal?.throwIfAborted();
|
|
398
|
+
throw new FsError(nativeError(error).code, { syscall: "createStagedFile", path: directoryPath });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async stagingPaths(staging, options) {
|
|
402
|
+
const directory = await this.path(staging.directory.path, { ...options, followFinal: false });
|
|
403
|
+
const file = await this.path(staging.file.path, { ...options, followFinal: false, missing: "final" });
|
|
404
|
+
const parent = await this.path(staging.parent.path, { ...options, followFinal: false });
|
|
405
|
+
if (dirname(directory) !== parent || dirname(file) !== directory)
|
|
406
|
+
throw new FsError("EAGAIN");
|
|
407
|
+
return { directory, file, parent };
|
|
408
|
+
}
|
|
409
|
+
checkStaging(staging, paths) {
|
|
410
|
+
this.expectStaging(paths.parent, staging.parent.stat, true);
|
|
411
|
+
const directory = this.expectStaging(paths.directory, staging.directory.stat, true);
|
|
412
|
+
if (directory?.type !== "directory" || (directory.mode & 0o777) !== 0o700)
|
|
413
|
+
throw new FsError("EAGAIN");
|
|
414
|
+
}
|
|
415
|
+
async publishStagedFile(staging, destination, options) {
|
|
416
|
+
return this.operation("publishStagedFile", staging.file.path, options, async () => {
|
|
417
|
+
options.signal?.throwIfAborted();
|
|
418
|
+
if (options.ancestors !== undefined || options.commitGuard !== undefined)
|
|
419
|
+
throw new FsError("ENOTSUP");
|
|
420
|
+
const paths = await this.stagingPaths(staging, options);
|
|
421
|
+
const target = await this.path(destination, { ...options, followFinal: false, missing: "final" });
|
|
422
|
+
options.signal?.throwIfAborted();
|
|
423
|
+
this.protectTerminal(destination);
|
|
424
|
+
this.checkStaging(staging, paths);
|
|
425
|
+
this.expectStaging(paths.file, staging.file.stat);
|
|
426
|
+
this.expectStaging(dirname(target), options.parent, true);
|
|
427
|
+
const existing = this.expectStaging(target, options.destination);
|
|
428
|
+
if (existing && existing.dev === staging.file.stat.dev && existing.ino === staging.file.stat.ino
|
|
429
|
+
|| target === paths.file || target === paths.directory || dirname(target) === paths.directory)
|
|
430
|
+
throw new FsError("EINVAL");
|
|
431
|
+
if (existing && existing.type !== "file")
|
|
432
|
+
throw new FsError("EAGAIN");
|
|
433
|
+
immediate.renameSync(paths.file, target);
|
|
434
|
+
}, destination, true);
|
|
435
|
+
}
|
|
436
|
+
async removeStagedFile(staging, options = {}) {
|
|
437
|
+
return this.operation("removeStagedFile", staging.directory.path, options, async () => {
|
|
438
|
+
const paths = await this.stagingPaths(staging, options);
|
|
439
|
+
options.signal?.throwIfAborted();
|
|
440
|
+
this.checkStaging(staging, paths);
|
|
441
|
+
const file = this.stagingSnapshot(paths.file);
|
|
442
|
+
if (file)
|
|
443
|
+
this.expectStaging(paths.file, staging.file.stat);
|
|
444
|
+
const children = immediate.readdirSync(paths.directory);
|
|
445
|
+
if (children.length !== (file ? 1 : 0) || file && children[0] !== basename(paths.file))
|
|
446
|
+
throw new FsError("ENOTEMPTY");
|
|
447
|
+
if (file)
|
|
448
|
+
immediate.unlinkSync(paths.file);
|
|
449
|
+
immediate.rmdirSync(paths.directory);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
async writeFileConditional(path, data, options) {
|
|
453
|
+
options.signal?.throwIfAborted();
|
|
454
|
+
try {
|
|
455
|
+
if (!(data instanceof Uint8Array))
|
|
456
|
+
throw new FsError("EINVAL");
|
|
457
|
+
this.stagingMetadata(options);
|
|
458
|
+
const target = await this.path(path, { ...options, followFinal: false, missing: "final" });
|
|
459
|
+
options.signal?.throwIfAborted();
|
|
460
|
+
this.expectStaging(dirname(target), options.parent, true);
|
|
461
|
+
const existing = this.expectStaging(target, options.expected);
|
|
462
|
+
if (existing && existing.type !== "file")
|
|
463
|
+
throw new FsError("EAGAIN");
|
|
464
|
+
const fd = immediate.openSync(target, constants.O_WRONLY | constants.O_NOFOLLOW | (existing ? 0 : constants.O_CREAT | constants.O_EXCL), options.mode ?? 0o666);
|
|
465
|
+
try {
|
|
466
|
+
if (!options.append)
|
|
467
|
+
immediate.ftruncateSync(fd, 0);
|
|
468
|
+
let offset = options.append ? Number(immediate.fstatSync(fd).size) : 0;
|
|
469
|
+
let written = 0;
|
|
470
|
+
while (written < data.length) {
|
|
471
|
+
const count = immediate.writeSync(fd, data, written, data.length - written, offset);
|
|
472
|
+
if (count === 0)
|
|
473
|
+
throw new FsError("EIO");
|
|
474
|
+
written += count;
|
|
475
|
+
offset += count;
|
|
476
|
+
}
|
|
477
|
+
if (options.mode !== undefined)
|
|
478
|
+
immediate.fchmodSync(fd, options.mode);
|
|
479
|
+
if (options.atimeMs !== undefined || options.mtimeMs !== undefined) {
|
|
480
|
+
const stat = immediate.fstatSync(fd);
|
|
481
|
+
immediate.futimesSync(fd, (options.atimeMs ?? stat.atimeMs) / 1000, (options.mtimeMs ?? stat.mtimeMs) / 1000);
|
|
482
|
+
}
|
|
483
|
+
return fileStat(immediate.fstatSync(fd, { bigint: true }));
|
|
484
|
+
}
|
|
485
|
+
finally {
|
|
486
|
+
immediate.closeSync(fd);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
options.signal?.throwIfAborted();
|
|
491
|
+
throw new FsError(nativeError(error).code, { syscall: "writeFileConditional", path });
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async removeFileConditional(path, options) {
|
|
495
|
+
return this.operation("removeFileConditional", path, options, async () => {
|
|
496
|
+
const target = await this.path(path, { ...options, followFinal: false });
|
|
497
|
+
options.signal?.throwIfAborted();
|
|
498
|
+
this.expectStaging(dirname(target), options.parent, true);
|
|
499
|
+
const file = this.expectStaging(target, options.expected);
|
|
500
|
+
if (file?.type !== "file" || file.nlink !== 1)
|
|
501
|
+
throw new FsError("EAGAIN");
|
|
502
|
+
immediate.unlinkSync(target);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
async prepareDirectory(path, options) {
|
|
506
|
+
options.signal?.throwIfAborted();
|
|
507
|
+
try {
|
|
508
|
+
this.stagingMetadata(options);
|
|
509
|
+
const target = await this.path(path, { ...options, followFinal: false, missing: "final" });
|
|
510
|
+
options.signal?.throwIfAborted();
|
|
511
|
+
this.protectTerminal(path);
|
|
512
|
+
this.expectStaging(dirname(target), options.parent, true);
|
|
513
|
+
const existing = this.expectStaging(target, options.expected, true);
|
|
514
|
+
if (existing && existing.type !== "directory")
|
|
515
|
+
throw new FsError("EAGAIN");
|
|
516
|
+
if (!existing)
|
|
517
|
+
immediate.mkdirSync(target, { mode: options.mode ?? 0o777 });
|
|
518
|
+
if (options.mode !== undefined)
|
|
519
|
+
immediate.chmodSync(target, options.mode);
|
|
520
|
+
if (options.atimeMs !== undefined || options.mtimeMs !== undefined) {
|
|
521
|
+
const stat = immediate.statSync(target);
|
|
522
|
+
immediate.utimesSync(target, (options.atimeMs ?? stat.atimeMs) / 1000, (options.mtimeMs ?? stat.mtimeMs) / 1000);
|
|
523
|
+
}
|
|
524
|
+
return this.stagingSnapshot(target);
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
options.signal?.throwIfAborted();
|
|
528
|
+
throw new FsError(nativeError(error).code, { syscall: "prepareDirectory", path });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
open(path, options) {
|
|
532
|
+
if (!platform.nativeFileSystem.open)
|
|
533
|
+
return this.operation("open", path, options, async () => { throw new FsError("ENOTSUP"); });
|
|
534
|
+
return openFileDescriptor(path, options, {
|
|
535
|
+
noFollow: true, positionedRead: true, positionedWrite: true, truncate: true, synchronization: "storage",
|
|
536
|
+
}, async (admitted) => {
|
|
537
|
+
let handle;
|
|
538
|
+
try {
|
|
539
|
+
const target = await this.path(path, {
|
|
540
|
+
...admitted, followFinal: !admitted.noFollow && admitted.creation !== "exclusive",
|
|
541
|
+
...(admitted.creation === "never" ? {} : { missing: "final" }),
|
|
542
|
+
});
|
|
543
|
+
let flags = (admitted.access === "read" ? constants.O_RDONLY : admitted.access === "write" ? constants.O_WRONLY : constants.O_RDWR)
|
|
544
|
+
| constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
545
|
+
if (admitted.creation !== "never")
|
|
546
|
+
flags |= constants.O_CREAT;
|
|
547
|
+
if (admitted.creation === "exclusive")
|
|
548
|
+
flags |= constants.O_EXCL;
|
|
549
|
+
if (admitted.append)
|
|
550
|
+
flags |= constants.O_APPEND;
|
|
551
|
+
let created = false;
|
|
552
|
+
if (admitted.exactMode && admitted.creation !== "never") {
|
|
553
|
+
try {
|
|
554
|
+
handle = await native.open(target, flags | constants.O_EXCL, admitted.mode);
|
|
555
|
+
created = true;
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
if (admitted.creation === "exclusive" || nativeError(error).code !== "EEXIST")
|
|
559
|
+
throw error;
|
|
560
|
+
handle = await native.open(target, flags & ~constants.O_CREAT, admitted.mode);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
else
|
|
564
|
+
handle = await native.open(target, flags, admitted.mode);
|
|
565
|
+
if (created)
|
|
566
|
+
await handle.chmod(admitted.mode);
|
|
567
|
+
admitted.signal?.throwIfAborted();
|
|
568
|
+
const stat = await handle.stat();
|
|
569
|
+
if (stat.isDirectory())
|
|
570
|
+
throw new FsError("EISDIR");
|
|
571
|
+
if (!stat.isFile())
|
|
572
|
+
throw new FsError("ENOTSUP");
|
|
573
|
+
admitted.signal?.throwIfAborted();
|
|
574
|
+
if (admitted.truncate)
|
|
575
|
+
await handle.truncate(0);
|
|
576
|
+
const resource = { handle };
|
|
577
|
+
handle = undefined;
|
|
578
|
+
return {
|
|
579
|
+
resource,
|
|
580
|
+
stat: (retained, forwarded) => this.operation("fstat", path, forwarded, async () => fileStat(await retained.handle.stat({ bigint: true }))),
|
|
581
|
+
read: (retained, buffer, position, forwarded) => this.operation("read", path, forwarded, async () => (await retained.handle.read(buffer, 0, buffer.byteLength, position)).bytesRead),
|
|
582
|
+
write: (retained, buffer, position, forwarded) => this.operation("write", path, forwarded, async () => (await retained.handle.write(buffer, 0, buffer.byteLength, position)).bytesWritten),
|
|
583
|
+
truncate: (retained, length, forwarded) => this.operation("ftruncate", path, forwarded, () => retained.handle.truncate(length)),
|
|
584
|
+
sync: (retained, dataOnly, forwarded) => this.operation(dataOnly ? "fdatasync" : "fsync", path, forwarded, () => dataOnly ? retained.handle.datasync() : retained.handle.sync()),
|
|
585
|
+
close: async (retained) => {
|
|
586
|
+
try {
|
|
587
|
+
await this.operation("close", path, {}, () => retained.handle.close());
|
|
588
|
+
}
|
|
589
|
+
finally {
|
|
590
|
+
retained.handle = undefined;
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
if (handle)
|
|
597
|
+
await finishCleanup(() => handle.close(), true);
|
|
598
|
+
handle = undefined;
|
|
599
|
+
admitted.signal?.throwIfAborted();
|
|
600
|
+
throw new FsError(nativeError(error).code, { syscall: "open", path });
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
protectTerminal(path) {
|
|
605
|
+
const terminal = path.split("/").filter(Boolean).at(-1);
|
|
606
|
+
if (terminal === "." || terminal === "..")
|
|
607
|
+
throw new FsError("EINVAL");
|
|
608
|
+
}
|
|
609
|
+
async readFile(path, options = {}) {
|
|
610
|
+
return this.operation("readFile", path, options, async () => {
|
|
611
|
+
const maxBytes = options.maxBytes ?? Infinity;
|
|
612
|
+
if (maxBytes !== Infinity)
|
|
613
|
+
integer(maxBytes);
|
|
614
|
+
return collectBytes(this.readStream(path, options), { maxBytes, ...options });
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
async writeFile(path, data, options = {}) {
|
|
618
|
+
return this.operation("writeFile", path, options, async () => {
|
|
619
|
+
if (!(data instanceof Uint8Array))
|
|
620
|
+
throw new FsError("EINVAL");
|
|
621
|
+
await this.writeStream(path, toByteSource(data), options);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
async appendFile(path, data, options = {}) {
|
|
625
|
+
return this.operation("appendFile", path, options, () => this.writeFile(path, data, { ...options, flag: "a" }));
|
|
626
|
+
}
|
|
627
|
+
async stat(path, options = {}) {
|
|
628
|
+
return this.operation("stat", path, options, async () => {
|
|
629
|
+
const target = await this.path(path, options);
|
|
630
|
+
options.signal?.throwIfAborted();
|
|
631
|
+
return fileStat(await native.stat(target, { bigint: true }));
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
async lstat(path, options = {}) {
|
|
635
|
+
return this.operation("lstat", path, options, async () => {
|
|
636
|
+
const target = await this.path(path, { ...options, followFinal: false });
|
|
637
|
+
options.signal?.throwIfAborted();
|
|
638
|
+
return fileStat(await native.lstat(target, { bigint: true }));
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
async readdir(path, options = {}) {
|
|
642
|
+
const limit = directoryEntryLimit(options, path);
|
|
643
|
+
return this.operation("readdir", path, options, async () => {
|
|
644
|
+
const target = await this.path(path, options);
|
|
645
|
+
options.signal?.throwIfAborted();
|
|
646
|
+
if (limit === undefined) {
|
|
647
|
+
const entries = await native.readdir(target, { withFileTypes: true });
|
|
648
|
+
return entries.map((entry) => ({ name: entry.name, type: fileType(entry) }));
|
|
649
|
+
}
|
|
650
|
+
const handle = await native.opendir(target, { bufferSize: 1 });
|
|
651
|
+
let failed = false;
|
|
652
|
+
try {
|
|
653
|
+
const entries = [];
|
|
654
|
+
while (true) {
|
|
655
|
+
options.signal?.throwIfAborted();
|
|
656
|
+
const entry = await handle.read();
|
|
657
|
+
options.signal?.throwIfAborted();
|
|
658
|
+
if (entry === null)
|
|
659
|
+
return entries;
|
|
660
|
+
admitDirectoryEntries(entries.length + 1, limit, path);
|
|
661
|
+
entries.push({ name: entry.name, type: fileType(entry) });
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
failed = true;
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
finally {
|
|
669
|
+
await finishCleanup(() => handle.close(), failed);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
async mkdir(path, options = {}) {
|
|
674
|
+
return this.operation("mkdir", path, options, async () => {
|
|
675
|
+
if (options.mode !== undefined)
|
|
676
|
+
integer(options.mode);
|
|
677
|
+
if (options.exactMode && (!platform.nativeFileSystem.permissions || options.recursive))
|
|
678
|
+
throw new FsError("ENOTSUP");
|
|
679
|
+
const target = await this.path(path, {
|
|
680
|
+
...options,
|
|
681
|
+
missing: "final", followFinal: !!options.recursive, deferTrailingSeparator: true,
|
|
682
|
+
...(options.recursive ? { createDirectories: { mode: options.mode ?? 0o777 } } : {}),
|
|
683
|
+
});
|
|
684
|
+
options.signal?.throwIfAborted();
|
|
685
|
+
await native.mkdir(target, { recursive: options.recursive ?? false, ...(options.mode === undefined ? {} : { mode: options.mode }) });
|
|
686
|
+
if (options.exactMode)
|
|
687
|
+
await native.chmod(target, options.mode ?? 0o777);
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
async rmdir(path, options = {}) {
|
|
691
|
+
return this.operation("rmdir", path, options, async () => {
|
|
692
|
+
const target = await this.path(path.replace(/\/+$/, "") || (path ? "/" : ""), { ...options, followFinal: false });
|
|
693
|
+
this.protectTerminal(path);
|
|
694
|
+
this.protectRoot(target, await this.root(options));
|
|
695
|
+
options.signal?.throwIfAborted();
|
|
696
|
+
await native.rmdir(target);
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
async rm(path, options = {}) {
|
|
700
|
+
return this.operation("rm", path, options, async () => {
|
|
701
|
+
let target;
|
|
702
|
+
try {
|
|
703
|
+
target = await this.path(path, { ...options, followFinal: false });
|
|
704
|
+
}
|
|
705
|
+
catch (error) {
|
|
706
|
+
if (options.force && toFsError(error).code === "ENOENT")
|
|
707
|
+
return;
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
this.protectTerminal(path);
|
|
711
|
+
this.protectRoot(target, await this.root(options));
|
|
712
|
+
options.signal?.throwIfAborted();
|
|
713
|
+
await native.rm(target, { recursive: options.recursive ?? false, force: options.force ?? false });
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
async unlink(path, options = {}) {
|
|
717
|
+
return this.operation("unlink", path, options, async () => {
|
|
718
|
+
const target = await this.path(path, { ...options, followFinal: false });
|
|
719
|
+
this.protectTerminal(path);
|
|
720
|
+
this.protectRoot(target, await this.root(options));
|
|
721
|
+
options.signal?.throwIfAborted();
|
|
722
|
+
await native.unlink(target);
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
async rename(source, destination, options = {}) {
|
|
726
|
+
return this.operation("rename", source, options, async () => {
|
|
727
|
+
if (options.noReplace && !this.renameNoReplace)
|
|
728
|
+
throw new FsError("ENOTSUP", { syscall: "rename", path: source, dest: destination });
|
|
729
|
+
const from = await this.path(source, { ...options, followFinal: false });
|
|
730
|
+
const to = await this.path(destination, { ...options, followFinal: false, missing: "final" });
|
|
731
|
+
this.protectTerminal(source);
|
|
732
|
+
this.protectTerminal(destination);
|
|
733
|
+
const root = await this.root(options);
|
|
734
|
+
this.protectRoot(from, root);
|
|
735
|
+
this.protectRoot(to, root);
|
|
736
|
+
options.signal?.throwIfAborted();
|
|
737
|
+
if (options.noReplace)
|
|
738
|
+
await this.renameNoReplace(from, to, options.signal ? { signal: options.signal } : {});
|
|
739
|
+
else
|
|
740
|
+
await native.rename(from, to);
|
|
741
|
+
}, destination);
|
|
742
|
+
}
|
|
743
|
+
async copyFile(source, destination, options = {}) {
|
|
744
|
+
return this.operation("copyFile", source, options, async () => {
|
|
745
|
+
const from = await this.path(source, options);
|
|
746
|
+
const to = await this.path(destination, { ...options, missing: "final", followFinal: !options.exclusive, deferTrailingSeparator: true });
|
|
747
|
+
let flags = options.exclusive ? constants.COPYFILE_EXCL : 0;
|
|
748
|
+
if (!to.endsWith("/")) {
|
|
749
|
+
const origin = await native.stat(from, { bigint: true });
|
|
750
|
+
let target;
|
|
751
|
+
try {
|
|
752
|
+
target = await native.lstat(to, { bigint: true });
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
if (nativeError(error).code !== "ENOENT")
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
758
|
+
if (target && options.exclusive)
|
|
759
|
+
throw new FsError("EEXIST");
|
|
760
|
+
if (target && origin.isFile()) {
|
|
761
|
+
const identity = compareIdentity(fileStat(origin), fileStat(target));
|
|
762
|
+
if (identity === "same")
|
|
763
|
+
throw new FsError("EINVAL");
|
|
764
|
+
if (identity === "unknown")
|
|
765
|
+
throw new FsError("ENOTSUP");
|
|
766
|
+
}
|
|
767
|
+
if (!target)
|
|
768
|
+
flags |= constants.COPYFILE_EXCL;
|
|
769
|
+
}
|
|
770
|
+
options.signal?.throwIfAborted();
|
|
771
|
+
await native.copyFile(from, to, flags);
|
|
772
|
+
}, destination);
|
|
773
|
+
}
|
|
774
|
+
async realpath(path, options = {}) {
|
|
775
|
+
return this.operation("realpath", path, options, async () => {
|
|
776
|
+
const target = await this.path(path, options);
|
|
777
|
+
return `/${relative(await this.root(options), target)}`;
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
async access(path, mode = constants.F_OK, options = {}) {
|
|
781
|
+
return this.operation("access", path, options, async () => {
|
|
782
|
+
integer(mode);
|
|
783
|
+
if (mode > 7)
|
|
784
|
+
throw new FsError("EINVAL");
|
|
785
|
+
const target = await this.path(path, options);
|
|
786
|
+
options.signal?.throwIfAborted();
|
|
787
|
+
await native.access(target, mode);
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
async readlink(path, options = {}) {
|
|
791
|
+
return this.operation("readlink", path, options, async () => {
|
|
792
|
+
const resolved = await this.path(path, { ...options, followFinal: false });
|
|
793
|
+
options.signal?.throwIfAborted();
|
|
794
|
+
const target = await native.readlink(resolved);
|
|
795
|
+
if (!isAbsolute(target))
|
|
796
|
+
return target;
|
|
797
|
+
return `/${this.absoluteTarget(await this.root(options), target).join("/")}`;
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
async symlink(target, path, options = {}) {
|
|
801
|
+
return this.operation("symlink", path, options, async () => {
|
|
802
|
+
validatePath(target);
|
|
803
|
+
if (!target)
|
|
804
|
+
throw new FsError("ENOENT");
|
|
805
|
+
const destination = await this.path(path, { ...options, followFinal: false, missing: "final" });
|
|
806
|
+
const root = await this.root(options);
|
|
807
|
+
const stored = target.startsWith("/") ? `${root === "/" ? "" : root}${target}` : target;
|
|
808
|
+
const components = target.startsWith("/")
|
|
809
|
+
? target.split("/")
|
|
810
|
+
: [...relative(root, resolve(destination, "..")).split("/"), ...target.split("/")];
|
|
811
|
+
try {
|
|
812
|
+
await this.walk(root, components.map((name) => ({ name, fromLink: true })), { ...options, checkTarget: true });
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
if (nativeError(error).code !== "ELOOP")
|
|
816
|
+
throw error;
|
|
817
|
+
}
|
|
818
|
+
options.signal?.throwIfAborted();
|
|
819
|
+
await native.symlink(stored, destination);
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
async link(existingPath, newPath, options = {}) {
|
|
823
|
+
return this.operation("link", existingPath, options, async () => {
|
|
824
|
+
const source = await this.path(existingPath, { ...options, followFinal: false });
|
|
825
|
+
const destination = await this.path(newPath, { ...options, followFinal: false, missing: "final" });
|
|
826
|
+
options.signal?.throwIfAborted();
|
|
827
|
+
await native.link(source, destination);
|
|
828
|
+
}, newPath);
|
|
829
|
+
}
|
|
830
|
+
async chmod(path, mode, options = {}) {
|
|
831
|
+
return this.operation("chmod", path, options, async () => {
|
|
832
|
+
if (!platform.nativeFileSystem.permissions)
|
|
833
|
+
throw new FsError("ENOTSUP");
|
|
834
|
+
integer(mode);
|
|
835
|
+
const conditional = snapshotConditionalChmod(path, options);
|
|
836
|
+
if (conditional) {
|
|
837
|
+
const target = await this.path(path, { ...conditional, followFinal: false });
|
|
838
|
+
const root = await this.root(conditional);
|
|
839
|
+
if ((target === root ? "/" : `/${relative(root, target)}`) !== path)
|
|
840
|
+
throw new FsError("EAGAIN");
|
|
841
|
+
if (conditional.expected.type !== "file" && conditional.expected.type !== "directory")
|
|
842
|
+
throw new FsError("ENOTSUP");
|
|
843
|
+
const parent = target === root ? root : dirname(target);
|
|
844
|
+
const validate = () => {
|
|
845
|
+
conditional.signal?.throwIfAborted();
|
|
846
|
+
for (const entry of conditional.ancestors)
|
|
847
|
+
this.expectStaging(join(root, entry.path.slice(1)), entry.stat, true);
|
|
848
|
+
this.expectStaging(parent, conditional.parent, true);
|
|
849
|
+
const current = this.expectStaging(target, conditional.expected, conditional.expected.type === "directory");
|
|
850
|
+
if (current?.mode !== conditional.expected.mode)
|
|
851
|
+
throw new FsError("EAGAIN");
|
|
852
|
+
};
|
|
853
|
+
validate();
|
|
854
|
+
if (conditional.commitGuard)
|
|
855
|
+
runStagingGuard(conditional.commitGuard);
|
|
856
|
+
validate();
|
|
857
|
+
immediate.chmodSync(target, mode);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const target = await this.path(path, options);
|
|
861
|
+
options.signal?.throwIfAborted();
|
|
862
|
+
await native.chmod(target, mode);
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
async utimes(path, atimeMs, mtimeMs, options = {}) {
|
|
866
|
+
return this.operation("utimes", path, options, async () => {
|
|
867
|
+
if (!platform.nativeFileSystem.timestamps)
|
|
868
|
+
throw new FsError("ENOTSUP");
|
|
869
|
+
if (!Number.isFinite(atimeMs) || !Number.isFinite(mtimeMs)
|
|
870
|
+
|| Math.abs(atimeMs) > 8.64e15 || Math.abs(mtimeMs) > 8.64e15)
|
|
871
|
+
throw new FsError("EINVAL");
|
|
872
|
+
const target = await this.path(path, options);
|
|
873
|
+
options.signal?.throwIfAborted();
|
|
874
|
+
const atimeSeconds = atimeMs / 1000;
|
|
875
|
+
const mtimeSeconds = mtimeMs / 1000;
|
|
876
|
+
await native.utimes(target, atimeSeconds < 0 ? String(atimeSeconds) : atimeSeconds, mtimeSeconds < 0 ? String(mtimeSeconds) : mtimeSeconds);
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
async truncate(path, length = 0, options = {}) {
|
|
880
|
+
return this.operation("truncate", path, options, async () => {
|
|
881
|
+
integer(length);
|
|
882
|
+
const target = await this.path(path, options);
|
|
883
|
+
options.signal?.throwIfAborted();
|
|
884
|
+
const handle = await native.open(target, constants.O_WRONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
885
|
+
try {
|
|
886
|
+
options.signal?.throwIfAborted();
|
|
887
|
+
if (!(await handle.stat()).isFile())
|
|
888
|
+
throw new FsError("ENOTSUP");
|
|
889
|
+
options.signal?.throwIfAborted();
|
|
890
|
+
await handle.truncate(length);
|
|
891
|
+
}
|
|
892
|
+
finally {
|
|
893
|
+
await handle.close();
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
async openResizeFile(path, options = {}) {
|
|
898
|
+
options.signal?.throwIfAborted();
|
|
899
|
+
const assertStock = () => {
|
|
900
|
+
const unsupported = () => { throw new FsError("ENOTSUP", { syscall: "openResizeFile", path }); };
|
|
901
|
+
if (Object.getPrototypeOf(this) !== RealFileSystem.prototype)
|
|
902
|
+
unsupported();
|
|
903
|
+
for (const name of ["openResizeFile", "truncate", "writeFile", "writeStream", "appendFile", "stat", "lstat", "realpath", "access",
|
|
904
|
+
"root", "absoluteTarget", "walk", "path"]) {
|
|
905
|
+
const descriptor = Object.getOwnPropertyDescriptor(this, name)
|
|
906
|
+
?? Object.getOwnPropertyDescriptor(RealFileSystem.prototype, name);
|
|
907
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== realImplementation[name]?.value)
|
|
908
|
+
unsupported();
|
|
909
|
+
}
|
|
910
|
+
const capabilities = Object.getOwnPropertyDescriptor(this, "capabilities")?.value;
|
|
911
|
+
if (capabilities?.readOnly === true)
|
|
912
|
+
throw new FsError("EROFS", { syscall: "openResizeFile", path });
|
|
913
|
+
if (capabilities?.retainedResize !== true)
|
|
914
|
+
unsupported();
|
|
915
|
+
};
|
|
916
|
+
assertStock();
|
|
917
|
+
const failure = (error, syscall) => error
|
|
918
|
+
? new FsError(nativeError(error).code, { syscall, path }) : error;
|
|
919
|
+
let resource;
|
|
920
|
+
try {
|
|
921
|
+
if (options.mode !== undefined)
|
|
922
|
+
integer(options.mode);
|
|
923
|
+
if (options.create !== undefined && typeof options.create !== "boolean")
|
|
924
|
+
throw new FsError("EINVAL");
|
|
925
|
+
const target = await this.path(path, {
|
|
926
|
+
...options, ...(options.create === true ? { missing: "final" } : {}),
|
|
927
|
+
deferTrailingSeparator: true, createFile: options.create === true,
|
|
928
|
+
});
|
|
929
|
+
options.signal?.throwIfAborted();
|
|
930
|
+
assertStock();
|
|
931
|
+
const flags = constants.O_WRONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK
|
|
932
|
+
| (options.create === true ? constants.O_CREAT : 0);
|
|
933
|
+
resource = await native.open(target, flags, options.mode ?? 0o666);
|
|
934
|
+
options.signal?.throwIfAborted();
|
|
935
|
+
const stats = await resource.stat();
|
|
936
|
+
options.signal?.throwIfAborted();
|
|
937
|
+
assertStock();
|
|
938
|
+
if (stats.isDirectory())
|
|
939
|
+
throw new FsError("EISDIR");
|
|
940
|
+
if (!stats.isFile())
|
|
941
|
+
throw new FsError("ENOTSUP");
|
|
942
|
+
}
|
|
943
|
+
catch (error) {
|
|
944
|
+
await finishCleanup(async () => { await resource?.close(); }, true);
|
|
945
|
+
options.signal?.throwIfAborted();
|
|
946
|
+
throw failure(error, "openResizeFile");
|
|
947
|
+
}
|
|
948
|
+
const handle = resource;
|
|
949
|
+
const pending = new Set();
|
|
950
|
+
let accepting = true;
|
|
951
|
+
let closing;
|
|
952
|
+
const assertOpen = (options, syscall) => {
|
|
953
|
+
options.signal?.throwIfAborted();
|
|
954
|
+
if (!accepting)
|
|
955
|
+
throw new FsError("EBADF", { syscall, path });
|
|
956
|
+
};
|
|
957
|
+
const perform = async (options, syscall, action) => {
|
|
958
|
+
assertOpen(options, syscall);
|
|
959
|
+
let settled;
|
|
960
|
+
const admitted = new Promise(resolve => { settled = resolve; });
|
|
961
|
+
pending.add(admitted);
|
|
962
|
+
try {
|
|
963
|
+
const value = await action();
|
|
964
|
+
options.signal?.throwIfAborted();
|
|
965
|
+
return value;
|
|
966
|
+
}
|
|
967
|
+
catch (error) {
|
|
968
|
+
options.signal?.throwIfAborted();
|
|
969
|
+
throw failure(error, syscall);
|
|
970
|
+
}
|
|
971
|
+
finally {
|
|
972
|
+
pending.delete(admitted);
|
|
973
|
+
settled();
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
return {
|
|
977
|
+
stat(options = {}) {
|
|
978
|
+
return perform(options, "fstat", async () => fileStat(await handle.stat({ bigint: true })));
|
|
979
|
+
},
|
|
980
|
+
async truncate(length, options = {}) {
|
|
981
|
+
assertOpen(options, "ftruncate");
|
|
982
|
+
try {
|
|
983
|
+
integer(length);
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
throw failure(error, "ftruncate");
|
|
987
|
+
}
|
|
988
|
+
return perform(options, "ftruncate", async () => { await handle.truncate(length); });
|
|
989
|
+
},
|
|
990
|
+
async seekEnd(options = {}) {
|
|
991
|
+
const signal = options.signal;
|
|
992
|
+
const operationOptions = signal === undefined ? {} : { signal };
|
|
993
|
+
assertOpen(operationOptions, "lseek");
|
|
994
|
+
try {
|
|
995
|
+
const binding = await loadNativeSeekBinding(signal);
|
|
996
|
+
assertOpen(operationOptions, "lseek");
|
|
997
|
+
const seek = binding.seekEnd;
|
|
998
|
+
assertOpen(operationOptions, "lseek");
|
|
999
|
+
if (typeof seek !== "function")
|
|
1000
|
+
throw new FsError("EIO");
|
|
1001
|
+
const descriptor = handle.fd;
|
|
1002
|
+
assertOpen(operationOptions, "lseek");
|
|
1003
|
+
return await perform(operationOptions, "lseek", () => callNativeSeekEnd(binding, seek, descriptor));
|
|
1004
|
+
}
|
|
1005
|
+
catch (error) {
|
|
1006
|
+
signal?.throwIfAborted();
|
|
1007
|
+
throw failure(error, "lseek");
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
close() {
|
|
1011
|
+
accepting = false;
|
|
1012
|
+
closing ??= Promise.all([...pending]).then(async () => {
|
|
1013
|
+
try {
|
|
1014
|
+
await handle.close();
|
|
1015
|
+
}
|
|
1016
|
+
catch (error) {
|
|
1017
|
+
throw failure(error, "close");
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
return closing;
|
|
1021
|
+
},
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
async openReadFile(path, options = {}) {
|
|
1025
|
+
const signal = options.signal;
|
|
1026
|
+
signal?.throwIfAborted();
|
|
1027
|
+
const assertStock = () => {
|
|
1028
|
+
const unsupported = () => { throw new FsError("ENOTSUP", { syscall: "openReadFile", path }); };
|
|
1029
|
+
if (Object.getPrototypeOf(this) !== RealFileSystem.prototype)
|
|
1030
|
+
unsupported();
|
|
1031
|
+
for (const name of ["openReadFile", "readFile", "readStream", "stat", "lstat", "realpath", "access",
|
|
1032
|
+
"root", "absoluteTarget", "walk", "path"]) {
|
|
1033
|
+
const descriptor = Object.getOwnPropertyDescriptor(this, name)
|
|
1034
|
+
?? Object.getOwnPropertyDescriptor(RealFileSystem.prototype, name);
|
|
1035
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== realImplementation[name]?.value)
|
|
1036
|
+
unsupported();
|
|
1037
|
+
}
|
|
1038
|
+
if (Object.getOwnPropertyDescriptor(this, "capabilities")?.value?.retainedRead !== true)
|
|
1039
|
+
unsupported();
|
|
1040
|
+
};
|
|
1041
|
+
assertStock();
|
|
1042
|
+
const failure = (error, syscall) => error
|
|
1043
|
+
? new FsError(nativeError(error).code, { syscall, path }) : error;
|
|
1044
|
+
let resource;
|
|
1045
|
+
let directory = false;
|
|
1046
|
+
try {
|
|
1047
|
+
const allowDirectory = options.allowDirectory === true;
|
|
1048
|
+
signal?.throwIfAborted();
|
|
1049
|
+
const target = await this.path(path, signal === undefined ? {} : { signal });
|
|
1050
|
+
signal?.throwIfAborted();
|
|
1051
|
+
assertStock();
|
|
1052
|
+
resource = await native.open(target, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
1053
|
+
signal?.throwIfAborted();
|
|
1054
|
+
const stats = await resource.stat();
|
|
1055
|
+
signal?.throwIfAborted();
|
|
1056
|
+
assertStock();
|
|
1057
|
+
directory = stats.isDirectory();
|
|
1058
|
+
if (directory && !allowDirectory)
|
|
1059
|
+
throw new FsError("EISDIR");
|
|
1060
|
+
if (!directory && !stats.isFile())
|
|
1061
|
+
throw new FsError("ENOTSUP");
|
|
1062
|
+
signal?.throwIfAborted();
|
|
1063
|
+
}
|
|
1064
|
+
catch (error) {
|
|
1065
|
+
await finishCleanup(async () => { await resource?.close(); }, true);
|
|
1066
|
+
signal?.throwIfAborted();
|
|
1067
|
+
throw failure(error, "openReadFile");
|
|
1068
|
+
}
|
|
1069
|
+
const handle = resource;
|
|
1070
|
+
const pending = new Set();
|
|
1071
|
+
let accepting = true;
|
|
1072
|
+
let closing;
|
|
1073
|
+
const assertOpen = (options, syscall) => {
|
|
1074
|
+
options.signal?.throwIfAborted();
|
|
1075
|
+
if (!accepting)
|
|
1076
|
+
throw new FsError("EBADF", { syscall, path });
|
|
1077
|
+
};
|
|
1078
|
+
const perform = async (options, syscall, action) => {
|
|
1079
|
+
assertOpen(options, syscall);
|
|
1080
|
+
let settled;
|
|
1081
|
+
const admitted = new Promise(resolve => { settled = resolve; });
|
|
1082
|
+
pending.add(admitted);
|
|
1083
|
+
try {
|
|
1084
|
+
const value = await action();
|
|
1085
|
+
options.signal?.throwIfAborted();
|
|
1086
|
+
return value;
|
|
1087
|
+
}
|
|
1088
|
+
catch (error) {
|
|
1089
|
+
options.signal?.throwIfAborted();
|
|
1090
|
+
throw failure(error, syscall);
|
|
1091
|
+
}
|
|
1092
|
+
finally {
|
|
1093
|
+
pending.delete(admitted);
|
|
1094
|
+
settled();
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
return {
|
|
1098
|
+
stat(options = {}) {
|
|
1099
|
+
return perform(options, "fstat", async () => fileStat(await handle.stat({ bigint: true })));
|
|
1100
|
+
},
|
|
1101
|
+
async read(position, maxBytes, options = {}) {
|
|
1102
|
+
assertOpen(options, "read");
|
|
1103
|
+
try {
|
|
1104
|
+
integer(position);
|
|
1105
|
+
integer(maxBytes, 1);
|
|
1106
|
+
}
|
|
1107
|
+
catch (error) {
|
|
1108
|
+
throw failure(error, "read");
|
|
1109
|
+
}
|
|
1110
|
+
if (maxBytes > Number.MAX_SAFE_INTEGER - position)
|
|
1111
|
+
throw new FsError("EINVAL", { syscall: "read", path });
|
|
1112
|
+
if (directory)
|
|
1113
|
+
throw new FsError("EISDIR", { syscall: "read", path });
|
|
1114
|
+
return perform(options, "read", async () => {
|
|
1115
|
+
let bytes;
|
|
1116
|
+
try {
|
|
1117
|
+
bytes = new Uint8Array(maxBytes);
|
|
1118
|
+
}
|
|
1119
|
+
catch {
|
|
1120
|
+
throw new FsError("EFBIG");
|
|
1121
|
+
}
|
|
1122
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.byteLength, position);
|
|
1123
|
+
if (!Number.isSafeInteger(bytesRead) || bytesRead < 0 || bytesRead > maxBytes)
|
|
1124
|
+
throw new FsError("EIO");
|
|
1125
|
+
return bytes.slice(0, bytesRead);
|
|
1126
|
+
});
|
|
1127
|
+
},
|
|
1128
|
+
async seekEnd(options = {}) {
|
|
1129
|
+
const signal = options.signal;
|
|
1130
|
+
const operationOptions = signal === undefined ? {} : { signal };
|
|
1131
|
+
assertOpen(operationOptions, "lseek");
|
|
1132
|
+
try {
|
|
1133
|
+
const binding = await loadNativeSeekBinding(signal);
|
|
1134
|
+
assertOpen(operationOptions, "lseek");
|
|
1135
|
+
const seek = binding.seekEnd;
|
|
1136
|
+
assertOpen(operationOptions, "lseek");
|
|
1137
|
+
if (typeof seek !== "function")
|
|
1138
|
+
throw new FsError("EIO");
|
|
1139
|
+
const descriptor = handle.fd;
|
|
1140
|
+
assertOpen(operationOptions, "lseek");
|
|
1141
|
+
return await perform(operationOptions, "lseek", () => callNativeSeekEnd(binding, seek, descriptor));
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
signal?.throwIfAborted();
|
|
1145
|
+
throw failure(error, "lseek");
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
close() {
|
|
1149
|
+
accepting = false;
|
|
1150
|
+
closing ??= Promise.all([...pending]).then(async () => {
|
|
1151
|
+
try {
|
|
1152
|
+
await handle.close();
|
|
1153
|
+
}
|
|
1154
|
+
catch (error) {
|
|
1155
|
+
throw failure(error, "close");
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
return closing;
|
|
1159
|
+
},
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
async *readStream(path, options = {}) {
|
|
1163
|
+
const syscall = "readStream";
|
|
1164
|
+
let handle;
|
|
1165
|
+
let failed = false;
|
|
1166
|
+
try {
|
|
1167
|
+
options.signal?.throwIfAborted();
|
|
1168
|
+
const start = options.start ?? 0;
|
|
1169
|
+
const end = options.endExclusive ?? Number.MAX_SAFE_INTEGER;
|
|
1170
|
+
const chunkSize = options.chunkSize ?? 64 * 1024;
|
|
1171
|
+
integer(start);
|
|
1172
|
+
integer(end);
|
|
1173
|
+
integer(chunkSize, 1);
|
|
1174
|
+
if (end < start)
|
|
1175
|
+
throw new FsError("EINVAL");
|
|
1176
|
+
const target = await this.path(path, options);
|
|
1177
|
+
options.signal?.throwIfAborted();
|
|
1178
|
+
handle = await native.open(target, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
1179
|
+
options.signal?.throwIfAborted();
|
|
1180
|
+
const stats = await handle.stat();
|
|
1181
|
+
if (stats.isDirectory())
|
|
1182
|
+
throw new FsError("EISDIR");
|
|
1183
|
+
if (!stats.isFile())
|
|
1184
|
+
throw new FsError("ENOTSUP");
|
|
1185
|
+
let position = start;
|
|
1186
|
+
while (position < end) {
|
|
1187
|
+
options.signal?.throwIfAborted();
|
|
1188
|
+
const bytes = new Uint8Array(Math.min(chunkSize, end - position));
|
|
1189
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.byteLength, position);
|
|
1190
|
+
options.signal?.throwIfAborted();
|
|
1191
|
+
if (bytesRead === 0)
|
|
1192
|
+
break;
|
|
1193
|
+
position += bytesRead;
|
|
1194
|
+
yield bytes.subarray(0, bytesRead);
|
|
1195
|
+
}
|
|
1196
|
+
options.signal?.throwIfAborted();
|
|
1197
|
+
}
|
|
1198
|
+
catch (error) {
|
|
1199
|
+
failed = true;
|
|
1200
|
+
options.signal?.throwIfAborted();
|
|
1201
|
+
throw new FsError(nativeError(error).code, { syscall, path });
|
|
1202
|
+
}
|
|
1203
|
+
finally {
|
|
1204
|
+
await finishCleanup(async () => {
|
|
1205
|
+
try {
|
|
1206
|
+
await handle?.close();
|
|
1207
|
+
}
|
|
1208
|
+
catch (error) {
|
|
1209
|
+
throw new FsError(nativeError(error).code, { syscall, path });
|
|
1210
|
+
}
|
|
1211
|
+
}, failed);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
async writeStream(path, source, options = {}) {
|
|
1215
|
+
return this.operation("writeStream", path, options, async () => {
|
|
1216
|
+
const flag = options.flag ?? "w";
|
|
1217
|
+
if (!["w", "wx", "a", "ax"].includes(flag))
|
|
1218
|
+
throw new FsError("EINVAL");
|
|
1219
|
+
if (options.mode !== undefined)
|
|
1220
|
+
integer(options.mode);
|
|
1221
|
+
const exclusive = flag.endsWith("x");
|
|
1222
|
+
const destination = await this.path(path, { ...options, missing: "final", followFinal: !exclusive, deferTrailingSeparator: true });
|
|
1223
|
+
const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW | constants.O_NONBLOCK
|
|
1224
|
+
| (flag.startsWith("a") ? constants.O_APPEND : constants.O_TRUNC)
|
|
1225
|
+
| (exclusive ? constants.O_EXCL : 0);
|
|
1226
|
+
options.signal?.throwIfAborted();
|
|
1227
|
+
const handle = await native.open(destination, flags, options.mode ?? 0o666);
|
|
1228
|
+
try {
|
|
1229
|
+
options.signal?.throwIfAborted();
|
|
1230
|
+
if (!(await handle.stat()).isFile())
|
|
1231
|
+
throw new FsError("ENOTSUP");
|
|
1232
|
+
options.signal?.throwIfAborted();
|
|
1233
|
+
for await (const chunk of source) {
|
|
1234
|
+
options.signal?.throwIfAborted();
|
|
1235
|
+
if (!(chunk instanceof Uint8Array))
|
|
1236
|
+
throw new FsError("EINVAL");
|
|
1237
|
+
let offset = 0;
|
|
1238
|
+
while (offset < chunk.byteLength) {
|
|
1239
|
+
options.signal?.throwIfAborted();
|
|
1240
|
+
const { bytesWritten } = await handle.write(chunk, offset, chunk.byteLength - offset, null);
|
|
1241
|
+
if (bytesWritten === 0)
|
|
1242
|
+
throw new FsError("EIO");
|
|
1243
|
+
offset += bytesWritten;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
finally {
|
|
1248
|
+
await handle.close();
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const realImplementation = Object.getOwnPropertyDescriptors(RealFileSystem.prototype);
|
|
1254
|
+
/** Construct and validate an existing root before returning the backend. */
|
|
1255
|
+
export async function createRealFileSystem(options) {
|
|
1256
|
+
const filesystem = new RealFileSystem(options);
|
|
1257
|
+
await filesystem.stat("/");
|
|
1258
|
+
return filesystem;
|
|
1259
|
+
}
|