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.
Files changed (374) hide show
  1. package/composition.json +23 -3
  2. package/dist/composition.json +23 -3
  3. package/dist/human-in-loop/approvals-commands.d.ts +1 -1
  4. package/dist/index.d.ts +1 -262
  5. package/dist/index.js +1 -573
  6. package/dist/runtime-platform.d.ts +2 -0
  7. package/dist/runtime-platform.js +2 -0
  8. package/dist/runtime-platform.workerd.d.ts +3 -0
  9. package/dist/runtime-platform.workerd.js +10 -0
  10. package/dist/runtime.d.ts +262 -0
  11. package/dist/runtime.js +576 -0
  12. package/node_modules/@noble/hashes/LICENSE +21 -0
  13. package/node_modules/@noble/hashes/README.md +478 -0
  14. package/node_modules/@noble/hashes/_blake.d.ts +20 -0
  15. package/node_modules/@noble/hashes/_blake.js +59 -0
  16. package/node_modules/@noble/hashes/_md.d.ts +95 -0
  17. package/node_modules/@noble/hashes/_md.js +209 -0
  18. package/node_modules/@noble/hashes/_u64.d.ts +36 -0
  19. package/node_modules/@noble/hashes/_u64.js +77 -0
  20. package/node_modules/@noble/hashes/argon2.d.ts +148 -0
  21. package/node_modules/@noble/hashes/argon2.js +577 -0
  22. package/node_modules/@noble/hashes/blake1.d.ts +162 -0
  23. package/node_modules/@noble/hashes/blake1.js +630 -0
  24. package/node_modules/@noble/hashes/blake2.d.ts +203 -0
  25. package/node_modules/@noble/hashes/blake2.js +573 -0
  26. package/node_modules/@noble/hashes/blake3.d.ts +68 -0
  27. package/node_modules/@noble/hashes/blake3.js +310 -0
  28. package/node_modules/@noble/hashes/eskdf.d.ts +91 -0
  29. package/node_modules/@noble/hashes/eskdf.js +225 -0
  30. package/node_modules/@noble/hashes/hkdf.d.ts +70 -0
  31. package/node_modules/@noble/hashes/hkdf.js +152 -0
  32. package/node_modules/@noble/hashes/hmac.d.ts +50 -0
  33. package/node_modules/@noble/hashes/hmac.js +93 -0
  34. package/node_modules/@noble/hashes/index.d.ts +1 -0
  35. package/node_modules/@noble/hashes/index.js +34 -0
  36. package/node_modules/@noble/hashes/legacy.d.ts +107 -0
  37. package/node_modules/@noble/hashes/legacy.js +348 -0
  38. package/node_modules/@noble/hashes/package.json +91 -0
  39. package/node_modules/@noble/hashes/pbkdf2.d.ts +66 -0
  40. package/node_modules/@noble/hashes/pbkdf2.js +176 -0
  41. package/node_modules/@noble/hashes/scrypt.d.ts +98 -0
  42. package/node_modules/@noble/hashes/scrypt.js +304 -0
  43. package/node_modules/@noble/hashes/sha2.d.ts +176 -0
  44. package/node_modules/@noble/hashes/sha2.js +446 -0
  45. package/node_modules/@noble/hashes/sha3-addons.d.ts +533 -0
  46. package/node_modules/@noble/hashes/sha3-addons.js +973 -0
  47. package/node_modules/@noble/hashes/sha3.d.ts +208 -0
  48. package/node_modules/@noble/hashes/sha3.js +475 -0
  49. package/node_modules/@noble/hashes/src/_blake.ts +67 -0
  50. package/node_modules/@noble/hashes/src/_md.ts +227 -0
  51. package/node_modules/@noble/hashes/src/_u64.ts +111 -0
  52. package/node_modules/@noble/hashes/src/argon2.ts +693 -0
  53. package/node_modules/@noble/hashes/src/blake1.ts +677 -0
  54. package/node_modules/@noble/hashes/src/blake2.ts +648 -0
  55. package/node_modules/@noble/hashes/src/blake3.ts +344 -0
  56. package/node_modules/@noble/hashes/src/eskdf.ts +265 -0
  57. package/node_modules/@noble/hashes/src/hkdf.ts +161 -0
  58. package/node_modules/@noble/hashes/src/hmac.ts +130 -0
  59. package/node_modules/@noble/hashes/src/index.ts +33 -0
  60. package/node_modules/@noble/hashes/src/legacy.ts +361 -0
  61. package/node_modules/@noble/hashes/src/pbkdf2.ts +219 -0
  62. package/node_modules/@noble/hashes/src/scrypt.ts +375 -0
  63. package/node_modules/@noble/hashes/src/sha2.ts +486 -0
  64. package/node_modules/@noble/hashes/src/sha3-addons.ts +1157 -0
  65. package/node_modules/@noble/hashes/src/sha3.ts +525 -0
  66. package/node_modules/@noble/hashes/src/utils.ts +978 -0
  67. package/node_modules/@noble/hashes/src/webcrypto.ts +283 -0
  68. package/node_modules/@noble/hashes/utils.d.ts +560 -0
  69. package/node_modules/@noble/hashes/utils.js +687 -0
  70. package/node_modules/@noble/hashes/webcrypto.d.ts +121 -0
  71. package/node_modules/@noble/hashes/webcrypto.js +228 -0
  72. package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +23 -9
  73. package/node_modules/@poe-code/config-mutations/README.md +2 -0
  74. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +4 -5
  75. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.d.ts +3 -4
  76. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +3 -3
  77. package/node_modules/@poe-code/config-mutations/dist/template/render.js +1 -1
  78. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.d.ts +2 -2
  79. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +3 -6
  80. package/node_modules/@poe-code/config-mutations/dist/types.d.ts +4 -2
  81. package/node_modules/@poe-code/process-runner/README.md +2 -1
  82. package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.d.ts +1 -0
  83. package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.js +1 -0
  84. package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.workerd.d.ts +2 -0
  85. package/node_modules/@poe-code/process-runner/dist/transfer-filesystem.workerd.js +1 -0
  86. package/node_modules/@poe-code/process-runner/dist/types.d.ts +1 -1
  87. package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +6 -5
  88. package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +67 -29
  89. package/node_modules/@poe-code/process-runner/package.json +13 -0
  90. package/node_modules/@poe-code/safe-fs/LICENSE +21 -0
  91. package/node_modules/@poe-code/safe-fs/PROVENANCE.json +428 -0
  92. package/node_modules/@poe-code/safe-fs/README.md +411 -0
  93. package/node_modules/@poe-code/safe-fs/dist/bridge/confinement.d.ts +3 -0
  94. package/node_modules/@poe-code/safe-fs/dist/bridge/confinement.js +171 -0
  95. package/node_modules/@poe-code/safe-fs/dist/bridge/filesystem.d.ts +93 -0
  96. package/node_modules/@poe-code/safe-fs/dist/bridge/filesystem.js +453 -0
  97. package/node_modules/@poe-code/safe-fs/dist/bridge/index.d.ts +7 -0
  98. package/node_modules/@poe-code/safe-fs/dist/bridge/index.js +19 -0
  99. package/node_modules/@poe-code/safe-fs/dist/bridge/stats.d.ts +4 -0
  100. package/node_modules/@poe-code/safe-fs/dist/bridge/stats.js +37 -0
  101. package/node_modules/@poe-code/safe-fs/dist/bridge/types.d.ts +81 -0
  102. package/node_modules/@poe-code/safe-fs/dist/bridge/types.js +1 -0
  103. package/node_modules/@poe-code/safe-fs/dist/bridge/values.d.ts +6 -0
  104. package/node_modules/@poe-code/safe-fs/dist/bridge/values.js +65 -0
  105. package/node_modules/@poe-code/safe-fs/dist/config/memory.d.ts +3 -0
  106. package/node_modules/@poe-code/safe-fs/dist/config/memory.js +7 -0
  107. package/node_modules/@poe-code/safe-fs/dist/config/real.d.ts +5 -0
  108. package/node_modules/@poe-code/safe-fs/dist/config/real.js +16 -0
  109. package/node_modules/@poe-code/safe-fs/dist/config/registry.d.ts +2 -0
  110. package/node_modules/@poe-code/safe-fs/dist/config/registry.js +15 -0
  111. package/node_modules/@poe-code/safe-fs/dist/config.d.ts +15 -0
  112. package/node_modules/@poe-code/safe-fs/dist/config.js +46 -0
  113. package/node_modules/@poe-code/safe-fs/dist/config.node.d.ts +2 -0
  114. package/node_modules/@poe-code/safe-fs/dist/config.node.js +11 -0
  115. package/node_modules/@poe-code/safe-fs/dist/config.portable.d.ts +3 -0
  116. package/node_modules/@poe-code/safe-fs/dist/config.portable.js +9 -0
  117. package/node_modules/@poe-code/safe-fs/dist/contracts/abort.d.ts +5 -0
  118. package/node_modules/@poe-code/safe-fs/dist/contracts/abort.js +34 -0
  119. package/node_modules/@poe-code/safe-fs/dist/contracts/cleanup.d.ts +1 -0
  120. package/node_modules/@poe-code/safe-fs/dist/contracts/cleanup.js +9 -0
  121. package/node_modules/@poe-code/safe-fs/dist/contracts/descriptor.d.ts +38 -0
  122. package/node_modules/@poe-code/safe-fs/dist/contracts/descriptor.js +1 -0
  123. package/node_modules/@poe-code/safe-fs/dist/contracts/errors.d.ts +53 -0
  124. package/node_modules/@poe-code/safe-fs/dist/contracts/errors.js +99 -0
  125. package/node_modules/@poe-code/safe-fs/dist/contracts/filesystem.d.ts +379 -0
  126. package/node_modules/@poe-code/safe-fs/dist/contracts/filesystem.js +1 -0
  127. package/node_modules/@poe-code/safe-fs/dist/contracts/index.d.ts +6 -0
  128. package/node_modules/@poe-code/safe-fs/dist/contracts/index.js +6 -0
  129. package/node_modules/@poe-code/safe-fs/dist/contracts/io.d.ts +12 -0
  130. package/node_modules/@poe-code/safe-fs/dist/contracts/io.js +321 -0
  131. package/node_modules/@poe-code/safe-fs/dist/contracts/object.d.ts +134 -0
  132. package/node_modules/@poe-code/safe-fs/dist/contracts/object.js +97 -0
  133. package/node_modules/@poe-code/safe-fs/dist/contracts/path.d.ts +3 -0
  134. package/node_modules/@poe-code/safe-fs/dist/contracts/path.js +2 -0
  135. package/node_modules/@poe-code/safe-fs/dist/contracts/portable-path.d.ts +21 -0
  136. package/node_modules/@poe-code/safe-fs/dist/contracts/portable-path.js +78 -0
  137. package/node_modules/@poe-code/safe-fs/dist/contracts/virtual-path.d.ts +15 -0
  138. package/node_modules/@poe-code/safe-fs/dist/contracts/virtual-path.js +162 -0
  139. package/node_modules/@poe-code/safe-fs/dist/core.d.ts +19 -0
  140. package/node_modules/@poe-code/safe-fs/dist/core.js +17 -0
  141. package/node_modules/@poe-code/safe-fs/dist/fs/capabilities.d.ts +10 -0
  142. package/node_modules/@poe-code/safe-fs/dist/fs/capabilities.js +216 -0
  143. package/node_modules/@poe-code/safe-fs/dist/fs/conditional-chmod.d.ts +7 -0
  144. package/node_modules/@poe-code/safe-fs/dist/fs/conditional-chmod.js +19 -0
  145. package/node_modules/@poe-code/safe-fs/dist/fs/descriptor.d.ts +21 -0
  146. package/node_modules/@poe-code/safe-fs/dist/fs/descriptor.js +279 -0
  147. package/node_modules/@poe-code/safe-fs/dist/fs/devices/index.d.ts +59 -0
  148. package/node_modules/@poe-code/safe-fs/dist/fs/devices/index.js +875 -0
  149. package/node_modules/@poe-code/safe-fs/dist/fs/devices/path.d.ts +7 -0
  150. package/node_modules/@poe-code/safe-fs/dist/fs/devices/path.js +250 -0
  151. package/node_modules/@poe-code/safe-fs/dist/fs/devices/stream.d.ts +6 -0
  152. package/node_modules/@poe-code/safe-fs/dist/fs/devices/stream.js +136 -0
  153. package/node_modules/@poe-code/safe-fs/dist/fs/directory-admission.d.ts +3 -0
  154. package/node_modules/@poe-code/safe-fs/dist/fs/directory-admission.js +14 -0
  155. package/node_modules/@poe-code/safe-fs/dist/fs/memory/atomic-view.d.ts +11 -0
  156. package/node_modules/@poe-code/safe-fs/dist/fs/memory/atomic-view.js +13 -0
  157. package/node_modules/@poe-code/safe-fs/dist/fs/memory/index.d.ts +186 -0
  158. package/node_modules/@poe-code/safe-fs/dist/fs/memory/index.js +3771 -0
  159. package/node_modules/@poe-code/safe-fs/dist/fs/memory/ledger.d.ts +26 -0
  160. package/node_modules/@poe-code/safe-fs/dist/fs/memory/ledger.js +96 -0
  161. package/node_modules/@poe-code/safe-fs/dist/fs/memory/limits.d.ts +13 -0
  162. package/node_modules/@poe-code/safe-fs/dist/fs/memory/limits.js +26 -0
  163. package/node_modules/@poe-code/safe-fs/dist/fs/memory/missing-target.d.ts +15 -0
  164. package/node_modules/@poe-code/safe-fs/dist/fs/memory/missing-target.js +144 -0
  165. package/node_modules/@poe-code/safe-fs/dist/fs/mount/comparison.d.ts +19 -0
  166. package/node_modules/@poe-code/safe-fs/dist/fs/mount/comparison.js +118 -0
  167. package/node_modules/@poe-code/safe-fs/dist/fs/mount/identity.d.ts +6 -0
  168. package/node_modules/@poe-code/safe-fs/dist/fs/mount/identity.js +29 -0
  169. package/node_modules/@poe-code/safe-fs/dist/fs/mount/index.d.ts +74 -0
  170. package/node_modules/@poe-code/safe-fs/dist/fs/mount/index.js +1291 -0
  171. package/node_modules/@poe-code/safe-fs/dist/fs/object-authority.d.ts +27 -0
  172. package/node_modules/@poe-code/safe-fs/dist/fs/object-authority.js +287 -0
  173. package/node_modules/@poe-code/safe-fs/dist/fs/object-publication/index.d.ts +45 -0
  174. package/node_modules/@poe-code/safe-fs/dist/fs/object-publication/index.js +531 -0
  175. package/node_modules/@poe-code/safe-fs/dist/fs/overlay/index.d.ts +87 -0
  176. package/node_modules/@poe-code/safe-fs/dist/fs/overlay/index.js +1185 -0
  177. package/node_modules/@poe-code/safe-fs/dist/fs/overlay/memory-publication.d.ts +42 -0
  178. package/node_modules/@poe-code/safe-fs/dist/fs/overlay/memory-publication.js +558 -0
  179. package/node_modules/@poe-code/safe-fs/dist/fs/path-namespace.d.ts +4 -0
  180. package/node_modules/@poe-code/safe-fs/dist/fs/path-namespace.js +63 -0
  181. package/node_modules/@poe-code/safe-fs/dist/fs/quota/index.d.ts +12 -0
  182. package/node_modules/@poe-code/safe-fs/dist/fs/quota/index.js +520 -0
  183. package/node_modules/@poe-code/safe-fs/dist/fs/readonly/index.d.ts +37 -0
  184. package/node_modules/@poe-code/safe-fs/dist/fs/readonly/index.js +208 -0
  185. package/node_modules/@poe-code/safe-fs/dist/fs/real/allocation.d.ts +1 -0
  186. package/node_modules/@poe-code/safe-fs/dist/fs/real/allocation.js +8 -0
  187. package/node_modules/@poe-code/safe-fs/dist/fs/real/index.d.ts +105 -0
  188. package/node_modules/@poe-code/safe-fs/dist/fs/real/index.js +1259 -0
  189. package/node_modules/@poe-code/safe-fs/dist/fs/s3/authority.d.ts +7 -0
  190. package/node_modules/@poe-code/safe-fs/dist/fs/s3/authority.js +38 -0
  191. package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.d.ts +130 -0
  192. package/node_modules/@poe-code/safe-fs/dist/fs/s3/filesystem.js +1137 -0
  193. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/bytes.d.ts +1 -0
  194. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/bytes.js +9 -0
  195. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/index.d.ts +2 -0
  196. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/index.js +1 -0
  197. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-fetch.d.ts +15 -0
  198. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-fetch.js +74 -0
  199. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-node.d.ts +4 -0
  200. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request-node.js +111 -0
  201. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request.d.ts +17 -0
  202. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/request.js +89 -0
  203. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/signature.d.ts +20 -0
  204. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/signature.js +76 -0
  205. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/transport.d.ts +3 -0
  206. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/transport.js +478 -0
  207. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/types.d.ts +30 -0
  208. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/types.js +1 -0
  209. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/xml.d.ts +14 -0
  210. package/node_modules/@poe-code/safe-fs/dist/fs/s3/http/xml.js +192 -0
  211. package/node_modules/@poe-code/safe-fs/dist/fs/s3/index.d.ts +8 -0
  212. package/node_modules/@poe-code/safe-fs/dist/fs/s3/index.js +4 -0
  213. package/node_modules/@poe-code/safe-fs/dist/fs/s3/key-order.d.ts +2 -0
  214. package/node_modules/@poe-code/safe-fs/dist/fs/s3/key-order.js +11 -0
  215. package/node_modules/@poe-code/safe-fs/dist/fs/s3/manifest-admission.d.ts +8 -0
  216. package/node_modules/@poe-code/safe-fs/dist/fs/s3/manifest-admission.js +167 -0
  217. package/node_modules/@poe-code/safe-fs/dist/fs/s3/mock.d.ts +48 -0
  218. package/node_modules/@poe-code/safe-fs/dist/fs/s3/mock.js +249 -0
  219. package/node_modules/@poe-code/safe-fs/dist/fs/s3/namespace.d.ts +13 -0
  220. package/node_modules/@poe-code/safe-fs/dist/fs/s3/namespace.js +400 -0
  221. package/node_modules/@poe-code/safe-fs/dist/fs/s3/registry.d.ts +10 -0
  222. package/node_modules/@poe-code/safe-fs/dist/fs/s3/registry.js +73 -0
  223. package/node_modules/@poe-code/safe-fs/dist/fs/s3/transport.d.ts +103 -0
  224. package/node_modules/@poe-code/safe-fs/dist/fs/s3/transport.js +28 -0
  225. package/node_modules/@poe-code/safe-fs/dist/fs/scoped.d.ts +19 -0
  226. package/node_modules/@poe-code/safe-fs/dist/fs/scoped.js +663 -0
  227. package/node_modules/@poe-code/safe-fs/dist/fs/staging-ancestry.d.ts +8 -0
  228. package/node_modules/@poe-code/safe-fs/dist/fs/staging-ancestry.js +115 -0
  229. package/node_modules/@poe-code/safe-fs/dist/fs/staging-cleanup.d.ts +5 -0
  230. package/node_modules/@poe-code/safe-fs/dist/fs/staging-cleanup.js +63 -0
  231. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/index.d.ts +2 -0
  232. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/index.js +1 -0
  233. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/resource-id.d.ts +18 -0
  234. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/resource-id.js +109 -0
  235. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/webdav.d.ts +141 -0
  236. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/webdav.js +1377 -0
  237. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/xml.d.ts +15 -0
  238. package/node_modules/@poe-code/safe-fs/dist/fs/webdav/xml.js +59 -0
  239. package/node_modules/@poe-code/safe-fs/dist/index.d.ts +25 -0
  240. package/node_modules/@poe-code/safe-fs/dist/index.js +21 -0
  241. package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/linux-x64-glibc.node +0 -0
  242. package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/loader.d.ts +5 -0
  243. package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/loader.mjs +120 -0
  244. package/node_modules/@poe-code/safe-fs/dist/native/fs-seek/manifest.json +46 -0
  245. package/node_modules/@poe-code/safe-fs/dist/node/filesystem.d.ts +19 -0
  246. package/node_modules/@poe-code/safe-fs/dist/node/filesystem.js +30 -0
  247. package/node_modules/@poe-code/safe-fs/dist/node/host.d.ts +3 -0
  248. package/node_modules/@poe-code/safe-fs/dist/node/host.js +78 -0
  249. package/node_modules/@poe-code/safe-fs/dist/node/index.d.ts +3 -0
  250. package/node_modules/@poe-code/safe-fs/dist/node/index.js +2 -0
  251. package/node_modules/@poe-code/safe-fs/dist/node/native-seek-unavailable.d.ts +1 -0
  252. package/node_modules/@poe-code/safe-fs/dist/node/native-seek-unavailable.js +5 -0
  253. package/node_modules/@poe-code/safe-fs/dist/node/native-seek.d.ts +3 -0
  254. package/node_modules/@poe-code/safe-fs/dist/node/native-seek.js +54 -0
  255. package/node_modules/@poe-code/safe-fs/dist/node/stats.d.ts +1 -0
  256. package/node_modules/@poe-code/safe-fs/dist/node/stats.js +1 -0
  257. package/node_modules/@poe-code/safe-fs/dist/node/values.d.ts +1 -0
  258. package/node_modules/@poe-code/safe-fs/dist/node/values.js +1 -0
  259. package/node_modules/@poe-code/safe-fs/dist/node-host.d.ts +1 -0
  260. package/node_modules/@poe-code/safe-fs/dist/node-host.js +1 -0
  261. package/node_modules/@poe-code/safe-fs/dist/node-unavailable.d.ts +1 -0
  262. package/node_modules/@poe-code/safe-fs/dist/node-unavailable.js +1 -0
  263. package/node_modules/@poe-code/safe-fs/dist/package.json +29 -0
  264. package/node_modules/@poe-code/safe-fs/dist/platform/browser-path.d.ts +1 -0
  265. package/node_modules/@poe-code/safe-fs/dist/platform/browser-path.js +1 -0
  266. package/node_modules/@poe-code/safe-fs/dist/platform/browser.d.ts +26 -0
  267. package/node_modules/@poe-code/safe-fs/dist/platform/browser.js +58 -0
  268. package/node_modules/@poe-code/safe-fs/dist/platform/node-path.d.ts +1 -0
  269. package/node_modules/@poe-code/safe-fs/dist/platform/node-path.js +1 -0
  270. package/node_modules/@poe-code/safe-fs/dist/platform/node.d.ts +26 -0
  271. package/node_modules/@poe-code/safe-fs/dist/platform/node.js +64 -0
  272. package/node_modules/@poe-code/safe-fs/dist/platform/transport-budget.d.ts +9 -0
  273. package/node_modules/@poe-code/safe-fs/dist/platform/transport-budget.js +17 -0
  274. package/node_modules/@poe-code/safe-fs/dist/python/emscripten.d.ts +15 -0
  275. package/node_modules/@poe-code/safe-fs/dist/python/emscripten.js +357 -0
  276. package/node_modules/@poe-code/safe-fs/dist/python/filesystem.d.ts +51 -0
  277. package/node_modules/@poe-code/safe-fs/dist/python/filesystem.js +243 -0
  278. package/node_modules/@poe-code/safe-fs/dist/python/flags.d.ts +3 -0
  279. package/node_modules/@poe-code/safe-fs/dist/python/flags.js +14 -0
  280. package/node_modules/@poe-code/safe-fs/dist/python/index.d.ts +9 -0
  281. package/node_modules/@poe-code/safe-fs/dist/python/index.js +5 -0
  282. package/node_modules/@poe-code/safe-fs/dist/python/native.d.ts +18 -0
  283. package/node_modules/@poe-code/safe-fs/dist/python/native.js +446 -0
  284. package/node_modules/@poe-code/safe-fs/dist/python/request.d.ts +2 -0
  285. package/node_modules/@poe-code/safe-fs/dist/python/request.js +61 -0
  286. package/node_modules/@poe-code/safe-fs/dist/python/stat.d.ts +8 -0
  287. package/node_modules/@poe-code/safe-fs/dist/python/stat.js +34 -0
  288. package/node_modules/@poe-code/safe-fs/dist/runtime-core.d.ts +27 -0
  289. package/node_modules/@poe-code/safe-fs/dist/runtime-core.js +57 -0
  290. package/node_modules/@poe-code/safe-fs/dist/testing/atomic-filesystem.d.ts +16 -0
  291. package/node_modules/@poe-code/safe-fs/dist/testing/atomic-filesystem.js +496 -0
  292. package/node_modules/@poe-code/safe-fs/dist/testing/object-io-control.d.ts +23 -0
  293. package/node_modules/@poe-code/safe-fs/dist/testing/object-io-control.js +39 -0
  294. package/node_modules/@poe-code/safe-fs/dist/testing/object-io-metrics.d.ts +31 -0
  295. package/node_modules/@poe-code/safe-fs/dist/testing/object-io-metrics.js +143 -0
  296. package/node_modules/@poe-code/safe-fs/dist/testing/object-publication.d.ts +19 -0
  297. package/node_modules/@poe-code/safe-fs/dist/testing/object-publication.js +209 -0
  298. package/node_modules/@poe-code/safe-fs/dist/xml.d.ts +1 -0
  299. package/node_modules/@poe-code/safe-fs/dist/xml.js +1 -0
  300. package/node_modules/@poe-code/safe-fs/package.json +189 -0
  301. package/node_modules/@poe-code/task-list/README.md +2 -0
  302. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.d.ts +1 -1
  303. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +11 -4
  304. package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +2 -2
  305. package/node_modules/@poe-code/task-list/dist/backends/utils.js +5 -7
  306. package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +1 -1
  307. package/node_modules/@poe-code/task-list/dist/default-host.d.ts +2 -0
  308. package/node_modules/@poe-code/task-list/dist/default-host.js +2 -0
  309. package/node_modules/@poe-code/task-list/dist/default-host.workerd.d.ts +4 -0
  310. package/node_modules/@poe-code/task-list/dist/default-host.workerd.js +6 -0
  311. package/node_modules/@poe-code/task-list/dist/filesystem.d.ts +3 -0
  312. package/node_modules/@poe-code/task-list/dist/filesystem.js +32 -0
  313. package/node_modules/@poe-code/task-list/dist/move.js +3 -3
  314. package/node_modules/@poe-code/task-list/dist/open.js +2 -5
  315. package/node_modules/@poe-code/task-list/dist/paths.d.ts +12 -0
  316. package/node_modules/@poe-code/task-list/dist/paths.js +7 -0
  317. package/node_modules/@poe-code/task-list/dist/types.d.ts +6 -5
  318. package/node_modules/@poe-code/task-list/package.json +9 -0
  319. package/node_modules/@poe-code/terminal-text/LICENSE +21 -0
  320. package/node_modules/@poe-code/terminal-text/README.md +12 -0
  321. package/node_modules/@poe-code/terminal-text/dist/index.d.ts +2 -0
  322. package/node_modules/@poe-code/terminal-text/dist/index.js +20 -0
  323. package/node_modules/@poe-code/terminal-text/package.json +22 -0
  324. package/node_modules/@poe-code/xml-ast/LICENSE +21 -0
  325. package/node_modules/@poe-code/xml-ast/README.md +28 -0
  326. package/node_modules/@poe-code/xml-ast/dist/index.d.ts +47 -0
  327. package/node_modules/@poe-code/xml-ast/dist/index.js +635 -0
  328. package/node_modules/@poe-code/xml-ast/package.json +25 -0
  329. package/node_modules/auth-store/README.md +35 -0
  330. package/node_modules/auth-store/dist/index.browser.d.ts +4 -0
  331. package/node_modules/auth-store/dist/index.browser.js +2 -0
  332. package/node_modules/auth-store/dist/provider-store.d.ts +1 -1
  333. package/node_modules/auth-store/dist/safe-fs-secret-store.d.ts +21 -0
  334. package/node_modules/auth-store/dist/safe-fs-secret-store.js +119 -0
  335. package/node_modules/auth-store/dist/secret-store.d.ts +12 -0
  336. package/node_modules/auth-store/dist/secret-store.js +1 -0
  337. package/node_modules/auth-store/dist/transaction-lock.d.ts +2 -4
  338. package/node_modules/auth-store/dist/types.d.ts +2 -9
  339. package/node_modules/auth-store/package.json +16 -2
  340. package/node_modules/tiny-http-mcp-server/dist/composition.json +16 -1
  341. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +25 -0
  342. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/authorization-state.js +4 -4
  343. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +6 -40
  344. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/oauth-form.d.ts +10 -0
  345. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/oauth-form.js +20 -0
  346. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/pkce.js +4 -3
  347. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/register-client.d.ts +14 -0
  348. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/register-client.js +53 -0
  349. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resumable.d.ts +16 -2
  350. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resumable.js +29 -9
  351. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/revoke-token.d.ts +12 -0
  352. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/revoke-token.js +23 -0
  353. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -19
  354. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.js +1 -1
  355. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.d.ts +6 -2
  356. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.js +5 -1
  357. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +5 -2
  358. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +4 -1
  359. package/node_modules/tiny-http-mcp-server/package.json +4 -4
  360. package/node_modules/tiny-mcp-client/README.md +2 -0
  361. package/node_modules/tiny-mcp-client/dist/index.browser.d.ts +25 -23
  362. package/node_modules/tiny-mcp-client/dist/index.browser.js +116 -1
  363. package/node_modules/tiny-mcp-client/dist/index.d.ts +25 -23
  364. package/node_modules/tiny-mcp-client/dist/index.js +1034 -561
  365. package/node_modules/tiny-mcp-client/package.json +15 -0
  366. package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
  367. package/node_modules/toolcraft-design/dist/components/table.js +6 -0
  368. package/node_modules/toolcraft-design/dist/escape-terminal-text.d.ts +1 -2
  369. package/node_modules/toolcraft-design/dist/escape-terminal-text.js +1 -20
  370. package/node_modules/toolcraft-design/dist/terminal-markdown/parser/frontmatter.js +2 -1
  371. package/node_modules/toolcraft-design/dist/terminal-markdown/parser.js +2 -1
  372. package/node_modules/toolcraft-schema/dist/json-schema/utils.js +3 -0
  373. package/node_modules/toolcraft-schema/package.json +1 -1
  374. package/package.json +32 -7
@@ -0,0 +1,1377 @@
1
+ import { FsError, isErrnoCode, isFsError } from "../../contracts/errors.js";
2
+ import { composeAbortSignals } from "../../contracts/abort.js";
3
+ import { readBytes } from "../../contracts/io.js";
4
+ import { validatePath } from "../../contracts/virtual-path.js";
5
+ import { davChild, davChildren, parseXmlSteps, scalar, XmlResponseLimitError, XmlResourceLimitError } from "./xml.js";
6
+ import { admitDirectoryEntries, directoryEntryLimit } from "../directory-admission.js";
7
+ import { assertCallbackAuthorityAllowed, compareEntries, registerEntryAuthority } from "../mount/comparison.js";
8
+ import { compareWebDavResources, ownedResponseIdentifier, recordOwnedResourceStat, registerResourceQuery, resourceIdentifier } from "./resource-id.js";
9
+ const timestampNamespace = "urn:virtual-bash:metadata";
10
+ /** Admit only five scalar members before native JSON parsing can allocate a graph. */
11
+ function preflightTimestamps(property, maxTimestampPropertyBytes) {
12
+ // Check the untrimmed text first; counting UTF-8 bytes does not allocate an encoded copy.
13
+ let bytes = 0;
14
+ for (const point of property.text) {
15
+ const code = point.codePointAt(0);
16
+ bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4;
17
+ if (bytes > maxTimestampPropertyBytes)
18
+ throw new Error("timestamp property exceeds byte limit");
19
+ }
20
+ const text = scalar(property);
21
+ let offset = 0;
22
+ const invalid = () => { throw new Error("invalid timestamp property structure"); };
23
+ const whitespace = () => {
24
+ while (" \t\r\n".includes(text[offset] ?? "\0"))
25
+ offset++;
26
+ };
27
+ const stringEnd = () => {
28
+ if (text[offset++] !== '"')
29
+ invalid();
30
+ while (offset < text.length) {
31
+ const char = text[offset++];
32
+ if (char === '"')
33
+ return;
34
+ if (char === "\\")
35
+ offset++;
36
+ }
37
+ invalid();
38
+ };
39
+ if (text[offset++] !== "{")
40
+ invalid();
41
+ const fields = new Set();
42
+ while (true) {
43
+ whitespace();
44
+ const start = offset;
45
+ stringEnd();
46
+ const field = JSON.parse(text.slice(start, offset));
47
+ if (typeof field !== "string" || !["version", "etag", "type", "atimeMs", "mtimeMs"].includes(field)
48
+ || fields.has(field) || fields.size >= 5)
49
+ return invalid();
50
+ fields.add(field);
51
+ whitespace();
52
+ if (text[offset++] !== ":")
53
+ invalid();
54
+ whitespace();
55
+ if (text[offset] === '"')
56
+ stringEnd();
57
+ else {
58
+ const start = offset;
59
+ while (offset < text.length && text[offset] !== "," && text[offset] !== "}") {
60
+ if ('{[:"\\]'.includes(text[offset]))
61
+ invalid();
62
+ offset++;
63
+ }
64
+ if (offset === start)
65
+ invalid();
66
+ }
67
+ whitespace();
68
+ const delimiter = text[offset++];
69
+ if (delimiter === "}")
70
+ break;
71
+ if (delimiter !== ",")
72
+ invalid();
73
+ }
74
+ whitespace();
75
+ if (offset !== text.length || fields.size !== 5)
76
+ invalid();
77
+ return text;
78
+ }
79
+ const propfindBody = '<?xml version="1.0" encoding="utf-8"?>'
80
+ + '<d:propfind xmlns:d="DAV:"><d:prop><d:resourcetype/><d:getcontentlength/>'
81
+ + `<d:getlastmodified/><d:creationdate/><d:getetag/><v:timestamps xmlns:v="${timestampNamespace}"/></d:prop></d:propfind>`;
82
+ const lockBody = '<d:lockinfo xmlns:d="DAV:"><d:lockscope><d:exclusive/></d:lockscope>'
83
+ + '<d:locktype><d:write/></d:locktype></d:lockinfo>';
84
+ const resourceIdBody = '<?xml version="1.0" encoding="utf-8"?>'
85
+ + '<d:propfind xmlns:d="DAV:"><d:prop><d:resource-id/></d:prop></d:propfind>';
86
+ function fail(code, syscall, path, message) {
87
+ throw new FsError(code, { syscall, path, ...(message === undefined ? {} : { message }) });
88
+ }
89
+ function positive(value, name, zero = false, unlimited = false) {
90
+ if (unlimited && value === Infinity)
91
+ return value;
92
+ if (!Number.isSafeInteger(value) || value < (zero ? 0 : 1)) {
93
+ throw new FsError("EINVAL", { message: `${name} must be a ${zero ? "nonnegative" : "positive"} safe integer` });
94
+ }
95
+ return value;
96
+ }
97
+ function createRequestTimeout(timeoutMs) {
98
+ const controller = new AbortController();
99
+ const timer = timeoutMs === undefined ? undefined : setTimeout(() => {
100
+ controller.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
101
+ }, timeoutMs);
102
+ if (typeof timer === "object" && timer !== null) {
103
+ const handle = timer;
104
+ handle.unref?.();
105
+ }
106
+ return {
107
+ signal: controller.signal,
108
+ dispose() {
109
+ clearTimeout(timer);
110
+ }
111
+ };
112
+ }
113
+ function nativeRequestStreamsSupported(url) {
114
+ let body;
115
+ try {
116
+ body = new ReadableStream({ start(destination) { destination.close(); } });
117
+ let duplexAccessed = false;
118
+ const init = {
119
+ method: "PUT", body,
120
+ get duplex() { duplexAccessed = true; return "half"; },
121
+ };
122
+ const request = new Request(url, init);
123
+ return duplexAccessed && !request.headers.has("Content-Type");
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ finally {
129
+ if (body && !body.locked)
130
+ void body.cancel().catch(() => { });
131
+ }
132
+ }
133
+ function strongEtag(value, path) {
134
+ if (!value || !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(value)) {
135
+ fail("ENOTSUP", "webdav", path, "operation requires a strong entity tag");
136
+ }
137
+ return value;
138
+ }
139
+ function normalize(path) {
140
+ if (typeof path !== "string" || path.includes("\0") || path.includes("\\")) {
141
+ fail("EINVAL", "resolve", String(path), "invalid WebDAV path");
142
+ }
143
+ try {
144
+ encodeURIComponent(path);
145
+ }
146
+ catch {
147
+ fail("EINVAL", "resolve", path, "path contains an unpaired surrogate");
148
+ }
149
+ const segments = [];
150
+ for (const segment of path.split("/")) {
151
+ if (!segment || segment === ".")
152
+ continue;
153
+ if (segment === "..") {
154
+ if (!segments.length)
155
+ fail("EACCES", "resolve", path, "path escapes WebDAV root");
156
+ segments.pop();
157
+ }
158
+ else
159
+ segments.push(segment);
160
+ }
161
+ return `/${segments.join("/")}`;
162
+ }
163
+ function requiresCollection(path) {
164
+ const last = path.split("/").at(-1);
165
+ return last === "" || last === "." || last === "..";
166
+ }
167
+ function validateUrlText(text) {
168
+ if (/[\u0000-\u0020\u007f\\?#]/.test(text))
169
+ throw new Error("invalid URL characters");
170
+ for (const segment of text.split("/")) {
171
+ const decoded = decodeURIComponent(segment);
172
+ if (decoded === "." || decoded === ".." || /[/\\\u0000]/.test(decoded)) {
173
+ throw new Error("ambiguous URL segment");
174
+ }
175
+ }
176
+ }
177
+ function urlSegments(url) {
178
+ const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
179
+ const segments = pathname.slice(1).split("/");
180
+ if (segments.length === 1 && segments[0] === "")
181
+ return [];
182
+ if (segments.some((segment) => segment === ""))
183
+ throw new Error("empty URL segment");
184
+ return segments.map((segment) => decodeURIComponent(segment));
185
+ }
186
+ function statusCode(element) {
187
+ const text = scalar(element);
188
+ if (!/^HTTP\/\d+\.\d+ [2-5]\d\d(?:[ \t].*)?$/.test(text))
189
+ throw new Error("invalid DAV status line");
190
+ return Number(text.split(" ")[1]);
191
+ }
192
+ export class WebDavFileSystem {
193
+ async open(path, options) {
194
+ options.signal?.throwIfAborted();
195
+ throw new FsError("ENOTSUP", { syscall: "open", path });
196
+ }
197
+ capabilities = Object.freeze({
198
+ open: false,
199
+ read: true, stat: true, readdir: true, realpath: true, access: true,
200
+ write: true, append: true, exclusiveCreate: true, explicitDirectories: true, implicitDirectories: false,
201
+ mkdir: true, recursiveMkdir: true, remove: true, recursiveRemove: true, rename: true, atomicRenameNoReplace: false, copy: true,
202
+ exclusiveCopy: true, readlink: false, truncate: false, randomAccessWrite: false,
203
+ removeDirectory: false, streamingAppend: false,
204
+ symlinks: false, hardlinks: false, permissions: false, timestamps: true,
205
+ atomicRename: false, streamingRead: true, streamingWrite: true,
206
+ });
207
+ base;
208
+ baseSegments;
209
+ transport;
210
+ requestStreamSupport;
211
+ headers;
212
+ maxResponseBytes;
213
+ maxXmlBytes;
214
+ maxTimestampPropertyBytes;
215
+ maxEntries;
216
+ xmlLimits;
217
+ timeoutMs;
218
+ walkDeadlines = new WeakMap();
219
+ overwritePolicy;
220
+ configuredComparison;
221
+ atomicEmptyDirectory;
222
+ etags = new WeakMap();
223
+ constructor(options) {
224
+ if (typeof options.fetch !== "function")
225
+ throw new FsError("EINVAL", { message: "an explicit fetch transport is required" });
226
+ if (options.requestStreamSupport !== undefined && options.requestStreamSupport !== "native"
227
+ && typeof options.requestStreamSupport !== "boolean") {
228
+ throw new FsError("EINVAL", { message: "requestStreamSupport must be native or a boolean" });
229
+ }
230
+ if (options.compareEntry !== undefined && typeof options.compareEntry !== "function") {
231
+ throw new FsError("EINVAL", { message: "compareEntry must be a function" });
232
+ }
233
+ if (options.compareEntry !== undefined)
234
+ assertCallbackAuthorityAllowed();
235
+ try {
236
+ validateUrlText(options.baseUrl);
237
+ this.base = new URL(options.baseUrl);
238
+ if (!["http:", "https:"].includes(this.base.protocol) || this.base.username || this.base.password
239
+ || this.base.search || this.base.hash)
240
+ throw new Error("invalid base URL");
241
+ this.baseSegments = urlSegments(this.base);
242
+ this.base.pathname = `/${this.baseSegments.map(encodeURIComponent).join("/")}${this.baseSegments.length ? "/" : ""}`;
243
+ this.headers = new Headers(options.headers);
244
+ for (const name of this.headers.keys()) {
245
+ if (["host", "destination", "depth", "overwrite", "if", "if-match", "if-none-match", "range",
246
+ "content-length", "content-type", "transfer-encoding", "connection", "proxy-authorization", "lock-token", "timeout"].includes(name)) {
247
+ throw new Error(`reserved header ${name}`);
248
+ }
249
+ }
250
+ if (this.base.protocol !== "https:" && [...this.headers.keys()].some((name) => name === "authorization" || name === "cookie")) {
251
+ throw new Error("explicit credentials require HTTPS");
252
+ }
253
+ }
254
+ catch (cause) {
255
+ throw new FsError("EINVAL", { message: "invalid WebDAV base URL or headers", cause });
256
+ }
257
+ const binding = options.atomicEmptyDirectory;
258
+ if (binding !== undefined) {
259
+ if (!binding || typeof binding !== "object" || binding.namespaceUrl !== this.base.href
260
+ || typeof binding.removeEmptyDirectory !== "function") {
261
+ throw new FsError("EINVAL", { message: "atomicEmptyDirectory must bind the canonical WebDAV namespace and a callback" });
262
+ }
263
+ this.atomicEmptyDirectory = Object.freeze({ namespaceUrl: this.base.href, removeEmptyDirectory: binding.removeEmptyDirectory });
264
+ }
265
+ this.transport = options.fetch === globalThis.fetch ? options.fetch.bind(globalThis) : options.fetch;
266
+ this.requestStreamSupport = options.requestStreamSupport ?? (options.fetch === globalThis.fetch ? "native" : false);
267
+ this.capabilities = Object.freeze({ ...this.capabilities,
268
+ removeDirectory: this.atomicEmptyDirectory !== undefined,
269
+ streamingAppend: this.requestStreamSupport !== false,
270
+ });
271
+ this.maxResponseBytes = options.maxResponseBytes === undefined ? Infinity : positive(options.maxResponseBytes, "maxResponseBytes", false, true);
272
+ this.maxTimestampPropertyBytes = positive(options.maxTimestampPropertyBytes ?? Infinity, "maxTimestampPropertyBytes", true, true);
273
+ this.maxXmlBytes = options.maxXmlBytes === undefined ? Infinity : positive(options.maxXmlBytes, "maxXmlBytes", false, true);
274
+ this.xmlLimits = Object.freeze({ ...options.xmlLimits });
275
+ for (const [name, value] of Object.entries(this.xmlLimits))
276
+ positive(value, name, false, true);
277
+ this.maxEntries = options.maxEntries === undefined ? Infinity : positive(options.maxEntries, "maxEntries", false, true);
278
+ this.timeoutMs = options.timeoutMs === undefined ? undefined : positive(options.timeoutMs, "timeoutMs");
279
+ this.overwritePolicy = options.overwritePolicy ?? "lock";
280
+ this.configuredComparison = options.compareEntry !== undefined;
281
+ if (!["lock", "etag"].includes(this.overwritePolicy))
282
+ throw new FsError("EINVAL", { message: "invalid overwritePolicy" });
283
+ if (this.timeoutMs !== undefined && this.timeoutMs > 2_147_483_647)
284
+ throw new FsError("EINVAL", { message: "timeoutMs exceeds timer range" });
285
+ registerResourceQuery(this, (path, settings) => this.resourceId(path, settings), originalWebDavComparison, options.compareEntry);
286
+ registerEntryAuthority(this, compareWebDavResources);
287
+ }
288
+ compareEntry(path, peer, peerPath, options = {}) {
289
+ return compareEntries(this, path, peer, peerPath, options);
290
+ }
291
+ async resourceId(input, options) {
292
+ const path = normalize(input);
293
+ return this.request("PROPFIND", path, options, {
294
+ headers: { Depth: "0", "Content-Type": "application/xml; charset=utf-8" }, body: resourceIdBody,
295
+ }, async (response, signal) => {
296
+ if (response.status !== 207)
297
+ this.httpError(response.status, "PROPFIND", path);
298
+ const responses = await this.multistatus(response, signal, "PROPFIND", path);
299
+ if (responses.length !== 1)
300
+ throw new Error("resource identity requires exactly one DAV:response");
301
+ const entry = responses[0];
302
+ const href = davChild(entry, "href");
303
+ if (!href)
304
+ throw new Error("missing resource identity response href");
305
+ if (this.hrefPath(scalar(href)) !== path)
306
+ fail("EACCES", "PROPFIND", path, "resource identity response addresses another resource");
307
+ const wholeStatus = davChild(entry, "status");
308
+ const propstats = davChildren(entry, "propstat");
309
+ if (wholeStatus) {
310
+ if (propstats.length)
311
+ throw new Error("conflicting response status and propstat");
312
+ this.httpError(statusCode(wholeStatus), "PROPFIND", path);
313
+ }
314
+ if (propstats.length === 0)
315
+ throw new Error("missing resource identity propstat");
316
+ let seen = false;
317
+ let identifier;
318
+ for (const propstat of propstats) {
319
+ const status = davChild(propstat, "status");
320
+ const prop = davChild(propstat, "prop");
321
+ if (!status || !prop)
322
+ throw new Error("incomplete resource identity propstat");
323
+ const code = statusCode(status);
324
+ if (code !== 200 && code !== 404)
325
+ this.httpError(code, "PROPFIND", path);
326
+ for (const property of davChildren(prop, "resource-id")) {
327
+ if (seen)
328
+ throw new Error("duplicate or conflicting resource identity property");
329
+ seen = true;
330
+ if (code === 404)
331
+ continue;
332
+ const value = davChild(property, "href");
333
+ if (!value || property.children.length !== 1 || property.text.trim())
334
+ throw new Error("invalid resource identity property");
335
+ identifier = resourceIdentifier(scalar(value));
336
+ }
337
+ }
338
+ const owned = ownedResponseIdentifier(response, path);
339
+ if (owned !== undefined && identifier !== undefined && resourceIdentifier(owned) !== identifier) {
340
+ throw new Error("resource identity contradicts provider provenance");
341
+ }
342
+ return identifier;
343
+ });
344
+ }
345
+ url(path, collection = false) {
346
+ const relative = path.slice(1).split("/").map(encodeURIComponent).join("/");
347
+ return `${this.base.href}${relative}${collection && relative ? "/" : ""}`;
348
+ }
349
+ hrefPath(href) {
350
+ try {
351
+ validateUrlText(href);
352
+ if (!(href.startsWith("/") && !href.startsWith("//")) && !/^https?:\/\//i.test(href))
353
+ throw new Error("href must be absolute");
354
+ const url = new URL(href, this.base);
355
+ if (url.origin !== this.base.origin || url.username || url.password || url.search || url.hash)
356
+ throw new Error("href is not confined");
357
+ const segments = urlSegments(url);
358
+ if (segments.length < this.baseSegments.length || this.baseSegments.some((segment, index) => segments[index] !== segment)) {
359
+ throw new Error("href escapes root");
360
+ }
361
+ return normalize(`/${segments.slice(this.baseSegments.length).join("/")}`);
362
+ }
363
+ catch (cause) {
364
+ throw new FsError("EACCES", { syscall: "webdav", message: "response URL escapes or ambiguously addresses WebDAV root", cause });
365
+ }
366
+ }
367
+ httpError(status, method, path, exclusive = false) {
368
+ const codes = {
369
+ 400: "EINVAL", 401: "EACCES", 403: "EACCES", 404: "ENOENT", 405: method === "MKCOL" ? "EEXIST" : "ENOTSUP",
370
+ 408: "ETIMEDOUT", 409: method === "PUT" || method === "MKCOL" ? "ENOENT" : "EINVAL", 410: "ENOENT",
371
+ 412: exclusive ? "EEXIST" : "EAGAIN", 413: "EFBIG", 414: "ENAMETOOLONG", 415: "ENOTSUP",
372
+ 423: "EBUSY", 429: "EAGAIN", 501: "ENOTSUP", 503: "EAGAIN", 504: "ETIMEDOUT", 507: "ENOSPC",
373
+ };
374
+ fail(status >= 300 && status < 400 ? "ENOTSUP" : codes[status] ?? "EIO", method, path, status >= 300 && status < 400 ? "WebDAV redirects are not followed" : `WebDAV HTTP status ${status}`);
375
+ }
376
+ async request(method, path, options, init, consume, collection = false, received) {
377
+ for await (const result of this.requestStream(method, path, options, init, async function* (response, signal) {
378
+ yield await consume(response, signal);
379
+ }, collection, received))
380
+ return result;
381
+ throw new Error("missing WebDAV response");
382
+ }
383
+ async *requestStream(method, path, options, init, consume, collection = false, received) {
384
+ if (options.signal?.aborted)
385
+ fail("ECANCELED", method, path);
386
+ const walk = this.walkDeadlines.get(options);
387
+ const deadline = walk?.deadline ?? createRequestTimeout(this.timeoutMs);
388
+ if (walk)
389
+ walk.deadline = deadline;
390
+ const timeout = deadline.signal;
391
+ try {
392
+ const headers = new Headers(this.headers);
393
+ for (const [name, value] of new Headers(init.headers))
394
+ headers.set(name, value);
395
+ headers.set("Cache-Control", "no-cache");
396
+ const signalScope = options.signal ? composeAbortSignals([timeout, options.signal]) : undefined;
397
+ const signal = signalScope?.signal ?? timeout;
398
+ let response;
399
+ const receive = (value, late) => {
400
+ if (value.redirected || value.type === "opaqueredirect")
401
+ fail("ENOTSUP", method, path, "transport followed or hid a redirect");
402
+ if (value.url && this.hrefPath(value.url) !== path)
403
+ fail("EACCES", method, path, "transport changed the requested resource");
404
+ received?.(value, late);
405
+ };
406
+ try {
407
+ let url = this.url(path, collection);
408
+ for (let attempt = 0;; attempt++) {
409
+ signal.throwIfAborted();
410
+ response = await new Promise((resolve, reject) => {
411
+ let abandoned = false;
412
+ const abort = () => {
413
+ abandoned = true;
414
+ signal.removeEventListener("abort", abort);
415
+ reject(signal.reason);
416
+ };
417
+ signal.addEventListener("abort", abort, { once: true });
418
+ try {
419
+ Promise.resolve(this.transport(url, {
420
+ ...init, method, headers, redirect: "manual", credentials: "omit", signal,
421
+ })).then((value) => {
422
+ signal.removeEventListener("abort", abort);
423
+ if (!abandoned)
424
+ resolve(value);
425
+ else {
426
+ try {
427
+ receive(value, true);
428
+ }
429
+ catch (ignoredError) {
430
+ void ignoredError;
431
+ }
432
+ finally {
433
+ if (value.body && !value.body.locked)
434
+ void value.body.cancel(signal.reason).catch(() => { });
435
+ }
436
+ }
437
+ }, (error) => {
438
+ signal.removeEventListener("abort", abort);
439
+ reject(error);
440
+ }).catch(reject);
441
+ }
442
+ catch (error) {
443
+ signal.removeEventListener("abort", abort);
444
+ reject(error);
445
+ }
446
+ });
447
+ receive(response, false);
448
+ signal.throwIfAborted();
449
+ const location = response.headers.get("Location");
450
+ if (attempt === 0 && method === "PROPFIND" && !url.endsWith("/")
451
+ && [301, 302, 307, 308].includes(response.status) && location !== null
452
+ && this.hrefPath(location) === path && new URL(location, this.base).href === `${url}/`) {
453
+ await response.body?.cancel();
454
+ signal.throwIfAborted();
455
+ url += "/";
456
+ continue;
457
+ }
458
+ yield* consume(response, signal);
459
+ return;
460
+ }
461
+ }
462
+ catch (cause) {
463
+ if (options.signal?.aborted)
464
+ throw new FsError("ECANCELED", { syscall: method, path, cause });
465
+ if (timeout.aborted)
466
+ throw new FsError("ETIMEDOUT", { syscall: method, path, cause });
467
+ if (isFsError(cause))
468
+ throw cause;
469
+ throw new FsError("EIO", { syscall: method, path, message: "WebDAV transport or response failure", cause });
470
+ }
471
+ finally {
472
+ signalScope?.dispose();
473
+ if (response?.body && !response.body.locked)
474
+ void response.body.cancel().catch(() => { });
475
+ }
476
+ }
477
+ finally {
478
+ if (!walk)
479
+ deadline.dispose();
480
+ }
481
+ }
482
+ async bytes(response, limit, signal) {
483
+ const expected = this.responseLength(response, limit);
484
+ let data = new Uint8Array(expected ?? 0);
485
+ let size = 0;
486
+ // Consume borrowed chunks immediately; only streaming callers need owned copies.
487
+ for await (const chunk of this.bodyChunks(response, limit, signal, false)) {
488
+ const nextSize = size + chunk.byteLength;
489
+ if (nextSize > data.byteLength) {
490
+ const grown = new Uint8Array(Math.min(limit, Math.max(nextSize, data.byteLength * 2)));
491
+ grown.set(data);
492
+ data = grown;
493
+ }
494
+ data.set(chunk, size);
495
+ size = nextSize;
496
+ }
497
+ // A view avoids a second full allocation when the length was unknown.
498
+ return data.subarray(0, size);
499
+ }
500
+ responseLength(response, limit) {
501
+ const length = response.headers.get("content-length");
502
+ if (length !== null && (!length.length || [...length].some(char => char < "0" || char > "9")
503
+ || !Number.isSafeInteger(Number(length)))) {
504
+ fail("EIO", "webdav", "", "invalid response Content-Length");
505
+ }
506
+ const encoding = response.headers.get("content-encoding");
507
+ const expected = length !== null && (!encoding || encoding.toLowerCase() === "identity") ? Number(length) : undefined;
508
+ if (expected !== undefined && expected > limit)
509
+ fail("EFBIG", "webdav", "", "response exceeds byte limit");
510
+ return expected;
511
+ }
512
+ async *bodyChunks(response, limit, signal, copy = true) {
513
+ const expected = this.responseLength(response, limit);
514
+ if (!response.body) {
515
+ if (expected !== undefined && expected !== 0)
516
+ fail("EIO", "webdav", "", "response body length differs from Content-Length");
517
+ return;
518
+ }
519
+ const reader = response.body.getReader();
520
+ let size = 0;
521
+ const abort = () => { void reader.cancel(signal.reason).catch(() => { }); };
522
+ signal.addEventListener("abort", abort, { once: true });
523
+ try {
524
+ while (true) {
525
+ signal.throwIfAborted();
526
+ const result = await reader.read();
527
+ signal.throwIfAborted();
528
+ if (result.done)
529
+ break;
530
+ size += result.value.byteLength;
531
+ if (size > limit)
532
+ fail("EFBIG", "webdav", "", "response exceeds byte limit");
533
+ if (expected !== undefined && size > expected)
534
+ fail("EIO", "webdav", "", "response body length differs from Content-Length");
535
+ yield copy ? new Uint8Array(result.value) : result.value;
536
+ }
537
+ if (expected !== undefined && size !== expected)
538
+ fail("EIO", "webdav", "", "response body length differs from Content-Length");
539
+ }
540
+ finally {
541
+ signal.removeEventListener("abort", abort);
542
+ void reader.cancel().catch(() => { });
543
+ reader.releaseLock();
544
+ }
545
+ }
546
+ async xml(response, signal, maxResponses) {
547
+ const data = await this.bytes(response, this.maxXmlBytes, signal);
548
+ const encoding = (data[0] === 0xff && data[1] === 0xfe) || (data[0] === 0x3c && data[1] === 0)
549
+ ? "utf-16le" : (data[0] === 0xfe && data[1] === 0xff) || (data[0] === 0 && data[1] === 0x3c) ? "utf-16be" : "utf-8";
550
+ const parser = parseXmlSteps(new TextDecoder(encoding, { fatal: true }).decode(data), {
551
+ ...(maxResponses === undefined ? {} : { maxResponses }),
552
+ ...this.xmlLimits, maxTextLength: this.maxXmlBytes,
553
+ });
554
+ let work = 0;
555
+ try {
556
+ while (true) {
557
+ signal.throwIfAborted();
558
+ const step = parser.next();
559
+ if (step.done)
560
+ return step.value;
561
+ work += step.value;
562
+ if (work >= 4096) {
563
+ work = 0;
564
+ // Yield to the host task queue so request deadlines and caller aborts can fire.
565
+ await new Promise(resolve => setTimeout(resolve, 0));
566
+ }
567
+ }
568
+ }
569
+ catch (error) {
570
+ if (error instanceof XmlResourceLimitError)
571
+ fail("EFBIG", "webdav", "", error.message);
572
+ throw error;
573
+ }
574
+ finally {
575
+ parser.return(undefined);
576
+ }
577
+ }
578
+ async multistatus(response, signal, method = "PROPFIND", path = "") {
579
+ const link = response.headers.get("link");
580
+ if (link && /\brel\s*=\s*(?:"[^"]*\bnext\b[^"]*"|'[^']*\bnext\b[^']*'|next\b)/i.test(link)) {
581
+ fail("ENOTSUP", method, path, "paginated WebDAV responses are unsupported");
582
+ }
583
+ let root;
584
+ try {
585
+ root = await this.xml(response, signal, this.maxEntries === Infinity ? undefined : this.maxEntries);
586
+ }
587
+ catch (error) {
588
+ if (error instanceof XmlResponseLimitError)
589
+ fail("EFBIG", method, path, "response exceeds entry limit");
590
+ throw error;
591
+ }
592
+ if (root.namespace !== "DAV:" || root.localName !== "multistatus")
593
+ throw new Error("expected DAV:multistatus");
594
+ const responses = davChildren(root, "response");
595
+ if (responses.length > this.maxEntries)
596
+ fail("EFBIG", method, path, "response exceeds entry limit");
597
+ return responses;
598
+ }
599
+ async lockFailure(response, signal, path) {
600
+ try {
601
+ const members = new Set();
602
+ let failure;
603
+ for (const element of await this.multistatus(response, signal, "LOCK", path)) {
604
+ const href = davChild(element, "href");
605
+ const status = davChild(element, "status");
606
+ if (!href || !status)
607
+ throw new Error("invalid LOCK multistatus");
608
+ const member = this.hrefPath(scalar(href));
609
+ if (member !== path && !member.startsWith(`${path}/`)) {
610
+ fail("EACCES", "LOCK", path, "LOCK response member is outside requested subtree");
611
+ }
612
+ if (members.has(member))
613
+ throw new Error("duplicate LOCK response href");
614
+ members.add(member);
615
+ const code = statusCode(status);
616
+ if (code >= 300 && (!failure || (failure.status === 424 && code !== 424))) {
617
+ failure = { member, status: code };
618
+ }
619
+ }
620
+ if (!failure)
621
+ throw new Error("LOCK multistatus without a reported failure");
622
+ this.httpError(failure.status, "LOCK", failure.member);
623
+ }
624
+ catch (cause) {
625
+ throw new FsError(isFsError(cause) ? cause.code : "EIO", {
626
+ syscall: "LOCK", path, message: "WebDAV LOCK multistatus failure", cause,
627
+ });
628
+ }
629
+ }
630
+ entryStat(element, path) {
631
+ const wholeStatus = davChild(element, "status");
632
+ if (wholeStatus) {
633
+ const code = statusCode(wholeStatus);
634
+ if (code < 200 || code >= 300)
635
+ this.httpError(code, "PROPFIND", path);
636
+ }
637
+ const properties = new Map();
638
+ const failures = new Map();
639
+ let timestampProperty;
640
+ let timestampSeen = false;
641
+ for (const propstat of davChildren(element, "propstat")) {
642
+ const status = davChild(propstat, "status");
643
+ const prop = davChild(propstat, "prop");
644
+ if (!status || !prop)
645
+ throw new Error("incomplete DAV:propstat");
646
+ const code = statusCode(status);
647
+ for (const property of prop.children) {
648
+ if (property.namespace === timestampNamespace && property.localName === "timestamps") {
649
+ if (timestampSeen)
650
+ throw new Error("duplicate timestamp property");
651
+ timestampSeen = true;
652
+ if (code === 200)
653
+ timestampProperty = property;
654
+ }
655
+ if (property.namespace !== "DAV:")
656
+ continue;
657
+ if (properties.has(property.localName) || failures.has(property.localName))
658
+ throw new Error("duplicate DAV property");
659
+ if (code >= 200 && code < 300)
660
+ properties.set(property.localName, property);
661
+ else
662
+ failures.set(property.localName, code);
663
+ }
664
+ }
665
+ const required = (name) => {
666
+ const property = properties.get(name);
667
+ if (property)
668
+ return property;
669
+ const failure = failures.get(name);
670
+ if (failure !== undefined && failure !== 404)
671
+ this.httpError(failure, "PROPFIND", path);
672
+ return fail("ENOTSUP", "stat", path, `server does not expose DAV:${name}`);
673
+ };
674
+ const resourceType = required("resourcetype");
675
+ const directory = !!davChild(resourceType, "collection");
676
+ if (resourceType.text.trim() || resourceType.children.some((child) => child.namespace !== "DAV:" || child.localName !== "collection")) {
677
+ fail("ENOTSUP", "stat", path, "unsupported DAV resource type");
678
+ }
679
+ const sizeText = directory ? "0" : scalar(required("getcontentlength"));
680
+ if (!/^\d+$/.test(sizeText) || !Number.isSafeInteger(Number(sizeText)))
681
+ throw new Error("invalid content length property");
682
+ const date = (name) => {
683
+ const property = properties.get(name);
684
+ if (!property)
685
+ return undefined;
686
+ const value = Date.parse(scalar(property));
687
+ if (!Number.isFinite(value))
688
+ throw new Error("invalid date property");
689
+ return value;
690
+ };
691
+ const birthtimeMs = date("creationdate");
692
+ const etag = properties.get("getetag");
693
+ const validator = etag && scalar(etag);
694
+ let mtimeMs = date("getlastmodified") ?? 0;
695
+ let atimeMs = 0;
696
+ if (timestampProperty) {
697
+ const timestamps = JSON.parse(preflightTimestamps(timestampProperty, this.maxTimestampPropertyBytes));
698
+ if (typeof timestamps !== "object" || timestamps === null
699
+ || !("version" in timestamps) || timestamps.version !== 1
700
+ || !("etag" in timestamps) || typeof timestamps.etag !== "string" || !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(timestamps.etag)
701
+ || !("type" in timestamps) || (timestamps.type !== "file" && timestamps.type !== "directory")
702
+ || !("atimeMs" in timestamps) || typeof timestamps.atimeMs !== "number" || !Number.isFinite(timestamps.atimeMs)
703
+ || Math.abs(timestamps.atimeMs) > 8.64e15
704
+ || !("mtimeMs" in timestamps) || typeof timestamps.mtimeMs !== "number" || !Number.isFinite(timestamps.mtimeMs)
705
+ || Math.abs(timestamps.mtimeMs) > 8.64e15)
706
+ throw new Error("invalid timestamp property");
707
+ if (timestamps.etag === validator && timestamps.type === (directory ? "directory" : "file")) {
708
+ mtimeMs = timestamps.mtimeMs;
709
+ atimeMs = timestamps.atimeMs;
710
+ }
711
+ }
712
+ const stat = {
713
+ type: directory ? "directory" : "file", size: Number(sizeText), mode: directory ? 0o40777 : 0o100666,
714
+ mtimeMs, atimeMs, ctimeMs: 0,
715
+ ...(birthtimeMs === undefined ? {} : { birthtimeMs }),
716
+ };
717
+ if (validator)
718
+ this.etags.set(stat, validator);
719
+ return stat;
720
+ }
721
+ async entries(path, depth, options, collection = depth === "1", limit) {
722
+ return this.request("PROPFIND", path, options, {
723
+ headers: { Depth: depth, "Content-Type": "application/xml; charset=utf-8" }, body: propfindBody,
724
+ }, async (response, signal) => {
725
+ if (response.status !== 207)
726
+ this.httpError(response.status, "PROPFIND", path);
727
+ const result = new Map();
728
+ let children = 0;
729
+ for (const element of await this.multistatus(response, signal)) {
730
+ signal.throwIfAborted();
731
+ const href = davChild(element, "href");
732
+ if (!href)
733
+ throw new Error("missing DAV:href");
734
+ const member = this.hrefPath(scalar(href));
735
+ const prefix = path === "/" ? "/" : `${path}/`;
736
+ if (member !== path && (depth === "0" || !member.startsWith(prefix) || member.slice(prefix.length).includes("/"))) {
737
+ fail("EACCES", "PROPFIND", path, "response member is outside requested depth");
738
+ }
739
+ if (result.has(member))
740
+ throw new Error("duplicate response href");
741
+ if (member !== path)
742
+ admitDirectoryEntries(++children, limit, path);
743
+ const stat = this.entryStat(element, member);
744
+ recordOwnedResourceStat(response, this, member, stat);
745
+ result.set(member, stat);
746
+ }
747
+ if (!result.has(path))
748
+ throw new Error("response omitted requested resource");
749
+ return result;
750
+ }, collection);
751
+ }
752
+ async mutation(method, path, options, init = {}, collection = false) {
753
+ await this.request(method, path, options, init, async (response, signal) => {
754
+ if (response.status === 207) {
755
+ for (const element of await this.multistatus(response, signal)) {
756
+ const href = davChild(element, "href");
757
+ const status = davChild(element, "status");
758
+ if (!href || !status)
759
+ throw new Error("invalid mutation multistatus");
760
+ const member = this.hrefPath(scalar(href));
761
+ const code = statusCode(status);
762
+ if (code < 200 || code >= 300) {
763
+ try {
764
+ this.httpError(code, method, member);
765
+ }
766
+ catch (cause) {
767
+ throw new FsError("EIO", { syscall: method, path, message: "WebDAV mutation partially failed", cause });
768
+ }
769
+ }
770
+ }
771
+ fail("EIO", method, path, "unexpected mutation multistatus without a reported failure");
772
+ }
773
+ const accepted = method === "PUT" ? [200, 201, 204] : method === "MKCOL" ? [201] : method === "DELETE" ? [204] : [201, 204];
774
+ if (!accepted.includes(response.status)) {
775
+ const headers = new Headers(init.headers);
776
+ this.httpError(response.status, method, path, headers.get("If-None-Match") === "*" || headers.get("Overwrite") === "F");
777
+ }
778
+ }, collection);
779
+ }
780
+ unsupported(operation, path) {
781
+ return fail("ENOTSUP", operation, path, `${operation} has no safe portable WebDAV equivalent`);
782
+ }
783
+ async maybeStat(path, options) {
784
+ try {
785
+ return await this.stat(path, options);
786
+ }
787
+ catch (error) {
788
+ if (isFsError(error, "ENOENT"))
789
+ return undefined;
790
+ throw error;
791
+ }
792
+ }
793
+ async withWalkDeadline(options, walk) {
794
+ if (this.walkDeadlines.has(options))
795
+ return walk(options);
796
+ const scopedOptions = { ...options };
797
+ const owned = {};
798
+ this.walkDeadlines.set(scopedOptions, owned);
799
+ try {
800
+ return await walk(scopedOptions);
801
+ }
802
+ finally {
803
+ owned.deadline?.dispose();
804
+ this.walkDeadlines.delete(scopedOptions);
805
+ }
806
+ }
807
+ async stat(path, options = {}) {
808
+ validatePath(path, options.pathLimits);
809
+ return this.statWithAncestors(path, options);
810
+ }
811
+ async statWithAncestors(path, options) {
812
+ const normalized = normalize(path);
813
+ const collection = requiresCollection(path);
814
+ return this.withWalkDeadline(options, async (scopedOptions) => {
815
+ try {
816
+ const stat = (await this.entries(normalized, "0", scopedOptions, collection)).get(normalized);
817
+ if (collection && stat.type !== "directory")
818
+ fail("ENOTDIR", "stat", path);
819
+ return stat;
820
+ }
821
+ catch (error) {
822
+ if (!isFsError(error, "ENOENT"))
823
+ throw error;
824
+ let parent = "";
825
+ for (const segment of normalized.slice(1).split("/").slice(0, -1)) {
826
+ parent += `/${segment}`;
827
+ const ancestor = (await this.entries(parent, "0", scopedOptions)).get(parent);
828
+ if (ancestor.type !== "directory")
829
+ fail("ENOTDIR", "stat", path);
830
+ }
831
+ throw error;
832
+ }
833
+ });
834
+ }
835
+ async lstat(path, options = {}) {
836
+ return this.stat(path, options);
837
+ }
838
+ async readdir(path, options = {}) {
839
+ let limit;
840
+ if (options.maxEntries !== undefined) {
841
+ if (options.signal?.aborted)
842
+ fail("ECANCELED", "readdir", path);
843
+ limit = directoryEntryLimit(options, path);
844
+ }
845
+ const normalized = normalize(path);
846
+ const entries = await this.entries(normalized, "1", options, true, limit);
847
+ if (entries.get(normalized).type !== "directory")
848
+ fail("ENOTDIR", "readdir", path);
849
+ return [...entries].filter(([member]) => member !== normalized)
850
+ .map(([member, stat]) => ({ name: member.slice(member.lastIndexOf("/") + 1), type: stat.type }))
851
+ .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
852
+ }
853
+ async readFile(path, options = {}) {
854
+ const normalized = normalize(path);
855
+ const limit = Math.min(this.maxResponseBytes, options.maxBytes === undefined ? this.maxResponseBytes : positive(options.maxBytes, "maxBytes", true, true));
856
+ if ((await this.stat(path, options)).type === "directory")
857
+ fail("EISDIR", "readFile", path);
858
+ return this.request("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async (response, signal) => {
859
+ if (response.status !== 200)
860
+ this.httpError(response.status, "GET", path);
861
+ return this.bytes(response, limit, signal);
862
+ });
863
+ }
864
+ async *readStream(path, options = {}) {
865
+ const normalized = normalize(path);
866
+ const start = positive(options.start ?? 0, "start", true);
867
+ const end = options.endExclusive === undefined ? Infinity : positive(options.endExclusive, "endExclusive", true);
868
+ const chunkSize = positive(options.chunkSize ?? 64 * 1024, "chunkSize");
869
+ if (end < start)
870
+ fail("EINVAL", "readStream", path, "endExclusive precedes start");
871
+ if ((await this.stat(path, options)).type === "directory")
872
+ fail("EISDIR", "readStream", path);
873
+ yield* this.requestStream("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async function* (response, signal) {
874
+ if (response.status !== 200)
875
+ this.httpError(response.status, "GET", path);
876
+ let position = 0;
877
+ for await (const chunk of this.bodyChunks(response, this.maxResponseBytes, signal)) {
878
+ const first = Math.max(0, start - position);
879
+ const last = Math.min(chunk.byteLength, end - position);
880
+ for (let offset = first; offset < last; offset += chunkSize) {
881
+ signal.throwIfAborted();
882
+ yield chunk.slice(offset, Math.min(last, offset + chunkSize));
883
+ }
884
+ position += chunk.byteLength;
885
+ if (position >= end)
886
+ return;
887
+ }
888
+ }.bind(this));
889
+ }
890
+ async prepareWrite(path, options) {
891
+ validatePath(path, options.pathLimits);
892
+ const normalized = normalize(path);
893
+ if (options.mode !== undefined)
894
+ this.unsupported("writeFile mode", path);
895
+ if (options.flag !== undefined && !["w", "wx", "a", "ax"].includes(options.flag))
896
+ fail("EINVAL", "writeFile", path);
897
+ if (options.signal?.aborted)
898
+ fail("ECANCELED", "writeFile", path);
899
+ if (normalized === "/")
900
+ fail("EISDIR", "writeFile", path);
901
+ return this.withWalkDeadline(options, async (scopedOptions) => {
902
+ if (requiresCollection(path)) {
903
+ await this.stat(path, scopedOptions);
904
+ fail("EISDIR", "writeFile", path);
905
+ }
906
+ let parent = "";
907
+ for (const segment of normalized.slice(1).split("/").slice(0, -1)) {
908
+ parent += `/${segment}`;
909
+ await this.stat(`${parent}/`, scopedOptions);
910
+ }
911
+ const exclusive = options.flag === "wx" || options.flag === "ax";
912
+ const existing = exclusive ? undefined : await this.maybeStat(normalized, scopedOptions);
913
+ if (existing?.type === "directory")
914
+ fail("EISDIR", "writeFile", path);
915
+ let prefix = new Uint8Array();
916
+ const headers = { "Content-Type": "application/octet-stream" };
917
+ if (exclusive || (options.flag === "a" && !existing))
918
+ headers["If-None-Match"] = "*";
919
+ if (options.flag === "a" && existing) {
920
+ const snapshot = await this.request("GET", normalized, scopedOptions, { headers: { "Accept-Encoding": "identity" } }, async (response, signal) => {
921
+ if (response.status !== 200)
922
+ this.httpError(response.status, "GET", path);
923
+ const etag = strongEtag(response.headers.get("ETag"), path);
924
+ if (response.headers.get("Content-Encoding") && response.headers.get("Content-Encoding").toLowerCase() !== "identity") {
925
+ fail("ENOTSUP", "appendFile", path, "conditional append requires an identity representation");
926
+ }
927
+ return { etag, data: await this.bytes(response, this.maxResponseBytes, signal) };
928
+ });
929
+ prefix = snapshot.data;
930
+ headers["If-Match"] = snapshot.etag;
931
+ }
932
+ return { normalized, headers, prefix };
933
+ });
934
+ }
935
+ async writeFile(path, data, options = {}) {
936
+ if (!(data instanceof Uint8Array))
937
+ fail("EINVAL", "writeFile", path, "data must be Uint8Array");
938
+ const { normalized, headers, prefix } = await this.prepareWrite(path, options);
939
+ if (options.flag === "a" && prefix.byteLength + data.byteLength > this.maxResponseBytes)
940
+ fail("EFBIG", "appendFile", path);
941
+ const body = new Uint8Array(prefix.byteLength + data.byteLength);
942
+ body.set(prefix);
943
+ body.set(data, prefix.byteLength);
944
+ await this.mutation("PUT", normalized, options, {
945
+ headers, body,
946
+ });
947
+ }
948
+ async writeStream(path, source, options = {}) {
949
+ if (options.signal?.aborted)
950
+ fail("ECANCELED", "writeStream", path);
951
+ if (this.requestStreamSupport === false
952
+ || (this.requestStreamSupport === "native" && !nativeRequestStreamsSupported(this.base.href))) {
953
+ fail("ENOTSUP", "writeStream", path, "transport does not declare supported request streams");
954
+ }
955
+ const { normalized, headers, prefix } = await this.prepareWrite(path, options);
956
+ const deadline = createRequestTimeout(this.timeoutMs);
957
+ const timeout = deadline.signal;
958
+ try {
959
+ const controller = new AbortController();
960
+ const signalScope = composeAbortSignals([timeout, controller.signal, ...(options.signal ? [options.signal] : [])]);
961
+ const signal = signalScope.signal;
962
+ const limit = this.maxResponseBytes;
963
+ const chunks = (async function* () {
964
+ let size = prefix.byteLength;
965
+ if (prefix.byteLength)
966
+ yield prefix;
967
+ for await (const chunk of readBytes(source, signal)) {
968
+ size += chunk.byteLength;
969
+ if (size > limit)
970
+ fail("EFBIG", "writeStream", path, "upload exceeds byte limit");
971
+ yield new Uint8Array(chunk);
972
+ }
973
+ })();
974
+ let finished = false;
975
+ let uploadError;
976
+ let body;
977
+ try {
978
+ body = new ReadableStream({
979
+ async pull(destination) {
980
+ try {
981
+ const result = await chunks.next();
982
+ if (result.done) {
983
+ finished = true;
984
+ destination.close();
985
+ }
986
+ else
987
+ destination.enqueue(result.value);
988
+ }
989
+ catch (cause) {
990
+ uploadError = cause;
991
+ destination.error(cause);
992
+ }
993
+ },
994
+ cancel(reason) { controller.abort(reason); void chunks.return(undefined).catch(() => { }); },
995
+ }, { highWaterMark: 0 });
996
+ try {
997
+ const request = { headers, body, duplex: "half" };
998
+ await this.mutation("PUT", normalized, options, request);
999
+ if (uploadError !== undefined)
1000
+ throw uploadError;
1001
+ if (!finished)
1002
+ fail("EIO", "writeStream", path, "server responded before consuming the upload");
1003
+ }
1004
+ catch (cause) {
1005
+ if (options.signal?.aborted)
1006
+ throw new FsError("ECANCELED", { syscall: "writeStream", path, cause });
1007
+ if (timeout.aborted)
1008
+ throw new FsError("ETIMEDOUT", { syscall: "writeStream", path, cause });
1009
+ if (isFsError(uploadError))
1010
+ throw uploadError;
1011
+ if (isFsError(cause))
1012
+ throw cause;
1013
+ throw new FsError("EIO", { syscall: "writeStream", path, cause });
1014
+ }
1015
+ }
1016
+ finally {
1017
+ controller.abort();
1018
+ signalScope.dispose();
1019
+ if (body && !body.locked)
1020
+ void body.cancel().catch(() => { });
1021
+ void chunks.return(undefined).catch(() => { });
1022
+ }
1023
+ }
1024
+ finally {
1025
+ deadline.dispose();
1026
+ }
1027
+ }
1028
+ async appendFile(path, data, options = {}) {
1029
+ await this.writeFile(path, data, { ...options, flag: "a" });
1030
+ }
1031
+ async mkdir(path, options = {}) {
1032
+ const normalized = normalize(path);
1033
+ if (options.mode !== undefined)
1034
+ this.unsupported("mkdir mode", path);
1035
+ if (!options.recursive) {
1036
+ if (normalized === "/")
1037
+ fail("EEXIST", "mkdir", path);
1038
+ return this.mutation("MKCOL", normalized, options, {}, true);
1039
+ }
1040
+ let current = "";
1041
+ for (const segment of normalized.slice(1).split("/").filter(Boolean)) {
1042
+ current += `/${segment}`;
1043
+ const existing = await this.maybeStat(`${current}/`, options);
1044
+ if (existing) {
1045
+ if (existing.type !== "directory")
1046
+ fail("ENOTDIR", "mkdir", current);
1047
+ continue;
1048
+ }
1049
+ try {
1050
+ await this.mutation("MKCOL", current, options, {}, true);
1051
+ }
1052
+ catch (error) {
1053
+ if (!isFsError(error, "EEXIST"))
1054
+ throw error;
1055
+ if ((await this.stat(`${current}/`, options)).type !== "directory")
1056
+ fail("ENOTDIR", "mkdir", current);
1057
+ }
1058
+ }
1059
+ if (normalized === "/")
1060
+ await this.stat(normalized, options);
1061
+ }
1062
+ async rmdir(path, options = {}) {
1063
+ try {
1064
+ const normalized = normalize(path);
1065
+ if (options.signal?.aborted)
1066
+ fail("ECANCELED", "rmdir", path);
1067
+ if (normalized === "/")
1068
+ fail("EBUSY", "rmdir", path, "cannot remove WebDAV root");
1069
+ const stat = await this.stat(path, options);
1070
+ if (options.signal?.aborted)
1071
+ fail("ECANCELED", "rmdir", path);
1072
+ if (stat.type !== "directory")
1073
+ fail("ENOTDIR", "rmdir", path);
1074
+ if (this.atomicEmptyDirectory) {
1075
+ await this.atomicRmdir(normalized, path, options);
1076
+ return;
1077
+ }
1078
+ const entries = await this.readdir(path, options);
1079
+ if (options.signal?.aborted)
1080
+ fail("ECANCELED", "rmdir", path);
1081
+ if (entries.length)
1082
+ fail("ENOTEMPTY", "rmdir", path);
1083
+ this.unsupported("rmdir", path);
1084
+ }
1085
+ catch (error) {
1086
+ if (isFsError(error) && (error.syscall !== "rmdir" || error.path !== path)) {
1087
+ throw new FsError(error.code, { syscall: "rmdir", path, cause: error });
1088
+ }
1089
+ throw error;
1090
+ }
1091
+ }
1092
+ async atomicRmdir(normalized, path, options) {
1093
+ const deadline = createRequestTimeout(this.timeoutMs);
1094
+ const timeout = deadline.signal;
1095
+ try {
1096
+ const signalScope = options.signal ? composeAbortSignals([timeout, options.signal]) : undefined;
1097
+ const signal = signalScope?.signal ?? timeout;
1098
+ try {
1099
+ signal.throwIfAborted();
1100
+ const receipt = await new Promise((resolve, reject) => {
1101
+ const abort = () => {
1102
+ signal.removeEventListener("abort", abort);
1103
+ reject(signal.reason);
1104
+ };
1105
+ signal.addEventListener("abort", abort, { once: true });
1106
+ try {
1107
+ Promise.resolve(this.atomicEmptyDirectory.removeEmptyDirectory(Object.freeze({
1108
+ operation: "atomic-empty-rmdir/v1", namespaceUrl: this.base.href, path: normalized, signal,
1109
+ }))).then((result) => {
1110
+ signal.removeEventListener("abort", abort);
1111
+ resolve(result);
1112
+ }, (error) => {
1113
+ signal.removeEventListener("abort", abort);
1114
+ reject(error);
1115
+ });
1116
+ }
1117
+ catch (error) {
1118
+ signal.removeEventListener("abort", abort);
1119
+ reject(error);
1120
+ }
1121
+ });
1122
+ signal.throwIfAborted();
1123
+ if (!receipt || receipt.operation !== "atomic-empty-rmdir/v1" || receipt.namespaceUrl !== this.base.href
1124
+ || receipt.path !== normalized || receipt.outcome !== "removed") {
1125
+ fail("EIO", "rmdir", path, "atomic empty-directory receipt mismatch; removal outcome is uncertain");
1126
+ }
1127
+ }
1128
+ catch (cause) {
1129
+ if (options.signal?.aborted)
1130
+ throw new FsError("ECANCELED", { syscall: "rmdir", path, cause });
1131
+ if (timeout.aborted)
1132
+ throw new FsError("ETIMEDOUT", { syscall: "rmdir", path, cause });
1133
+ if (isFsError(cause))
1134
+ throw cause;
1135
+ const code = cause && typeof cause === "object" && "code" in cause && isErrnoCode(cause.code) ? cause.code : "EIO";
1136
+ throw new FsError(code, { syscall: "rmdir", path, cause });
1137
+ }
1138
+ finally {
1139
+ signalScope?.dispose();
1140
+ }
1141
+ }
1142
+ finally {
1143
+ deadline.dispose();
1144
+ }
1145
+ }
1146
+ async rm(path, options = {}) {
1147
+ const normalized = normalize(path);
1148
+ if (normalized === "/")
1149
+ fail("EBUSY", "rm", path, "cannot remove WebDAV root");
1150
+ try {
1151
+ const stat = await this.stat(path, options);
1152
+ if (stat.type === "directory" && !options.recursive)
1153
+ this.unsupported("nonrecursive directory removal", path);
1154
+ await this.mutation("DELETE", normalized, options, {}, stat.type === "directory");
1155
+ }
1156
+ catch (error) {
1157
+ if (!(options.force && isFsError(error, "ENOENT")))
1158
+ throw error;
1159
+ }
1160
+ }
1161
+ async guardTransferIdentity(source, destination, options) {
1162
+ if (!this.configuredComparison && this.compareEntry === originalWebDavComparison)
1163
+ return;
1164
+ const comparison = await compareEntries(this, source, this, destination, options);
1165
+ if (source === destination) {
1166
+ if (comparison === "distinct")
1167
+ fail("EIO", "compareEntry", source, "comparison contradicts identical WebDAV paths");
1168
+ return;
1169
+ }
1170
+ if (comparison === "same")
1171
+ fail("EINVAL", "compareEntry", destination, "source and destination refer to the same entry");
1172
+ if (comparison === "unknown")
1173
+ fail("ENOTSUP", "compareEntry", destination, "configured authority cannot establish distinct entries");
1174
+ }
1175
+ async transfer(method, source, destination, options) {
1176
+ const from = normalize(source);
1177
+ const to = normalize(destination);
1178
+ if (from === "/" || to === "/")
1179
+ fail("EBUSY", method, source, "cannot replace or transfer WebDAV root");
1180
+ const stat = await this.stat(source, options);
1181
+ if (method === "COPY" && stat.type === "directory")
1182
+ fail("EISDIR", "copyFile", source);
1183
+ if (requiresCollection(destination) && stat.type !== "directory")
1184
+ fail("ENOTDIR", method, destination);
1185
+ if (from === to) {
1186
+ await this.guardTransferIdentity(from, to, options);
1187
+ if (method === "MOVE")
1188
+ return;
1189
+ fail(options.exclusive ? "EEXIST" : "EINVAL", "copyFile", source, "source and destination are identical");
1190
+ }
1191
+ if (stat.type === "directory" && to.startsWith(`${from}/`))
1192
+ fail("EINVAL", method, destination, "cannot move directory into itself");
1193
+ const existing = await this.maybeStat(destination, options);
1194
+ if (existing)
1195
+ await this.guardTransferIdentity(from, to, options);
1196
+ if (existing && options.exclusive)
1197
+ fail("EEXIST", method, destination);
1198
+ const checkType = (target) => {
1199
+ if (target.type === "directory" && stat.type === "file")
1200
+ fail("EISDIR", method, destination);
1201
+ if (target.type !== "directory" && stat.type === "directory")
1202
+ fail("ENOTDIR", method, destination);
1203
+ };
1204
+ if (existing)
1205
+ checkType(existing);
1206
+ if (stat.type === "directory" && from.startsWith(`${to}/`))
1207
+ fail("EINVAL", method, destination, "cannot replace an ancestor of the source");
1208
+ const destinationUrl = this.url(to, stat.type === "directory");
1209
+ const headers = {
1210
+ Destination: destinationUrl, Overwrite: existing ? "T" : "F",
1211
+ Depth: method === "COPY" ? "0" : "infinity",
1212
+ };
1213
+ const sourceEtag = this.etags.get(stat);
1214
+ if (existing && sourceEtag && /^"[\x21\x23-\x7e\x80-\xff]*"$/.test(sourceEtag))
1215
+ headers["If-Match"] = sourceEtag;
1216
+ const mutate = () => this.mutation(method, from, options, { headers }, stat.type === "directory");
1217
+ if (!existing)
1218
+ return mutate();
1219
+ if (this.overwritePolicy === "etag" && existing.type === "file") {
1220
+ headers.If = `<${destinationUrl}> ([${strongEtag(this.etags.get(existing), destination)}])`;
1221
+ return mutate();
1222
+ }
1223
+ await this.withDestinationLock(to, existing, options, async (token) => {
1224
+ const locked = await this.stat(destination, options);
1225
+ checkType(locked);
1226
+ if (locked.type === "directory" && (await this.readdir(destination, options)).length)
1227
+ fail("ENOTEMPTY", method, destination);
1228
+ headers.If = `<${destinationUrl}> (${token})`;
1229
+ await mutate();
1230
+ });
1231
+ }
1232
+ async withDestinationLock(path, stat, options, operation) {
1233
+ let token;
1234
+ const collection = stat.type === "directory";
1235
+ const validator = this.etags.get(stat);
1236
+ const unlock = async (value) => {
1237
+ await this.request("UNLOCK", path, {}, { headers: { "Lock-Token": value } }, async (response) => {
1238
+ if (response.status !== 204)
1239
+ this.httpError(response.status, "UNLOCK", path);
1240
+ }, collection).catch(() => { });
1241
+ };
1242
+ try {
1243
+ await this.request("LOCK", path, options, {
1244
+ headers: { "Content-Type": "application/xml; charset=utf-8", Depth: "infinity", Timeout: "Second-60",
1245
+ "If-Match": validator && /^"[\x21\x23-\x7e\x80-\xff]*"$/.test(validator) ? validator : "*" },
1246
+ body: lockBody,
1247
+ }, async (response, signal) => {
1248
+ if (response.status === 207)
1249
+ return this.lockFailure(response, signal, path);
1250
+ if (response.status !== 200)
1251
+ this.httpError(response.status, "LOCK", path);
1252
+ const root = await this.xml(response, signal);
1253
+ const discovery = davChild(root, "lockdiscovery");
1254
+ const active = discovery && davChild(discovery, "activelock");
1255
+ const scope = active && davChild(active, "lockscope");
1256
+ const type = active && davChild(active, "locktype");
1257
+ const depth = active && davChild(active, "depth");
1258
+ const lockToken = active && davChild(active, "locktoken");
1259
+ const href = lockToken && davChild(lockToken, "href");
1260
+ const lockRoot = active && davChild(active, "lockroot");
1261
+ const rootHref = lockRoot && davChild(lockRoot, "href");
1262
+ const timeout = active && davChild(active, "timeout");
1263
+ if (root.namespace !== "DAV:" || root.localName !== "prop" || !scope || !davChild(scope, "exclusive")
1264
+ || davChild(scope, "shared")
1265
+ || !type || !davChild(type, "write") || !depth || scalar(depth) !== "infinity"
1266
+ || !href || `<${scalar(href)}>` !== token
1267
+ || (lockRoot !== undefined && (!rootHref || this.hrefPath(scalar(rootHref)) !== path))
1268
+ || !timeout || !/^Second-[1-9]\d*$/i.test(scalar(timeout)) || Number(scalar(timeout).slice(7)) > 4_294_967_295) {
1269
+ fail("ENOTSUP", "LOCK", path, "server did not grant a finite exclusive depth-infinity write lock");
1270
+ }
1271
+ }, collection, (response, late) => {
1272
+ if (response.status !== 200)
1273
+ return;
1274
+ const header = response.headers.get("Lock-Token");
1275
+ if (!header || !/^<[a-z][a-z0-9+.-]*:[^<>\s\x00-\x20\x7f]+>$/i.test(header))
1276
+ throw new Error("invalid LOCK token");
1277
+ if (late)
1278
+ void unlock(header);
1279
+ else
1280
+ token = header;
1281
+ });
1282
+ await operation(token);
1283
+ }
1284
+ finally {
1285
+ if (token)
1286
+ await unlock(token);
1287
+ }
1288
+ }
1289
+ async rename(source, destination, options = {}) {
1290
+ if (options.noReplace) {
1291
+ options.signal?.throwIfAborted();
1292
+ fail("ENOTSUP", "rename", source, "atomic no-replace rename is unsupported");
1293
+ }
1294
+ await this.transfer("MOVE", source, destination, options);
1295
+ }
1296
+ async copyFile(source, destination, options = {}) {
1297
+ await this.transfer("COPY", source, destination, options);
1298
+ }
1299
+ async realpath(path, options = {}) {
1300
+ const normalized = normalize(path);
1301
+ await this.stat(path, options);
1302
+ return normalized;
1303
+ }
1304
+ async access(path, mode = 0, options = {}) {
1305
+ if (!Number.isInteger(mode) || mode < 0 || mode > 7)
1306
+ fail("EINVAL", "access", path);
1307
+ if (options.signal?.aborted)
1308
+ fail("ECANCELED", "access", path);
1309
+ if (mode & 2)
1310
+ this.unsupported("access write/execute permission checks", path);
1311
+ const stat = await this.stat(path, options);
1312
+ if (options.signal?.aborted)
1313
+ fail("ECANCELED", "access", path);
1314
+ if ((mode & 1) && stat.type !== "directory")
1315
+ this.unsupported("access execute permission checks", path);
1316
+ if (mode & 4) {
1317
+ if (stat.type === "directory")
1318
+ await this.readdir(path, options);
1319
+ else
1320
+ await this.request("GET", normalize(path), options, { headers: { "Accept-Encoding": "identity" } }, async (response) => {
1321
+ if (response.status !== 200)
1322
+ this.httpError(response.status, "GET", path);
1323
+ });
1324
+ }
1325
+ if (options.signal?.aborted)
1326
+ fail("ECANCELED", "access", path);
1327
+ }
1328
+ async readlink(path, _options = {}) { return this.unsupported("readlink", path); }
1329
+ async symlink(_target, path, _options = {}) { this.unsupported("symlink", path); }
1330
+ async link(_source, path, _options = {}) { this.unsupported("link", path); }
1331
+ async chmod(path, _mode, _options = {}) { this.unsupported("chmod", path); }
1332
+ async utimes(path, atimeMs, mtimeMs, options = {}) {
1333
+ const normalized = normalize(path);
1334
+ if (![atimeMs, mtimeMs].every(value => Number.isFinite(value) && Math.abs(value) <= 8.64e15))
1335
+ fail("EINVAL", "utimes", path);
1336
+ const stat = await this.stat(path, options);
1337
+ const etag = strongEtag(this.etags.get(stat), path);
1338
+ const metadata = JSON.stringify({ version: 1, etag, type: stat.type, atimeMs, mtimeMs })
1339
+ .replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
1340
+ await this.request("PROPPATCH", normalized, options, {
1341
+ headers: { "Content-Type": "application/xml; charset=utf-8", "If-Match": etag },
1342
+ body: `<d:propertyupdate xmlns:d="DAV:" xmlns:v="${timestampNamespace}"><d:set><d:prop>`
1343
+ + `<v:timestamps>${metadata}</v:timestamps></d:prop></d:set></d:propertyupdate>`,
1344
+ }, async (response, signal) => {
1345
+ if (response.status !== 207)
1346
+ this.httpError(response.status, "PROPPATCH", path);
1347
+ const entries = await this.multistatus(response, signal);
1348
+ if (entries.length !== 1)
1349
+ throw new Error("PROPPATCH must report the requested resource");
1350
+ const entry = entries[0];
1351
+ const href = davChild(entry, "href");
1352
+ if (!href || this.hrefPath(scalar(href)) !== normalized)
1353
+ fail("EACCES", "PROPPATCH", path, "unexpected response resource");
1354
+ const status = davChild(entry, "status");
1355
+ if (status)
1356
+ this.httpError(statusCode(status), "PROPPATCH", path);
1357
+ const propstats = davChildren(entry, "propstat");
1358
+ if (propstats.length !== 1)
1359
+ throw new Error("PROPPATCH must report the requested property");
1360
+ const prop = davChild(propstats[0], "prop");
1361
+ const result = davChild(propstats[0], "status");
1362
+ const property = prop?.children[0];
1363
+ if (!result || prop?.children.length !== 1 || property?.namespace !== timestampNamespace
1364
+ || property.localName !== "timestamps")
1365
+ throw new Error("invalid PROPPATCH property result");
1366
+ const code = statusCode(result);
1367
+ if (code !== 200)
1368
+ this.httpError(code, "PROPPATCH", path);
1369
+ }, stat.type === "directory");
1370
+ const updated = await this.stat(path, options);
1371
+ if (updated.atimeMs !== atimeMs || updated.mtimeMs !== mtimeMs) {
1372
+ fail("EAGAIN", "utimes", path, "server did not retain requested timestamps for the current representation");
1373
+ }
1374
+ }
1375
+ async truncate(path, _length, _options = {}) { this.unsupported("truncate", path); }
1376
+ }
1377
+ const originalWebDavComparison = /* @__PURE__ */ (() => WebDavFileSystem.prototype.compareEntry)();