toolcraft 0.0.749 → 0.0.750
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,249 @@
|
|
|
1
|
+
function utf8ByteLength(value) {
|
|
2
|
+
let bytes = 0;
|
|
3
|
+
for (let i = 0; i < value.length; i++) {
|
|
4
|
+
const code = value.charCodeAt(i);
|
|
5
|
+
if (code < 0x80)
|
|
6
|
+
bytes += 1;
|
|
7
|
+
else if (code < 0x800)
|
|
8
|
+
bytes += 2;
|
|
9
|
+
else if (code >= 0xd800 && code <= 0xdbff && i + 1 < value.length && value.charCodeAt(i + 1) >= 0xdc00 && value.charCodeAt(i + 1) <= 0xdfff) {
|
|
10
|
+
bytes += 4;
|
|
11
|
+
i++;
|
|
12
|
+
}
|
|
13
|
+
else
|
|
14
|
+
bytes += 3;
|
|
15
|
+
}
|
|
16
|
+
return bytes;
|
|
17
|
+
}
|
|
18
|
+
import { compareKeys } from "./key-order.js";
|
|
19
|
+
import { collectBytes } from "../../contracts/io.js";
|
|
20
|
+
import { recordMockS3Head } from "./authority.js";
|
|
21
|
+
import { S3ServiceError } from "./transport.js";
|
|
22
|
+
export class MockS3Client {
|
|
23
|
+
capabilities = Object.freeze({ conditionalPut: true, conditionalCopy: true, conditionalDelete: true, streamingRead: true, streamingWrite: true });
|
|
24
|
+
buckets = new Map();
|
|
25
|
+
cursors = new Map();
|
|
26
|
+
history = [];
|
|
27
|
+
pageSize;
|
|
28
|
+
now;
|
|
29
|
+
authorize;
|
|
30
|
+
nextToken = 0;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.pageSize = options.pageSize ?? 1000;
|
|
33
|
+
if (!Number.isInteger(this.pageSize) || this.pageSize < 1 || this.pageSize > 1000) {
|
|
34
|
+
throw new RangeError("pageSize must be an integer from 1 to 1000");
|
|
35
|
+
}
|
|
36
|
+
this.now = options.now ?? (() => new Date());
|
|
37
|
+
this.authorize = options.authorize;
|
|
38
|
+
for (const bucket of options.buckets)
|
|
39
|
+
this.buckets.set(bucket, new Map());
|
|
40
|
+
}
|
|
41
|
+
get requests() {
|
|
42
|
+
return structuredClone(this.history);
|
|
43
|
+
}
|
|
44
|
+
async begin(operation, input, options) {
|
|
45
|
+
if (options?.abortSignal?.aborted)
|
|
46
|
+
throw new S3ServiceError("AbortError", 499);
|
|
47
|
+
const request = { operation, input: structuredClone(input) };
|
|
48
|
+
this.history.push(structuredClone(request));
|
|
49
|
+
await this.authorize?.(request);
|
|
50
|
+
if (options?.abortSignal?.aborted)
|
|
51
|
+
throw new S3ServiceError("AbortError", 499);
|
|
52
|
+
if (!this.buckets.has(input.Bucket))
|
|
53
|
+
throw new S3ServiceError("NoSuchBucket", 404);
|
|
54
|
+
if ("Key" in input && (input.Key.length === 0 || utf8ByteLength(input.Key) > 1024)) {
|
|
55
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
bucket(name) {
|
|
59
|
+
const bucket = this.buckets.get(name);
|
|
60
|
+
if (!bucket)
|
|
61
|
+
throw new S3ServiceError("NoSuchBucket", 404);
|
|
62
|
+
return bucket;
|
|
63
|
+
}
|
|
64
|
+
object(input) {
|
|
65
|
+
const object = this.bucket(input.Bucket).get(input.Key);
|
|
66
|
+
if (!object)
|
|
67
|
+
throw new S3ServiceError("NoSuchKey", 404);
|
|
68
|
+
return object;
|
|
69
|
+
}
|
|
70
|
+
head(object) {
|
|
71
|
+
return {
|
|
72
|
+
ContentLength: object.body.byteLength,
|
|
73
|
+
ETag: object.etag,
|
|
74
|
+
LastModified: new Date(object.modified),
|
|
75
|
+
Metadata: { ...object.metadata },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async store(body, metadata = {}) {
|
|
79
|
+
const metadataBytes = Object.entries(metadata).reduce((total, [key, value]) => total + utf8ByteLength(key) + utf8ByteLength(value), 0);
|
|
80
|
+
if (metadataBytes > 2048)
|
|
81
|
+
throw new S3ServiceError("MetadataTooLarge", 400);
|
|
82
|
+
return {
|
|
83
|
+
body: new Uint8Array(body),
|
|
84
|
+
etag: `"${Array.from(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new Uint8Array(body))), byte => byte.toString(16).padStart(2, "0")).join("")}"`,
|
|
85
|
+
modified: new Date(this.now()),
|
|
86
|
+
metadata: { ...metadata },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async headObject(input, options) {
|
|
90
|
+
await this.begin("headObject", input, options);
|
|
91
|
+
const output = this.head(this.object(input));
|
|
92
|
+
recordMockS3Head(output, input, this.bucket(input.Bucket));
|
|
93
|
+
return output;
|
|
94
|
+
}
|
|
95
|
+
async getObject(input, options) {
|
|
96
|
+
await this.begin("getObject", input, options);
|
|
97
|
+
const object = this.object(input);
|
|
98
|
+
return { ...this.head(object), Body: new Uint8Array(object.body) };
|
|
99
|
+
}
|
|
100
|
+
async putObject(input, options) {
|
|
101
|
+
if (!(input.Body instanceof Uint8Array))
|
|
102
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
103
|
+
input = { ...input, Body: new Uint8Array(input.Body), ...(input.Metadata ? { Metadata: { ...input.Metadata } } : {}) };
|
|
104
|
+
const object = await this.store(input.Body, input.Metadata);
|
|
105
|
+
await this.begin("putObject", input, options);
|
|
106
|
+
const bucket = this.bucket(input.Bucket);
|
|
107
|
+
const previous = bucket.get(input.Key);
|
|
108
|
+
if (input.IfMatch !== undefined && !previous)
|
|
109
|
+
throw new S3ServiceError("NoSuchKey", 404);
|
|
110
|
+
if ((input.IfNoneMatch === "*" && previous)
|
|
111
|
+
|| (input.IfMatch !== undefined && previous?.etag !== input.IfMatch)) {
|
|
112
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
113
|
+
}
|
|
114
|
+
bucket.set(input.Key, object);
|
|
115
|
+
return { ETag: object.etag };
|
|
116
|
+
}
|
|
117
|
+
async getObjectStream(input, options) {
|
|
118
|
+
await this.begin("getObject", input, options);
|
|
119
|
+
const object = this.object(input);
|
|
120
|
+
if (input.IfMatch !== undefined && input.IfMatch !== object.etag)
|
|
121
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
122
|
+
let start = 0;
|
|
123
|
+
let end = object.body.length;
|
|
124
|
+
if (input.Range !== undefined) {
|
|
125
|
+
const range = /^bytes=(\d+)-(\d+)$/.exec(input.Range);
|
|
126
|
+
if (!range)
|
|
127
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
128
|
+
start = Number(range[1]);
|
|
129
|
+
end = Math.min(Number(range[2]) + 1, end);
|
|
130
|
+
if (start >= end)
|
|
131
|
+
throw new S3ServiceError("InvalidRange", 416);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...this.head(object), ContentLength: end - start,
|
|
135
|
+
Body: (async function* () {
|
|
136
|
+
for (let offset = start; offset < end; offset += 64 * 1024) {
|
|
137
|
+
if (options?.abortSignal?.aborted)
|
|
138
|
+
throw new S3ServiceError("AbortError", 499);
|
|
139
|
+
yield object.body.slice(offset, Math.min(offset + 64 * 1024, end));
|
|
140
|
+
}
|
|
141
|
+
})(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async putObjectStream(input, options) {
|
|
145
|
+
const snapshot = { ...input, Body: new Uint8Array(), ...(input.Metadata ? { Metadata: { ...input.Metadata } } : {}) };
|
|
146
|
+
await this.begin("putObject", snapshot, options);
|
|
147
|
+
const body = await collectBytes(input.Body, { maxBytes: 5_000_000_000, ...(options?.abortSignal ? { signal: options.abortSignal } : {}) });
|
|
148
|
+
const object = await this.store(body, snapshot.Metadata);
|
|
149
|
+
options?.abortSignal?.throwIfAborted();
|
|
150
|
+
const bucket = this.bucket(input.Bucket);
|
|
151
|
+
const previous = bucket.get(input.Key);
|
|
152
|
+
if (input.IfMatch !== undefined && !previous)
|
|
153
|
+
throw new S3ServiceError("NoSuchKey", 404);
|
|
154
|
+
if ((input.IfNoneMatch === "*" && previous) || (input.IfMatch !== undefined && previous?.etag !== input.IfMatch)) {
|
|
155
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
156
|
+
}
|
|
157
|
+
bucket.set(input.Key, object);
|
|
158
|
+
return { ETag: object.etag };
|
|
159
|
+
}
|
|
160
|
+
async deleteObject(input, options) {
|
|
161
|
+
await this.begin("deleteObject", input, options);
|
|
162
|
+
const bucket = this.bucket(input.Bucket);
|
|
163
|
+
const previous = bucket.get(input.Key);
|
|
164
|
+
if (input.IfMatch !== undefined && !previous)
|
|
165
|
+
throw new S3ServiceError("NoSuchKey", 404);
|
|
166
|
+
if (previous && input.IfMatch !== undefined && input.IfMatch !== "*" && previous.etag !== input.IfMatch) {
|
|
167
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
168
|
+
}
|
|
169
|
+
bucket.delete(input.Key);
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
async copyObject(input, options) {
|
|
173
|
+
await this.begin("copyObject", input, options);
|
|
174
|
+
let decoded;
|
|
175
|
+
try {
|
|
176
|
+
decoded = decodeURIComponent(input.CopySource.replace(/^\//, ""));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
180
|
+
}
|
|
181
|
+
const separator = decoded.indexOf("/");
|
|
182
|
+
if (separator < 1)
|
|
183
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
184
|
+
const source = this.object({ Bucket: decoded.slice(0, separator), Key: decoded.slice(separator + 1) });
|
|
185
|
+
const copy = await this.store(source.body, input.MetadataDirective === "REPLACE" ? input.Metadata : source.metadata);
|
|
186
|
+
options?.abortSignal?.throwIfAborted();
|
|
187
|
+
if (input.CopySourceIfMatch !== undefined && input.CopySourceIfMatch !== this.object({ Bucket: decoded.slice(0, separator), Key: decoded.slice(separator + 1) }).etag) {
|
|
188
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
189
|
+
}
|
|
190
|
+
const bucket = this.bucket(input.Bucket);
|
|
191
|
+
if (input.IfNoneMatch === "*" && bucket.has(input.Key))
|
|
192
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
193
|
+
if (input.IfMatch !== undefined && bucket.get(input.Key)?.etag !== input.IfMatch)
|
|
194
|
+
throw new S3ServiceError("PreconditionFailed", 412);
|
|
195
|
+
bucket.set(input.Key, copy);
|
|
196
|
+
return { CopyObjectResult: { ETag: copy.etag, LastModified: new Date(copy.modified) } };
|
|
197
|
+
}
|
|
198
|
+
async listObjectsV2(input, options) {
|
|
199
|
+
await this.begin("listObjectsV2", input, options);
|
|
200
|
+
const maxKeys = input.MaxKeys ?? 1000;
|
|
201
|
+
if (!Number.isInteger(maxKeys) || maxKeys < 0 || maxKeys > 1000)
|
|
202
|
+
throw new S3ServiceError("InvalidArgument", 400);
|
|
203
|
+
const prefix = input.Prefix ?? "";
|
|
204
|
+
const delimiter = input.Delimiter ?? "";
|
|
205
|
+
const scope = JSON.stringify([input.Bucket, prefix, delimiter]);
|
|
206
|
+
const cursor = input.ContinuationToken === undefined ? undefined : this.cursors.get(input.ContinuationToken);
|
|
207
|
+
if (input.ContinuationToken !== undefined && (!cursor || cursor.scope !== scope)) {
|
|
208
|
+
throw new S3ServiceError("InvalidArgument", 400, "invalid continuation token");
|
|
209
|
+
}
|
|
210
|
+
const entries = new Map();
|
|
211
|
+
for (const [key, object] of this.bucket(input.Bucket)) {
|
|
212
|
+
if (!key.startsWith(prefix))
|
|
213
|
+
continue;
|
|
214
|
+
const separator = delimiter === "" ? -1 : key.indexOf(delimiter, prefix.length);
|
|
215
|
+
if (separator >= 0) {
|
|
216
|
+
const commonPrefix = key.slice(0, separator + delimiter.length);
|
|
217
|
+
entries.set(commonPrefix, commonPrefix);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
entries.set(key, { Key: key, Size: object.body.byteLength, ETag: object.etag, LastModified: new Date(object.modified) });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const ordered = [...entries.keys()].sort(compareKeys)
|
|
224
|
+
.filter((key) => cursor === undefined || compareKeys(key, cursor.after) > 0);
|
|
225
|
+
const selected = ordered.slice(0, Math.min(maxKeys, this.pageSize));
|
|
226
|
+
const contents = [];
|
|
227
|
+
const prefixes = [];
|
|
228
|
+
for (const key of selected) {
|
|
229
|
+
const entry = entries.get(key);
|
|
230
|
+
if (typeof entry === "string")
|
|
231
|
+
prefixes.push({ Prefix: entry });
|
|
232
|
+
else if (entry)
|
|
233
|
+
contents.push(entry);
|
|
234
|
+
}
|
|
235
|
+
const truncated = maxKeys > 0 && selected.length < ordered.length;
|
|
236
|
+
let token;
|
|
237
|
+
if (truncated) {
|
|
238
|
+
token = `mock-s3-cursor-${++this.nextToken}`;
|
|
239
|
+
this.cursors.set(token, { scope, after: selected[selected.length - 1] });
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
Contents: contents,
|
|
243
|
+
CommonPrefixes: prefixes,
|
|
244
|
+
KeyCount: selected.length,
|
|
245
|
+
IsTruncated: truncated,
|
|
246
|
+
...(token === undefined ? {} : { NextContinuationToken: token }),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FileSystem } from '../../contracts/filesystem.js';
|
|
2
|
+
import { type ObjectFileDescriptorOptions } from '../object-publication/index.js';
|
|
3
|
+
import type { S3Transport } from './transport.js';
|
|
4
|
+
export interface S3NamespaceOptions extends ObjectFileDescriptorOptions {
|
|
5
|
+
readonly client: S3Transport;
|
|
6
|
+
readonly bucket: string;
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly maxBytes?: number;
|
|
9
|
+
readonly maxEntries?: number;
|
|
10
|
+
readonly maxManifestBytes?: number;
|
|
11
|
+
readonly maxAttempts?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function createS3NamespaceFileSystem(options: S3NamespaceOptions): Promise<FileSystem>;
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { FsError } from '../../contracts/errors.js';
|
|
2
|
+
import { collectBytes } from '../../contracts/io.js';
|
|
3
|
+
import { dirname, normalizePath, validatePath } from '../../contracts/virtual-path.js';
|
|
4
|
+
import { withObjectFileDescriptors } from '../object-publication/index.js';
|
|
5
|
+
import { admitManifest } from './manifest-admission.js';
|
|
6
|
+
function limit(value) {
|
|
7
|
+
if (value === undefined || value === Infinity)
|
|
8
|
+
return Infinity;
|
|
9
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
10
|
+
throw new RangeError('Invalid S3 namespace limit');
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
export async function createS3NamespaceFileSystem(options) {
|
|
14
|
+
const client = options.client;
|
|
15
|
+
if (client?.capabilities?.conditionalPut !== true || client.capabilities.streamingRead !== true || !client.getObjectStream)
|
|
16
|
+
throw new FsError('ENOTSUP', { message: 'S3 namespaces require verified conditional PUT and bounded streaming reads' });
|
|
17
|
+
if (![options.bucket, options.key].every(value => typeof value === 'string' && value.length > 0 && !value.includes('\0')))
|
|
18
|
+
throw new TypeError('An explicit S3 bucket and manifest key are required');
|
|
19
|
+
const maxBytes = limit(options.maxBytes);
|
|
20
|
+
const maxEntries = limit(options.maxEntries);
|
|
21
|
+
const maxManifestBytes = limit(options.maxManifestBytes);
|
|
22
|
+
const maxAttempts = limit(options.maxAttempts);
|
|
23
|
+
const descriptorOptions = options;
|
|
24
|
+
for (const key of ['chunkBytes', 'maxStagedBytes', 'maxStagedPages', 'maxFileBytes', 'maxOpenFiles']) {
|
|
25
|
+
if (descriptorOptions[key] !== undefined)
|
|
26
|
+
limit(descriptorOptions[key]);
|
|
27
|
+
}
|
|
28
|
+
const object = Object.freeze({ Bucket: options.bucket, Key: options.key });
|
|
29
|
+
const scope = Object.freeze({});
|
|
30
|
+
let identity;
|
|
31
|
+
const path = (value) => {
|
|
32
|
+
validatePath(value);
|
|
33
|
+
if (!value)
|
|
34
|
+
throw new FsError('ENOENT');
|
|
35
|
+
return normalizePath(value);
|
|
36
|
+
};
|
|
37
|
+
const entryPath = (value) => {
|
|
38
|
+
const name = path(value);
|
|
39
|
+
const terminal = value.split('/').filter(Boolean).at(-1);
|
|
40
|
+
if (terminal === '.' || terminal === '..')
|
|
41
|
+
throw new FsError('EINVAL');
|
|
42
|
+
return name;
|
|
43
|
+
};
|
|
44
|
+
const status = (error) => error?.$metadata?.httpStatusCode;
|
|
45
|
+
const validate = (value) => {
|
|
46
|
+
if (!value || value.version !== 1 || typeof value.identity !== 'string' || value.identity.length < 1 || value.identity.length > 128
|
|
47
|
+
|| Object.keys(value).length !== 4 || !Object.keys(value).every(key => ['version', 'identity', 'nextInode', 'nodes'].includes(key))
|
|
48
|
+
|| identity !== undefined && value.identity !== identity || !Number.isSafeInteger(value.nextInode) || value.nextInode < 2
|
|
49
|
+
|| !value.nodes || typeof value.nodes !== 'object' || Array.isArray(value.nodes))
|
|
50
|
+
throw new FsError('EIO', { message: 'Invalid or replaced S3 namespace' });
|
|
51
|
+
const entries = Object.entries(value.nodes);
|
|
52
|
+
if (entries.length > maxEntries)
|
|
53
|
+
throw new FsError('ENOSPC');
|
|
54
|
+
const inodes = new Set();
|
|
55
|
+
let size = 0;
|
|
56
|
+
for (const [name, node] of entries) {
|
|
57
|
+
if (path(name) !== name || !node || !['file', 'directory'].includes(node.type) || !Array.isArray(node.bytes)
|
|
58
|
+
|| Object.keys(node).length !== 6 || !Object.keys(node).every(key => ['ino', 'revision', 'type', 'mode', 'time', 'bytes'].includes(key))
|
|
59
|
+
|| !Number.isSafeInteger(node.ino) || node.ino < 1 || node.ino >= value.nextInode || inodes.has(node.ino)
|
|
60
|
+
|| !Number.isSafeInteger(node.revision) || node.revision < 1 || !Number.isSafeInteger(node.mode) || node.mode < 0 || node.mode > 0o7777
|
|
61
|
+
|| !Number.isFinite(node.time) || node.type === 'directory' && node.bytes.length !== 0)
|
|
62
|
+
throw new FsError('EIO', { message: 'Invalid S3 namespace entry' });
|
|
63
|
+
inodes.add(node.ino);
|
|
64
|
+
size += node.bytes.length;
|
|
65
|
+
if (size > maxBytes)
|
|
66
|
+
throw new FsError('ENOSPC');
|
|
67
|
+
if (node.bytes.some(byte => !Number.isInteger(byte) || byte < 0 || byte > 255))
|
|
68
|
+
throw new FsError('EIO');
|
|
69
|
+
if (name !== '/' && value.nodes[dirname(name)]?.type !== 'directory')
|
|
70
|
+
throw new FsError('EIO', { message: 'Disconnected S3 namespace entry' });
|
|
71
|
+
}
|
|
72
|
+
if (value.nodes['/']?.type !== 'directory' || value.nodes['/'].ino !== 1)
|
|
73
|
+
throw new FsError('EIO');
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
76
|
+
const read = async (forwarded = {}) => {
|
|
77
|
+
forwarded.signal?.throwIfAborted();
|
|
78
|
+
try {
|
|
79
|
+
const response = await client.getObjectStream(object, forwarded.signal ? { abortSignal: forwarded.signal } : {});
|
|
80
|
+
const body = await collectBytes(admitManifest(response.Body, { maxManifestBytes, maxEntries, maxBytes, ...(forwarded.signal ? { signal: forwarded.signal } : {}) }), { ...forwarded, maxBytes: maxManifestBytes });
|
|
81
|
+
if (typeof response.ETag !== 'string' || !response.ETag || response.ETag.startsWith('W/'))
|
|
82
|
+
throw new FsError('EIO', { message: 'S3 namespace requires a strong object validator' });
|
|
83
|
+
let decoded;
|
|
84
|
+
try {
|
|
85
|
+
decoded = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(body));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new FsError('EIO', { message: 'Invalid S3 namespace encoding' });
|
|
89
|
+
}
|
|
90
|
+
return { value: validate(decoded), etag: response.ETag };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
forwarded.signal?.throwIfAborted();
|
|
94
|
+
if (error instanceof FsError || status(error) === 404 && identity === undefined)
|
|
95
|
+
throw error;
|
|
96
|
+
throw new FsError('EIO', { message: 'S3 namespace read failed' });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const commit = async (value, etag, forwarded) => {
|
|
100
|
+
validate(value);
|
|
101
|
+
let encodedBound = 256 + value.identity.length * 6;
|
|
102
|
+
for (const [name, node] of Object.entries(value.nodes))
|
|
103
|
+
encodedBound += 256 + name.length * 6 + node.bytes.length * 4;
|
|
104
|
+
if (encodedBound > maxManifestBytes)
|
|
105
|
+
throw new FsError('EFBIG');
|
|
106
|
+
const body = new TextEncoder().encode(JSON.stringify(value));
|
|
107
|
+
if (body.length > maxManifestBytes)
|
|
108
|
+
throw new FsError('EFBIG');
|
|
109
|
+
forwarded.signal?.throwIfAborted();
|
|
110
|
+
try {
|
|
111
|
+
await client.putObject({ ...object, Body: body, ...(etag === undefined ? { IfNoneMatch: '*' } : { IfMatch: etag }) }, forwarded.signal ? { abortSignal: forwarded.signal } : {});
|
|
112
|
+
forwarded.signal?.throwIfAborted();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
forwarded.signal?.throwIfAborted();
|
|
117
|
+
if (status(error) === 412 || status(error) === 409)
|
|
118
|
+
return false;
|
|
119
|
+
throw new FsError('EIO', { message: 'S3 namespace commit could not be confirmed' });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
for (let attempt = 0; identity === undefined && attempt < maxAttempts; attempt++) {
|
|
123
|
+
try {
|
|
124
|
+
identity = (await read()).value.identity;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (status(error) !== 404)
|
|
128
|
+
throw error;
|
|
129
|
+
const initial = { version: 1, identity: crypto.randomUUID(), nextInode: 2, nodes: {
|
|
130
|
+
'/': { ino: 1, revision: 1, type: 'directory', mode: 0o755, time: Date.now(), bytes: [] },
|
|
131
|
+
} };
|
|
132
|
+
if (await commit(initial, undefined, {}))
|
|
133
|
+
identity = initial.identity;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (identity === undefined)
|
|
137
|
+
throw new FsError('EAGAIN');
|
|
138
|
+
const mutate = async (forwarded, action) => {
|
|
139
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
140
|
+
const current = await read(forwarded);
|
|
141
|
+
const result = action(current.value);
|
|
142
|
+
if (await commit(current.value, current.etag, forwarded))
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
throw new FsError('EAGAIN', { message: 'S3 namespace conditional retry limit exceeded' });
|
|
146
|
+
};
|
|
147
|
+
const entry = (value, name) => {
|
|
148
|
+
const node = value.nodes[name];
|
|
149
|
+
if (!node)
|
|
150
|
+
throw new FsError('ENOENT', { path: name });
|
|
151
|
+
return node;
|
|
152
|
+
};
|
|
153
|
+
const directory = (value, name) => {
|
|
154
|
+
const node = entry(value, name);
|
|
155
|
+
if (node.type !== 'directory')
|
|
156
|
+
throw new FsError('ENOTDIR', { path: name });
|
|
157
|
+
return node;
|
|
158
|
+
};
|
|
159
|
+
const stat = (node) => Object.freeze({ type: node.type, size: node.bytes.length,
|
|
160
|
+
mode: (node.type === 'file' ? 0o100000 : 0o040000) | node.mode, mtimeMs: node.time, atimeMs: node.time, ctimeMs: node.time,
|
|
161
|
+
identityScope: scope, ino: node.ino, revision: node.revision });
|
|
162
|
+
const create = (value, name, type, mode) => {
|
|
163
|
+
directory(value, dirname(name));
|
|
164
|
+
if (value.nodes[name])
|
|
165
|
+
throw new FsError('EEXIST', { path: name });
|
|
166
|
+
if (Object.keys(value.nodes).length >= maxEntries || !Number.isSafeInteger(value.nextInode + 1))
|
|
167
|
+
throw new FsError('ENOSPC');
|
|
168
|
+
const node = { ino: value.nextInode++, revision: 1, type, mode, time: Date.now(), bytes: [] };
|
|
169
|
+
value.nodes[name] = node;
|
|
170
|
+
return node;
|
|
171
|
+
};
|
|
172
|
+
const resolveName = (value, input, allowMissing = false, newDirectory = false, parentMode) => {
|
|
173
|
+
path(input);
|
|
174
|
+
const components = input.split('/').filter(Boolean);
|
|
175
|
+
let current = '/';
|
|
176
|
+
for (let index = 0; index < components.length; index++) {
|
|
177
|
+
directory(value, current);
|
|
178
|
+
const component = components[index];
|
|
179
|
+
if (component === '.')
|
|
180
|
+
continue;
|
|
181
|
+
if (component === '..') {
|
|
182
|
+
current = dirname(current);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
current = (current === '/' ? '' : current) + '/' + component;
|
|
186
|
+
if (!value.nodes[current]) {
|
|
187
|
+
if (index + 1 < components.length && parentMode !== undefined)
|
|
188
|
+
create(value, current, 'directory', parentMode);
|
|
189
|
+
else if (index + 1 < components.length || !allowMissing)
|
|
190
|
+
throw new FsError('ENOENT', { path: current });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (input.endsWith('/')) {
|
|
194
|
+
if (value.nodes[current])
|
|
195
|
+
directory(value, current);
|
|
196
|
+
else if (!newDirectory)
|
|
197
|
+
throw new FsError('ENOTDIR');
|
|
198
|
+
}
|
|
199
|
+
return current;
|
|
200
|
+
};
|
|
201
|
+
const write = (value, name, data, exclusive = false, mode = 0o644, append = false) => {
|
|
202
|
+
let node = value.nodes[name];
|
|
203
|
+
if (node && exclusive)
|
|
204
|
+
throw new FsError('EEXIST', { path: name });
|
|
205
|
+
node ??= create(value, name, 'file', mode);
|
|
206
|
+
if (node.type !== 'file')
|
|
207
|
+
throw new FsError('EISDIR', { path: name });
|
|
208
|
+
const otherBytes = Object.values(value.nodes).reduce((size, current) => size + (current === node ? 0 : current.bytes.length), 0);
|
|
209
|
+
if (data.length + (append ? node.bytes.length : 0) > maxBytes - otherBytes)
|
|
210
|
+
throw new FsError('ENOSPC');
|
|
211
|
+
node.bytes = append ? [...node.bytes, ...data] : Array.from(data);
|
|
212
|
+
node.revision++;
|
|
213
|
+
node.time = Date.now();
|
|
214
|
+
return node;
|
|
215
|
+
};
|
|
216
|
+
const remove = (value, name, recursive, required) => {
|
|
217
|
+
if (name === '/')
|
|
218
|
+
throw new FsError('EBUSY');
|
|
219
|
+
const node = entry(value, name);
|
|
220
|
+
if (required && node.type !== required)
|
|
221
|
+
throw new FsError(required === 'file' ? 'EISDIR' : 'ENOTDIR');
|
|
222
|
+
const children = Object.keys(value.nodes).filter(candidate => candidate.startsWith(name + '/'));
|
|
223
|
+
if (children.length && !recursive)
|
|
224
|
+
throw new FsError('ENOTEMPTY');
|
|
225
|
+
for (const child of children)
|
|
226
|
+
delete value.nodes[child];
|
|
227
|
+
delete value.nodes[name];
|
|
228
|
+
};
|
|
229
|
+
const conditionalRemove = (input, forwarded, recursive) => mutate(forwarded, value => {
|
|
230
|
+
const name = resolveName(value, input);
|
|
231
|
+
const node = entry(value, name);
|
|
232
|
+
const parent = directory(value, dirname(name));
|
|
233
|
+
if (forwarded.expected?.identityScope !== scope || forwarded.parent.identityScope !== scope
|
|
234
|
+
|| ![forwarded.expected.ino, forwarded.parent.ino].every(Number.isSafeInteger)
|
|
235
|
+
|| !recursive && !Number.isSafeInteger(forwarded.expected.revision))
|
|
236
|
+
throw new FsError('ENOTSUP');
|
|
237
|
+
if (forwarded.expected.ino !== node.ino || forwarded.expected.type !== node.type
|
|
238
|
+
|| forwarded.parent.ino !== parent.ino || forwarded.parent.type !== 'directory'
|
|
239
|
+
|| !recursive && (forwarded.expected.revision !== node.revision || forwarded.expected.size !== node.bytes.length
|
|
240
|
+
|| forwarded.expected.mode !== stat(node).mode || forwarded.expected.mtimeMs !== node.time || forwarded.expected.ctimeMs !== node.time))
|
|
241
|
+
throw new FsError('EAGAIN');
|
|
242
|
+
remove(value, name, recursive, recursive ? 'directory' : undefined);
|
|
243
|
+
});
|
|
244
|
+
const observe = async (input, forwarded = {}) => {
|
|
245
|
+
const { value } = await read(forwarded);
|
|
246
|
+
return stat(entry(value, resolveName(value, input)));
|
|
247
|
+
};
|
|
248
|
+
const filesystem = {
|
|
249
|
+
capabilities: Object.freeze({ read: true, write: true, append: true, stat: true, readdir: true, realpath: true, access: true,
|
|
250
|
+
mkdir: true, recursiveMkdir: true, explicitDirectories: true, implicitDirectories: false, exclusiveCreate: true,
|
|
251
|
+
remove: true, removeDirectory: true, recursiveRemove: true, atomicTreeRemoval: true, atomicEntryRemoval: true, atomicEntryRemovalReceipt: false,
|
|
252
|
+
rename: true, atomicRename: true, atomicRenameNoReplace: true, copy: true, exclusiveCopy: true,
|
|
253
|
+
readOnly: false, symlinks: false, hardlinks: false, permissions: false, timestamps: false }),
|
|
254
|
+
stat: observe,
|
|
255
|
+
lstat: observe,
|
|
256
|
+
async readFile(input, forwarded = {}) {
|
|
257
|
+
const { value } = await read(forwarded);
|
|
258
|
+
const node = entry(value, resolveName(value, input));
|
|
259
|
+
if (node.type !== 'file')
|
|
260
|
+
throw new FsError('EISDIR');
|
|
261
|
+
if (forwarded.maxBytes !== undefined && node.bytes.length > forwarded.maxBytes)
|
|
262
|
+
throw new FsError('EFBIG');
|
|
263
|
+
return Uint8Array.from(node.bytes);
|
|
264
|
+
},
|
|
265
|
+
async writeFile(input, data, forwarded = {}) {
|
|
266
|
+
if (!(data instanceof Uint8Array))
|
|
267
|
+
throw new FsError('EINVAL');
|
|
268
|
+
if (data.length > maxBytes)
|
|
269
|
+
throw new FsError('ENOSPC');
|
|
270
|
+
const flag = forwarded.flag ?? 'w';
|
|
271
|
+
if (!['w', 'wx', 'a', 'ax'].includes(flag))
|
|
272
|
+
throw new FsError('EINVAL');
|
|
273
|
+
const owned = Uint8Array.from(data);
|
|
274
|
+
await mutate(forwarded, value => write(value, resolveName(value, input, true), owned, flag === 'wx' || flag === 'ax', forwarded.mode, flag === 'a' || flag === 'ax'));
|
|
275
|
+
},
|
|
276
|
+
async appendFile(input, data, forwarded = {}) {
|
|
277
|
+
if (!(data instanceof Uint8Array))
|
|
278
|
+
throw new FsError('EINVAL');
|
|
279
|
+
if (data.length > maxBytes)
|
|
280
|
+
throw new FsError('ENOSPC');
|
|
281
|
+
const owned = Uint8Array.from(data);
|
|
282
|
+
await mutate(forwarded, value => write(value, resolveName(value, input, true), owned, false, forwarded.mode, true));
|
|
283
|
+
},
|
|
284
|
+
async readdir(input, forwarded = {}) {
|
|
285
|
+
const { value } = await read(forwarded);
|
|
286
|
+
const name = resolveName(value, input);
|
|
287
|
+
directory(value, name);
|
|
288
|
+
const entries = Object.entries(value.nodes).filter(([candidate]) => candidate !== '/' && dirname(candidate) === name);
|
|
289
|
+
if (forwarded.maxEntries !== undefined && entries.length > forwarded.maxEntries)
|
|
290
|
+
throw new FsError('EFBIG');
|
|
291
|
+
return entries.map(([candidate, node]) => ({ name: candidate.slice(candidate.lastIndexOf('/') + 1), type: node.type }));
|
|
292
|
+
},
|
|
293
|
+
async mkdir(input, forwarded = {}) {
|
|
294
|
+
await mutate(forwarded, value => {
|
|
295
|
+
const name = resolveName(value, input, true, true, forwarded.recursive ? forwarded.mode ?? 0o755 : undefined);
|
|
296
|
+
if (forwarded.recursive && value.nodes[name])
|
|
297
|
+
directory(value, name);
|
|
298
|
+
else
|
|
299
|
+
create(value, name, 'directory', forwarded.mode ?? 0o755);
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
async rm(input, forwarded = {}) {
|
|
303
|
+
entryPath(input);
|
|
304
|
+
await mutate(forwarded, value => {
|
|
305
|
+
const name = resolveName(value, input, forwarded.force === true, true);
|
|
306
|
+
if (!value.nodes[name] && forwarded.force)
|
|
307
|
+
return;
|
|
308
|
+
remove(value, name, forwarded.recursive === true);
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
async unlink(input, forwarded = {}) { entryPath(input); await mutate(forwarded, value => remove(value, resolveName(value, input), false, 'file')); },
|
|
312
|
+
async rmdir(input, forwarded = {}) { entryPath(input); await mutate(forwarded, value => remove(value, resolveName(value, input), false, 'directory')); },
|
|
313
|
+
async removeTreeConditional(input, forwarded) { entryPath(input); return conditionalRemove(input, forwarded, true); },
|
|
314
|
+
async removeEntryConditional(input, forwarded) {
|
|
315
|
+
forwarded.signal?.throwIfAborted();
|
|
316
|
+
if (forwarded.returnRemainingStat === true)
|
|
317
|
+
throw new FsError('ENOTSUP');
|
|
318
|
+
entryPath(input);
|
|
319
|
+
return conditionalRemove(input, forwarded, false);
|
|
320
|
+
},
|
|
321
|
+
async rename(source, destination, forwarded = {}) {
|
|
322
|
+
entryPath(source);
|
|
323
|
+
entryPath(destination);
|
|
324
|
+
await mutate(forwarded, value => {
|
|
325
|
+
const from = resolveName(value, source);
|
|
326
|
+
const node = entry(value, from);
|
|
327
|
+
const to = resolveName(value, destination, true, node.type === 'directory');
|
|
328
|
+
if (from === to)
|
|
329
|
+
return;
|
|
330
|
+
if (from === '/' || to === '/')
|
|
331
|
+
throw new FsError('EBUSY');
|
|
332
|
+
if (to.startsWith(from + '/'))
|
|
333
|
+
throw new FsError('EINVAL');
|
|
334
|
+
directory(value, dirname(to));
|
|
335
|
+
if (value.nodes[to]) {
|
|
336
|
+
if (forwarded.noReplace)
|
|
337
|
+
throw new FsError('EEXIST');
|
|
338
|
+
remove(value, to, false, node.type);
|
|
339
|
+
}
|
|
340
|
+
for (const name of Object.keys(value.nodes).filter(name => name === from || name.startsWith(from + '/'))) {
|
|
341
|
+
value.nodes[to + name.slice(from.length)] = value.nodes[name];
|
|
342
|
+
delete value.nodes[name];
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
async copyFile(source, destination, forwarded = {}) {
|
|
347
|
+
await mutate(forwarded, value => {
|
|
348
|
+
const from = resolveName(value, source), to = resolveName(value, destination, true);
|
|
349
|
+
const node = entry(value, from);
|
|
350
|
+
if (node.type !== 'file')
|
|
351
|
+
throw new FsError('EISDIR');
|
|
352
|
+
if (from === to)
|
|
353
|
+
throw new FsError('EINVAL');
|
|
354
|
+
write(value, to, Uint8Array.from(node.bytes), forwarded.exclusive, node.mode);
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
async realpath(input, forwarded = {}) { return resolveName((await read(forwarded)).value, input); },
|
|
358
|
+
async access(input, mode = 0, forwarded = {}) {
|
|
359
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 7)
|
|
360
|
+
throw new FsError('EINVAL');
|
|
361
|
+
resolveName((await read(forwarded)).value, input);
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
const version = (node) => {
|
|
365
|
+
const bytes = Uint8Array.from(node.bytes);
|
|
366
|
+
let closed = false;
|
|
367
|
+
return { revision: node.ino + ':' + node.revision, stat: stat(node),
|
|
368
|
+
async read(position, maxLength, forwarded = {}) {
|
|
369
|
+
forwarded.signal?.throwIfAborted();
|
|
370
|
+
if (closed)
|
|
371
|
+
throw new FsError('EBADF');
|
|
372
|
+
return bytes.slice(position, position + maxLength);
|
|
373
|
+
},
|
|
374
|
+
async close() { closed = true; },
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
return withObjectFileDescriptors(filesystem, {
|
|
378
|
+
async acquire(input, forwarded) {
|
|
379
|
+
const { value } = await read(forwarded);
|
|
380
|
+
const node = value.nodes[resolveName(value, input, true)];
|
|
381
|
+
if (!node)
|
|
382
|
+
return undefined;
|
|
383
|
+
if (node.type !== 'file')
|
|
384
|
+
throw new FsError('EISDIR');
|
|
385
|
+
return version(node);
|
|
386
|
+
},
|
|
387
|
+
async publish(input, expectedRevision, source, forwarded) {
|
|
388
|
+
const bytes = await collectBytes(source, { ...forwarded, ...(maxBytes === Infinity ? {} : { maxBytes: maxBytes }) });
|
|
389
|
+
if (bytes.length !== forwarded.size)
|
|
390
|
+
throw new FsError('EIO');
|
|
391
|
+
const published = await mutate(forwarded, value => {
|
|
392
|
+
const name = resolveName(value, input, true), node = value.nodes[name];
|
|
393
|
+
if (expectedRevision === null ? node !== undefined : !node || node.ino + ':' + node.revision !== expectedRevision)
|
|
394
|
+
throw new FsError('EAGAIN');
|
|
395
|
+
return write(value, name, bytes, false, forwarded.mode);
|
|
396
|
+
});
|
|
397
|
+
return version(published);
|
|
398
|
+
},
|
|
399
|
+
}, descriptorOptions);
|
|
400
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FileStat, FileSystem, FsOptions, EntryComparison } from "../../contracts/filesystem.js";
|
|
2
|
+
import type { EntryView } from "../mount/comparison.js";
|
|
3
|
+
export interface OwnedS3Entry {
|
|
4
|
+
readonly storage: object;
|
|
5
|
+
readonly key: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function recordS3Observation(filesystem: FileSystem, path: string, stat: FileStat, entry: OwnedS3Entry): void;
|
|
8
|
+
export declare function registerS3EntryOwner(filesystem: FileSystem, normalize: (path: string) => string, intact: () => boolean, baseComparison: NonNullable<FileSystem["compareEntry"]>, comparison?: NonNullable<FileSystem["compareEntry"]>): void;
|
|
9
|
+
export declare function getOwnedS3Entry(view: EntryView): OwnedS3Entry | undefined;
|
|
10
|
+
export declare function compareOwnedS3Entries(own: EntryView, peer: EntryView, options: FsOptions): Promise<EntryComparison>;
|