@poe-platform/safe-js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/dist/agent-defs/agents/claude-code.d.ts +2 -0
  4. package/dist/agent-defs/agents/claude-desktop.d.ts +2 -0
  5. package/dist/agent-defs/agents/codex.d.ts +2 -0
  6. package/dist/agent-defs/agents/cursor.d.ts +2 -0
  7. package/dist/agent-defs/agents/gemini-cli.d.ts +2 -0
  8. package/dist/agent-defs/agents/goose.d.ts +2 -0
  9. package/dist/agent-defs/agents/index.d.ts +10 -0
  10. package/dist/agent-defs/agents/kimi.d.ts +2 -0
  11. package/dist/agent-defs/agents/opencode.d.ts +2 -0
  12. package/dist/agent-defs/agents/pi.d.ts +2 -0
  13. package/dist/agent-defs/agents/poe-agent.d.ts +2 -0
  14. package/dist/agent-defs/capabilities.d.ts +14 -0
  15. package/dist/agent-defs/index.d.ts +6 -0
  16. package/dist/agent-defs/registry.d.ts +3 -0
  17. package/dist/agent-defs/specifier.d.ts +7 -0
  18. package/dist/agent-defs/types.d.ts +35 -0
  19. package/dist/agent-harness-tools/binary-exists.d.ts +10 -0
  20. package/dist/agent-harness-tools/execution-env.d.ts +108 -0
  21. package/dist/agent-harness-tools/hooks.d.ts +30 -0
  22. package/dist/agent-harness-tools/index.d.ts +18 -0
  23. package/dist/agent-harness-tools/log-stream.d.ts +28 -0
  24. package/dist/agent-harness-tools/participant.d.ts +10 -0
  25. package/dist/agent-harness-tools/paths.d.ts +14 -0
  26. package/dist/agent-harness-tools/plan-readiness.d.ts +7 -0
  27. package/dist/agent-harness-tools/plans.d.ts +35 -0
  28. package/dist/agent-harness-tools/poe-command-execution.d.ts +37 -0
  29. package/dist/agent-harness-tools/run-logs.d.ts +17 -0
  30. package/dist/agent-harness-tools/run-poe-command.d.ts +33 -0
  31. package/dist/agent-harness-tools/runner.d.ts +33 -0
  32. package/dist/agent-harness-tools/select-agent.d.ts +18 -0
  33. package/dist/agent-harness-tools/sequence.d.ts +7 -0
  34. package/dist/agent-harness-tools/skill-config.d.ts +1 -0
  35. package/dist/agent-harness-tools/stage.d.ts +22 -0
  36. package/dist/agent-harness-tools/workspace-transfer.d.ts +3 -0
  37. package/dist/agent-spawn/acp/middleware.d.ts +38 -0
  38. package/dist/agent-spawn/acp/types.d.ts +180 -0
  39. package/dist/agent-spawn/adapters/claude.d.ts +3 -0
  40. package/dist/agent-spawn/adapters/codex.d.ts +2 -0
  41. package/dist/agent-spawn/adapters/cursor.d.ts +2 -0
  42. package/dist/agent-spawn/adapters/index.d.ts +12 -0
  43. package/dist/agent-spawn/adapters/kimi.d.ts +2 -0
  44. package/dist/agent-spawn/adapters/native.d.ts +5 -0
  45. package/dist/agent-spawn/adapters/opencode.d.ts +2 -0
  46. package/dist/agent-spawn/adapters/pi.d.ts +2 -0
  47. package/dist/agent-spawn/environment.d.ts +2 -0
  48. package/dist/agent-spawn/parallel.d.ts +30 -0
  49. package/dist/agent-spawn/retry.d.ts +26 -0
  50. package/dist/agent-spawn/types.d.ts +272 -0
  51. package/dist/auth-store/create-secret-store.d.ts +2 -0
  52. package/dist/auth-store/encrypted-file-store.d.ts +47 -0
  53. package/dist/auth-store/index.d.ts +7 -0
  54. package/dist/auth-store/keychain-store.d.ts +25 -0
  55. package/dist/auth-store/provider-store.d.ts +14 -0
  56. package/dist/auth-store/types.d.ts +22 -0
  57. package/dist/config-mutations/execution/run-mutations.d.ts +7 -0
  58. package/dist/config-mutations/fs-utils.d.ts +18 -0
  59. package/dist/config-mutations/index.d.ts +8 -0
  60. package/dist/config-mutations/mutations/config-mutation.d.ts +47 -0
  61. package/dist/config-mutations/mutations/file-mutation.d.ts +62 -0
  62. package/dist/config-mutations/mutations/template-mutation.d.ts +40 -0
  63. package/dist/config-mutations/template/render.d.ts +6 -0
  64. package/dist/config-mutations/types.d.ts +168 -0
  65. package/dist/poe-code-config/compile/schema-compiler.d.ts +11 -0
  66. package/dist/poe-code-config/config.d.ts +2 -0
  67. package/dist/poe-code-config/configured-services.d.ts +29 -0
  68. package/dist/poe-code-config/index.d.ts +16 -0
  69. package/dist/poe-code-config/inspect.d.ts +13 -0
  70. package/dist/poe-code-config/memory.d.ts +14 -0
  71. package/dist/poe-code-config/merge-callbacks.d.ts +38 -0
  72. package/dist/poe-code-config/merge.d.ts +2 -0
  73. package/dist/poe-code-config/plan-scope.d.ts +8 -0
  74. package/dist/poe-code-config/provider-config.d.ts +16 -0
  75. package/dist/poe-code-config/resolve.d.ts +2 -0
  76. package/dist/poe-code-config/runtime.d.ts +114 -0
  77. package/dist/poe-code-config/schema.d.ts +14 -0
  78. package/dist/poe-code-config/state/fs.d.ts +24 -0
  79. package/dist/poe-code-config/state/index.d.ts +12 -0
  80. package/dist/poe-code-config/state/jobs.d.ts +34 -0
  81. package/dist/poe-code-config/state/templates.d.ts +18 -0
  82. package/dist/poe-code-config/store.d.ts +12 -0
  83. package/dist/poe-code-config/types.d.ts +88 -0
  84. package/dist/process-runner/docker/build-context.d.ts +5 -0
  85. package/dist/process-runner/docker/context.d.ts +3 -0
  86. package/dist/process-runner/docker/docker-execution-env.d.ts +28 -0
  87. package/dist/process-runner/docker/docker-runner.d.ts +2 -0
  88. package/dist/process-runner/docker/engine.d.ts +3 -0
  89. package/dist/process-runner/host/host-execution-env.d.ts +2 -0
  90. package/dist/process-runner/host/host-runner.d.ts +3 -0
  91. package/dist/process-runner/index.d.ts +10 -0
  92. package/dist/process-runner/testing/index.d.ts +2 -0
  93. package/dist/process-runner/testing/mock-runner.d.ts +3 -0
  94. package/dist/process-runner/types.d.ts +191 -0
  95. package/dist/process-runner/workspace-transfer.d.ts +61 -0
  96. package/dist/providers/auth/api-key.d.ts +5 -0
  97. package/dist/providers/auth/types.d.ts +14 -0
  98. package/dist/providers/compatibility.d.ts +4 -0
  99. package/dist/providers/index.d.ts +8 -0
  100. package/dist/providers/providers/anthropic.d.ts +1 -0
  101. package/dist/providers/providers/cloudflare.d.ts +1 -0
  102. package/dist/providers/providers/generated.d.ts +5 -0
  103. package/dist/providers/providers/openai.d.ts +1 -0
  104. package/dist/providers/providers/poe.d.ts +2 -0
  105. package/dist/providers/registry.d.ts +44 -0
  106. package/dist/providers/types.d.ts +54 -0
  107. package/dist/safe-fs/bridge/filesystem.d.ts +88 -0
  108. package/dist/safe-fs/bridge/index.d.ts +6 -0
  109. package/dist/safe-fs/bridge/types.d.ts +75 -0
  110. package/dist/safe-fs/config.d.ts +15 -0
  111. package/dist/safe-fs/config.node.d.ts +2 -0
  112. package/dist/safe-fs/contracts/errors.d.ts +50 -0
  113. package/dist/safe-fs/contracts/filesystem.d.ts +96 -0
  114. package/dist/safe-fs/contracts/index.d.ts +4 -0
  115. package/dist/safe-fs/contracts/io.d.ts +8 -0
  116. package/dist/safe-fs/contracts/path.d.ts +8 -0
  117. package/dist/safe-fs/contracts/portable-path.d.ts +13 -0
  118. package/dist/safe-fs/contracts/virtual-path.d.ts +7 -0
  119. package/dist/safe-fs/core.d.ts +12 -0
  120. package/dist/safe-fs/fs/memory/index.d.ts +58 -0
  121. package/dist/safe-fs/fs/mount/comparison.d.ts +19 -0
  122. package/dist/safe-fs/fs/mount/index.d.ts +44 -0
  123. package/dist/safe-fs/fs/overlay/index.d.ts +73 -0
  124. package/dist/safe-fs/fs/readonly/index.d.ts +31 -0
  125. package/dist/safe-fs/fs/real/index.d.ts +76 -0
  126. package/dist/safe-fs/fs/s3/filesystem.d.ts +93 -0
  127. package/dist/safe-fs/fs/s3/http/index.d.ts +2 -0
  128. package/dist/safe-fs/fs/s3/http/transport.d.ts +3 -0
  129. package/dist/safe-fs/fs/s3/http/types.d.ts +30 -0
  130. package/dist/safe-fs/fs/s3/index.d.ts +6 -0
  131. package/dist/safe-fs/fs/s3/mock.d.ts +48 -0
  132. package/dist/safe-fs/fs/s3/transport.d.ts +103 -0
  133. package/dist/safe-fs/fs/webdav/index.d.ts +2 -0
  134. package/dist/safe-fs/fs/webdav/webdav.d.ts +101 -0
  135. package/dist/safe-fs/index.d.ts +14 -0
  136. package/dist/safe-fs/node/filesystem.d.ts +11 -0
  137. package/dist/safe-fs/node/index.d.ts +2 -0
  138. package/dist/safe-fs/node-host.d.ts +1 -0
  139. package/dist/safe-fs/node-unavailable.d.ts +1 -0
  140. package/dist/safe-fs/platform/browser.d.ts +12 -0
  141. package/dist/safe-fs/platform/node.d.ts +13 -0
  142. package/dist/safe-js/browser/chunks/chunk-QGN6DKT5.js +4335 -0
  143. package/dist/safe-js/browser/chunks/chunk-QGN6DKT5.js.map +7 -0
  144. package/dist/safe-js/browser/safe-fs-core.js +67 -0
  145. package/dist/safe-js/browser/safe-fs-core.js.map +7 -0
  146. package/dist/safe-js/browser/safe-fs.js +67 -0
  147. package/dist/safe-js/browser/safe-fs.js.map +7 -0
  148. package/dist/safe-js/chunks/chunk-2QRYPKQQ.js +4315 -0
  149. package/dist/safe-js/chunks/chunk-2QRYPKQQ.js.map +7 -0
  150. package/dist/safe-js/chunks/chunk-4YIDX44G.js +40 -0
  151. package/dist/safe-js/chunks/chunk-4YIDX44G.js.map +7 -0
  152. package/dist/safe-js/chunks/chunk-7M2SJPSZ.js +2613 -0
  153. package/dist/safe-js/chunks/chunk-7M2SJPSZ.js.map +7 -0
  154. package/dist/safe-js/chunks/chunk-DC5B3GYM.js +8248 -0
  155. package/dist/safe-js/chunks/chunk-DC5B3GYM.js.map +7 -0
  156. package/dist/safe-js/chunks/chunk-Z3D76J4U.js +30732 -0
  157. package/dist/safe-js/chunks/chunk-Z3D76J4U.js.map +7 -0
  158. package/dist/safe-js/cli.d.ts +36 -0
  159. package/dist/safe-js/cli.js +724 -0
  160. package/dist/safe-js/cli.js.map +7 -0
  161. package/dist/safe-js/core.d.ts +6 -0
  162. package/dist/safe-js/core.js +14 -0
  163. package/dist/safe-js/core.js.map +7 -0
  164. package/dist/safe-js/dump.d.ts +2 -0
  165. package/dist/safe-js/error/format.d.ts +15 -0
  166. package/dist/safe-js/error/shape.d.ts +28 -0
  167. package/dist/safe-js/index.d.ts +56 -0
  168. package/dist/safe-js/index.js +454 -0
  169. package/dist/safe-js/index.js.map +7 -0
  170. package/dist/safe-js/interp/arguments.d.ts +10 -0
  171. package/dist/safe-js/interp/async.d.ts +56 -0
  172. package/dist/safe-js/interp/budget.d.ts +95 -0
  173. package/dist/safe-js/interp/exceptions.d.ts +45 -0
  174. package/dist/safe-js/interp/generator.d.ts +16 -0
  175. package/dist/safe-js/interp/globals/console-json.d.ts +14 -0
  176. package/dist/safe-js/interp/host-bridge.d.ts +29 -0
  177. package/dist/safe-js/interp/host-call.d.ts +130 -0
  178. package/dist/safe-js/interp/interpreter.d.ts +63 -0
  179. package/dist/safe-js/interp/promise-replay.d.ts +70 -0
  180. package/dist/safe-js/interp/regex/compile-guard.d.ts +33 -0
  181. package/dist/safe-js/interp/regex/parse.d.ts +62 -0
  182. package/dist/safe-js/interp/scope.d.ts +43 -0
  183. package/dist/safe-js/interp/values.d.ts +136 -0
  184. package/dist/safe-js/lint/index.d.ts +38 -0
  185. package/dist/safe-js/lint/rules/AS-export-import-meta.d.ts +21 -0
  186. package/dist/safe-js/lint/rules/module-registry.d.ts +33 -0
  187. package/dist/safe-js/lint.d.ts +1 -0
  188. package/dist/safe-js/loader/extract-block.d.ts +10 -0
  189. package/dist/safe-js/loader/find-exported.d.ts +2 -0
  190. package/dist/safe-js/loader/frontmatter.d.ts +5 -0
  191. package/dist/safe-js/migrate.d.ts +33 -0
  192. package/dist/safe-js/migration-file.d.ts +55 -0
  193. package/dist/safe-js/modules/agent.d.ts +132 -0
  194. package/dist/safe-js/modules/env.d.ts +14 -0
  195. package/dist/safe-js/modules/fail.d.ts +6 -0
  196. package/dist/safe-js/modules/fs-config.d.ts +12 -0
  197. package/dist/safe-js/modules/fs.d.ts +82 -0
  198. package/dist/safe-js/modules/harness.d.ts +19 -0
  199. package/dist/safe-js/modules/log.d.ts +16 -0
  200. package/dist/safe-js/modules/mcp-transport.d.ts +26 -0
  201. package/dist/safe-js/modules/mcp.d.ts +63 -0
  202. package/dist/safe-js/modules/metric.d.ts +4 -0
  203. package/dist/safe-js/modules/registry.d.ts +16 -0
  204. package/dist/safe-js/modules/time.d.ts +12 -0
  205. package/dist/safe-js/observability/otel.d.ts +21 -0
  206. package/dist/safe-js/output-stream.d.ts +12 -0
  207. package/dist/safe-js/parse/parse-export.d.ts +36 -0
  208. package/dist/safe-js/parse/parser.d.ts +356 -0
  209. package/dist/safe-js/parse/tokenizer.d.ts +26 -0
  210. package/dist/safe-js/parse.d.ts +4 -0
  211. package/dist/safe-js/random.d.ts +9 -0
  212. package/dist/safe-js/restore.d.ts +26 -0
  213. package/dist/safe-js/run.d.ts +74 -0
  214. package/dist/safe-js/runner/run-harness.d.ts +32 -0
  215. package/dist/safe-js/safe-fs-core.js +130 -0
  216. package/dist/safe-js/safe-fs-core.js.map +7 -0
  217. package/dist/safe-js/safe-fs-node.js +94 -0
  218. package/dist/safe-js/safe-fs-node.js.map +7 -0
  219. package/dist/safe-js/safe-fs.js +94 -0
  220. package/dist/safe-js/safe-fs.js.map +7 -0
  221. package/dist/safe-js/snapshot/arguments.d.ts +17 -0
  222. package/dist/safe-js/snapshot/backend.d.ts +19 -0
  223. package/dist/safe-js/snapshot/dump-format.d.ts +7 -0
  224. package/dist/safe-js/snapshot/dump.d.ts +23 -0
  225. package/dist/safe-js/snapshot/float32array.d.ts +12 -0
  226. package/dist/safe-js/snapshot/migration.d.ts +35 -0
  227. package/dist/safe-js/snapshot/policy.d.ts +49 -0
  228. package/dist/safe-js/snapshot/replay-data.d.ts +71 -0
  229. package/dist/safe-js/snapshot/validation.d.ts +15 -0
  230. package/dist/task-list/backends/gh-issues-client.d.ts +19 -0
  231. package/dist/task-list/backends/gh-issues-sync.d.ts +46 -0
  232. package/dist/task-list/backends/gh-issues.d.ts +21 -0
  233. package/dist/task-list/index.d.ts +9 -0
  234. package/dist/task-list/move.d.ts +2 -0
  235. package/dist/task-list/open.d.ts +5 -0
  236. package/dist/task-list/state-machine.d.ts +16 -0
  237. package/dist/task-list/state.d.ts +29 -0
  238. package/dist/task-list/types.d.ts +206 -0
  239. package/dist/tiny-mcp-client/index.d.ts +662 -0
  240. package/dist/tiny-stdio-mcp-server/content/audio.d.ts +15 -0
  241. package/dist/tiny-stdio-mcp-server/content/convert.d.ts +16 -0
  242. package/dist/tiny-stdio-mcp-server/content/file-type.d.ts +11 -0
  243. package/dist/tiny-stdio-mcp-server/content/file.d.ts +28 -0
  244. package/dist/tiny-stdio-mcp-server/content/image.d.ts +15 -0
  245. package/dist/tiny-stdio-mcp-server/content/index.d.ts +7 -0
  246. package/dist/tiny-stdio-mcp-server/content/mime.d.ts +7 -0
  247. package/dist/tiny-stdio-mcp-server/content/remote.d.ts +5 -0
  248. package/dist/tiny-stdio-mcp-server/index.d.ts +9 -0
  249. package/dist/tiny-stdio-mcp-server/schema.d.ts +20 -0
  250. package/dist/tiny-stdio-mcp-server/server.d.ts +35 -0
  251. package/dist/tiny-stdio-mcp-server/types.d.ts +247 -0
  252. package/dist/toolcraft-design/acp/components.d.ts +19 -0
  253. package/dist/toolcraft-design/acp/index.d.ts +4 -0
  254. package/dist/toolcraft-design/acp/writer.d.ts +13 -0
  255. package/dist/toolcraft-design/components/browser.d.ts +15 -0
  256. package/dist/toolcraft-design/components/catalog.d.ts +26 -0
  257. package/dist/toolcraft-design/components/color.d.ts +31 -0
  258. package/dist/toolcraft-design/components/command-errors.d.ts +18 -0
  259. package/dist/toolcraft-design/components/detail-card.d.ts +22 -0
  260. package/dist/toolcraft-design/components/file-changes.d.ts +16 -0
  261. package/dist/toolcraft-design/components/help-formatter-plain.d.ts +5 -0
  262. package/dist/toolcraft-design/components/help-formatter.d.ts +50 -0
  263. package/dist/toolcraft-design/components/inspector-card.d.ts +21 -0
  264. package/dist/toolcraft-design/components/logger.d.ts +11 -0
  265. package/dist/toolcraft-design/components/resource-browser.d.ts +21 -0
  266. package/dist/toolcraft-design/components/symbols.d.ts +12 -0
  267. package/dist/toolcraft-design/components/table.d.ts +16 -0
  268. package/dist/toolcraft-design/components/template.d.ts +19 -0
  269. package/dist/toolcraft-design/components/text.d.ts +16 -0
  270. package/dist/toolcraft-design/dashboard/buffer.d.ts +25 -0
  271. package/dist/toolcraft-design/dashboard/components/footer.d.ts +8 -0
  272. package/dist/toolcraft-design/dashboard/dashboard.d.ts +20 -0
  273. package/dist/toolcraft-design/dashboard/index.d.ts +8 -0
  274. package/dist/toolcraft-design/dashboard/should-use-dashboard.d.ts +10 -0
  275. package/dist/toolcraft-design/dashboard/snapshot.d.ts +10 -0
  276. package/dist/toolcraft-design/dashboard/terminal.d.ts +37 -0
  277. package/dist/toolcraft-design/dashboard/types.d.ts +38 -0
  278. package/dist/toolcraft-design/explorer/events.d.ts +77 -0
  279. package/dist/toolcraft-design/explorer/index.d.ts +8 -0
  280. package/dist/toolcraft-design/explorer/keymap.d.ts +31 -0
  281. package/dist/toolcraft-design/explorer/runtime.d.ts +2 -0
  282. package/dist/toolcraft-design/explorer/state.d.ts +224 -0
  283. package/dist/toolcraft-design/index.d.ts +62 -0
  284. package/dist/toolcraft-design/internal/output-format.d.ts +6 -0
  285. package/dist/toolcraft-design/internal/strip-ansi.d.ts +1 -0
  286. package/dist/toolcraft-design/internal/theme-detect.d.ts +12 -0
  287. package/dist/toolcraft-design/internal/theme-state.d.ts +12 -0
  288. package/dist/toolcraft-design/prompts/index.d.ts +91 -0
  289. package/dist/toolcraft-design/prompts/interactive/cancel-symbol.d.ts +2 -0
  290. package/dist/toolcraft-design/prompts/interactive/select.d.ts +18 -0
  291. package/dist/toolcraft-design/prompts/primitives/cancel.d.ts +2 -0
  292. package/dist/toolcraft-design/prompts/primitives/intro.d.ts +1 -0
  293. package/dist/toolcraft-design/prompts/primitives/log.d.ts +18 -0
  294. package/dist/toolcraft-design/prompts/primitives/note.d.ts +1 -0
  295. package/dist/toolcraft-design/prompts/primitives/outro.d.ts +1 -0
  296. package/dist/toolcraft-design/prompts/primitives/spinner.d.ts +6 -0
  297. package/dist/toolcraft-design/prompts/theme.d.ts +11 -0
  298. package/dist/toolcraft-design/screen/screen.d.ts +40 -0
  299. package/dist/toolcraft-design/screen/style.d.ts +16 -0
  300. package/dist/toolcraft-design/static/index.d.ts +4 -0
  301. package/dist/toolcraft-design/static/menu.d.ts +11 -0
  302. package/dist/toolcraft-design/static/spinner.d.ts +14 -0
  303. package/dist/toolcraft-design/terminal/driver.d.ts +34 -0
  304. package/dist/toolcraft-design/terminal/input.d.ts +25 -0
  305. package/dist/toolcraft-design/terminal-markdown/ast.d.ts +98 -0
  306. package/dist/toolcraft-design/terminal-markdown/html-renderer.d.ts +7 -0
  307. package/dist/toolcraft-design/terminal-markdown/index.d.ts +12 -0
  308. package/dist/toolcraft-design/terminal-markdown/parser.d.ts +5 -0
  309. package/dist/toolcraft-design/terminal-markdown/plaintext-renderer.d.ts +11 -0
  310. package/dist/toolcraft-design/terminal-markdown/renderer.d.ts +7 -0
  311. package/dist/toolcraft-design/tokens/brand.d.ts +5 -0
  312. package/dist/toolcraft-design/tokens/colors.d.ts +37 -0
  313. package/dist/toolcraft-design/tokens/index.d.ts +6 -0
  314. package/dist/toolcraft-design/tokens/spacing.d.ts +6 -0
  315. package/dist/toolcraft-design/tokens/typography.d.ts +7 -0
  316. package/dist/toolcraft-design/tokens/widths.d.ts +5 -0
  317. package/dist/toolcraft-schema/index.d.ts +184 -0
  318. package/dist/toolcraft-schema/json-schema/index.d.ts +5 -0
  319. package/dist/toolcraft-schema/json-schema/types.d.ts +33 -0
  320. package/dist/toolcraft-schema/json-schema-document.d.ts +14 -0
  321. package/dist/toolcraft-schema/json.d.ts +10 -0
  322. package/dist/toolcraft-schema/oneof.d.ts +15 -0
  323. package/dist/toolcraft-schema/record.d.ts +5 -0
  324. package/dist/toolcraft-schema/union.d.ts +8 -0
  325. package/dist/toolcraft-schema/validate.d.ts +17 -0
  326. package/package.json +89 -0
@@ -0,0 +1,4335 @@
1
+ // packages/safe-fs/src/platform/browser.ts
2
+ var platform = Object.freeze({
3
+ errno(_code) {
4
+ return void 0;
5
+ },
6
+ callbackAuthorities: false,
7
+ randomUUID() {
8
+ const crypto = globalThis.crypto;
9
+ if (typeof crypto?.randomUUID === "function") return crypto.randomUUID();
10
+ if (typeof crypto?.getRandomValues !== "function") {
11
+ throw new FsError("ENOTSUP", { message: "secure crypto is required for overlay staging" });
12
+ }
13
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
14
+ bytes[6] = bytes[6] & 15 | 64;
15
+ bytes[8] = bytes[8] & 63 | 128;
16
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
17
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
18
+ }
19
+ });
20
+ var operationKey = /* @__PURE__ */ Symbol("filesystem-comparison");
21
+ var contexts = /* @__PURE__ */ new WeakSet();
22
+ var comparisonContext = Object.freeze({
23
+ active(options) {
24
+ const context = options[operationKey];
25
+ return context !== void 0 && contexts.has(context);
26
+ },
27
+ async run(options, action) {
28
+ const context = Object.freeze({});
29
+ contexts.add(context);
30
+ const nested = { ...options, [operationKey]: context };
31
+ try {
32
+ return await action(nested);
33
+ } finally {
34
+ contexts.delete(context);
35
+ }
36
+ }
37
+ });
38
+
39
+ // packages/safe-fs/src/contracts/errors.ts
40
+ var descriptions = {
41
+ EACCES: "permission denied",
42
+ EAGAIN: "resource temporarily unavailable",
43
+ EBADF: "bad file descriptor",
44
+ EBUSY: "resource busy or locked",
45
+ ECANCELED: "operation canceled",
46
+ EEXIST: "file already exists",
47
+ EFBIG: "file too large",
48
+ EINTR: "interrupted system call",
49
+ EINVAL: "invalid argument",
50
+ EIO: "input/output error",
51
+ EISDIR: "illegal operation on a directory",
52
+ ELOOP: "too many symbolic links encountered",
53
+ EMFILE: "too many open files",
54
+ ENAMETOOLONG: "name too long",
55
+ ENFILE: "file table overflow",
56
+ ENOENT: "no such file or directory",
57
+ ENOMEM: "not enough memory",
58
+ ENOSPC: "no space left on device",
59
+ ENOSYS: "function not implemented",
60
+ ENOTDIR: "not a directory",
61
+ ENOTEMPTY: "directory not empty",
62
+ ENOTSUP: "operation not supported",
63
+ EOPNOTSUPP: "operation not supported",
64
+ EPERM: "operation not permitted",
65
+ EPIPE: "broken pipe",
66
+ EROFS: "read-only file system",
67
+ ETIMEDOUT: "operation timed out",
68
+ EXDEV: "cross-device link not permitted"
69
+ };
70
+ var FsError = class extends Error {
71
+ code;
72
+ errno;
73
+ syscall;
74
+ path;
75
+ dest;
76
+ constructor(code, options = {}) {
77
+ if (!isErrnoCode(code)) throw new TypeError(`Unsupported errno code: ${String(code)}`);
78
+ const errno = platform.errno(code);
79
+ const operation = options.syscall ? `, ${options.syscall}` : "";
80
+ const path = options.path === void 0 ? "" : ` '${options.path}'`;
81
+ const destination = options.dest === void 0 ? "" : ` -> '${options.dest}'`;
82
+ super(`${code}: ${options.message ?? descriptions[code]}${operation}${path}${destination}`, options);
83
+ this.name = "FsError";
84
+ this.code = code;
85
+ this.errno = errno;
86
+ if (options.syscall !== void 0) this.syscall = options.syscall;
87
+ if (options.path !== void 0) this.path = options.path;
88
+ if (options.dest !== void 0) this.dest = options.dest;
89
+ }
90
+ };
91
+ function isErrnoCode(code) {
92
+ return typeof code === "string" && Object.hasOwn(descriptions, code);
93
+ }
94
+ function isFsError(error, code) {
95
+ return error instanceof FsError && (code === void 0 || error.code === code);
96
+ }
97
+ function toFsError(error, options = {}) {
98
+ if (error instanceof FsError && Object.keys(options).length === 0) return error;
99
+ const source = typeof error === "object" && error !== null ? error : {};
100
+ const details = {
101
+ ...typeof source.syscall === "string" ? { syscall: source.syscall } : {},
102
+ ...typeof source.path === "string" ? { path: source.path } : {},
103
+ ...typeof source.dest === "string" ? { dest: source.dest } : {},
104
+ cause: error,
105
+ ...options
106
+ };
107
+ return new FsError(isErrnoCode(source.code) ? source.code : "EIO", details);
108
+ }
109
+
110
+ // packages/safe-fs/src/contracts/filesystem.ts
111
+ var ACCESS_MODES = Object.freeze({ F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1 });
112
+
113
+ // packages/safe-fs/src/contracts/cleanup.ts
114
+ async function finishCleanup(cleanup, failed) {
115
+ try {
116
+ await cleanup();
117
+ } catch (error) {
118
+ if (!failed) throw error;
119
+ }
120
+ }
121
+
122
+ // packages/safe-fs/src/contracts/io.ts
123
+ function toByteSource(input) {
124
+ if (typeof input !== "string" && !(input instanceof Uint8Array)) {
125
+ throw new TypeError("Byte source input must be a string or Uint8Array");
126
+ }
127
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : new Uint8Array(input);
128
+ return (async function* () {
129
+ if (bytes.byteLength > 0) yield bytes;
130
+ })();
131
+ }
132
+ async function collectBytes(source, options) {
133
+ if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0) {
134
+ throw new RangeError("maxBytes must be a nonnegative safe integer");
135
+ }
136
+ const chunks = [];
137
+ let size = 0;
138
+ options.signal?.throwIfAborted();
139
+ for await (const chunk of readBytes(source, options.signal)) {
140
+ if (chunk.byteLength > options.maxBytes - size) {
141
+ throw new FsError("EFBIG", { syscall: "collectBytes", message: "output exceeds maxBytes" });
142
+ }
143
+ if (chunk.byteLength > 0) chunks.push(new Uint8Array(chunk));
144
+ size += chunk.byteLength;
145
+ }
146
+ options.signal?.throwIfAborted();
147
+ const result = new Uint8Array(size);
148
+ let offset = 0;
149
+ for (const chunk of chunks) {
150
+ result.set(chunk, offset);
151
+ offset += chunk.byteLength;
152
+ }
153
+ return result;
154
+ }
155
+ async function abortable(operation, signal) {
156
+ signal?.throwIfAborted();
157
+ if (!signal) return operation();
158
+ return new Promise((resolve, reject) => {
159
+ const onAbort = () => {
160
+ signal.removeEventListener("abort", onAbort);
161
+ reject(signal.reason);
162
+ };
163
+ signal.addEventListener("abort", onAbort, { once: true });
164
+ try {
165
+ Promise.resolve(operation()).then(
166
+ (result) => {
167
+ signal.removeEventListener("abort", onAbort);
168
+ resolve(result);
169
+ },
170
+ (error) => {
171
+ signal.removeEventListener("abort", onAbort);
172
+ reject(error);
173
+ }
174
+ );
175
+ } catch (error) {
176
+ signal.removeEventListener("abort", onAbort);
177
+ reject(error);
178
+ }
179
+ });
180
+ }
181
+ async function* readBytes(source, signal) {
182
+ signal?.throwIfAborted();
183
+ const iterator = source[Symbol.asyncIterator]();
184
+ let finished = false;
185
+ let failed = false;
186
+ try {
187
+ while (true) {
188
+ const result = await abortable(() => iterator.next(), signal);
189
+ if (result.done) {
190
+ finished = true;
191
+ return;
192
+ }
193
+ if (!(result.value instanceof Uint8Array)) throw new TypeError("Byte sources must yield Uint8Array chunks");
194
+ yield result.value;
195
+ }
196
+ } catch (error) {
197
+ failed = true;
198
+ throw error;
199
+ } finally {
200
+ if (!finished && iterator.return) {
201
+ const cleanup = Promise.resolve().then(() => iterator.return());
202
+ if (signal?.aborted) void cleanup.catch(() => {
203
+ });
204
+ else await finishCleanup(() => abortable(() => cleanup, signal), failed);
205
+ }
206
+ }
207
+ }
208
+
209
+ // packages/safe-fs/src/contracts/virtual-path.ts
210
+ function validatePath(path) {
211
+ if (typeof path !== "string" || path.includes("\0")) {
212
+ throw new FsError("EINVAL", { syscall: "resolve", message: "paths must be strings without NUL bytes" });
213
+ }
214
+ }
215
+ function resolvePath(cwd, ...paths) {
216
+ validatePath(cwd);
217
+ if (!cwd.startsWith("/")) throw new FsError("EINVAL", { syscall: "resolve", path: cwd, message: "cwd must be absolute" });
218
+ for (const path of paths) validatePath(path);
219
+ const components = [];
220
+ for (const path of [cwd, ...paths]) {
221
+ if (path.startsWith("/")) components.length = 0;
222
+ for (const component of path.split("/")) {
223
+ if (component === "" || component === ".") continue;
224
+ if (component === "..") components.pop();
225
+ else components.push(component);
226
+ }
227
+ }
228
+ return `/${components.join("/")}`;
229
+ }
230
+ function normalizePath(path, cwd = "/") {
231
+ return resolvePath(cwd, path);
232
+ }
233
+ function dirname(path) {
234
+ if (typeof path !== "string") throw new TypeError("path must be a string");
235
+ let end = path.length;
236
+ while (end > 0 && path[end - 1] === "/") end--;
237
+ if (end === 0) return path.startsWith("/") ? "/" : ".";
238
+ const boundary = path.lastIndexOf("/", end - 1);
239
+ if (boundary < 0) return ".";
240
+ if (boundary === 0) return "/";
241
+ if (boundary === 1 && path.startsWith("/")) return "//";
242
+ return path.slice(0, boundary);
243
+ }
244
+ function relativePath(from, to) {
245
+ const source = normalizePath(from).split("/").filter(Boolean);
246
+ const target = normalizePath(to).split("/").filter(Boolean);
247
+ let common = 0;
248
+ while (common < source.length && common < target.length && source[common] === target[common]) common++;
249
+ return [...Array(source.length - common).fill(".."), ...target.slice(common)].join("/");
250
+ }
251
+ function isPathWithin(root, path) {
252
+ const normalizedRoot = normalizePath(root);
253
+ const normalizedPath = normalizePath(path);
254
+ return normalizedRoot === "/" || normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`);
255
+ }
256
+ function assertPathWithin(root, path) {
257
+ const normalizedPath = normalizePath(path);
258
+ if (!isPathWithin(root, normalizedPath)) throw new FsError("EACCES", { syscall: "resolve", path, message: "path escapes the allowed root" });
259
+ return normalizedPath;
260
+ }
261
+
262
+ // packages/safe-fs/src/contracts/portable-path.ts
263
+ function assertString(value) {
264
+ if (typeof value !== "string") throw new TypeError("path must be a string");
265
+ }
266
+ function basename(path, suffix = "") {
267
+ assertString(path);
268
+ assertString(suffix);
269
+ if (suffix && path === suffix) return "";
270
+ let end = path.length;
271
+ while (end > 0 && path[end - 1] === "/") end--;
272
+ if (end === 0 && suffix && suffix.length <= path.length) return path;
273
+ const start = path.lastIndexOf("/", end - 1) + 1;
274
+ const base = path.slice(start, end);
275
+ if (suffix.length > base.length && suffix.length <= path.length && suffix.endsWith(base)) return path.slice(start);
276
+ return suffix && suffix.length < base.length && base.endsWith(suffix) ? base.slice(0, -suffix.length) : base;
277
+ }
278
+ var dirname2 = dirname;
279
+ function extname(path) {
280
+ const base = basename(path);
281
+ const position = base.lastIndexOf(".");
282
+ return position <= 0 || base === ".." ? "" : base.slice(position);
283
+ }
284
+ function isAbsolutePath(path) {
285
+ assertString(path);
286
+ return path.startsWith("/");
287
+ }
288
+ function joinPath(...paths) {
289
+ for (const path of paths) assertString(path);
290
+ const joined = paths.filter(Boolean).join("/");
291
+ if (!joined) return ".";
292
+ const absolute = joined.startsWith("/");
293
+ const components = [];
294
+ for (const component of joined.split("/")) {
295
+ if (!component || component === ".") continue;
296
+ if (component === "..") {
297
+ if (components.length && components.at(-1) !== "..") components.pop();
298
+ else if (!absolute) components.push(component);
299
+ } else components.push(component);
300
+ }
301
+ const result = `${absolute ? "/" : ""}${components.join("/")}` || ".";
302
+ return joined.endsWith("/") && result !== "/" ? `${result}/` : result;
303
+ }
304
+ var posixPath = Object.freeze({ basename, dirname: dirname2, extname, join: joinPath, isAbsolute: isAbsolutePath });
305
+
306
+ // packages/safe-fs/src/fs/mount/identity.ts
307
+ function complete(stat) {
308
+ const { identityScope, dev, ino } = stat;
309
+ return (typeof identityScope === "object" && identityScope !== null || typeof identityScope === "symbol") && typeof dev === "number" && Number.isSafeInteger(dev) && dev >= 0 && typeof ino === "number" && Number.isSafeInteger(ino) && ino >= 0;
310
+ }
311
+ function compareIdentity(left, right) {
312
+ if (!left || !right || !complete(left) || !complete(right)) return "unknown";
313
+ return left.identityScope === right.identityScope && left.dev === right.dev && left.ino === right.ino ? "same" : "distinct";
314
+ }
315
+
316
+ // packages/safe-fs/src/fs/mount/comparison.ts
317
+ var resolvers = /* @__PURE__ */ new WeakMap();
318
+ var authorities = /* @__PURE__ */ new WeakMap();
319
+ function assertCallbackAuthorityAllowed() {
320
+ if (!platform.callbackAuthorities) throw new FsError("ENOTSUP", { message: "custom comparison authorities require Node context" });
321
+ }
322
+ function registerEntryView(filesystem, resolver) {
323
+ if (resolvers.has(filesystem)) throw new TypeError("entry view already registered");
324
+ resolvers.set(filesystem, resolver);
325
+ }
326
+ function registerEntryAuthority(filesystem, authority) {
327
+ if (authorities.has(filesystem)) throw new TypeError("entry authority already registered");
328
+ authorities.set(filesystem, authority);
329
+ }
330
+ async function resolveEntryView(filesystem, path, options = {}) {
331
+ let location = { filesystem, path };
332
+ let readOnly2 = false;
333
+ const visited = /* @__PURE__ */ new Set();
334
+ for (; ; ) {
335
+ options.signal?.throwIfAborted();
336
+ if (visited.has(location.filesystem)) throw new FsError("EIO", { path, message: "cyclic entry view" });
337
+ visited.add(location.filesystem);
338
+ readOnly2 ||= location.readOnly === true || location.filesystem.capabilities.readOnly === true;
339
+ if (location.stat) return { filesystem: location.filesystem, path: location.path, stat: location.stat, readOnly: readOnly2 };
340
+ const resolve = resolvers.get(location.filesystem);
341
+ if (!resolve) {
342
+ let stat;
343
+ let followedPath;
344
+ try {
345
+ followedPath = await location.filesystem.realpath(location.path, options);
346
+ options.signal?.throwIfAborted();
347
+ stat = await location.filesystem.lstat(followedPath, options);
348
+ } catch (error) {
349
+ options.signal?.throwIfAborted();
350
+ throw error;
351
+ }
352
+ options.signal?.throwIfAborted();
353
+ if (stat.type === "symlink") throw new FsError("EIO", { path, message: "followed entry changed during observation" });
354
+ return { filesystem: location.filesystem, path: followedPath, stat, readOnly: readOnly2 };
355
+ }
356
+ let next;
357
+ try {
358
+ next = await resolve(location.path, options);
359
+ } catch (error) {
360
+ options.signal?.throwIfAborted();
361
+ throw error;
362
+ }
363
+ options.signal?.throwIfAborted();
364
+ if (next.filesystem === location.filesystem && next.stat) {
365
+ return { filesystem: next.filesystem, path: next.path, stat: next.stat, readOnly: readOnly2 || next.readOnly === true };
366
+ }
367
+ location = next;
368
+ }
369
+ }
370
+ async function compareResolvedEntries(own, peer, options = {}) {
371
+ options.signal?.throwIfAborted();
372
+ if (comparisonContext.active(options)) return "unknown";
373
+ const identity = compareIdentity(own.stat, peer.stat);
374
+ if (identity !== "unknown") return identity;
375
+ return comparisonContext.run(options, async (options2) => {
376
+ const queried = /* @__PURE__ */ new Set();
377
+ let result = "unknown";
378
+ for (const [left, right] of [[own, peer], [peer, own]]) {
379
+ options2.signal?.throwIfAborted();
380
+ const authority = authorities.get(left.filesystem);
381
+ const key = authority ?? left.filesystem;
382
+ if (queried.has(key)) continue;
383
+ queried.add(key);
384
+ let answer;
385
+ try {
386
+ if (authority) answer = await authority(left, right, options2);
387
+ else if (left.filesystem.compareEntry) {
388
+ assertCallbackAuthorityAllowed();
389
+ answer = await left.filesystem.compareEntry(left.path, right.filesystem, right.path, options2);
390
+ } else continue;
391
+ } catch (error) {
392
+ options2.signal?.throwIfAborted();
393
+ throw error;
394
+ }
395
+ options2.signal?.throwIfAborted();
396
+ if (answer !== "same" && answer !== "distinct" && answer !== "unknown") {
397
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "invalid entry comparison answer" });
398
+ }
399
+ if (answer === "unknown") continue;
400
+ if (result !== "unknown" && result !== answer) {
401
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting entry comparison answers" });
402
+ }
403
+ result = answer;
404
+ }
405
+ return result;
406
+ });
407
+ }
408
+ async function compareEntries(filesystem, path, peer, peerPath, options = {}) {
409
+ options.signal?.throwIfAborted();
410
+ if (comparisonContext.active(options)) return "unknown";
411
+ const own = await resolveEntryView(filesystem, path, options);
412
+ const other = await resolveEntryView(peer, peerPath, options);
413
+ return compareResolvedEntries(own, other, options);
414
+ }
415
+
416
+ // packages/safe-fs/src/fs/s3/registry.ts
417
+ var entries = /* @__PURE__ */ new WeakMap();
418
+ function getOwnedS3Entry(view) {
419
+ return entries.get(view.filesystem)?.(view);
420
+ }
421
+
422
+ // packages/safe-fs/src/fs/webdav/resource-id.ts
423
+ var responses = /* @__PURE__ */ new WeakMap();
424
+ var observations = /* @__PURE__ */ new WeakMap();
425
+ function recordOwnedResourceStat(response, filesystem, path, stat) {
426
+ const entry = responses.get(response)?.get(path);
427
+ if (entry) observations.set(stat, { filesystem, path, entry });
428
+ }
429
+ function getOwnedWebDavEntry(view) {
430
+ const observation = observations.get(view.stat);
431
+ return observation?.filesystem === view.filesystem && observation.path === view.path ? observation.entry : void 0;
432
+ }
433
+ function ownedResponseIdentifier(response, path) {
434
+ return responses.get(response)?.get(path)?.identifier;
435
+ }
436
+ var queries = /* @__PURE__ */ new WeakMap();
437
+ var comparisons = /* @__PURE__ */ new WeakMap();
438
+ var callbacks = /* @__PURE__ */ new WeakMap();
439
+ function registerResourceQuery(filesystem, query, baseComparison, callback) {
440
+ comparisons.set(filesystem, baseComparison);
441
+ if (callback) callbacks.set(filesystem, callback);
442
+ queries.set(filesystem, async (path, options) => {
443
+ options.signal?.throwIfAborted();
444
+ const identifier = await query(path, options);
445
+ options.signal?.throwIfAborted();
446
+ return identifier;
447
+ });
448
+ }
449
+ function resourceIdentifier(value) {
450
+ if (!/^[A-Za-z][A-Za-z0-9+.-]*:[^\s<>"{}|\\^`]+$/.test(value) || /%(?![0-9A-Fa-f]{2})/.test(value) || /[^\x21-\x7e]/.test(value)) throw new Error("invalid DAV:resource-id URI");
451
+ new URL(value);
452
+ if (/^urn:uuid:/i.test(value)) {
453
+ if (!/^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) {
454
+ throw new Error("invalid DAV:resource-id UUID");
455
+ }
456
+ return value.toLowerCase();
457
+ }
458
+ return value;
459
+ }
460
+ var compareWebDavResources = async (own, peer, options) => {
461
+ options.signal?.throwIfAborted();
462
+ let explicit = false;
463
+ let answer = "unknown";
464
+ const visited = /* @__PURE__ */ new Set();
465
+ for (const [left, right] of [[own, peer], [peer, own]]) {
466
+ const baseComparison = comparisons.get(left.filesystem);
467
+ if (!baseComparison) continue;
468
+ const method = left.filesystem.compareEntry;
469
+ const comparison = method === baseComparison ? callbacks.get(left.filesystem) : method;
470
+ if (method === baseComparison && !comparison) continue;
471
+ explicit = true;
472
+ if (!comparison || visited.has(left.filesystem)) continue;
473
+ visited.add(left.filesystem);
474
+ options.signal?.throwIfAborted();
475
+ assertCallbackAuthorityAllowed();
476
+ const result = await comparison.call(left.filesystem, left.path, right.filesystem, right.path, options);
477
+ options.signal?.throwIfAborted();
478
+ if (result !== "same" && result !== "distinct" && result !== "unknown") {
479
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "invalid explicit WebDAV comparison" });
480
+ }
481
+ if (result === "unknown") continue;
482
+ if (answer !== "unknown" && answer !== result) {
483
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting explicit WebDAV comparisons" });
484
+ }
485
+ answer = result;
486
+ }
487
+ const builtin = await compareProtocolEntries(own, peer, options);
488
+ if (builtin === "same" && answer === "distinct") {
489
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "explicit WebDAV comparison contradicts built-in identity" });
490
+ }
491
+ if (builtin === "same") return "same";
492
+ return explicit ? answer : builtin;
493
+ };
494
+ async function compareProtocolEntries(own, peer, options) {
495
+ const left = getOwnedWebDavEntry(own);
496
+ const right = getOwnedWebDavEntry(peer);
497
+ const ownQuery = queries.get(own.filesystem);
498
+ const peerQuery = queries.get(peer.filesystem);
499
+ if (!ownQuery || !peerQuery) return "unknown";
500
+ options.signal?.throwIfAborted();
501
+ const ownId = await ownQuery(own.path, options);
502
+ options.signal?.throwIfAborted();
503
+ const peerId = await peerQuery(peer.path, options);
504
+ options.signal?.throwIfAborted();
505
+ if (ownId === void 0 || peerId === void 0) return "unknown";
506
+ if (left && right) {
507
+ const same = left.storage === right.storage && left.resource === right.resource;
508
+ if (same !== (ownId === peerId)) throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting owned and protocol WebDAV identities" });
509
+ }
510
+ return ownId === peerId ? "same" : "distinct";
511
+ }
512
+
513
+ // packages/safe-fs/src/fs/memory/index.ts
514
+ var typeModes = { file: 32768, directory: 16384, symlink: 40960 };
515
+ var ownedStats = /* @__PURE__ */ new WeakMap();
516
+ var ownedStores = /* @__PURE__ */ new WeakMap();
517
+ var registeredAuthorities = /* @__PURE__ */ new WeakSet();
518
+ var compareOwnedMemory = async (own, peer, options) => {
519
+ options.signal?.throwIfAborted();
520
+ let explicit = false;
521
+ let answer = "unknown";
522
+ const visited = /* @__PURE__ */ new Set();
523
+ for (const [left, right] of [[own, peer], [peer, own]]) {
524
+ if (!registeredAuthorities.has(left.filesystem) || visited.has(left.filesystem)) continue;
525
+ visited.add(left.filesystem);
526
+ const comparison = left.filesystem.compareEntry;
527
+ if (comparison === memoryImplementation.compareEntry?.value) continue;
528
+ explicit = true;
529
+ if (!comparison) continue;
530
+ options.signal?.throwIfAborted();
531
+ assertCallbackAuthorityAllowed();
532
+ const result = await comparison.call(left.filesystem, left.path, right.filesystem, right.path, options);
533
+ options.signal?.throwIfAborted();
534
+ if (result !== "same" && result !== "distinct" && result !== "unknown") {
535
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "invalid explicit Memory comparison" });
536
+ }
537
+ if (result === "unknown") continue;
538
+ if (answer !== "unknown" && answer !== result) {
539
+ throw new FsError("EIO", { path: own.path, dest: peer.path, message: "conflicting explicit Memory comparisons" });
540
+ }
541
+ answer = result;
542
+ }
543
+ if (explicit) return answer;
544
+ const owner = ownedStores.get(own.filesystem);
545
+ const observation = ownedStats.get(own.stat);
546
+ if (!owner?.intact() || observation?.filesystem !== own.filesystem || observation.path !== own.path || observation.root !== owner.root) return "unknown";
547
+ const qualified = getOwnedS3Entry(peer) ?? getOwnedWebDavEntry(peer);
548
+ options.signal?.throwIfAborted();
549
+ return qualified ? "distinct" : "unknown";
550
+ };
551
+ var MemoryFileSystem = class {
552
+ capabilities = Object.freeze({
553
+ readOnly: false,
554
+ symlinks: true,
555
+ hardlinks: true,
556
+ permissions: true,
557
+ timestamps: true,
558
+ atomicRename: true,
559
+ streamingRead: true,
560
+ streamingWrite: true
561
+ });
562
+ identityScope = /* @__PURE__ */ Symbol();
563
+ nextInode = 1;
564
+ root = this.directory(493);
565
+ constructor() {
566
+ const root = this.root;
567
+ ownedStores.set(this, {
568
+ root,
569
+ intact: () => this.root === root
570
+ });
571
+ if (this.compareEntry === memoryImplementation.compareEntry?.value) {
572
+ registeredAuthorities.add(this);
573
+ registerEntryAuthority(this, compareOwnedMemory);
574
+ }
575
+ }
576
+ compareEntry(path, peer, peerPath, options = {}) {
577
+ return compareEntries(this, path, peer, peerPath, options);
578
+ }
579
+ metadata(mode2) {
580
+ const now = Date.now();
581
+ return {
582
+ mode: mode2,
583
+ ino: this.nextInode++,
584
+ nlink: 1,
585
+ atimeMs: now,
586
+ mtimeMs: now,
587
+ ctimeMs: now,
588
+ birthtimeMs: now
589
+ };
590
+ }
591
+ directory(mode2) {
592
+ return { ...this.metadata(typeModes.directory | mode2), type: "directory", entries: /* @__PURE__ */ new Map() };
593
+ }
594
+ fail(code, syscall, path, dest) {
595
+ throw new FsError(code, { syscall, path, ...dest === void 0 ? {} : { dest } });
596
+ }
597
+ validatePath(path, syscall) {
598
+ if (typeof path !== "string" || path.includes("\0")) this.fail("EINVAL", syscall, path);
599
+ if (path.length === 0) this.fail("ENOENT", syscall, path);
600
+ }
601
+ permission(node, mask, syscall, path) {
602
+ if ((node.mode >> 6 & mask) !== mask) this.fail("EACCES", syscall, path);
603
+ }
604
+ mode(mode2, fallback, syscall, path) {
605
+ const value = mode2 ?? fallback;
606
+ if (!Number.isInteger(value) || value < 0 || value > 4095) this.fail("EINVAL", syscall, path);
607
+ return value;
608
+ }
609
+ integer(value, syscall, path) {
610
+ if (!Number.isSafeInteger(value) || value < 0) this.fail("EINVAL", syscall, path);
611
+ }
612
+ changed(node) {
613
+ node.mtimeMs = node.ctimeMs = Date.now();
614
+ }
615
+ resolve(path, syscall, options = {}) {
616
+ this.validatePath(path, syscall);
617
+ const pending = path.split("/").filter(Boolean);
618
+ if (path.endsWith("/")) pending.push(".");
619
+ const stack = [{ node: this.root, name: "" }];
620
+ let links = 0;
621
+ while (pending.length > 0) {
622
+ const component = pending.shift();
623
+ const current = stack[stack.length - 1].node;
624
+ if (current.type !== "directory") this.fail("ENOTDIR", syscall, path);
625
+ this.permission(current, 1, syscall, path);
626
+ if (component === ".") continue;
627
+ if (component === "..") {
628
+ if (stack.length > 1) stack.pop();
629
+ continue;
630
+ }
631
+ if (new TextEncoder().encode(component).byteLength > 255) this.fail("ENAMETOOLONG", syscall, path);
632
+ let node = current.entries.get(component);
633
+ if (!node && options.createDirectories !== void 0) {
634
+ this.permission(current, 3, syscall, path);
635
+ node = this.directory(options.createDirectories);
636
+ current.entries.set(component, node);
637
+ this.changed(current);
638
+ }
639
+ if (!node) {
640
+ if (options.allowMissing && pending.length === 0) {
641
+ return { node, parent: current, name: component, path: [...stack.map((entry2) => entry2.name), component].join("/") };
642
+ }
643
+ this.fail("ENOENT", syscall, path);
644
+ }
645
+ if (node.type === "symlink" && (options.followFinal !== false || pending.length > 0)) {
646
+ if (++links > 40) this.fail("ELOOP", syscall, path);
647
+ const target = node.target.split("/").filter(Boolean);
648
+ if (node.target.endsWith("/")) target.push(".");
649
+ pending.unshift(...target);
650
+ if (node.target.startsWith("/")) stack.splice(1);
651
+ continue;
652
+ }
653
+ stack.push({ node, name: component });
654
+ }
655
+ const entry = stack[stack.length - 1];
656
+ return {
657
+ node: entry.node,
658
+ parent: stack[stack.length - 2]?.node ?? this.root,
659
+ name: entry.name,
660
+ path: stack.map((part) => part.name).join("/") || "/"
661
+ };
662
+ }
663
+ file(path, syscall) {
664
+ const node = this.resolve(path, syscall).node;
665
+ if (node.type !== "file") this.fail("EISDIR", syscall, path);
666
+ return node;
667
+ }
668
+ entry(path, syscall, allowMissing = false) {
669
+ this.validatePath(path, syscall);
670
+ const location = this.resolve(path.replace(/\/+$/, "") || "/", syscall, { followFinal: false, allowMissing });
671
+ if (path.endsWith("/") && location.node && location.node.type !== "directory") this.fail("ENOTDIR", syscall, path);
672
+ return location;
673
+ }
674
+ terminalDot(path) {
675
+ return /(?:^|\/)\.{1,2}\/*$/.test(path);
676
+ }
677
+ snapshot(node) {
678
+ return {
679
+ type: node.type,
680
+ size: node.type === "file" ? node.data.byteLength : node.type === "symlink" ? new TextEncoder().encode(node.target).byteLength : 0,
681
+ mode: node.mode,
682
+ ...ownedStores.get(this)?.intact() ? { identityScope: this.identityScope } : {},
683
+ ino: node.ino,
684
+ dev: 0,
685
+ uid: 0,
686
+ gid: 0,
687
+ nlink: node.type === "directory" ? 2 + [...node.entries.values()].filter((entry) => entry.type === "directory").length : node.nlink,
688
+ atimeMs: node.atimeMs,
689
+ mtimeMs: node.mtimeMs,
690
+ ctimeMs: node.ctimeMs,
691
+ birthtimeMs: node.birthtimeMs
692
+ };
693
+ }
694
+ bytes(data) {
695
+ if (!(data instanceof Uint8Array)) throw new TypeError("Memory files require Uint8Array data");
696
+ return new Uint8Array(data);
697
+ }
698
+ allocate(length, syscall, path) {
699
+ try {
700
+ return new Uint8Array(length);
701
+ } catch (cause) {
702
+ throw new FsError("EFBIG", { syscall, path, cause });
703
+ }
704
+ }
705
+ openWrite(path, options, syscall) {
706
+ const flag = options.flag ?? "w";
707
+ if (!["w", "wx", "a", "ax"].includes(flag)) this.fail("EINVAL", syscall, path);
708
+ const mode2 = this.mode(options.mode, 438, syscall, path);
709
+ const exclusive = flag === "wx" || flag === "ax";
710
+ const location = this.resolve(path, syscall, { followFinal: !exclusive, allowMissing: true });
711
+ if (location.node) {
712
+ if (exclusive) this.fail("EEXIST", syscall, path);
713
+ if (location.node.type !== "file") this.fail("EISDIR", syscall, path);
714
+ this.permission(location.node, 2, syscall, path);
715
+ return location.node;
716
+ }
717
+ this.permission(location.parent, 3, syscall, path);
718
+ const node = { ...this.metadata(typeModes.file | mode2), type: "file", data: new Uint8Array() };
719
+ location.parent.entries.set(location.name, node);
720
+ this.changed(location.parent);
721
+ return node;
722
+ }
723
+ append(node, data, syscall, path) {
724
+ const length = node.data.byteLength + data.byteLength;
725
+ let storage;
726
+ if (length <= node.data.buffer.byteLength) {
727
+ storage = new Uint8Array(node.data.buffer);
728
+ } else {
729
+ storage = this.allocate(Math.max(length, node.data.byteLength * 2, 64), syscall, path);
730
+ storage.set(node.data);
731
+ }
732
+ storage.set(data, node.data.byteLength);
733
+ node.data = storage.subarray(0, length);
734
+ this.changed(node);
735
+ }
736
+ async readFile(path, options = {}) {
737
+ options.signal?.throwIfAborted();
738
+ if (options.maxBytes !== void 0) this.integer(options.maxBytes, "readFile", path);
739
+ const node = this.file(path, "readFile");
740
+ this.permission(node, 4, "readFile", path);
741
+ if (options.maxBytes !== void 0 && node.data.byteLength > options.maxBytes) this.fail("EFBIG", "readFile", path);
742
+ node.atimeMs = Date.now();
743
+ return new Uint8Array(node.data);
744
+ }
745
+ async writeFile(path, data, options = {}) {
746
+ options.signal?.throwIfAborted();
747
+ const copied = this.bytes(data);
748
+ const node = this.openWrite(path, options, "writeFile");
749
+ if (options.flag === "a" || options.flag === "ax") this.append(node, copied, "writeFile", path);
750
+ else {
751
+ node.data = copied;
752
+ this.changed(node);
753
+ }
754
+ }
755
+ async appendFile(path, data, options = {}) {
756
+ options.signal?.throwIfAborted();
757
+ const copied = this.bytes(data);
758
+ this.append(this.openWrite(path, { ...options, flag: "a" }, "appendFile"), copied, "appendFile", path);
759
+ }
760
+ async stat(path, options = {}) {
761
+ options.signal?.throwIfAborted();
762
+ return this.snapshot(this.resolve(path, "stat").node);
763
+ }
764
+ async lstat(path, options = {}) {
765
+ options.signal?.throwIfAborted();
766
+ const stat = this.snapshot(this.resolve(path, "lstat", { followFinal: false }).node);
767
+ ownedStats.set(stat, { filesystem: this, path, root: this.root });
768
+ return stat;
769
+ }
770
+ async readdir(path, options = {}) {
771
+ options.signal?.throwIfAborted();
772
+ const node = this.resolve(path, "readdir").node;
773
+ if (node.type !== "directory") this.fail("ENOTDIR", "readdir", path);
774
+ this.permission(node, 4, "readdir", path);
775
+ node.atimeMs = Date.now();
776
+ return [...node.entries].map(([name, entry]) => ({ name, type: entry.type })).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
777
+ }
778
+ async mkdir(path, options = {}) {
779
+ options.signal?.throwIfAborted();
780
+ this.validatePath(path, "mkdir");
781
+ const mode2 = this.mode(options.mode, 511, "mkdir", path);
782
+ if (options.recursive) {
783
+ const node = this.resolve(path, "mkdir", { createDirectories: mode2 }).node;
784
+ if (node.type !== "directory") this.fail("EEXIST", "mkdir", path);
785
+ return;
786
+ }
787
+ const location = this.resolve(path.replace(/\/+$/, "") || "/", "mkdir", { allowMissing: true, followFinal: false });
788
+ if (location.node) this.fail("EEXIST", "mkdir", path);
789
+ this.permission(location.parent, 3, "mkdir", path);
790
+ location.parent.entries.set(location.name, this.directory(mode2));
791
+ this.changed(location.parent);
792
+ }
793
+ async rmdir(path, options = {}) {
794
+ options.signal?.throwIfAborted();
795
+ const location = this.entry(path, "rmdir");
796
+ const node = location.node;
797
+ if (this.terminalDot(path)) this.fail("EINVAL", "rmdir", path);
798
+ if (node === this.root) this.fail("EBUSY", "rmdir", path);
799
+ if (node.type !== "directory") this.fail("ENOTDIR", "rmdir", path);
800
+ this.permission(location.parent, 3, "rmdir", path);
801
+ if (node.entries.size > 0) this.fail("ENOTEMPTY", "rmdir", path);
802
+ location.parent.entries.delete(location.name);
803
+ node.nlink = 0;
804
+ node.ctimeMs = Date.now();
805
+ this.changed(location.parent);
806
+ }
807
+ async rm(path, options = {}) {
808
+ options.signal?.throwIfAborted();
809
+ let location;
810
+ try {
811
+ location = this.entry(path, "rm");
812
+ } catch (error) {
813
+ if (options.force && error instanceof FsError && error.code === "ENOENT") return;
814
+ throw error;
815
+ }
816
+ const node = location.node;
817
+ if (this.terminalDot(path)) this.fail("EINVAL", "rm", path);
818
+ if (node === this.root) this.fail("EBUSY", "rm", path);
819
+ this.permission(location.parent, 3, "rm", path);
820
+ if (node.type === "directory" && !options.recursive) this.fail("EISDIR", "rm", path);
821
+ const removed = [node];
822
+ for (let index = 0; index < removed.length; index++) {
823
+ const entry = removed[index];
824
+ if (entry.type === "directory" && entry.entries.size > 0) {
825
+ this.permission(entry, 7, "rm", path);
826
+ for (const child of entry.entries.values()) removed.push(child);
827
+ }
828
+ }
829
+ location.parent.entries.delete(location.name);
830
+ for (const entry of removed) {
831
+ entry.nlink--;
832
+ entry.ctimeMs = Date.now();
833
+ }
834
+ this.changed(location.parent);
835
+ }
836
+ async rename(source, destination, options = {}) {
837
+ options.signal?.throwIfAborted();
838
+ try {
839
+ const origin = this.entry(source, "rename");
840
+ const target = this.entry(destination, "rename", true);
841
+ const node = origin.node;
842
+ if (this.terminalDot(source) || this.terminalDot(destination)) this.fail("EINVAL", "rename", source, destination);
843
+ if (node === this.root || target.node === this.root) this.fail("EBUSY", "rename", source, destination);
844
+ if (destination.endsWith("/") && node.type !== "directory") this.fail("ENOTDIR", "rename", source, destination);
845
+ if (target.node === node) return;
846
+ if (node.type === "directory" && target.path.startsWith(`${origin.path}/`)) this.fail("EINVAL", "rename", source, destination);
847
+ this.permission(origin.parent, 3, "rename", source);
848
+ this.permission(target.parent, 3, "rename", destination);
849
+ if (target.node) {
850
+ if (node.type === "directory" && target.node.type !== "directory") this.fail("ENOTDIR", "rename", source, destination);
851
+ if (node.type !== "directory" && target.node.type === "directory") this.fail("EISDIR", "rename", source, destination);
852
+ if (target.node.type === "directory" && target.node.entries.size > 0) this.fail("ENOTEMPTY", "rename", source, destination);
853
+ target.node.nlink--;
854
+ target.node.ctimeMs = Date.now();
855
+ }
856
+ origin.parent.entries.delete(origin.name);
857
+ target.parent.entries.set(target.name, node);
858
+ this.changed(origin.parent);
859
+ this.changed(target.parent);
860
+ node.ctimeMs = Date.now();
861
+ } catch (error) {
862
+ if (error instanceof FsError) throw new FsError(error.code, { syscall: "rename", path: source, dest: destination, cause: error });
863
+ throw error;
864
+ }
865
+ }
866
+ async copyFile(source, destination, options = {}) {
867
+ options.signal?.throwIfAborted();
868
+ try {
869
+ const origin = this.file(source, "copyFile");
870
+ this.permission(origin, 4, "copyFile", source);
871
+ const target = this.resolve(destination, "copyFile", { followFinal: !options.exclusive, allowMissing: true });
872
+ if (target.node && options.exclusive) this.fail("EEXIST", "copyFile", source, destination);
873
+ if (target.node === origin) this.fail("EINVAL", "copyFile", source, destination);
874
+ const node = this.openWrite(destination, { mode: origin.mode & 4095, flag: options.exclusive ? "wx" : "w" }, "copyFile");
875
+ node.data = new Uint8Array(origin.data);
876
+ node.mode = origin.mode;
877
+ this.changed(node);
878
+ origin.atimeMs = Date.now();
879
+ } catch (error) {
880
+ if (error instanceof FsError) throw new FsError(error.code, { syscall: "copyFile", path: source, dest: destination, cause: error });
881
+ throw error;
882
+ }
883
+ }
884
+ async realpath(path, options = {}) {
885
+ options.signal?.throwIfAborted();
886
+ return this.resolve(path, "realpath").path;
887
+ }
888
+ async access(path, mode2 = 0, options = {}) {
889
+ options.signal?.throwIfAborted();
890
+ if (!Number.isInteger(mode2) || mode2 < 0 || mode2 > 7) this.fail("EINVAL", "access", path);
891
+ this.permission(this.resolve(path, "access").node, mode2, "access", path);
892
+ }
893
+ async readlink(path, options = {}) {
894
+ options.signal?.throwIfAborted();
895
+ const node = this.resolve(path, "readlink", { followFinal: false }).node;
896
+ if (node.type !== "symlink") this.fail("EINVAL", "readlink", path);
897
+ return node.target;
898
+ }
899
+ async symlink(target, path, options = {}) {
900
+ options.signal?.throwIfAborted();
901
+ this.validatePath(target, "symlink");
902
+ const location = this.resolve(path, "symlink", { followFinal: false, allowMissing: true });
903
+ if (location.node) this.fail("EEXIST", "symlink", path);
904
+ this.permission(location.parent, 3, "symlink", path);
905
+ location.parent.entries.set(location.name, { ...this.metadata(typeModes.symlink | 511), type: "symlink", target });
906
+ this.changed(location.parent);
907
+ }
908
+ async link(existingPath, newPath, options = {}) {
909
+ options.signal?.throwIfAborted();
910
+ const node = this.resolve(existingPath, "link", { followFinal: false }).node;
911
+ if (node.type === "directory") this.fail("EPERM", "link", existingPath, newPath);
912
+ const target = this.resolve(newPath, "link", { followFinal: false, allowMissing: true });
913
+ if (target.node) this.fail("EEXIST", "link", existingPath, newPath);
914
+ this.permission(target.parent, 3, "link", newPath);
915
+ target.parent.entries.set(target.name, node);
916
+ node.nlink++;
917
+ node.ctimeMs = Date.now();
918
+ this.changed(target.parent);
919
+ }
920
+ async chmod(path, mode2, options = {}) {
921
+ options.signal?.throwIfAborted();
922
+ const permissions = this.mode(mode2, 0, "chmod", path);
923
+ const node = this.resolve(path, "chmod").node;
924
+ node.mode = typeModes[node.type] | permissions;
925
+ node.ctimeMs = Date.now();
926
+ }
927
+ async utimes(path, atimeMs, mtimeMs, options = {}) {
928
+ options.signal?.throwIfAborted();
929
+ if (!Number.isFinite(atimeMs) || !Number.isFinite(mtimeMs)) this.fail("EINVAL", "utimes", path);
930
+ const node = this.resolve(path, "utimes").node;
931
+ node.atimeMs = atimeMs;
932
+ node.mtimeMs = mtimeMs;
933
+ node.ctimeMs = Date.now();
934
+ }
935
+ async truncate(path, length = 0, options = {}) {
936
+ options.signal?.throwIfAborted();
937
+ this.integer(length, "truncate", path);
938
+ const node = this.file(path, "truncate");
939
+ this.permission(node, 2, "truncate", path);
940
+ const data = this.allocate(length, "truncate", path);
941
+ data.set(node.data.subarray(0, length));
942
+ node.data = data;
943
+ this.changed(node);
944
+ }
945
+ async *readStream(path, options = {}) {
946
+ options.signal?.throwIfAborted();
947
+ const start = options.start ?? 0;
948
+ const chunkSize = options.chunkSize ?? 64 * 1024;
949
+ this.integer(start, "readStream", path);
950
+ this.integer(chunkSize, "readStream", path);
951
+ if (chunkSize === 0) this.fail("EINVAL", "readStream", path);
952
+ if (options.endExclusive !== void 0) {
953
+ this.integer(options.endExclusive, "readStream", path);
954
+ if (options.endExclusive < start) this.fail("EINVAL", "readStream", path);
955
+ }
956
+ const node = this.file(path, "readStream");
957
+ this.permission(node, 4, "readStream", path);
958
+ const data = node.data;
959
+ const end = Math.min(options.endExclusive ?? data.byteLength, data.byteLength);
960
+ node.atimeMs = Date.now();
961
+ for (let offset = start; offset < end; offset += chunkSize) {
962
+ options.signal?.throwIfAborted();
963
+ yield data.slice(offset, Math.min(offset + chunkSize, end));
964
+ }
965
+ options.signal?.throwIfAborted();
966
+ }
967
+ async writeStream(path, source, options = {}) {
968
+ options.signal?.throwIfAborted();
969
+ const node = this.openWrite(path, options, "writeStream");
970
+ if (options.flag !== "a" && options.flag !== "ax") {
971
+ node.data = new Uint8Array();
972
+ this.changed(node);
973
+ }
974
+ for await (const chunk of source) {
975
+ options.signal?.throwIfAborted();
976
+ this.append(node, this.bytes(chunk), "writeStream", path);
977
+ }
978
+ options.signal?.throwIfAborted();
979
+ }
980
+ };
981
+ var memoryImplementation = Object.getOwnPropertyDescriptors(MemoryFileSystem.prototype);
982
+ function createMemoryFileSystem() {
983
+ return new MemoryFileSystem();
984
+ }
985
+
986
+ // packages/safe-fs/src/fs/readonly/index.ts
987
+ function readOnly(syscall, path, dest) {
988
+ throw new FsError("EROFS", { syscall, path, ...dest === void 0 ? {} : { dest } });
989
+ }
990
+ function snapshotStat(stat) {
991
+ const { type, size, allocatedBytes, mode: mode2, mtimeMs, atimeMs, ctimeMs, birthtimeMs, identityScope, ino, dev, nlink, uid, gid } = stat;
992
+ return {
993
+ type,
994
+ size,
995
+ mode: mode2,
996
+ mtimeMs,
997
+ atimeMs,
998
+ ctimeMs,
999
+ ...allocatedBytes === void 0 ? {} : { allocatedBytes },
1000
+ ...birthtimeMs === void 0 ? {} : { birthtimeMs },
1001
+ ...identityScope === void 0 ? {} : { identityScope },
1002
+ ...ino === void 0 ? {} : { ino },
1003
+ ...dev === void 0 ? {} : { dev },
1004
+ ...nlink === void 0 ? {} : { nlink },
1005
+ ...uid === void 0 ? {} : { uid },
1006
+ ...gid === void 0 ? {} : { gid }
1007
+ };
1008
+ }
1009
+ var ReadOnlyFileSystem = class {
1010
+ #filesystem;
1011
+ #capabilities;
1012
+ constructor(filesystem) {
1013
+ this.#filesystem = filesystem;
1014
+ registerEntryView(this, async (path) => ({ filesystem: this.#filesystem, path, readOnly: true }));
1015
+ const streamingRead = typeof filesystem.readStream === "function" ? filesystem.capabilities.streamingRead : false;
1016
+ this.#capabilities = Object.freeze({
1017
+ readOnly: true,
1018
+ symlinks: filesystem.capabilities.symlinks === true && typeof filesystem.readlink === "function",
1019
+ hardlinks: false,
1020
+ permissions: false,
1021
+ timestamps: false,
1022
+ atomicRename: false,
1023
+ ...streamingRead === void 0 ? {} : { streamingRead },
1024
+ streamingWrite: false
1025
+ });
1026
+ }
1027
+ get capabilities() {
1028
+ return this.#capabilities;
1029
+ }
1030
+ async readFile(path, options) {
1031
+ return new Uint8Array(await this.#filesystem.readFile(path, options));
1032
+ }
1033
+ async stat(path, options) {
1034
+ return snapshotStat(await this.#filesystem.stat(path, options));
1035
+ }
1036
+ async lstat(path, options) {
1037
+ return snapshotStat(await this.#filesystem.lstat(path, options));
1038
+ }
1039
+ compareEntry(path, peer, peerPath, options = {}) {
1040
+ return compareEntries(this, path, peer, peerPath, options);
1041
+ }
1042
+ async readdir(path, options) {
1043
+ return (await this.#filesystem.readdir(path, options)).map((entry) => ({ name: entry.name, type: entry.type }));
1044
+ }
1045
+ async realpath(path, options) {
1046
+ return this.#filesystem.realpath(path, options);
1047
+ }
1048
+ async access(path, mode2 = ACCESS_MODES.F_OK, options) {
1049
+ if (!Number.isInteger(mode2) || mode2 < 0 || mode2 > 7) {
1050
+ throw new FsError("EINVAL", { syscall: "access", path });
1051
+ }
1052
+ if ((mode2 & ACCESS_MODES.W_OK) !== 0) readOnly("access", path);
1053
+ return this.#filesystem.access(path, mode2, options);
1054
+ }
1055
+ async readlink(path, options) {
1056
+ if (typeof this.#filesystem.readlink !== "function") {
1057
+ throw new FsError("ENOTSUP", { syscall: "readlink", path });
1058
+ }
1059
+ return this.#filesystem.readlink(path, options);
1060
+ }
1061
+ async *readStream(path, options) {
1062
+ if (typeof this.#filesystem.readStream !== "function") {
1063
+ throw new FsError("ENOTSUP", { syscall: "readStream", path });
1064
+ }
1065
+ for await (const chunk of readBytes(this.#filesystem.readStream(path, options), options?.signal)) {
1066
+ yield new Uint8Array(chunk);
1067
+ }
1068
+ }
1069
+ async writeFile(path, _data, _options) {
1070
+ readOnly("writeFile", path);
1071
+ }
1072
+ async appendFile(path, _data, _options) {
1073
+ readOnly("appendFile", path);
1074
+ }
1075
+ async writeStream(path, _source, _options) {
1076
+ readOnly("writeStream", path);
1077
+ }
1078
+ async mkdir(path, _options) {
1079
+ readOnly("mkdir", path);
1080
+ }
1081
+ async rm(path, _options) {
1082
+ readOnly("rm", path);
1083
+ }
1084
+ async unlink(path, _options) {
1085
+ readOnly("unlink", path);
1086
+ }
1087
+ async rmdir(path, _options) {
1088
+ readOnly("rmdir", path);
1089
+ }
1090
+ async rename(source, destination, _options) {
1091
+ readOnly("rename", source, destination);
1092
+ }
1093
+ async copyFile(source, destination, _options) {
1094
+ readOnly("copyFile", source, destination);
1095
+ }
1096
+ async symlink(target, path, _options) {
1097
+ readOnly("symlink", target, path);
1098
+ }
1099
+ async link(existingPath, newPath, _options) {
1100
+ readOnly("link", existingPath, newPath);
1101
+ }
1102
+ async chmod(path, _mode, _options) {
1103
+ readOnly("chmod", path);
1104
+ }
1105
+ async utimes(path, _atimeMs, _mtimeMs, _options) {
1106
+ readOnly("utimes", path);
1107
+ }
1108
+ async truncate(path, _length, _options) {
1109
+ readOnly("truncate", path);
1110
+ }
1111
+ };
1112
+ function createReadOnlyFileSystem(filesystem) {
1113
+ return new ReadOnlyFileSystem(filesystem);
1114
+ }
1115
+
1116
+ // packages/safe-fs/src/fs/mount/index.ts
1117
+ var syntheticStat = Object.freeze({
1118
+ type: "directory",
1119
+ size: 0,
1120
+ mode: 16749,
1121
+ mtimeMs: 0,
1122
+ atimeMs: 0,
1123
+ ctimeMs: 0
1124
+ });
1125
+ function within(parent, path) {
1126
+ return parent === "/" || path === parent || path.startsWith(`${parent}/`);
1127
+ }
1128
+ function globalPath(path) {
1129
+ return typeof path === "string" && path.startsWith("/") ? path : `/${String(path)}`;
1130
+ }
1131
+ function fail(code) {
1132
+ throw new FsError(code);
1133
+ }
1134
+ function snapshotStat2(stat) {
1135
+ const { type, size, allocatedBytes, mode: mode2, mtimeMs, atimeMs, ctimeMs, birthtimeMs, identityScope, ino, dev, nlink, uid, gid } = stat;
1136
+ return {
1137
+ type,
1138
+ size,
1139
+ mode: mode2,
1140
+ mtimeMs,
1141
+ atimeMs,
1142
+ ctimeMs,
1143
+ ...allocatedBytes === void 0 ? {} : { allocatedBytes },
1144
+ ...birthtimeMs === void 0 ? {} : { birthtimeMs },
1145
+ ...identityScope === void 0 ? {} : { identityScope },
1146
+ ...ino === void 0 ? {} : { ino },
1147
+ ...dev === void 0 ? {} : { dev },
1148
+ ...nlink === void 0 ? {} : { nlink },
1149
+ ...uid === void 0 ? {} : { uid },
1150
+ ...gid === void 0 ? {} : { gid }
1151
+ };
1152
+ }
1153
+ var MountFileSystem = class _MountFileSystem {
1154
+ capabilities;
1155
+ mounts;
1156
+ constructor(options) {
1157
+ const mounts = [];
1158
+ const paths = /* @__PURE__ */ new Set();
1159
+ const add = (path, backend) => {
1160
+ if (backend instanceof _MountFileSystem) {
1161
+ for (const child of backend.mounts) {
1162
+ add(path === "/" ? child.path : child.path === "/" ? path : `${path}${child.path}`, child.backend);
1163
+ }
1164
+ return;
1165
+ }
1166
+ if (paths.has(path)) {
1167
+ throw new FsError("EINVAL", { syscall: "mount", path });
1168
+ }
1169
+ paths.add(path);
1170
+ mounts.push({ path, backend });
1171
+ };
1172
+ add("/", options.root);
1173
+ for (const [input, backend] of Object.entries(options.mounts ?? {})) {
1174
+ try {
1175
+ validatePath(input);
1176
+ } catch (error) {
1177
+ throw new FsError("EINVAL", { syscall: "mount", path: globalPath(input), cause: error });
1178
+ }
1179
+ const path = normalizePath(input);
1180
+ if (!input.startsWith("/") || path === "/") {
1181
+ throw new FsError("EINVAL", { syscall: "mount", path: globalPath(input) });
1182
+ }
1183
+ add(path, backend);
1184
+ }
1185
+ this.mounts = mounts.sort((left, right) => right.path.length - left.path.length);
1186
+ registerEntryView(this, (path, options2) => this.operation("compareEntry", path, options2, async () => {
1187
+ const location = await this.resolve(path, options2);
1188
+ if (location.synthetic) return { filesystem: this, path, stat: syntheticStat, readOnly: true };
1189
+ return { filesystem: location.mount.backend, path: location.local };
1190
+ }));
1191
+ registerEntryAuthority(this, async () => "unknown");
1192
+ const all = (capability, methods = []) => mounts.every(({ backend }) => backend.capabilities[capability] === true && methods.every((method) => typeof backend[method] === "function"));
1193
+ const streaming = (capability, method) => all(capability, [method]) ? true : mounts.some(({ backend }) => backend.capabilities[capability] !== false && typeof backend[method] === "function") ? void 0 : false;
1194
+ const streamingRead = streaming("streamingRead", "readStream");
1195
+ const streamingWrite = streaming("streamingWrite", "writeStream");
1196
+ this.capabilities = Object.freeze({
1197
+ get snapshotRmdir() {
1198
+ return mounts.some(({ backend }) => backend.capabilities.snapshotRmdir === true);
1199
+ },
1200
+ readOnly: all("readOnly"),
1201
+ symlinks: all("symlinks", ["readlink", "symlink"]),
1202
+ hardlinks: all("hardlinks", ["link"]),
1203
+ permissions: all("permissions", ["chmod"]),
1204
+ timestamps: all("timestamps", ["utimes"]),
1205
+ atomicRename: mounts.length === 1 && all("atomicRename"),
1206
+ ...streamingRead === void 0 ? {} : { streamingRead },
1207
+ ...streamingWrite === void 0 ? {} : { streamingWrite }
1208
+ });
1209
+ }
1210
+ select(path) {
1211
+ return this.mounts.find((mount) => within(mount.path, path));
1212
+ }
1213
+ protected(path) {
1214
+ return path === "/" || this.mounts.some((mount) => within(path, mount.path));
1215
+ }
1216
+ error(error, syscall, path, options, dest) {
1217
+ if (options.signal?.aborted && error === options.signal.reason) return error;
1218
+ return new FsError(toFsError(error).code, {
1219
+ cause: error,
1220
+ syscall,
1221
+ path: globalPath(path),
1222
+ ...dest === void 0 ? {} : { dest: globalPath(dest) }
1223
+ });
1224
+ }
1225
+ async operation(syscall, path, options, action, dest) {
1226
+ try {
1227
+ options.signal?.throwIfAborted();
1228
+ const result = await action();
1229
+ options.signal?.throwIfAborted();
1230
+ return result;
1231
+ } catch (error) {
1232
+ throw this.error(error, syscall, path, options, dest);
1233
+ }
1234
+ }
1235
+ components(path) {
1236
+ validatePath(path);
1237
+ if (!path) fail("ENOENT");
1238
+ const parts = path.split("/").filter(Boolean).map((name) => ({ name }));
1239
+ if (path.endsWith("/")) parts.push({ name: ".", trailing: true });
1240
+ return parts;
1241
+ }
1242
+ async lookup(path, options, parent, plannedParent = false) {
1243
+ options.signal?.throwIfAborted();
1244
+ const mount = this.select(path);
1245
+ const local = mount.path === "/" ? path : path.slice(mount.path.length) || "/";
1246
+ let stat;
1247
+ if (!parent?.synthetic && !plannedParent || parent?.mount !== mount) {
1248
+ try {
1249
+ stat = await mount.backend.lstat(local, options);
1250
+ } catch (error) {
1251
+ if (toFsError(error).code !== "ENOENT") throw error;
1252
+ }
1253
+ }
1254
+ options.signal?.throwIfAborted();
1255
+ if (path === mount.path && stat?.type !== "directory") fail(stat ? "ENOTDIR" : "ENOENT");
1256
+ const synthetic = this.protected(path) && stat?.type !== "directory";
1257
+ return { path, mount, local, stat: synthetic ? syntheticStat : stat, synthetic };
1258
+ }
1259
+ async resolve(path, options, settings = {}) {
1260
+ let pending = this.components(path);
1261
+ const stack = [await this.lookup("/", options)];
1262
+ let boundary;
1263
+ let verification;
1264
+ const verified = async (location) => {
1265
+ if (verification) {
1266
+ const canonical = await verification.mount.backend.realpath(verification.path, options);
1267
+ validatePath(canonical);
1268
+ if (!canonical.startsWith("/") || normalizePath(canonical) !== canonical) fail("EIO");
1269
+ if (verification.finalName !== void 0) {
1270
+ try {
1271
+ await verification.mount.backend.lstat(`${verification.path}/${verification.finalName}`, options);
1272
+ } catch (error) {
1273
+ if (location.stat || toFsError(error).code !== "ENOENT") throw error;
1274
+ }
1275
+ }
1276
+ const expected = verification.finalName === void 0 ? canonical : `${canonical === "/" ? "" : canonical}/${verification.finalName}`;
1277
+ if (location.mount !== verification.mount || location.local !== expected) fail("ENOTSUP");
1278
+ }
1279
+ return location;
1280
+ };
1281
+ let links = 0;
1282
+ while (pending.length > 0) {
1283
+ options.signal?.throwIfAborted();
1284
+ const component = pending.shift();
1285
+ const current = stack[stack.length - 1];
1286
+ if (current.stat?.type !== "directory") fail("ENOTDIR");
1287
+ if (!current.synthetic && !settings.createDirectories?.has(current.path)) {
1288
+ const mode2 = current.mount.backend.capabilities.permissions === false ? 0 : 1;
1289
+ await current.mount.backend.access(current.local, mode2, options);
1290
+ }
1291
+ if (component.name === ".") continue;
1292
+ if (component.name === "..") {
1293
+ if (boundary && current.path === boundary.path) fail("EACCES");
1294
+ if (stack.length > 1) stack.pop();
1295
+ if (boundary && stack[stack.length - 1].mount !== boundary) fail("EACCES");
1296
+ continue;
1297
+ }
1298
+ const nextPath = `${current.path === "/" ? "" : current.path}/${component.name}`;
1299
+ if (boundary && this.select(nextPath) !== boundary) fail("EACCES");
1300
+ let next = settings.createDirectories?.get(nextPath) ?? await this.lookup(nextPath, options, current, settings.createDirectories?.has(current.path));
1301
+ if (!next.stat) {
1302
+ if (settings.createDirectories) {
1303
+ if (current.synthetic) fail("ENOTSUP");
1304
+ this.mutable(next);
1305
+ next = { ...next, stat: syntheticStat };
1306
+ settings.createDirectories.set(nextPath, next);
1307
+ } else if (settings.allowMissing && (pending.length === 0 || settings.missingDirectory && pending.length === 1 && pending[0].trailing)) {
1308
+ if (current.synthetic) fail("ENOTSUP");
1309
+ return verified(next);
1310
+ } else {
1311
+ fail("ENOENT");
1312
+ }
1313
+ }
1314
+ const final = pending.length === 0 || settings.entry && pending.length === 1 && pending[0].trailing;
1315
+ if (next.stat?.type === "symlink" && (settings.followFinal !== false || !final)) {
1316
+ if (++links > 40) fail("ELOOP");
1317
+ boundary ??= next.mount;
1318
+ if (boundary !== next.mount) fail("EACCES");
1319
+ if (!verification) {
1320
+ const suffix = pending.map((part) => part.name);
1321
+ const last = suffix.at(-1);
1322
+ const finalName = settings.followFinal === false && last !== void 0 && last !== "." && last !== ".." ? suffix.pop() : void 0;
1323
+ verification = {
1324
+ mount: next.mount,
1325
+ path: `${next.local}${suffix.length === 0 ? "" : `/${suffix.join("/")}`}`,
1326
+ finalName
1327
+ };
1328
+ }
1329
+ const readlink = this.optional(next, "readlink", "symlinks");
1330
+ const target = await readlink.call(next.mount.backend, next.local, options);
1331
+ const targetParts = this.components(target);
1332
+ if (target.startsWith("/")) {
1333
+ const rootIndex = stack.findIndex((entry) => entry.path === next.mount.path);
1334
+ if (rootIndex < 0) fail("EACCES");
1335
+ stack.splice(rootIndex + 1);
1336
+ }
1337
+ pending = [...targetParts, ...pending];
1338
+ } else {
1339
+ stack.push(next);
1340
+ }
1341
+ }
1342
+ return verified(stack[stack.length - 1]);
1343
+ }
1344
+ mutable(location) {
1345
+ if (this.protected(location.path)) fail("EBUSY");
1346
+ if (location.mount.backend.capabilities.readOnly) fail("EROFS");
1347
+ }
1348
+ entryPath(path) {
1349
+ const terminal = path.split("/").filter(Boolean).at(-1);
1350
+ if (terminal === "." || terminal === "..") fail("EINVAL");
1351
+ }
1352
+ optional(location, method, capability) {
1353
+ const backend = location.mount.backend;
1354
+ const implementation = backend[method];
1355
+ if (!implementation || capability && backend.capabilities[capability] === false) fail("ENOTSUP");
1356
+ return implementation;
1357
+ }
1358
+ readFile(path, options = {}) {
1359
+ return this.operation("readFile", path, options, async () => {
1360
+ const location = await this.resolve(path, options);
1361
+ if (location.synthetic) fail("EISDIR");
1362
+ return location.mount.backend.readFile(location.local, options);
1363
+ });
1364
+ }
1365
+ writeFile(path, data, options = {}) {
1366
+ return this.operation("writeFile", path, options, async () => {
1367
+ const location = await this.resolve(path, options, { allowMissing: true, followFinal: !options.flag?.endsWith("x") });
1368
+ this.mutable(location);
1369
+ await location.mount.backend.writeFile(location.local, data, options);
1370
+ });
1371
+ }
1372
+ appendFile(path, data, options = {}) {
1373
+ return this.operation("appendFile", path, options, async () => {
1374
+ const location = await this.resolve(path, options, { allowMissing: true });
1375
+ this.mutable(location);
1376
+ await location.mount.backend.appendFile(location.local, data, options);
1377
+ });
1378
+ }
1379
+ stat(path, options = {}) {
1380
+ return this.operation("stat", path, options, async () => snapshotStat2((await this.resolve(path, options)).stat));
1381
+ }
1382
+ lstat(path, options = {}) {
1383
+ return this.operation("lstat", path, options, async () => snapshotStat2((await this.resolve(path, options, { followFinal: false })).stat));
1384
+ }
1385
+ readdir(path, options = {}) {
1386
+ return this.operation("readdir", path, options, async () => {
1387
+ const location = await this.resolve(path, options);
1388
+ if (location.stat?.type !== "directory") fail("ENOTDIR");
1389
+ const entries2 = /* @__PURE__ */ new Map();
1390
+ if (!location.synthetic) {
1391
+ for (const entry of await location.mount.backend.readdir(location.local, options)) {
1392
+ const { name, type } = entry;
1393
+ if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\0")) fail("EIO");
1394
+ entries2.set(name, { name, type });
1395
+ }
1396
+ }
1397
+ for (const mount of this.mounts) {
1398
+ if (mount.path !== location.path && within(location.path, mount.path)) {
1399
+ const suffix = mount.path.slice(location.path === "/" ? 1 : location.path.length + 1);
1400
+ const name = suffix.split("/")[0];
1401
+ entries2.set(name, { name, type: "directory" });
1402
+ }
1403
+ }
1404
+ return [...entries2.values()].sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
1405
+ });
1406
+ }
1407
+ mkdir(path, options = {}) {
1408
+ return this.operation("mkdir", path, options, async () => {
1409
+ const directories = /* @__PURE__ */ new Map();
1410
+ const location = await this.resolve(path, options, options.recursive ? { createDirectories: directories } : { followFinal: false, allowMissing: true, missingDirectory: true, entry: true });
1411
+ if (options.recursive) {
1412
+ if (location.stat?.type !== "directory") fail("EEXIST");
1413
+ for (const directory of directories.values()) {
1414
+ options.signal?.throwIfAborted();
1415
+ await directory.mount.backend.mkdir(directory.local, { ...options, recursive: false });
1416
+ }
1417
+ return;
1418
+ }
1419
+ this.mutable(location);
1420
+ await location.mount.backend.mkdir(location.local, options);
1421
+ });
1422
+ }
1423
+ rmdir(path, options = {}) {
1424
+ return this.operation("rmdir", path, options, async () => {
1425
+ const location = await this.resolve(path, options, { followFinal: false, entry: true });
1426
+ this.mutable(location);
1427
+ this.entryPath(path);
1428
+ if (location.stat?.type !== "directory") fail("ENOTDIR");
1429
+ const backend = location.mount.backend;
1430
+ if (!backend.rmdir) fail("ENOTSUP");
1431
+ options.signal?.throwIfAborted();
1432
+ await backend.rmdir(location.local, options);
1433
+ });
1434
+ }
1435
+ rm(path, options = {}) {
1436
+ return this.operation("rm", path, options, async () => {
1437
+ let location;
1438
+ try {
1439
+ location = await this.resolve(path, options, { followFinal: false, entry: true });
1440
+ } catch (error) {
1441
+ if (options.force && isFsError(error, "ENOENT")) return;
1442
+ throw error;
1443
+ }
1444
+ this.mutable(location);
1445
+ this.entryPath(path);
1446
+ await location.mount.backend.rm(location.local, options);
1447
+ });
1448
+ }
1449
+ rename(source, destination, options = {}) {
1450
+ return this.operation("rename", source, options, async () => {
1451
+ const origin = await this.resolve(source, options, { followFinal: false, entry: true });
1452
+ const target = await this.resolve(destination, options, {
1453
+ followFinal: false,
1454
+ allowMissing: true,
1455
+ entry: true,
1456
+ missingDirectory: origin.stat?.type === "directory"
1457
+ });
1458
+ if (this.protected(origin.path) || this.protected(target.path)) fail("EBUSY");
1459
+ if (origin.mount !== target.mount) fail("EXDEV");
1460
+ this.mutable(origin);
1461
+ this.mutable(target);
1462
+ this.entryPath(source);
1463
+ this.entryPath(destination);
1464
+ await origin.mount.backend.rename(origin.local, target.local, options);
1465
+ }, destination);
1466
+ }
1467
+ copyFile(source, destination, options = {}) {
1468
+ return this.operation("copyFile", source, options, async () => {
1469
+ const origin = await this.resolve(source, options);
1470
+ if (origin.stat?.type === "directory") fail("EISDIR");
1471
+ const target = await this.resolve(destination, options, { allowMissing: true, followFinal: !options.exclusive });
1472
+ if (this.protected(target.path)) fail("EBUSY");
1473
+ this.mutable(target);
1474
+ if (options.exclusive && target.stat) fail("EEXIST");
1475
+ if (origin.mount.backend === target.mount.backend && origin.local === target.local) fail("EINVAL");
1476
+ let identity = compareIdentity(origin.stat, target.stat);
1477
+ if (identity === "same") fail("EINVAL");
1478
+ if (target.stat?.type === "directory") fail("EISDIR");
1479
+ if (target.stat && identity === "unknown") {
1480
+ identity = await compareEntries(origin.mount.backend, origin.local, target.mount.backend, target.local, options);
1481
+ if (identity === "same") fail("EINVAL");
1482
+ }
1483
+ if (target.stat && identity === "unknown") fail("ENOTSUP");
1484
+ if (origin.mount.backend === target.mount.backend) {
1485
+ await origin.mount.backend.copyFile(origin.local, target.local, { ...options, exclusive: options.exclusive || !target.stat });
1486
+ return;
1487
+ }
1488
+ const reader = origin.mount.backend;
1489
+ const writer = target.mount.backend;
1490
+ const writeOptions = {
1491
+ ...options.signal ? { signal: options.signal } : {},
1492
+ flag: options.exclusive || !target.stat ? "wx" : "w",
1493
+ ...writer.capabilities.permissions === true ? { mode: origin.stat.mode & 4095 } : {}
1494
+ };
1495
+ if (reader.readStream && reader.capabilities.streamingRead !== false && writer.writeStream && writer.capabilities.streamingWrite !== false) {
1496
+ const source2 = readBytes(reader.readStream(origin.local, options), options.signal);
1497
+ let failed = false;
1498
+ try {
1499
+ await writer.writeStream(target.local, source2, writeOptions);
1500
+ } catch (error) {
1501
+ failed = true;
1502
+ throw error;
1503
+ } finally {
1504
+ await finishCleanup(() => source2.return(void 0), failed);
1505
+ }
1506
+ } else {
1507
+ const maxBytes = 64 * 1024 * 1024;
1508
+ const data = await reader.readFile(origin.local, { ...options, maxBytes });
1509
+ if (data.byteLength > maxBytes) fail("EFBIG");
1510
+ options.signal?.throwIfAborted();
1511
+ await writer.writeFile(target.local, data, writeOptions);
1512
+ }
1513
+ }, destination);
1514
+ }
1515
+ realpath(path, options = {}) {
1516
+ return this.operation("realpath", path, options, async () => (await this.resolve(path, options)).path);
1517
+ }
1518
+ compareEntry(path, peer, peerPath, options = {}) {
1519
+ return this.operation("compareEntry", path, options, () => compareEntries(this, path, peer, peerPath, options), peerPath);
1520
+ }
1521
+ access(path, mode2 = 0, options = {}) {
1522
+ return this.operation("access", path, options, async () => {
1523
+ if (!Number.isInteger(mode2) || mode2 < 0 || mode2 > 7) fail("EINVAL");
1524
+ const location = await this.resolve(path, options);
1525
+ if (location.synthetic) {
1526
+ if (mode2 & 2) fail("EACCES");
1527
+ } else {
1528
+ await location.mount.backend.access(location.local, mode2, options);
1529
+ }
1530
+ });
1531
+ }
1532
+ readlink(path, options = {}) {
1533
+ return this.operation("readlink", path, options, async () => {
1534
+ const location = await this.resolve(path, options, { followFinal: false });
1535
+ if (location.stat?.type !== "symlink") fail("EINVAL");
1536
+ return this.optional(location, "readlink", "symlinks").call(location.mount.backend, location.local, options);
1537
+ });
1538
+ }
1539
+ symlink(target, path, options = {}) {
1540
+ return this.operation("symlink", path, options, async () => {
1541
+ this.components(target);
1542
+ const location = await this.resolve(path, options, { followFinal: false, allowMissing: true });
1543
+ this.mutable(location);
1544
+ await this.optional(location, "symlink", "symlinks").call(location.mount.backend, target, location.local, options);
1545
+ });
1546
+ }
1547
+ link(existingPath, newPath, options = {}) {
1548
+ return this.operation("link", existingPath, options, async () => {
1549
+ const origin = await this.resolve(existingPath, options, { followFinal: false, entry: true });
1550
+ const target = await this.resolve(newPath, options, { followFinal: false, allowMissing: true, entry: true });
1551
+ if (this.protected(origin.path) || this.protected(target.path)) fail("EBUSY");
1552
+ if (origin.mount !== target.mount) fail("EXDEV");
1553
+ this.mutable(origin);
1554
+ this.mutable(target);
1555
+ await this.optional(origin, "link", "hardlinks").call(origin.mount.backend, origin.local, target.local, options);
1556
+ }, newPath);
1557
+ }
1558
+ chmod(path, mode2, options = {}) {
1559
+ return this.operation("chmod", path, options, async () => {
1560
+ const location = await this.resolve(path, options);
1561
+ this.mutable(location);
1562
+ await this.optional(location, "chmod", "permissions").call(location.mount.backend, location.local, mode2, options);
1563
+ });
1564
+ }
1565
+ utimes(path, atimeMs, mtimeMs, options = {}) {
1566
+ return this.operation("utimes", path, options, async () => {
1567
+ const location = await this.resolve(path, options);
1568
+ this.mutable(location);
1569
+ await this.optional(location, "utimes", "timestamps").call(location.mount.backend, location.local, atimeMs, mtimeMs, options);
1570
+ });
1571
+ }
1572
+ truncate(path, length = 0, options = {}) {
1573
+ return this.operation("truncate", path, options, async () => {
1574
+ const location = await this.resolve(path, options);
1575
+ this.mutable(location);
1576
+ await this.optional(location, "truncate").call(location.mount.backend, location.local, length, options);
1577
+ });
1578
+ }
1579
+ async *readStream(path, options = {}) {
1580
+ try {
1581
+ options.signal?.throwIfAborted();
1582
+ const location = await this.resolve(path, options);
1583
+ if (location.synthetic) fail("EISDIR");
1584
+ const source = this.optional(location, "readStream", "streamingRead").call(location.mount.backend, location.local, options);
1585
+ for await (const chunk of readBytes(source, options.signal)) {
1586
+ options.signal?.throwIfAborted();
1587
+ yield chunk;
1588
+ }
1589
+ options.signal?.throwIfAborted();
1590
+ } catch (error) {
1591
+ throw this.error(error, "readStream", path, options);
1592
+ }
1593
+ }
1594
+ writeStream(path, source, options = {}) {
1595
+ return this.operation("writeStream", path, options, async () => {
1596
+ const location = await this.resolve(path, options, { allowMissing: true, followFinal: !options.flag?.endsWith("x") });
1597
+ this.mutable(location);
1598
+ await this.optional(location, "writeStream", "streamingWrite").call(location.mount.backend, location.local, source, options);
1599
+ });
1600
+ }
1601
+ };
1602
+ function createMountFileSystem(options) {
1603
+ return new MountFileSystem(options);
1604
+ }
1605
+
1606
+ // packages/safe-fs/src/fs/overlay/index.ts
1607
+ function snapshotStat3(stat) {
1608
+ const { type, size, allocatedBytes, mode: mode2, mtimeMs, atimeMs, ctimeMs, birthtimeMs, identityScope, ino, dev, nlink, uid, gid } = stat;
1609
+ return {
1610
+ type,
1611
+ size,
1612
+ mode: mode2,
1613
+ mtimeMs,
1614
+ atimeMs,
1615
+ ctimeMs,
1616
+ ...allocatedBytes === void 0 ? {} : { allocatedBytes },
1617
+ ...birthtimeMs === void 0 ? {} : { birthtimeMs },
1618
+ ...identityScope === void 0 ? {} : { identityScope },
1619
+ ...ino === void 0 ? {} : { ino },
1620
+ ...dev === void 0 ? {} : { dev },
1621
+ ...nlink === void 0 ? {} : { nlink },
1622
+ ...uid === void 0 ? {} : { uid },
1623
+ ...gid === void 0 ? {} : { gid }
1624
+ };
1625
+ }
1626
+ function hasCode(error, code) {
1627
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
1628
+ }
1629
+ function fail2(code, path, message) {
1630
+ throw new FsError(code, { syscall: "overlay", path, ...message === void 0 ? {} : { message } });
1631
+ }
1632
+ function integer(value, path) {
1633
+ if (!Number.isSafeInteger(value) || value < 0) fail2("EINVAL", path);
1634
+ }
1635
+ function mode(value, path) {
1636
+ if (value !== void 0 && (!Number.isInteger(value) || value < 0 || value > 4095)) fail2("EINVAL", path);
1637
+ }
1638
+ function movedPath(path, moves) {
1639
+ for (const move of moves) {
1640
+ if (isPathWithin(move.source, path)) path = move.destination + path.slice(move.source.length);
1641
+ }
1642
+ return path;
1643
+ }
1644
+ async function waitForTurn(previous, signal) {
1645
+ signal?.throwIfAborted();
1646
+ if (!signal) return previous;
1647
+ return new Promise((resolve, reject) => {
1648
+ const abort = () => {
1649
+ signal.removeEventListener("abort", abort);
1650
+ reject(signal.reason);
1651
+ };
1652
+ signal.addEventListener("abort", abort, { once: true });
1653
+ void previous.then(() => {
1654
+ signal.removeEventListener("abort", abort);
1655
+ resolve();
1656
+ });
1657
+ });
1658
+ }
1659
+ var OverlayFileSystem = class {
1660
+ capabilities;
1661
+ #upper;
1662
+ #lower;
1663
+ maxBufferBytes;
1664
+ whiteouts = /* @__PURE__ */ new Set();
1665
+ opaque = /* @__PURE__ */ new Set();
1666
+ garbage = /* @__PURE__ */ new Set();
1667
+ activeStages = /* @__PURE__ */ new Set();
1668
+ linkMetadata = /* @__PURE__ */ new Map();
1669
+ linkOrigins = /* @__PURE__ */ new Map();
1670
+ queue = Promise.resolve();
1671
+ constructor(options) {
1672
+ if (options.upper === options.lower) throw new TypeError("Overlay upper and lower must be distinct backends");
1673
+ this.#upper = options.upper;
1674
+ this.#lower = options.lower;
1675
+ registerEntryView(this, (path, options2) => this.run(options2, async () => {
1676
+ const entry = await this.required(path, options2);
1677
+ return { filesystem: entry.backend, path: entry.path, readOnly: this.capabilities.readOnly === true };
1678
+ }, false));
1679
+ this.maxBufferBytes = options.maxBufferBytes ?? 64 * 1024 * 1024;
1680
+ integer(this.maxBufferBytes, "/");
1681
+ const writable = !this.#upper.capabilities.readOnly && this.#upper.capabilities.atomicRename === true;
1682
+ const readable = [this.#upper, this.#lower].map((backend) => typeof backend.readStream === "function" ? backend.capabilities.streamingRead : false);
1683
+ const streamingRead = readable.every((capability) => capability === true) ? true : readable.every((capability) => capability === false) ? false : void 0;
1684
+ const streamingWrite = writable && this.#upper.capabilities.streamingWrite === true && this.#upper.capabilities.streamingRead === true && typeof this.#upper.writeStream === "function" && typeof this.#upper.readStream === "function" ? readable.every((capability) => capability === true) ? true : void 0 : false;
1685
+ this.capabilities = Object.freeze({
1686
+ readOnly: !writable,
1687
+ atomicRename: false,
1688
+ hardlinks: false,
1689
+ symlinks: writable && this.#upper.capabilities.symlinks === true && typeof this.#upper.symlink === "function" && typeof this.#upper.readlink === "function" && (this.#lower.capabilities.symlinks !== true || typeof this.#lower.readlink === "function"),
1690
+ permissions: writable && this.#upper.capabilities.permissions === true && typeof this.#upper.chmod === "function",
1691
+ timestamps: writable && this.#upper.capabilities.timestamps === true && typeof this.#upper.utimes === "function",
1692
+ ...streamingRead === void 0 ? {} : { streamingRead },
1693
+ ...streamingWrite === void 0 ? {} : { streamingWrite }
1694
+ });
1695
+ Object.defineProperty(this, "capabilities", { writable: false, configurable: false });
1696
+ }
1697
+ async run(options, operation, cleanup = true) {
1698
+ options.signal?.throwIfAborted();
1699
+ const previous = this.queue;
1700
+ let release;
1701
+ this.queue = new Promise((resolve) => {
1702
+ release = resolve;
1703
+ });
1704
+ try {
1705
+ await waitForTurn(previous, options.signal);
1706
+ } catch (error) {
1707
+ void previous.then(release);
1708
+ throw error;
1709
+ }
1710
+ try {
1711
+ options.signal?.throwIfAborted();
1712
+ if (cleanup) await this.cleanGarbage(false);
1713
+ return await operation();
1714
+ } finally {
1715
+ release();
1716
+ }
1717
+ }
1718
+ writable(path) {
1719
+ if (this.#upper.capabilities.readOnly) fail2("EROFS", path);
1720
+ if (this.#upper.capabilities.atomicRename !== true) fail2("ENOTSUP", path, "staged mutations require an atomic-rename-capable upper");
1721
+ }
1722
+ permission(entry, mask) {
1723
+ if (entry.backend.capabilities.permissions && (entry.stat.mode >> 6 & mask) !== mask) fail2("EACCES", entry.path);
1724
+ }
1725
+ async maybeStat(backend, path, options) {
1726
+ options.signal?.throwIfAborted();
1727
+ let stat;
1728
+ try {
1729
+ stat = await backend.lstat(path, options);
1730
+ } catch (error) {
1731
+ options.signal?.throwIfAborted();
1732
+ if (hasCode(error, "ENOENT")) return void 0;
1733
+ throw error;
1734
+ }
1735
+ return snapshotStat3(stat);
1736
+ }
1737
+ hidden(path) {
1738
+ return [...this.garbage, ...this.activeStages].some((root) => isPathWithin(root, path));
1739
+ }
1740
+ async lowerVisible(path, options) {
1741
+ for (const root of this.whiteouts) if (isPathWithin(root, path)) return false;
1742
+ for (const root of this.opaque) if (root !== path && isPathWithin(root, path)) return false;
1743
+ const ancestors = path.split("/").filter(Boolean).slice(0, -1);
1744
+ let parent = "";
1745
+ for (const component of ancestors) {
1746
+ parent += `/${component}`;
1747
+ const stat = await this.maybeStat(this.#lower, parent, options);
1748
+ if (!stat || stat.type !== "directory") return false;
1749
+ }
1750
+ return true;
1751
+ }
1752
+ async lookup(path, options) {
1753
+ if (this.hidden(path)) return void 0;
1754
+ const upper = await this.maybeStat(this.#upper, path, options);
1755
+ if (upper) return { path, backend: this.#upper, stat: { ...upper, ...upper.type === "symlink" ? this.linkMetadata.get(path) : {} } };
1756
+ if (!await this.lowerVisible(path, options)) return void 0;
1757
+ const lower = await this.maybeStat(this.#lower, path, options);
1758
+ return lower ? { path, backend: this.#lower, stat: lower } : void 0;
1759
+ }
1760
+ async resolve(path, options, followFinal = true, allowMissing = false, createMode) {
1761
+ validatePath(path);
1762
+ if (!path) fail2("ENOENT", path);
1763
+ const pending = path.split("/").filter(Boolean);
1764
+ if (path.endsWith("/")) pending.push(".");
1765
+ const verifications = [];
1766
+ const verified = async (location) => {
1767
+ for (const verification of verifications) {
1768
+ const canonical = await verification.backend.realpath(verification.path, options);
1769
+ options.signal?.throwIfAborted();
1770
+ validatePath(canonical);
1771
+ if (!canonical.startsWith("/")) fail2("EIO", verification.path, "backend returned a nonabsolute realpath");
1772
+ if (verification.finalName !== void 0) {
1773
+ try {
1774
+ await verification.backend.lstat(`${verification.path}/${verification.finalName}`, options);
1775
+ } catch (error) {
1776
+ if (location.entry || !hasCode(error, "ENOENT")) throw error;
1777
+ }
1778
+ }
1779
+ const expected = verification.finalName === void 0 ? canonical : `${canonical === "/" ? "" : canonical}/${verification.finalName}`;
1780
+ if (location.path !== movedPath(expected, verification.moves ?? [])) fail2("ENOTSUP", path, "backend cannot confirm the overlay symlink namespace");
1781
+ }
1782
+ options.signal?.throwIfAborted();
1783
+ return location;
1784
+ };
1785
+ let current = "/";
1786
+ let links = 0;
1787
+ while (pending.length) {
1788
+ options.signal?.throwIfAborted();
1789
+ const parent = await this.lookup(current, options);
1790
+ if (!parent) fail2("ENOENT", current);
1791
+ if (parent.stat.type !== "directory") fail2("ENOTDIR", current);
1792
+ this.permission(parent, 1);
1793
+ const component = pending.shift();
1794
+ if (component === ".") continue;
1795
+ if (component === "..") {
1796
+ current = dirname(current);
1797
+ continue;
1798
+ }
1799
+ if (new TextEncoder().encode(component).byteLength > 255) fail2("ENAMETOOLONG", path);
1800
+ const candidate = current === "/" ? `/${component}` : `${current}/${component}`;
1801
+ if (this.hidden(candidate)) fail2(allowMissing || createMode !== void 0 ? "EBUSY" : "ENOENT", candidate);
1802
+ let entry2 = await this.lookup(candidate, options);
1803
+ if (!entry2 && createMode !== void 0) {
1804
+ if (verifications.length) fail2("ENOTSUP", path, "recursive creation through symlinks cannot be verified before mutation");
1805
+ await this.makeDirectory(candidate, createMode, options);
1806
+ entry2 = await this.lookup(candidate, options);
1807
+ }
1808
+ if (!entry2) {
1809
+ if (allowMissing && pending.length === 0) return verified({ path: candidate });
1810
+ fail2("ENOENT", candidate);
1811
+ }
1812
+ if (entry2.stat.type === "symlink" && (followFinal || pending.length > 0)) {
1813
+ if (++links > 40) fail2("ELOOP", path);
1814
+ const suffix = [...pending];
1815
+ const last = suffix.at(-1);
1816
+ const finalName = !followFinal && last !== void 0 && last !== "." && last !== ".." ? suffix.pop() : void 0;
1817
+ verifications.push({
1818
+ backend: entry2.backend,
1819
+ path: candidate + (suffix.length ? `/${suffix.join("/")}` : ""),
1820
+ finalName
1821
+ });
1822
+ const origin = entry2.backend === this.#upper ? this.linkOrigins.get(candidate) : void 0;
1823
+ if (origin) verifications.push({
1824
+ backend: this.#lower,
1825
+ path: origin.path + (suffix.length ? `/${suffix.join("/")}` : ""),
1826
+ finalName,
1827
+ moves: origin.moves
1828
+ });
1829
+ const target = await this.linkTarget(entry2, options);
1830
+ const parts = target.split("/").filter(Boolean);
1831
+ if (target.endsWith("/")) parts.push(".");
1832
+ pending.unshift(...parts);
1833
+ if (target.startsWith("/")) current = "/";
1834
+ } else current = candidate;
1835
+ }
1836
+ const entry = await this.lookup(current, options);
1837
+ if (!entry) fail2("ENOENT", current);
1838
+ return verified({ path: current, entry });
1839
+ }
1840
+ async linkTarget(entry, options) {
1841
+ if (!entry.backend.readlink) fail2("ENOTSUP", entry.path);
1842
+ const target = await entry.backend.readlink(entry.path, options);
1843
+ options.signal?.throwIfAborted();
1844
+ validatePath(target);
1845
+ if (!target) fail2("ENOENT", entry.path);
1846
+ return target;
1847
+ }
1848
+ async movableLinkTarget(entry, options) {
1849
+ const target = await this.linkTarget(entry, options);
1850
+ const canonical = await entry.backend.realpath(entry.path, options);
1851
+ options.signal?.throwIfAborted();
1852
+ const parts = target.split("/").filter(Boolean);
1853
+ if (target.endsWith("/")) parts.push(".");
1854
+ let current = target.startsWith("/") ? "/" : dirname(entry.path);
1855
+ let stat = await this.maybeStat(entry.backend, current, options);
1856
+ for (const part of parts) {
1857
+ if (!stat) fail2("ENOENT", current);
1858
+ if (stat.type !== "directory") fail2("ENOTSUP", entry.path, "symlink copy-up requires an alias-free target path");
1859
+ if (part === ".") continue;
1860
+ current = part === ".." ? dirname(current) : `${current === "/" ? "" : current}/${part}`;
1861
+ stat = await this.maybeStat(entry.backend, current, options);
1862
+ if (stat?.type === "symlink") fail2("ENOTSUP", entry.path, "symlink copy-up cannot preserve an ambiguous target alias");
1863
+ }
1864
+ if (!stat) fail2("ENOENT", current);
1865
+ if (canonical !== current) fail2("ENOTSUP", entry.path, "backend cannot prove symlink semantics survive copy-up");
1866
+ return target;
1867
+ }
1868
+ async required(path, options, followFinal = true) {
1869
+ return (await this.resolve(path, options, followFinal)).entry;
1870
+ }
1871
+ async listing(entry, options) {
1872
+ if (entry.stat.type !== "directory") fail2("ENOTDIR", entry.path);
1873
+ this.permission(entry, 4);
1874
+ const names = /* @__PURE__ */ new Set();
1875
+ const upper = await this.maybeStat(this.#upper, entry.path, options);
1876
+ if (upper?.type === "directory") {
1877
+ for (const child of await this.#upper.readdir(entry.path, options)) names.add(child.name);
1878
+ }
1879
+ if (await this.lowerVisible(entry.path, options) && !this.opaque.has(entry.path)) {
1880
+ const lower = await this.maybeStat(this.#lower, entry.path, options);
1881
+ if (lower?.type === "directory") {
1882
+ for (const child of await this.#lower.readdir(entry.path, options)) names.add(child.name);
1883
+ }
1884
+ }
1885
+ const entries2 = [];
1886
+ for (const name of [...names].sort()) {
1887
+ if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\0")) fail2("EIO", entry.path, "invalid backend directory entry");
1888
+ const child = await this.lookup(entry.path === "/" ? `/${name}` : `${entry.path}/${name}`, options);
1889
+ if (child) entries2.push({ name, type: child.stat.type });
1890
+ }
1891
+ return entries2;
1892
+ }
1893
+ async bytes(entry, options) {
1894
+ if (entry.stat.type !== "file") fail2("EISDIR", entry.path);
1895
+ this.permission(entry, 4);
1896
+ if (options.maxBytes !== void 0) integer(options.maxBytes, entry.path);
1897
+ const limit = Math.min(options.maxBytes ?? this.maxBufferBytes, this.maxBufferBytes);
1898
+ if (entry.stat.size > limit) fail2("EFBIG", entry.path);
1899
+ const result = await entry.backend.readFile(entry.path, { ...options, maxBytes: limit });
1900
+ options.signal?.throwIfAborted();
1901
+ if (!(result instanceof Uint8Array)) fail2("EIO", entry.path, "backend returned non-byte data");
1902
+ if (result.byteLength > limit) fail2("EFBIG", entry.path);
1903
+ return new Uint8Array(result);
1904
+ }
1905
+ async cleanGarbage(strict) {
1906
+ const failures = [];
1907
+ for (const path of this.garbage) {
1908
+ try {
1909
+ await this.#upper.rm(path, { recursive: true, force: true });
1910
+ this.garbage.delete(path);
1911
+ } catch (error) {
1912
+ failures.push(error);
1913
+ }
1914
+ }
1915
+ if (strict && failures.length) throw new AggregateError(failures, "Overlay staging cleanup failed");
1916
+ }
1917
+ async cleanup(options = {}) {
1918
+ return this.run(options, () => this.cleanGarbage(true));
1919
+ }
1920
+ async staged(options, operation) {
1921
+ const root = `/.virtual-bash-overlay-${platform.randomUUID()}`;
1922
+ if (await this.maybeStat(this.#upper, root, options) || await this.maybeStat(this.#lower, root, options)) fail2("EEXIST", root);
1923
+ this.activeStages.add(root);
1924
+ try {
1925
+ await this.#upper.mkdir(root, { ...options, mode: 448 });
1926
+ return await operation(`${root}/entry`);
1927
+ } finally {
1928
+ this.activeStages.delete(root);
1929
+ this.garbage.add(root);
1930
+ await this.cleanGarbage(false);
1931
+ }
1932
+ }
1933
+ async preserve(path, stat, options) {
1934
+ if (this.#upper.chmod) await this.#upper.chmod(path, stat.mode & 4095, options);
1935
+ else if (((await this.#upper.lstat(path, options)).mode & 4095) !== (stat.mode & 4095)) fail2("ENOTSUP", path, "upper cannot preserve modes");
1936
+ if (!this.#upper.utimes) fail2("ENOTSUP", path, "upper cannot preserve timestamps during copy-up");
1937
+ await this.#upper.utimes(path, stat.atimeMs, stat.mtimeMs, options);
1938
+ }
1939
+ async clone(entry, destination, options, streaming = false) {
1940
+ if (entry.stat.type !== "directory" && ((entry.stat.nlink ?? 1) > 1 || entry.backend.capabilities.hardlinks === true && entry.stat.nlink === void 0)) {
1941
+ fail2("ENOTSUP", entry.path, "copy-up cannot preserve hardlink identity");
1942
+ }
1943
+ if (entry.stat.type === "symlink") {
1944
+ if (!this.capabilities.symlinks || !entry.backend.readlink || !this.#upper.symlink) fail2("ENOTSUP", entry.path);
1945
+ await this.#upper.symlink(await this.movableLinkTarget(entry, options), destination, options);
1946
+ return;
1947
+ }
1948
+ if (entry.stat.type === "directory") await this.#upper.mkdir(destination, { ...options, mode: entry.stat.mode & 4095 });
1949
+ else if (streaming && entry.backend.readStream && entry.backend.capabilities.streamingRead !== false && this.#upper.writeStream) {
1950
+ this.permission(entry, 4);
1951
+ if (entry.stat.size > this.maxBufferBytes) fail2("EFBIG", entry.path);
1952
+ const source = this.bounded(entry.backend.readStream(entry.path, options), entry.path, options);
1953
+ await this.streamToUpper(destination, source, { ...options, mode: 384, flag: "wx" });
1954
+ } else await this.#upper.writeFile(destination, await this.bytes(entry, options), { ...options, mode: 384, flag: "wx" });
1955
+ await this.preserve(destination, entry.stat, options);
1956
+ }
1957
+ rememberLink(entry, destination) {
1958
+ this.linkMetadata.delete(destination);
1959
+ if (entry.stat.type === "symlink") {
1960
+ this.linkMetadata.set(destination, { mode: entry.stat.mode, atimeMs: entry.stat.atimeMs, mtimeMs: entry.stat.mtimeMs });
1961
+ }
1962
+ }
1963
+ async ensureDirectory(path, options) {
1964
+ const entry = await this.lookup(path, options);
1965
+ if (!entry) fail2("ENOENT", path);
1966
+ if (entry.stat.type !== "directory") fail2("ENOTDIR", path);
1967
+ if (entry.backend === this.#upper) return;
1968
+ await this.copyUp(entry, options);
1969
+ }
1970
+ async copyUp(entry, options) {
1971
+ if (entry.backend === this.#upper) return;
1972
+ if (entry.path === "/") fail2("ENOTSUP", entry.path, "upper must have a directory root");
1973
+ let origin;
1974
+ if (entry.stat.type === "symlink") {
1975
+ const target = await this.movableLinkTarget(entry, options);
1976
+ origin = { path: entry.path, target, canonical: normalizePath(target, dirname(entry.path)), moves: [] };
1977
+ }
1978
+ await this.ensureDirectory(dirname(entry.path), options);
1979
+ await this.staged(options, async (temporary) => {
1980
+ await this.clone(entry, temporary, options);
1981
+ options.signal?.throwIfAborted();
1982
+ await this.#upper.rename(temporary, entry.path, options);
1983
+ this.rememberLink(entry, entry.path);
1984
+ if (origin) this.linkOrigins.set(entry.path, origin);
1985
+ });
1986
+ }
1987
+ async parent(path, options) {
1988
+ const parent = await this.lookup(dirname(path), options);
1989
+ if (!parent) fail2("ENOENT", dirname(path));
1990
+ if (parent.stat.type !== "directory") fail2("ENOTDIR", dirname(path));
1991
+ this.permission(parent, 3);
1992
+ await this.ensureDirectory(parent.path, options);
1993
+ }
1994
+ async makeDirectory(path, permissions, options) {
1995
+ await this.parent(path, options);
1996
+ await this.staged(options, async (temporary) => {
1997
+ await this.#upper.mkdir(temporary, { ...options, mode: permissions });
1998
+ options.signal?.throwIfAborted();
1999
+ await this.#upper.rename(temporary, path, options);
2000
+ this.opaque.add(path);
2001
+ this.linkMetadata.delete(path);
2002
+ this.linkOrigins.delete(path);
2003
+ });
2004
+ }
2005
+ writeOptions(path, options) {
2006
+ this.writable(path);
2007
+ mode(options.mode, path);
2008
+ if (!["w", "wx", "a", "ax"].includes(options.flag ?? "w")) fail2("EINVAL", path);
2009
+ }
2010
+ async writeLocation(path, options) {
2011
+ const exclusive = options.flag === "wx" || options.flag === "ax";
2012
+ const location = await this.resolve(path, options, !exclusive, true);
2013
+ if (exclusive && location.entry) fail2("EEXIST", path);
2014
+ if (location.entry) {
2015
+ if (location.entry.stat.type !== "file") fail2("EISDIR", path);
2016
+ this.permission(location.entry, 2);
2017
+ }
2018
+ return location;
2019
+ }
2020
+ async replace(location, options, operation, streaming = false) {
2021
+ await this.parent(location.path, options);
2022
+ await this.staged(options, async (temporary) => {
2023
+ if (location.entry) await this.clone(location.entry, temporary, options, streaming);
2024
+ await operation(temporary);
2025
+ options.signal?.throwIfAborted();
2026
+ await this.#upper.rename(temporary, location.path, options);
2027
+ this.linkMetadata.delete(location.path);
2028
+ this.linkOrigins.delete(location.path);
2029
+ });
2030
+ }
2031
+ async readFile(path, options = {}) {
2032
+ return this.run(options, async () => this.bytes(await this.required(path, options), options));
2033
+ }
2034
+ async writeFile(path, data, options = {}) {
2035
+ options.signal?.throwIfAborted();
2036
+ this.writeOptions(path, options);
2037
+ if (!(data instanceof Uint8Array)) throw new TypeError("Overlay files require Uint8Array data");
2038
+ if (data.byteLength > this.maxBufferBytes) fail2("EFBIG", path);
2039
+ const bytes = new Uint8Array(data);
2040
+ return this.run(options, async () => {
2041
+ const location = await this.writeLocation(path, options);
2042
+ const append = options.flag === "a" || options.flag === "ax";
2043
+ if (append && (location.entry?.stat.size ?? 0) + bytes.byteLength > this.maxBufferBytes) fail2("EFBIG", path);
2044
+ await this.replace(location, options, async (temporary) => {
2045
+ await this.#upper.writeFile(temporary, bytes, { ...options, flag: append ? "a" : "w" });
2046
+ });
2047
+ });
2048
+ }
2049
+ async appendFile(path, data, options = {}) {
2050
+ return this.writeFile(path, data, { ...options, flag: "a" });
2051
+ }
2052
+ async stat(path, options = {}) {
2053
+ return this.run(options, async () => snapshotStat3((await this.required(path, options)).stat), false);
2054
+ }
2055
+ async lstat(path, options = {}) {
2056
+ return this.run(options, async () => snapshotStat3((await this.required(path, options, false)).stat), false);
2057
+ }
2058
+ compareEntry(path, peer, peerPath, options = {}) {
2059
+ return compareEntries(this, path, peer, peerPath, options);
2060
+ }
2061
+ async readdir(path, options = {}) {
2062
+ return this.run(options, async () => this.listing(await this.required(path, options), options), false);
2063
+ }
2064
+ async realpath(path, options = {}) {
2065
+ return this.run(options, async () => (await this.required(path, options)).path, false);
2066
+ }
2067
+ async access(path, requestedMode = 0, options = {}) {
2068
+ return this.run(options, async () => {
2069
+ if (!Number.isInteger(requestedMode) || requestedMode < 0 || requestedMode > 7) fail2("EINVAL", path);
2070
+ const entry = await this.required(path, options);
2071
+ if (requestedMode & 2) this.writable(path);
2072
+ this.permission(entry, requestedMode);
2073
+ await entry.backend.access(entry.path, requestedMode & ~2, options);
2074
+ }, false);
2075
+ }
2076
+ async mkdir(path, options = {}) {
2077
+ return this.run(options, async () => {
2078
+ this.writable(path);
2079
+ mode(options.mode, path);
2080
+ validatePath(path);
2081
+ if (options.recursive) {
2082
+ const entry = (await this.resolve(path, options, true, false, options.mode ?? 511)).entry;
2083
+ if (entry.stat.type !== "directory") fail2("EEXIST", path);
2084
+ } else {
2085
+ const location = await this.resolve(path.replace(/\/+$/, "") || (path ? "/" : ""), options, false, true);
2086
+ if (location.entry) fail2("EEXIST", path);
2087
+ await this.makeDirectory(location.path, options.mode ?? 511, options);
2088
+ }
2089
+ });
2090
+ }
2091
+ async rmdir(path, options = {}) {
2092
+ try {
2093
+ await this.run(options, async () => {
2094
+ this.writable(path);
2095
+ validatePath(path);
2096
+ const entry = await this.required(path.replace(/\/+$/, "") || (path ? "/" : ""), options, false);
2097
+ if (/(?:^|\/)\.{1,2}\/*$/.test(path)) fail2("EINVAL", path);
2098
+ if (entry.path === "/") fail2("EBUSY", path);
2099
+ if (entry.stat.type !== "directory") fail2("ENOTDIR", path);
2100
+ const parent = await this.required(dirname(entry.path), options);
2101
+ this.permission(parent, 3);
2102
+ if ((await this.listing(entry, options)).length) fail2("ENOTEMPTY", path);
2103
+ options.signal?.throwIfAborted();
2104
+ if (this.#upper.capabilities.snapshotRmdir === true) {
2105
+ fail2("ENOTSUP", path, "snapshot-marker upper removal cannot safely publish an overlay whiteout");
2106
+ }
2107
+ if (entry.backend === this.#upper) {
2108
+ if (!this.#upper.rmdir) fail2("ENOTSUP", path);
2109
+ await this.#upper.rmdir(entry.path, options);
2110
+ }
2111
+ this.whiteouts.add(entry.path);
2112
+ for (const key of this.linkMetadata.keys()) if (isPathWithin(entry.path, key)) this.linkMetadata.delete(key);
2113
+ for (const key of this.linkOrigins.keys()) if (isPathWithin(entry.path, key)) this.linkOrigins.delete(key);
2114
+ }, false);
2115
+ } catch (error) {
2116
+ options.signal?.throwIfAborted();
2117
+ throw new FsError(toFsError(error).code, { syscall: "rmdir", path, cause: error });
2118
+ }
2119
+ }
2120
+ async rm(path, options = {}) {
2121
+ return this.run(options, async () => {
2122
+ this.writable(path);
2123
+ let entry;
2124
+ try {
2125
+ entry = await this.required(path, options, false);
2126
+ } catch (error) {
2127
+ if (options.force && hasCode(error, "ENOENT")) return;
2128
+ throw error;
2129
+ }
2130
+ if (entry.path === "/") fail2("EBUSY", path);
2131
+ if (entry.stat.type === "directory" && !options.recursive && (await this.listing(entry, options)).length) fail2("ENOTEMPTY", path);
2132
+ await this.parent(entry.path, options);
2133
+ if (entry.backend === this.#upper) {
2134
+ await this.staged(options, async (temporary) => {
2135
+ options.signal?.throwIfAborted();
2136
+ await this.#upper.rename(entry.path, temporary, options);
2137
+ this.whiteouts.add(entry.path);
2138
+ });
2139
+ } else {
2140
+ options.signal?.throwIfAborted();
2141
+ this.whiteouts.add(entry.path);
2142
+ }
2143
+ for (const key of this.linkMetadata.keys()) if (isPathWithin(entry.path, key)) this.linkMetadata.delete(key);
2144
+ for (const key of this.linkOrigins.keys()) if (isPathWithin(entry.path, key)) this.linkOrigins.delete(key);
2145
+ });
2146
+ }
2147
+ async materialize(entry, options) {
2148
+ if (entry.stat.type === "symlink" && entry.backend === this.#upper) await this.movableLinkTarget(entry, options);
2149
+ const children = entry.stat.type === "directory" ? await this.listing(entry, options) : [];
2150
+ await this.copyUp(entry, options);
2151
+ for (const child of children) {
2152
+ const path = `${entry.path}/${child.name}`;
2153
+ const visible = await this.lookup(path, options);
2154
+ if (!visible) fail2("EIO", path, "visible tree changed during materialization");
2155
+ await this.materialize(visible, options);
2156
+ }
2157
+ if (entry.stat.type === "directory") await this.preserve(entry.path, entry.stat, options);
2158
+ }
2159
+ async rename(source, destination, options = {}) {
2160
+ return this.run(options, async () => {
2161
+ this.writable(source);
2162
+ const original = await this.required(source, options, false);
2163
+ const target = await this.resolve(destination, options, false, true);
2164
+ if (original.path === target.path) return;
2165
+ if (original.path === "/" || target.path === "/") fail2("EBUSY", source);
2166
+ if (original.stat.type === "directory" && isPathWithin(original.path, target.path)) fail2("EINVAL", destination);
2167
+ if (target.entry) {
2168
+ if (original.stat.type === "directory" && target.entry.stat.type !== "directory") fail2("ENOTDIR", destination);
2169
+ if (original.stat.type !== "directory" && target.entry.stat.type === "directory") fail2("EISDIR", destination);
2170
+ if (target.entry.stat.type === "directory" && (await this.listing(target.entry, options)).length) fail2("ENOTEMPTY", destination);
2171
+ }
2172
+ await this.parent(original.path, options);
2173
+ await this.parent(target.path, options);
2174
+ await this.materialize(original, options);
2175
+ options.signal?.throwIfAborted();
2176
+ const move = { source: original.path, destination: target.path };
2177
+ const movedOrigins = [];
2178
+ for (const [path, origin] of this.linkOrigins) {
2179
+ if (!isPathWithin(original.path, path)) continue;
2180
+ const destination2 = movedPath(path, [move]);
2181
+ const moves = origin.target.startsWith("/") ? origin.moves : [...origin.moves, move];
2182
+ if (normalizePath(origin.target, dirname(destination2)) !== movedPath(origin.canonical, moves)) {
2183
+ fail2("ENOTSUP", path, "relocation changes an unprovable relative symlink target");
2184
+ }
2185
+ movedOrigins.push([destination2, { ...origin, moves }]);
2186
+ }
2187
+ options.signal?.throwIfAborted();
2188
+ await this.#upper.rename(original.path, target.path, options);
2189
+ this.whiteouts.add(original.path);
2190
+ if (original.stat.type === "directory") this.opaque.add(target.path);
2191
+ const moved = [...this.linkMetadata].filter(([path]) => isPathWithin(original.path, path));
2192
+ for (const path of this.linkMetadata.keys()) {
2193
+ if (isPathWithin(original.path, path) || isPathWithin(target.path, path)) this.linkMetadata.delete(path);
2194
+ }
2195
+ for (const [path, metadata] of moved) this.linkMetadata.set(target.path + path.slice(original.path.length), metadata);
2196
+ for (const path of this.linkOrigins.keys()) {
2197
+ if (isPathWithin(original.path, path) || isPathWithin(target.path, path)) this.linkOrigins.delete(path);
2198
+ }
2199
+ for (const [path, origin] of movedOrigins) this.linkOrigins.set(path, origin);
2200
+ });
2201
+ }
2202
+ async copyFile(source, destination, options = {}) {
2203
+ return this.run(options, async () => {
2204
+ this.writable(destination);
2205
+ const original = await this.required(source, options);
2206
+ const target = await this.writeLocation(destination, { ...options, flag: options.exclusive ? "wx" : "w" });
2207
+ if (original.path === target.path) fail2("EINVAL", destination, "source and destination are the same file");
2208
+ if (target.entry) {
2209
+ let identity = compareIdentity(original.stat, target.entry.stat);
2210
+ if (identity === "unknown") identity = await compareEntries(original.backend, original.path, target.entry.backend, target.entry.path, options);
2211
+ if (identity === "same") fail2("EINVAL", destination, "source and destination are the same file");
2212
+ if (identity === "unknown") fail2("ENOTSUP", destination, "backing-entry identity is unknown");
2213
+ }
2214
+ if (target.entry?.backend !== this.#upper) {
2215
+ const upper = await this.maybeStat(this.#upper, target.path, options);
2216
+ if (upper) {
2217
+ let identity = compareIdentity(original.stat, upper);
2218
+ if (identity === "unknown") identity = await compareEntries(original.backend, original.path, this.#upper, target.path, options);
2219
+ if (identity === "same") fail2("EINVAL", destination, "source and destination are the same file");
2220
+ if (identity === "unknown") fail2("ENOTSUP", destination, "upper backing-entry identity is unknown");
2221
+ }
2222
+ }
2223
+ await this.cleanGarbage(false);
2224
+ const bytes = await this.bytes(original, options);
2225
+ await this.replace(target, options, async (temporary) => {
2226
+ await this.#upper.writeFile(temporary, bytes, { ...options, mode: original.stat.mode & 4095, flag: "w" });
2227
+ if (this.#upper.chmod) await this.#upper.chmod(temporary, original.stat.mode & 4095, options);
2228
+ else if (((await this.#upper.lstat(temporary, options)).mode & 4095) !== (original.stat.mode & 4095)) fail2("ENOTSUP", destination);
2229
+ });
2230
+ }, false);
2231
+ }
2232
+ async readlink(path, options = {}) {
2233
+ return this.run(options, async () => {
2234
+ const entry = await this.required(path, options, false);
2235
+ if (entry.stat.type !== "symlink") fail2("EINVAL", path);
2236
+ if (!entry.backend.readlink) fail2("ENOTSUP", path);
2237
+ return entry.backend.readlink(entry.path, options);
2238
+ }, false);
2239
+ }
2240
+ async symlink(target, path, options = {}) {
2241
+ return this.run(options, async () => {
2242
+ this.writable(path);
2243
+ if (!this.capabilities.symlinks || !this.#upper.symlink) fail2("ENOTSUP", path);
2244
+ validatePath(target);
2245
+ if (!target) fail2("ENOENT", path);
2246
+ const location = await this.resolve(path, options, false, true);
2247
+ if (location.entry) fail2("EEXIST", path);
2248
+ await this.replace(location, options, (temporary) => this.#upper.symlink(target, temporary, options));
2249
+ });
2250
+ }
2251
+ async link(_existingPath, newPath, options = {}) {
2252
+ options.signal?.throwIfAborted();
2253
+ fail2("ENOTSUP", newPath, "overlay hardlinks are not supported");
2254
+ }
2255
+ async metadata(path, capability, options, operation) {
2256
+ return this.run(options, async () => {
2257
+ this.writable(path);
2258
+ if (!this.capabilities[capability]) fail2("ENOTSUP", path);
2259
+ const entry = await this.required(path, options);
2260
+ if (entry.stat.type === "directory") {
2261
+ await this.copyUp(entry, options);
2262
+ await operation(entry.path);
2263
+ } else await this.replace({ path: entry.path, entry }, options, operation);
2264
+ });
2265
+ }
2266
+ async chmod(path, permissions, options = {}) {
2267
+ options.signal?.throwIfAborted();
2268
+ if (permissions === void 0) fail2("EINVAL", path);
2269
+ mode(permissions, path);
2270
+ return this.metadata(path, "permissions", options, (target) => this.#upper.chmod(target, permissions, options));
2271
+ }
2272
+ async utimes(path, atimeMs, mtimeMs, options = {}) {
2273
+ options.signal?.throwIfAborted();
2274
+ if (!Number.isFinite(atimeMs) || !Number.isFinite(mtimeMs)) fail2("EINVAL", path);
2275
+ return this.metadata(path, "timestamps", options, (target) => this.#upper.utimes(target, atimeMs, mtimeMs, options));
2276
+ }
2277
+ async truncate(path, length = 0, options = {}) {
2278
+ integer(length, path);
2279
+ return this.run(options, async () => {
2280
+ this.writable(path);
2281
+ if (length > this.maxBufferBytes) fail2("EFBIG", path);
2282
+ const entry = await this.required(path, options);
2283
+ if (entry.stat.type !== "file") fail2("EISDIR", path);
2284
+ this.permission(entry, 2);
2285
+ await this.replace({ path: entry.path, entry }, options, async (temporary) => {
2286
+ if (this.#upper.truncate) await this.#upper.truncate(temporary, length, options);
2287
+ else {
2288
+ const previous = await this.#upper.readFile(temporary, { ...options, maxBytes: this.maxBufferBytes });
2289
+ const bytes = new Uint8Array(length);
2290
+ bytes.set(previous.subarray(0, length));
2291
+ await this.#upper.writeFile(temporary, bytes, options);
2292
+ }
2293
+ });
2294
+ });
2295
+ }
2296
+ async *readStream(path, options = {}) {
2297
+ options.signal?.throwIfAborted();
2298
+ const start = options.start ?? 0;
2299
+ const chunkSize = options.chunkSize ?? 64 * 1024;
2300
+ integer(start, path);
2301
+ integer(chunkSize, path);
2302
+ if (!chunkSize) fail2("EINVAL", path);
2303
+ if (options.endExclusive !== void 0) {
2304
+ integer(options.endExclusive, path);
2305
+ if (options.endExclusive < start) fail2("EINVAL", path);
2306
+ }
2307
+ const entry = await this.run(options, async () => {
2308
+ const entry2 = await this.required(path, options);
2309
+ if (entry2.stat.type !== "file") fail2("EISDIR", path);
2310
+ this.permission(entry2, 4);
2311
+ return entry2;
2312
+ });
2313
+ if (entry.backend.readStream && entry.backend.capabilities.streamingRead !== false) {
2314
+ for await (const chunk of readBytes(entry.backend.readStream(entry.path, options), options.signal)) {
2315
+ yield new Uint8Array(chunk);
2316
+ }
2317
+ options.signal?.throwIfAborted();
2318
+ return;
2319
+ }
2320
+ const bytes = await this.bytes(entry, options);
2321
+ const end = Math.min(bytes.byteLength, options.endExclusive ?? bytes.byteLength);
2322
+ for (let offset = start; offset < end; offset += chunkSize) {
2323
+ options.signal?.throwIfAborted();
2324
+ yield bytes.slice(offset, Math.min(end, offset + chunkSize));
2325
+ }
2326
+ options.signal?.throwIfAborted();
2327
+ }
2328
+ async *bounded(source, path, options) {
2329
+ let size = 0;
2330
+ for await (const chunk of readBytes(source, options.signal)) {
2331
+ if (chunk.byteLength > this.maxBufferBytes - size) fail2("EFBIG", path);
2332
+ size += chunk.byteLength;
2333
+ yield new Uint8Array(chunk);
2334
+ }
2335
+ }
2336
+ async streamToUpper(path, source, options) {
2337
+ if (!this.#upper.writeStream) fail2("ENOTSUP", path);
2338
+ const input = readBytes(source, options.signal);
2339
+ let failed = false;
2340
+ try {
2341
+ await this.#upper.writeStream(path, input, options);
2342
+ } catch (error) {
2343
+ failed = true;
2344
+ throw error;
2345
+ } finally {
2346
+ await finishCleanup(() => input.return(void 0), failed);
2347
+ }
2348
+ }
2349
+ async writeStream(path, source, options = {}) {
2350
+ options.signal?.throwIfAborted();
2351
+ this.writeOptions(path, options);
2352
+ await this.run(options, () => this.writeLocation(path, options));
2353
+ if (this.capabilities.streamingWrite !== false) {
2354
+ await this.staged(options, async (incoming) => {
2355
+ const bounded = this.bounded(source, path, options);
2356
+ await this.streamToUpper(incoming, bounded, { ...options, flag: "wx", mode: 384 });
2357
+ const size = (await this.#upper.stat(incoming, options)).size;
2358
+ await this.run(options, async () => {
2359
+ const location = await this.writeLocation(path, options);
2360
+ const append = options.flag === "a" || options.flag === "ax";
2361
+ if (append && (location.entry?.stat.size ?? 0) + size > this.maxBufferBytes) fail2("EFBIG", path);
2362
+ await this.replace(location, options, async (temporary) => {
2363
+ await this.streamToUpper(temporary, this.#upper.readStream(incoming, options), { ...options, flag: append ? "a" : "w" });
2364
+ }, true);
2365
+ });
2366
+ });
2367
+ return;
2368
+ }
2369
+ const bytes = await collectBytes(source, { maxBytes: this.maxBufferBytes, ...options.signal ? { signal: options.signal } : {} });
2370
+ await this.writeFile(path, bytes, options);
2371
+ }
2372
+ };
2373
+ function createOverlayFileSystem(options) {
2374
+ return new OverlayFileSystem(options);
2375
+ }
2376
+
2377
+ // packages/safe-fs/src/contracts/abort.ts
2378
+ function composeAbortSignals(signals) {
2379
+ const controller = new AbortController();
2380
+ const listeners = /* @__PURE__ */ new Map();
2381
+ const dispose = () => {
2382
+ for (const [signal, listener] of listeners) signal.removeEventListener("abort", listener);
2383
+ listeners.clear();
2384
+ };
2385
+ for (const signal of signals) {
2386
+ if (signal.aborted) {
2387
+ controller.abort(signal.reason);
2388
+ return { signal: controller.signal, dispose };
2389
+ }
2390
+ }
2391
+ try {
2392
+ for (const signal of new Set(signals)) {
2393
+ const listener = () => {
2394
+ dispose();
2395
+ controller.abort(signal.reason);
2396
+ };
2397
+ listeners.set(signal, listener);
2398
+ signal.addEventListener("abort", listener, { once: true });
2399
+ if (signal.aborted) listener();
2400
+ if (controller.signal.aborted) break;
2401
+ }
2402
+ } catch (error) {
2403
+ dispose();
2404
+ throw error;
2405
+ }
2406
+ return { signal: controller.signal, dispose };
2407
+ }
2408
+
2409
+ // packages/safe-fs/src/fs/webdav/xml.ts
2410
+ var xmlNamespace = "http://www.w3.org/XML/1998/namespace";
2411
+ var xmlnsNamespace = "http://www.w3.org/2000/xmlns/";
2412
+ function invalid(message) {
2413
+ throw new SyntaxError(`Invalid WebDAV XML: ${message}`);
2414
+ }
2415
+ function validCharacter(point) {
2416
+ return point === 9 || point === 10 || point === 13 || point >= 32 && point <= 55295 || point >= 57344 && point <= 65533 || point >= 65536 && point <= 1114111;
2417
+ }
2418
+ function nameStart(point) {
2419
+ return point === 95 || point >= 65 && point <= 90 || point >= 97 && point <= 122 || point >= 192 && point <= 214 || point >= 216 && point <= 246 || point >= 248 && point <= 767 || point >= 880 && point <= 893 || point >= 895 && point <= 8191 || point >= 8204 && point <= 8205 || point >= 8304 && point <= 8591 || point >= 11264 && point <= 12271 || point >= 12289 && point <= 55295 || point >= 63744 && point <= 64975 || point >= 65008 && point <= 65533 || point >= 65536 && point <= 983039;
2420
+ }
2421
+ function namePart(point) {
2422
+ return nameStart(point) || point === 45 || point === 46 || point === 183 || point >= 48 && point <= 57 || point >= 768 && point <= 879 || point >= 8255 && point <= 8256;
2423
+ }
2424
+ function qualifiedName(name) {
2425
+ const parts = name.split(":");
2426
+ if (parts.length > 2 || parts.some((part) => {
2427
+ const points = [...part].map((character) => character.codePointAt(0));
2428
+ return points.length === 0 || !nameStart(points[0]) || points.slice(1).some((point) => !namePart(point));
2429
+ })) invalid("invalid qualified name");
2430
+ return parts.length === 1 ? ["", parts[0]] : [parts[0], parts[1]];
2431
+ }
2432
+ function entities(text) {
2433
+ let result = "";
2434
+ let offset = 0;
2435
+ while (offset < text.length) {
2436
+ const start = text.indexOf("&", offset);
2437
+ if (start < 0) return result + text.slice(offset);
2438
+ result += text.slice(offset, start);
2439
+ const end = text.indexOf(";", start + 1);
2440
+ if (end < 0) invalid("unterminated entity");
2441
+ const entity = text.slice(start + 1, end);
2442
+ const predefined = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
2443
+ if (Object.hasOwn(predefined, entity)) result += predefined[entity];
2444
+ else {
2445
+ const hexadecimal = entity.startsWith("#x");
2446
+ const digits = entity.slice(hexadecimal ? 2 : 1);
2447
+ if (!entity.startsWith("#") || !(hexadecimal ? /^[0-9a-fA-F]+$/ : /^[0-9]+$/).test(digits)) {
2448
+ invalid("undeclared entity");
2449
+ }
2450
+ const point = Number.parseInt(digits, hexadecimal ? 16 : 10);
2451
+ if (!validCharacter(point)) invalid("invalid character reference");
2452
+ result += String.fromCodePoint(point);
2453
+ }
2454
+ offset = end + 1;
2455
+ }
2456
+ return result;
2457
+ }
2458
+ function parseXml(input, limits = {}) {
2459
+ const maxDepth = limits.maxDepth ?? 64;
2460
+ const maxNodes = limits.maxNodes ?? 1e5;
2461
+ const maxAttributes = limits.maxAttributes ?? 1e4;
2462
+ for (const limit of [maxDepth, maxNodes, maxAttributes]) {
2463
+ if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("XML limits must be positive integers");
2464
+ }
2465
+ for (const character of input) {
2466
+ if (!validCharacter(character.codePointAt(0))) invalid("invalid character");
2467
+ }
2468
+ const source = input.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n");
2469
+ const stack = [];
2470
+ let root;
2471
+ let offset = 0;
2472
+ let nodes = 0;
2473
+ let attributeCount = 0;
2474
+ const whitespace = () => {
2475
+ while (offset < source.length && " \n\r".includes(source[offset])) offset++;
2476
+ };
2477
+ const readName = () => {
2478
+ const start = offset;
2479
+ while (offset < source.length && !" \r\n/=>?".includes(source[offset])) offset++;
2480
+ const name = source.slice(start, offset);
2481
+ qualifiedName(name);
2482
+ return name;
2483
+ };
2484
+ const appendText = (text) => {
2485
+ const parent = stack.at(-1);
2486
+ if (parent) parent.element.text += text;
2487
+ else if (!/^[ \t\r\n]*$/.test(text)) invalid("text outside the root");
2488
+ };
2489
+ while (offset < source.length) {
2490
+ if (source[offset] !== "<") {
2491
+ const next = source.indexOf("<", offset);
2492
+ const text = source.slice(offset, next < 0 ? source.length : next);
2493
+ if (text.includes("]]>")) invalid("CDATA terminator in text");
2494
+ appendText(entities(text));
2495
+ offset += text.length;
2496
+ } else if (source.startsWith("<!--", offset)) {
2497
+ const end = source.indexOf("-->", offset + 4);
2498
+ if (end < 0 || source.slice(offset + 4, end).includes("--") || source.slice(offset + 4, end).endsWith("-")) {
2499
+ invalid("malformed comment");
2500
+ }
2501
+ offset = end + 3;
2502
+ } else if (source.startsWith("<![CDATA[", offset)) {
2503
+ if (!stack.length) invalid("CDATA outside root");
2504
+ const end = source.indexOf("]]>", offset + 9);
2505
+ if (end < 0) invalid("unterminated CDATA");
2506
+ appendText(source.slice(offset + 9, end));
2507
+ offset = end + 3;
2508
+ } else if (source.startsWith("<?", offset)) {
2509
+ const start = offset;
2510
+ offset += 2;
2511
+ const target = readName();
2512
+ const end = source.indexOf("?>", offset);
2513
+ if (end < 0) invalid("unterminated processing instruction");
2514
+ const content = source.slice(offset, end);
2515
+ if (target.toLowerCase() === "xml") {
2516
+ if (start !== 0 || target !== "xml" || !/^\s+version\s*=\s*(['"])1\.0\1(?:\s+encoding\s*=\s*(['"])(?:UTF-8|UTF-16|UTF-16LE|UTF-16BE)\2)?(?:\s+standalone\s*=\s*(['"])(?:yes|no)\3)?\s*$/i.test(content)) {
2517
+ invalid("unsupported XML declaration");
2518
+ }
2519
+ } else if (content && !" \n\r".includes(content[0])) invalid("invalid processing instruction");
2520
+ offset = end + 2;
2521
+ } else if (source.startsWith("<!", offset)) {
2522
+ invalid("DTD and entity declarations are forbidden");
2523
+ } else if (source.startsWith("</", offset)) {
2524
+ offset += 2;
2525
+ const name = readName();
2526
+ whitespace();
2527
+ if (source[offset++] !== ">" || stack.pop()?.name !== name) invalid("mismatched closing tag");
2528
+ } else {
2529
+ offset++;
2530
+ const name = readName();
2531
+ const attributes = /* @__PURE__ */ new Map();
2532
+ const namespaces = new Map(stack.at(-1)?.namespaces ?? [["xml", xmlNamespace]]);
2533
+ while (true) {
2534
+ const beforeSpace = offset;
2535
+ whitespace();
2536
+ if (source[offset] === "/" || source[offset] === ">") break;
2537
+ if (offset === beforeSpace) invalid("attributes require whitespace");
2538
+ const attribute = readName();
2539
+ if (attributes.has(attribute)) invalid("duplicate attribute");
2540
+ if (++attributeCount > maxAttributes || attributes.size >= 128) invalid("XML attribute limit exceeded");
2541
+ whitespace();
2542
+ if (source[offset++] !== "=") invalid("missing attribute equals");
2543
+ whitespace();
2544
+ const quote = source[offset++];
2545
+ if (quote !== '"' && quote !== "'") invalid("unquoted attribute");
2546
+ const end = source.indexOf(quote, offset);
2547
+ if (end < 0) invalid("unterminated attribute");
2548
+ const raw = source.slice(offset, end);
2549
+ if (raw.includes("<")) invalid("less-than in attribute");
2550
+ const value = entities(raw.replace(/[\t\n\r]/g, " "));
2551
+ attributes.set(attribute, value);
2552
+ offset = end + 1;
2553
+ if (attribute === "xmlns" || attribute.startsWith("xmlns:")) {
2554
+ const prefix2 = attribute === "xmlns" ? "" : attribute.slice(6);
2555
+ if (prefix2 === "xmlns" || value === xmlnsNamespace || prefix2 === "xml" !== (value === xmlNamespace) || prefix2 !== "" && value === "") invalid("invalid namespace binding");
2556
+ namespaces.set(prefix2, value);
2557
+ if (namespaces.size > 256) invalid("XML namespace scope limit exceeded");
2558
+ }
2559
+ }
2560
+ const expanded = /* @__PURE__ */ new Set();
2561
+ for (const attribute of attributes.keys()) {
2562
+ if (attribute === "xmlns" || attribute.startsWith("xmlns:")) continue;
2563
+ const [prefix2, localName2] = qualifiedName(attribute);
2564
+ if (prefix2 && !namespaces.has(prefix2)) invalid("unbound attribute prefix");
2565
+ const key = JSON.stringify([prefix2 ? namespaces.get(prefix2) : "", localName2]);
2566
+ if (expanded.has(key)) invalid("duplicate expanded attribute");
2567
+ expanded.add(key);
2568
+ }
2569
+ const [prefix, localName] = qualifiedName(name);
2570
+ if (prefix === "xmlns" || prefix && !namespaces.has(prefix)) invalid("unbound element prefix");
2571
+ const element = { namespace: namespaces.get(prefix) ?? "", localName, children: [], text: "" };
2572
+ if (++nodes > maxNodes || stack.length + 1 > maxDepth) invalid("XML resource limit exceeded");
2573
+ const parent = stack.at(-1);
2574
+ if (parent) parent.element.children.push(element);
2575
+ else if (root) invalid("multiple root elements");
2576
+ else root = element;
2577
+ const empty = source[offset] === "/";
2578
+ if (empty) offset++;
2579
+ if (source[offset++] !== ">") invalid("unterminated start tag");
2580
+ if (!empty) stack.push({ element, name, namespaces });
2581
+ }
2582
+ }
2583
+ if (stack.length || !root) invalid("incomplete document");
2584
+ return root;
2585
+ }
2586
+ function davChildren(element, localName) {
2587
+ return element.children.filter((child) => child.namespace === "DAV:" && child.localName === localName);
2588
+ }
2589
+ function davChild(element, localName) {
2590
+ const children = davChildren(element, localName);
2591
+ if (children.length > 1) invalid(`duplicate DAV:${localName}`);
2592
+ return children[0];
2593
+ }
2594
+ function scalar(element) {
2595
+ if (element.children.length) invalid("expected text-only element");
2596
+ return element.text.trim();
2597
+ }
2598
+
2599
+ // packages/safe-fs/src/fs/webdav/webdav.ts
2600
+ var timestampNamespace = "urn:virtual-bash:metadata";
2601
+ var propfindBody = `<?xml version="1.0" encoding="utf-8"?><d:propfind xmlns:d="DAV:"><d:prop><d:resourcetype/><d:getcontentlength/><d:getlastmodified/><d:creationdate/><d:getetag/><v:timestamps xmlns:v="${timestampNamespace}"/></d:prop></d:propfind>`;
2602
+ var lockBody = '<d:lockinfo xmlns:d="DAV:"><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype></d:lockinfo>';
2603
+ var resourceIdBody = '<?xml version="1.0" encoding="utf-8"?><d:propfind xmlns:d="DAV:"><d:prop><d:resource-id/></d:prop></d:propfind>';
2604
+ function fail3(code, syscall, path, message) {
2605
+ throw new FsError(code, { syscall, path, ...message === void 0 ? {} : { message } });
2606
+ }
2607
+ function positive(value, name, zero = false) {
2608
+ if (!Number.isSafeInteger(value) || value < (zero ? 0 : 1)) {
2609
+ throw new FsError("EINVAL", { message: `${name} must be a ${zero ? "nonnegative" : "positive"} safe integer` });
2610
+ }
2611
+ return value;
2612
+ }
2613
+ function createRequestTimeout(timeoutMs) {
2614
+ const controller = new AbortController();
2615
+ const timer = setTimeout(() => {
2616
+ controller.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
2617
+ }, timeoutMs);
2618
+ if (typeof timer === "object" && timer !== null) {
2619
+ const handle = timer;
2620
+ handle.unref?.();
2621
+ }
2622
+ return {
2623
+ signal: controller.signal,
2624
+ dispose() {
2625
+ clearTimeout(timer);
2626
+ }
2627
+ };
2628
+ }
2629
+ function nativeRequestStreamsSupported(url) {
2630
+ let body;
2631
+ try {
2632
+ body = new ReadableStream({ start(destination) {
2633
+ destination.close();
2634
+ } });
2635
+ let duplexAccessed = false;
2636
+ const init = {
2637
+ method: "PUT",
2638
+ body,
2639
+ get duplex() {
2640
+ duplexAccessed = true;
2641
+ return "half";
2642
+ }
2643
+ };
2644
+ const request = new Request(url, init);
2645
+ return duplexAccessed && !request.headers.has("Content-Type");
2646
+ } catch {
2647
+ return false;
2648
+ } finally {
2649
+ if (body && !body.locked) void body.cancel().catch(() => {
2650
+ });
2651
+ }
2652
+ }
2653
+ function strongEtag(value, path) {
2654
+ if (!value || !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(value)) {
2655
+ fail3("ENOTSUP", "webdav", path, "operation requires a strong entity tag");
2656
+ }
2657
+ return value;
2658
+ }
2659
+ function normalize(path) {
2660
+ if (typeof path !== "string" || path.includes("\0") || path.includes("\\")) {
2661
+ fail3("EINVAL", "resolve", String(path), "invalid WebDAV path");
2662
+ }
2663
+ try {
2664
+ encodeURIComponent(path);
2665
+ } catch {
2666
+ fail3("EINVAL", "resolve", path, "path contains an unpaired surrogate");
2667
+ }
2668
+ const segments = [];
2669
+ for (const segment of path.split("/")) {
2670
+ if (!segment || segment === ".") continue;
2671
+ if (segment === "..") {
2672
+ if (!segments.length) fail3("EACCES", "resolve", path, "path escapes WebDAV root");
2673
+ segments.pop();
2674
+ } else segments.push(segment);
2675
+ }
2676
+ return `/${segments.join("/")}`;
2677
+ }
2678
+ function validateDirectoryAccessPath(path) {
2679
+ if (typeof path !== "string") fail3("EINVAL", "resolve", String(path), "invalid WebDAV path");
2680
+ let bytes = 0;
2681
+ let components = 0;
2682
+ let inComponent = false;
2683
+ for (let offset = 0; offset < path.length; offset++) {
2684
+ const point = path.codePointAt(offset);
2685
+ if (point === 47) inComponent = false;
2686
+ else if (!inComponent) {
2687
+ components++;
2688
+ inComponent = true;
2689
+ }
2690
+ bytes += point <= 127 ? 1 : point <= 2047 ? 2 : point <= 65535 ? 3 : 4;
2691
+ if (point > 65535) offset++;
2692
+ if (bytes > 65536 || components > 256) {
2693
+ fail3("ENAMETOOLONG", "access", path, "directory access exceeds the 64KiB path or 256 component limit");
2694
+ }
2695
+ }
2696
+ }
2697
+ function requiresCollection(path) {
2698
+ const last = path.split("/").at(-1);
2699
+ return last === "" || last === "." || last === "..";
2700
+ }
2701
+ function validateUrlText(text) {
2702
+ if (/[\u0000-\u0020\u007f\\?#]/.test(text)) throw new Error("invalid URL characters");
2703
+ for (const segment of text.split("/")) {
2704
+ const decoded = decodeURIComponent(segment);
2705
+ if (decoded === "." || decoded === ".." || /[/\\\u0000]/.test(decoded)) {
2706
+ throw new Error("ambiguous URL segment");
2707
+ }
2708
+ }
2709
+ }
2710
+ function urlSegments(url) {
2711
+ const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
2712
+ const segments = pathname.slice(1).split("/");
2713
+ if (segments.length === 1 && segments[0] === "") return [];
2714
+ if (segments.some((segment) => segment === "")) throw new Error("empty URL segment");
2715
+ return segments.map((segment) => decodeURIComponent(segment));
2716
+ }
2717
+ function statusCode(element) {
2718
+ const text = scalar(element);
2719
+ if (!/^HTTP\/\d+\.\d+ [2-5]\d\d(?:[ \t].*)?$/.test(text)) throw new Error("invalid DAV status line");
2720
+ return Number(text.split(" ")[1]);
2721
+ }
2722
+ var WebDavFileSystem = class {
2723
+ capabilities = Object.freeze({
2724
+ symlinks: false,
2725
+ hardlinks: false,
2726
+ permissions: false,
2727
+ timestamps: true,
2728
+ atomicRename: false,
2729
+ streamingRead: true,
2730
+ streamingWrite: true
2731
+ });
2732
+ base;
2733
+ baseSegments;
2734
+ transport;
2735
+ requestStreamSupport;
2736
+ headers;
2737
+ maxResponseBytes;
2738
+ maxXmlBytes;
2739
+ maxEntries;
2740
+ timeoutMs;
2741
+ overwritePolicy;
2742
+ configuredComparison;
2743
+ atomicEmptyDirectory;
2744
+ etags = /* @__PURE__ */ new WeakMap();
2745
+ constructor(options) {
2746
+ if (typeof options.fetch !== "function") throw new FsError("EINVAL", { message: "an explicit fetch transport is required" });
2747
+ if (options.requestStreamSupport !== void 0 && options.requestStreamSupport !== "native" && typeof options.requestStreamSupport !== "boolean") {
2748
+ throw new FsError("EINVAL", { message: "requestStreamSupport must be native or a boolean" });
2749
+ }
2750
+ if (options.compareEntry !== void 0 && typeof options.compareEntry !== "function") {
2751
+ throw new FsError("EINVAL", { message: "compareEntry must be a function" });
2752
+ }
2753
+ if (options.compareEntry !== void 0) assertCallbackAuthorityAllowed();
2754
+ try {
2755
+ validateUrlText(options.baseUrl);
2756
+ this.base = new URL(options.baseUrl);
2757
+ if (!["http:", "https:"].includes(this.base.protocol) || this.base.username || this.base.password || this.base.search || this.base.hash) throw new Error("invalid base URL");
2758
+ this.baseSegments = urlSegments(this.base);
2759
+ this.base.pathname = `/${this.baseSegments.map(encodeURIComponent).join("/")}${this.baseSegments.length ? "/" : ""}`;
2760
+ this.headers = new Headers(options.headers);
2761
+ for (const name of this.headers.keys()) {
2762
+ if ([
2763
+ "host",
2764
+ "destination",
2765
+ "depth",
2766
+ "overwrite",
2767
+ "if",
2768
+ "if-match",
2769
+ "if-none-match",
2770
+ "range",
2771
+ "content-length",
2772
+ "content-type",
2773
+ "transfer-encoding",
2774
+ "connection",
2775
+ "proxy-authorization",
2776
+ "lock-token",
2777
+ "timeout"
2778
+ ].includes(name)) {
2779
+ throw new Error(`reserved header ${name}`);
2780
+ }
2781
+ }
2782
+ if (this.base.protocol !== "https:" && [...this.headers.keys()].some((name) => name === "authorization" || name === "cookie")) {
2783
+ throw new Error("explicit credentials require HTTPS");
2784
+ }
2785
+ } catch (cause) {
2786
+ throw new FsError("EINVAL", { message: "invalid WebDAV base URL or headers", cause });
2787
+ }
2788
+ const binding = options.atomicEmptyDirectory;
2789
+ if (binding !== void 0) {
2790
+ if (!binding || typeof binding !== "object" || binding.namespaceUrl !== this.base.href || typeof binding.removeEmptyDirectory !== "function") {
2791
+ throw new FsError("EINVAL", { message: "atomicEmptyDirectory must bind the canonical WebDAV namespace and a callback" });
2792
+ }
2793
+ this.atomicEmptyDirectory = Object.freeze({ namespaceUrl: this.base.href, removeEmptyDirectory: binding.removeEmptyDirectory });
2794
+ }
2795
+ this.transport = options.fetch === globalThis.fetch ? options.fetch.bind(globalThis) : options.fetch;
2796
+ this.requestStreamSupport = options.requestStreamSupport ?? (options.fetch === globalThis.fetch ? "native" : false);
2797
+ this.maxResponseBytes = positive(options.maxResponseBytes ?? 64 * 1024 * 1024, "maxResponseBytes");
2798
+ this.maxXmlBytes = positive(options.maxXmlBytes ?? 2 * 1024 * 1024, "maxXmlBytes");
2799
+ this.maxEntries = positive(options.maxEntries ?? 1e4, "maxEntries");
2800
+ this.timeoutMs = positive(options.timeoutMs ?? 3e4, "timeoutMs");
2801
+ this.overwritePolicy = options.overwritePolicy ?? "lock";
2802
+ this.configuredComparison = options.compareEntry !== void 0;
2803
+ if (!["lock", "etag"].includes(this.overwritePolicy)) throw new FsError("EINVAL", { message: "invalid overwritePolicy" });
2804
+ if (this.timeoutMs > 2147483647) throw new FsError("EINVAL", { message: "timeoutMs exceeds timer range" });
2805
+ registerResourceQuery(this, (path, settings) => this.resourceId(path, settings), originalWebDavComparison, options.compareEntry);
2806
+ registerEntryAuthority(this, compareWebDavResources);
2807
+ }
2808
+ compareEntry(path, peer, peerPath, options = {}) {
2809
+ return compareEntries(this, path, peer, peerPath, options);
2810
+ }
2811
+ async resourceId(input, options) {
2812
+ const path = normalize(input);
2813
+ return this.request("PROPFIND", path, options, {
2814
+ headers: { Depth: "0", "Content-Type": "application/xml; charset=utf-8" },
2815
+ body: resourceIdBody
2816
+ }, async (response, signal) => {
2817
+ if (response.status !== 207) this.httpError(response.status, "PROPFIND", path);
2818
+ const responses2 = await this.multistatus(response, signal, "PROPFIND", path);
2819
+ if (responses2.length !== 1) throw new Error("resource identity requires exactly one DAV:response");
2820
+ const entry = responses2[0];
2821
+ const href = davChild(entry, "href");
2822
+ if (!href) throw new Error("missing resource identity response href");
2823
+ if (this.hrefPath(scalar(href)) !== path) fail3("EACCES", "PROPFIND", path, "resource identity response addresses another resource");
2824
+ const wholeStatus = davChild(entry, "status");
2825
+ const propstats = davChildren(entry, "propstat");
2826
+ if (wholeStatus) {
2827
+ if (propstats.length) throw new Error("conflicting response status and propstat");
2828
+ this.httpError(statusCode(wholeStatus), "PROPFIND", path);
2829
+ }
2830
+ if (propstats.length === 0) throw new Error("missing resource identity propstat");
2831
+ let seen = false;
2832
+ let identifier;
2833
+ for (const propstat of propstats) {
2834
+ const status = davChild(propstat, "status");
2835
+ const prop = davChild(propstat, "prop");
2836
+ if (!status || !prop) throw new Error("incomplete resource identity propstat");
2837
+ const code = statusCode(status);
2838
+ if (code !== 200 && code !== 404) this.httpError(code, "PROPFIND", path);
2839
+ for (const property of davChildren(prop, "resource-id")) {
2840
+ if (seen) throw new Error("duplicate or conflicting resource identity property");
2841
+ seen = true;
2842
+ if (code === 404) continue;
2843
+ const value = davChild(property, "href");
2844
+ if (!value || property.children.length !== 1 || property.text.trim()) throw new Error("invalid resource identity property");
2845
+ identifier = resourceIdentifier(scalar(value));
2846
+ }
2847
+ }
2848
+ const owned = ownedResponseIdentifier(response, path);
2849
+ if (owned !== void 0 && identifier !== void 0 && resourceIdentifier(owned) !== identifier) {
2850
+ throw new Error("resource identity contradicts provider provenance");
2851
+ }
2852
+ return identifier;
2853
+ });
2854
+ }
2855
+ url(path, collection = false) {
2856
+ const relative = path.slice(1).split("/").map(encodeURIComponent).join("/");
2857
+ return `${this.base.href}${relative}${collection && relative ? "/" : ""}`;
2858
+ }
2859
+ hrefPath(href) {
2860
+ try {
2861
+ validateUrlText(href);
2862
+ if (!(href.startsWith("/") && !href.startsWith("//")) && !/^https?:\/\//i.test(href)) throw new Error("href must be absolute");
2863
+ const url = new URL(href, this.base);
2864
+ if (url.origin !== this.base.origin || url.username || url.password || url.search || url.hash) throw new Error("href is not confined");
2865
+ const segments = urlSegments(url);
2866
+ if (segments.length < this.baseSegments.length || this.baseSegments.some((segment, index) => segments[index] !== segment)) {
2867
+ throw new Error("href escapes root");
2868
+ }
2869
+ return normalize(`/${segments.slice(this.baseSegments.length).join("/")}`);
2870
+ } catch (cause) {
2871
+ throw new FsError("EACCES", { syscall: "webdav", message: "response URL escapes or ambiguously addresses WebDAV root", cause });
2872
+ }
2873
+ }
2874
+ httpError(status, method, path, exclusive = false) {
2875
+ const codes = {
2876
+ 400: "EINVAL",
2877
+ 401: "EACCES",
2878
+ 403: "EACCES",
2879
+ 404: "ENOENT",
2880
+ 405: method === "MKCOL" ? "EEXIST" : "ENOTSUP",
2881
+ 408: "ETIMEDOUT",
2882
+ 409: method === "PUT" || method === "MKCOL" ? "ENOENT" : "EINVAL",
2883
+ 410: "ENOENT",
2884
+ 412: exclusive ? "EEXIST" : "EAGAIN",
2885
+ 413: "EFBIG",
2886
+ 414: "ENAMETOOLONG",
2887
+ 415: "ENOTSUP",
2888
+ 423: "EBUSY",
2889
+ 429: "EAGAIN",
2890
+ 501: "ENOTSUP",
2891
+ 503: "EAGAIN",
2892
+ 504: "ETIMEDOUT",
2893
+ 507: "ENOSPC"
2894
+ };
2895
+ fail3(
2896
+ status >= 300 && status < 400 ? "ENOTSUP" : codes[status] ?? "EIO",
2897
+ method,
2898
+ path,
2899
+ status >= 300 && status < 400 ? "WebDAV redirects are not followed" : `WebDAV HTTP status ${status}`
2900
+ );
2901
+ }
2902
+ async request(method, path, options, init, consume, collection = false, received) {
2903
+ for await (const result of this.requestStream(method, path, options, init, async function* (response, signal) {
2904
+ yield await consume(response, signal);
2905
+ }, collection, received)) return result;
2906
+ throw new Error("missing WebDAV response");
2907
+ }
2908
+ async *requestStream(method, path, options, init, consume, collection = false, received) {
2909
+ if (options.signal?.aborted) fail3("ECANCELED", method, path);
2910
+ const deadline = createRequestTimeout(this.timeoutMs);
2911
+ const timeout = deadline.signal;
2912
+ try {
2913
+ const headers = new Headers(this.headers);
2914
+ for (const [name, value] of new Headers(init.headers)) headers.set(name, value);
2915
+ headers.set("Cache-Control", "no-cache");
2916
+ const signalScope = options.signal ? composeAbortSignals([timeout, options.signal]) : void 0;
2917
+ const signal = signalScope?.signal ?? timeout;
2918
+ let response;
2919
+ const receive = (value, late) => {
2920
+ if (value.redirected || value.type === "opaqueredirect") fail3("ENOTSUP", method, path, "transport followed or hid a redirect");
2921
+ if (value.url && this.hrefPath(value.url) !== path) fail3("EACCES", method, path, "transport changed the requested resource");
2922
+ received?.(value, late);
2923
+ };
2924
+ try {
2925
+ let url = this.url(path, collection);
2926
+ for (let attempt = 0; ; attempt++) {
2927
+ signal.throwIfAborted();
2928
+ response = await new Promise((resolve, reject) => {
2929
+ let abandoned = false;
2930
+ const abort = () => {
2931
+ abandoned = true;
2932
+ signal.removeEventListener("abort", abort);
2933
+ reject(signal.reason);
2934
+ };
2935
+ signal.addEventListener("abort", abort, { once: true });
2936
+ try {
2937
+ Promise.resolve(this.transport(url, {
2938
+ ...init,
2939
+ method,
2940
+ headers,
2941
+ redirect: "manual",
2942
+ credentials: "omit",
2943
+ signal
2944
+ })).then((value) => {
2945
+ signal.removeEventListener("abort", abort);
2946
+ if (!abandoned) resolve(value);
2947
+ else {
2948
+ try {
2949
+ receive(value, true);
2950
+ } catch (ignoredError) {
2951
+ void ignoredError;
2952
+ } finally {
2953
+ if (value.body && !value.body.locked) void value.body.cancel(signal.reason).catch(() => {
2954
+ });
2955
+ }
2956
+ }
2957
+ }, (error) => {
2958
+ signal.removeEventListener("abort", abort);
2959
+ reject(error);
2960
+ }).catch(reject);
2961
+ } catch (error) {
2962
+ signal.removeEventListener("abort", abort);
2963
+ reject(error);
2964
+ }
2965
+ });
2966
+ receive(response, false);
2967
+ signal.throwIfAborted();
2968
+ const location = response.headers.get("Location");
2969
+ if (attempt === 0 && method === "PROPFIND" && !url.endsWith("/") && [301, 302, 307, 308].includes(response.status) && location !== null && this.hrefPath(location) === path && new URL(location, this.base).href === `${url}/`) {
2970
+ await response.body?.cancel();
2971
+ signal.throwIfAborted();
2972
+ url += "/";
2973
+ continue;
2974
+ }
2975
+ yield* consume(response, signal);
2976
+ return;
2977
+ }
2978
+ } catch (cause) {
2979
+ if (options.signal?.aborted) throw new FsError("ECANCELED", { syscall: method, path, cause });
2980
+ if (timeout.aborted) throw new FsError("ETIMEDOUT", { syscall: method, path, cause });
2981
+ if (isFsError(cause)) throw cause;
2982
+ throw new FsError("EIO", { syscall: method, path, message: "WebDAV transport or response failure", cause });
2983
+ } finally {
2984
+ signalScope?.dispose();
2985
+ if (response?.body && !response.body.locked) void response.body.cancel().catch(() => {
2986
+ });
2987
+ }
2988
+ } finally {
2989
+ deadline.dispose();
2990
+ }
2991
+ }
2992
+ async bytes(response, limit, signal) {
2993
+ const chunks = [];
2994
+ let size = 0;
2995
+ for await (const chunk of this.bodyChunks(response, limit, signal)) {
2996
+ chunks.push(chunk);
2997
+ size += chunk.byteLength;
2998
+ }
2999
+ const data = new Uint8Array(size);
3000
+ let offset = 0;
3001
+ for (const chunk of chunks) {
3002
+ data.set(chunk, offset);
3003
+ offset += chunk.byteLength;
3004
+ }
3005
+ return data;
3006
+ }
3007
+ async *bodyChunks(response, limit, signal) {
3008
+ const length = response.headers.get("content-length");
3009
+ if (length !== null && (!/^\d+$/.test(length) || !Number.isSafeInteger(Number(length)))) {
3010
+ fail3("EIO", "webdav", "", "invalid response Content-Length");
3011
+ }
3012
+ const encoding = response.headers.get("content-encoding");
3013
+ const expected = length !== null && (!encoding || encoding.toLowerCase() === "identity") ? Number(length) : void 0;
3014
+ if (expected !== void 0 && expected > limit) fail3("EFBIG", "webdav", "", "response exceeds byte limit");
3015
+ if (!response.body) {
3016
+ if (expected !== void 0 && expected !== 0) fail3("EIO", "webdav", "", "response body length differs from Content-Length");
3017
+ return;
3018
+ }
3019
+ const reader = response.body.getReader();
3020
+ let size = 0;
3021
+ const abort = () => {
3022
+ void reader.cancel(signal.reason).catch(() => {
3023
+ });
3024
+ };
3025
+ signal.addEventListener("abort", abort, { once: true });
3026
+ try {
3027
+ while (true) {
3028
+ signal.throwIfAborted();
3029
+ const result = await reader.read();
3030
+ signal.throwIfAborted();
3031
+ if (result.done) break;
3032
+ size += result.value.byteLength;
3033
+ if (size > limit) fail3("EFBIG", "webdav", "", "response exceeds byte limit");
3034
+ if (expected !== void 0 && size > expected) fail3("EIO", "webdav", "", "response body length differs from Content-Length");
3035
+ yield new Uint8Array(result.value);
3036
+ }
3037
+ if (expected !== void 0 && size !== expected) fail3("EIO", "webdav", "", "response body length differs from Content-Length");
3038
+ } finally {
3039
+ signal.removeEventListener("abort", abort);
3040
+ void reader.cancel().catch(() => {
3041
+ });
3042
+ reader.releaseLock();
3043
+ }
3044
+ }
3045
+ async xml(response, signal) {
3046
+ const data = await this.bytes(response, this.maxXmlBytes, signal);
3047
+ const encoding = data[0] === 255 && data[1] === 254 || data[0] === 60 && data[1] === 0 ? "utf-16le" : data[0] === 254 && data[1] === 255 || data[0] === 0 && data[1] === 60 ? "utf-16be" : "utf-8";
3048
+ return parseXml(new TextDecoder(encoding, { fatal: true }).decode(data), {
3049
+ maxNodes: this.maxXmlBytes,
3050
+ maxAttributes: this.maxXmlBytes
3051
+ });
3052
+ }
3053
+ async multistatus(response, signal, method = "PROPFIND", path = "") {
3054
+ const link = response.headers.get("link");
3055
+ if (link && /\brel\s*=\s*(?:"[^"]*\bnext\b[^"]*"|'[^']*\bnext\b[^']*'|next\b)/i.test(link)) {
3056
+ fail3("ENOTSUP", method, path, "paginated WebDAV responses are unsupported");
3057
+ }
3058
+ const root = await this.xml(response, signal);
3059
+ if (root.namespace !== "DAV:" || root.localName !== "multistatus") throw new Error("expected DAV:multistatus");
3060
+ const responses2 = davChildren(root, "response");
3061
+ if (responses2.length > this.maxEntries) fail3("EFBIG", method, path, "response exceeds entry limit");
3062
+ return responses2;
3063
+ }
3064
+ async lockFailure(response, signal, path) {
3065
+ try {
3066
+ const members = /* @__PURE__ */ new Set();
3067
+ let failure;
3068
+ for (const element of await this.multistatus(response, signal, "LOCK", path)) {
3069
+ const href = davChild(element, "href");
3070
+ const status = davChild(element, "status");
3071
+ if (!href || !status) throw new Error("invalid LOCK multistatus");
3072
+ const member = this.hrefPath(scalar(href));
3073
+ if (member !== path && !member.startsWith(`${path}/`)) {
3074
+ fail3("EACCES", "LOCK", path, "LOCK response member is outside requested subtree");
3075
+ }
3076
+ if (members.has(member)) throw new Error("duplicate LOCK response href");
3077
+ members.add(member);
3078
+ const code = statusCode(status);
3079
+ if (code >= 300 && (!failure || failure.status === 424 && code !== 424)) {
3080
+ failure = { member, status: code };
3081
+ }
3082
+ }
3083
+ if (!failure) throw new Error("LOCK multistatus without a reported failure");
3084
+ this.httpError(failure.status, "LOCK", failure.member);
3085
+ } catch (cause) {
3086
+ throw new FsError(isFsError(cause) ? cause.code : "EIO", {
3087
+ syscall: "LOCK",
3088
+ path,
3089
+ message: "WebDAV LOCK multistatus failure",
3090
+ cause
3091
+ });
3092
+ }
3093
+ }
3094
+ entryStat(element, path) {
3095
+ const wholeStatus = davChild(element, "status");
3096
+ if (wholeStatus) {
3097
+ const code = statusCode(wholeStatus);
3098
+ if (code < 200 || code >= 300) this.httpError(code, "PROPFIND", path);
3099
+ }
3100
+ const properties = /* @__PURE__ */ new Map();
3101
+ const failures = /* @__PURE__ */ new Map();
3102
+ let timestampProperty;
3103
+ let timestampSeen = false;
3104
+ for (const propstat of davChildren(element, "propstat")) {
3105
+ const status = davChild(propstat, "status");
3106
+ const prop = davChild(propstat, "prop");
3107
+ if (!status || !prop) throw new Error("incomplete DAV:propstat");
3108
+ const code = statusCode(status);
3109
+ for (const property of prop.children) {
3110
+ if (property.namespace === timestampNamespace && property.localName === "timestamps") {
3111
+ if (timestampSeen) throw new Error("duplicate timestamp property");
3112
+ timestampSeen = true;
3113
+ if (code === 200) timestampProperty = property;
3114
+ }
3115
+ if (property.namespace !== "DAV:") continue;
3116
+ if (properties.has(property.localName) || failures.has(property.localName)) throw new Error("duplicate DAV property");
3117
+ if (code >= 200 && code < 300) properties.set(property.localName, property);
3118
+ else failures.set(property.localName, code);
3119
+ }
3120
+ }
3121
+ const required = (name) => {
3122
+ const property = properties.get(name);
3123
+ if (property) return property;
3124
+ const failure = failures.get(name);
3125
+ if (failure !== void 0 && failure !== 404) this.httpError(failure, "PROPFIND", path);
3126
+ return fail3("ENOTSUP", "stat", path, `server does not expose DAV:${name}`);
3127
+ };
3128
+ const resourceType = required("resourcetype");
3129
+ const directory = !!davChild(resourceType, "collection");
3130
+ if (resourceType.text.trim() || resourceType.children.some((child) => child.namespace !== "DAV:" || child.localName !== "collection")) {
3131
+ fail3("ENOTSUP", "stat", path, "unsupported DAV resource type");
3132
+ }
3133
+ const sizeText = directory ? "0" : scalar(required("getcontentlength"));
3134
+ if (!/^\d+$/.test(sizeText) || !Number.isSafeInteger(Number(sizeText))) throw new Error("invalid content length property");
3135
+ const date = (name) => {
3136
+ const property = properties.get(name);
3137
+ if (!property) return void 0;
3138
+ const value = Date.parse(scalar(property));
3139
+ if (!Number.isFinite(value)) throw new Error("invalid date property");
3140
+ return value;
3141
+ };
3142
+ const birthtimeMs = date("creationdate");
3143
+ const etag = properties.get("getetag");
3144
+ const validator = etag && scalar(etag);
3145
+ let mtimeMs = date("getlastmodified") ?? 0;
3146
+ let atimeMs = 0;
3147
+ if (timestampProperty) {
3148
+ const timestamps = JSON.parse(scalar(timestampProperty));
3149
+ if (typeof timestamps !== "object" || timestamps === null || !("version" in timestamps) || timestamps.version !== 1 || !("etag" in timestamps) || typeof timestamps.etag !== "string" || !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(timestamps.etag) || !("type" in timestamps) || timestamps.type !== "file" && timestamps.type !== "directory" || !("atimeMs" in timestamps) || typeof timestamps.atimeMs !== "number" || !Number.isFinite(timestamps.atimeMs) || Math.abs(timestamps.atimeMs) > 864e13 || !("mtimeMs" in timestamps) || typeof timestamps.mtimeMs !== "number" || !Number.isFinite(timestamps.mtimeMs) || Math.abs(timestamps.mtimeMs) > 864e13) throw new Error("invalid timestamp property");
3150
+ if (timestamps.etag === validator && timestamps.type === (directory ? "directory" : "file")) {
3151
+ mtimeMs = timestamps.mtimeMs;
3152
+ atimeMs = timestamps.atimeMs;
3153
+ }
3154
+ }
3155
+ const stat = {
3156
+ type: directory ? "directory" : "file",
3157
+ size: Number(sizeText),
3158
+ mode: directory ? 16895 : 33206,
3159
+ mtimeMs,
3160
+ atimeMs,
3161
+ ctimeMs: 0,
3162
+ ...birthtimeMs === void 0 ? {} : { birthtimeMs }
3163
+ };
3164
+ if (validator) this.etags.set(stat, validator);
3165
+ return stat;
3166
+ }
3167
+ async entries(path, depth, options, collection = depth === "1") {
3168
+ return this.request("PROPFIND", path, options, {
3169
+ headers: { Depth: depth, "Content-Type": "application/xml; charset=utf-8" },
3170
+ body: propfindBody
3171
+ }, async (response, signal) => {
3172
+ if (response.status !== 207) this.httpError(response.status, "PROPFIND", path);
3173
+ const result = /* @__PURE__ */ new Map();
3174
+ for (const element of await this.multistatus(response, signal)) {
3175
+ const href = davChild(element, "href");
3176
+ if (!href) throw new Error("missing DAV:href");
3177
+ const member = this.hrefPath(scalar(href));
3178
+ const prefix = path === "/" ? "/" : `${path}/`;
3179
+ if (member !== path && (depth === "0" || !member.startsWith(prefix) || member.slice(prefix.length).includes("/"))) {
3180
+ fail3("EACCES", "PROPFIND", path, "response member is outside requested depth");
3181
+ }
3182
+ if (result.has(member)) throw new Error("duplicate response href");
3183
+ const stat = this.entryStat(element, member);
3184
+ recordOwnedResourceStat(response, this, member, stat);
3185
+ result.set(member, stat);
3186
+ }
3187
+ if (!result.has(path)) throw new Error("response omitted requested resource");
3188
+ return result;
3189
+ }, collection);
3190
+ }
3191
+ async mutation(method, path, options, init = {}, collection = false) {
3192
+ await this.request(method, path, options, init, async (response, signal) => {
3193
+ if (response.status === 207) {
3194
+ for (const element of await this.multistatus(response, signal)) {
3195
+ const href = davChild(element, "href");
3196
+ const status = davChild(element, "status");
3197
+ if (!href || !status) throw new Error("invalid mutation multistatus");
3198
+ const member = this.hrefPath(scalar(href));
3199
+ const code = statusCode(status);
3200
+ if (code < 200 || code >= 300) {
3201
+ try {
3202
+ this.httpError(code, method, member);
3203
+ } catch (cause) {
3204
+ throw new FsError("EIO", { syscall: method, path, message: "WebDAV mutation partially failed", cause });
3205
+ }
3206
+ }
3207
+ }
3208
+ fail3("EIO", method, path, "unexpected mutation multistatus without a reported failure");
3209
+ }
3210
+ const accepted = method === "PUT" ? [200, 201, 204] : method === "MKCOL" ? [201] : method === "DELETE" ? [204] : [201, 204];
3211
+ if (!accepted.includes(response.status)) {
3212
+ const headers = new Headers(init.headers);
3213
+ this.httpError(response.status, method, path, headers.get("If-None-Match") === "*" || headers.get("Overwrite") === "F");
3214
+ }
3215
+ }, collection);
3216
+ }
3217
+ unsupported(operation, path) {
3218
+ return fail3("ENOTSUP", operation, path, `${operation} has no safe portable WebDAV equivalent`);
3219
+ }
3220
+ async maybeStat(path, options) {
3221
+ try {
3222
+ return await this.stat(path, options);
3223
+ } catch (error) {
3224
+ if (isFsError(error, "ENOENT")) return void 0;
3225
+ throw error;
3226
+ }
3227
+ }
3228
+ async stat(path, options = {}) {
3229
+ const normalized = normalize(path);
3230
+ const collection = requiresCollection(path);
3231
+ try {
3232
+ const stat = (await this.entries(normalized, "0", options, collection)).get(normalized);
3233
+ if (collection && stat.type !== "directory") fail3("ENOTDIR", "stat", path);
3234
+ return stat;
3235
+ } catch (error) {
3236
+ if (!isFsError(error, "ENOENT")) throw error;
3237
+ let parent = "";
3238
+ for (const segment of normalized.slice(1).split("/").slice(0, -1)) {
3239
+ parent += `/${segment}`;
3240
+ const ancestor = (await this.entries(parent, "0", options)).get(parent);
3241
+ if (ancestor.type !== "directory") fail3("ENOTDIR", "stat", path);
3242
+ }
3243
+ throw error;
3244
+ }
3245
+ }
3246
+ async lstat(path, options = {}) {
3247
+ return this.stat(path, options);
3248
+ }
3249
+ async readdir(path, options = {}) {
3250
+ const normalized = normalize(path);
3251
+ const entries2 = await this.entries(normalized, "1", options);
3252
+ if (entries2.get(normalized).type !== "directory") fail3("ENOTDIR", "readdir", path);
3253
+ return [...entries2].filter(([member]) => member !== normalized).map(([member, stat]) => ({ name: member.slice(member.lastIndexOf("/") + 1), type: stat.type })).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
3254
+ }
3255
+ async readFile(path, options = {}) {
3256
+ const normalized = normalize(path);
3257
+ const limit = Math.min(this.maxResponseBytes, positive(options.maxBytes ?? this.maxResponseBytes, "maxBytes", true));
3258
+ if ((await this.stat(path, options)).type === "directory") fail3("EISDIR", "readFile", path);
3259
+ return this.request("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async (response, signal) => {
3260
+ if (response.status !== 200) this.httpError(response.status, "GET", path);
3261
+ return this.bytes(response, limit, signal);
3262
+ });
3263
+ }
3264
+ async *readStream(path, options = {}) {
3265
+ const normalized = normalize(path);
3266
+ const start = positive(options.start ?? 0, "start", true);
3267
+ const end = options.endExclusive === void 0 ? Infinity : positive(options.endExclusive, "endExclusive", true);
3268
+ const chunkSize = positive(options.chunkSize ?? 64 * 1024, "chunkSize");
3269
+ if (end < start) fail3("EINVAL", "readStream", path, "endExclusive precedes start");
3270
+ if ((await this.stat(path, options)).type === "directory") fail3("EISDIR", "readStream", path);
3271
+ yield* this.requestStream("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async function* (response, signal) {
3272
+ if (response.status !== 200) this.httpError(response.status, "GET", path);
3273
+ let position = 0;
3274
+ for await (const chunk of this.bodyChunks(response, this.maxResponseBytes, signal)) {
3275
+ const first = Math.max(0, start - position);
3276
+ const last = Math.min(chunk.byteLength, end - position);
3277
+ for (let offset = first; offset < last; offset += chunkSize) {
3278
+ signal.throwIfAborted();
3279
+ yield chunk.slice(offset, Math.min(last, offset + chunkSize));
3280
+ }
3281
+ position += chunk.byteLength;
3282
+ if (position >= end) return;
3283
+ }
3284
+ }.bind(this));
3285
+ }
3286
+ async prepareWrite(path, options) {
3287
+ const normalized = normalize(path);
3288
+ if (options.mode !== void 0) this.unsupported("writeFile mode", path);
3289
+ if (options.flag !== void 0 && !["w", "wx", "a", "ax"].includes(options.flag)) fail3("EINVAL", "writeFile", path);
3290
+ if (options.signal?.aborted) fail3("ECANCELED", "writeFile", path);
3291
+ if (normalized === "/") fail3("EISDIR", "writeFile", path);
3292
+ if (requiresCollection(path)) {
3293
+ await this.stat(path, options);
3294
+ fail3("EISDIR", "writeFile", path);
3295
+ }
3296
+ let parent = "";
3297
+ for (const segment of normalized.slice(1).split("/").slice(0, -1)) {
3298
+ parent += `/${segment}`;
3299
+ await this.stat(`${parent}/`, options);
3300
+ }
3301
+ const exclusive = options.flag === "wx" || options.flag === "ax";
3302
+ const existing = exclusive ? void 0 : await this.maybeStat(normalized, options);
3303
+ if (existing?.type === "directory") fail3("EISDIR", "writeFile", path);
3304
+ let prefix = new Uint8Array();
3305
+ const headers = { "Content-Type": "application/octet-stream" };
3306
+ if (exclusive || options.flag === "a" && !existing) headers["If-None-Match"] = "*";
3307
+ if (options.flag === "a" && existing) {
3308
+ const snapshot = await this.request("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async (response, signal) => {
3309
+ if (response.status !== 200) this.httpError(response.status, "GET", path);
3310
+ const etag = strongEtag(response.headers.get("ETag"), path);
3311
+ if (response.headers.get("Content-Encoding") && response.headers.get("Content-Encoding").toLowerCase() !== "identity") {
3312
+ fail3("ENOTSUP", "appendFile", path, "conditional append requires an identity representation");
3313
+ }
3314
+ return { etag, data: await this.bytes(response, this.maxResponseBytes, signal) };
3315
+ });
3316
+ prefix = snapshot.data;
3317
+ headers["If-Match"] = snapshot.etag;
3318
+ }
3319
+ return { normalized, headers, prefix };
3320
+ }
3321
+ async writeFile(path, data, options = {}) {
3322
+ if (!(data instanceof Uint8Array)) fail3("EINVAL", "writeFile", path, "data must be Uint8Array");
3323
+ const { normalized, headers, prefix } = await this.prepareWrite(path, options);
3324
+ if (options.flag === "a" && prefix.byteLength + data.byteLength > this.maxResponseBytes) fail3("EFBIG", "appendFile", path);
3325
+ const body = new Uint8Array(prefix.byteLength + data.byteLength);
3326
+ body.set(prefix);
3327
+ body.set(data, prefix.byteLength);
3328
+ await this.mutation("PUT", normalized, options, {
3329
+ headers,
3330
+ body
3331
+ });
3332
+ }
3333
+ async writeStream(path, source, options = {}) {
3334
+ if (options.signal?.aborted) fail3("ECANCELED", "writeStream", path);
3335
+ if (this.requestStreamSupport === false || this.requestStreamSupport === "native" && !nativeRequestStreamsSupported(this.base.href)) {
3336
+ fail3("ENOTSUP", "writeStream", path, "transport does not declare supported request streams");
3337
+ }
3338
+ const { normalized, headers, prefix } = await this.prepareWrite(path, options);
3339
+ const deadline = createRequestTimeout(this.timeoutMs);
3340
+ const timeout = deadline.signal;
3341
+ try {
3342
+ const controller = new AbortController();
3343
+ const signalScope = composeAbortSignals([timeout, controller.signal, ...options.signal ? [options.signal] : []]);
3344
+ const signal = signalScope.signal;
3345
+ const limit = this.maxResponseBytes;
3346
+ const chunks = (async function* () {
3347
+ let size = prefix.byteLength;
3348
+ if (prefix.byteLength) yield prefix;
3349
+ for await (const chunk of readBytes(source, signal)) {
3350
+ size += chunk.byteLength;
3351
+ if (size > limit) fail3("EFBIG", "writeStream", path, "upload exceeds byte limit");
3352
+ yield new Uint8Array(chunk);
3353
+ }
3354
+ })();
3355
+ let finished = false;
3356
+ let uploadError;
3357
+ let body;
3358
+ try {
3359
+ body = new ReadableStream({
3360
+ async pull(destination) {
3361
+ try {
3362
+ const result = await chunks.next();
3363
+ if (result.done) {
3364
+ finished = true;
3365
+ destination.close();
3366
+ } else destination.enqueue(result.value);
3367
+ } catch (cause) {
3368
+ uploadError = cause;
3369
+ destination.error(cause);
3370
+ }
3371
+ },
3372
+ cancel(reason) {
3373
+ controller.abort(reason);
3374
+ void chunks.return(void 0).catch(() => {
3375
+ });
3376
+ }
3377
+ }, { highWaterMark: 0 });
3378
+ try {
3379
+ const request = { headers, body, duplex: "half" };
3380
+ await this.mutation("PUT", normalized, options, request);
3381
+ if (uploadError !== void 0) throw uploadError;
3382
+ if (!finished) fail3("EIO", "writeStream", path, "server responded before consuming the upload");
3383
+ } catch (cause) {
3384
+ if (options.signal?.aborted) throw new FsError("ECANCELED", { syscall: "writeStream", path, cause });
3385
+ if (timeout.aborted) throw new FsError("ETIMEDOUT", { syscall: "writeStream", path, cause });
3386
+ if (isFsError(uploadError)) throw uploadError;
3387
+ if (isFsError(cause)) throw cause;
3388
+ throw new FsError("EIO", { syscall: "writeStream", path, cause });
3389
+ }
3390
+ } finally {
3391
+ controller.abort();
3392
+ signalScope.dispose();
3393
+ if (body && !body.locked) void body.cancel().catch(() => {
3394
+ });
3395
+ void chunks.return(void 0).catch(() => {
3396
+ });
3397
+ }
3398
+ } finally {
3399
+ deadline.dispose();
3400
+ }
3401
+ }
3402
+ async appendFile(path, data, options = {}) {
3403
+ await this.writeFile(path, data, { ...options, flag: "a" });
3404
+ }
3405
+ async mkdir(path, options = {}) {
3406
+ const normalized = normalize(path);
3407
+ if (options.mode !== void 0) this.unsupported("mkdir mode", path);
3408
+ if (!options.recursive) {
3409
+ if (normalized === "/") fail3("EEXIST", "mkdir", path);
3410
+ return this.mutation("MKCOL", normalized, options, {}, true);
3411
+ }
3412
+ let current = "";
3413
+ for (const segment of normalized.slice(1).split("/").filter(Boolean)) {
3414
+ current += `/${segment}`;
3415
+ const existing = await this.maybeStat(`${current}/`, options);
3416
+ if (existing) {
3417
+ if (existing.type !== "directory") fail3("ENOTDIR", "mkdir", current);
3418
+ continue;
3419
+ }
3420
+ try {
3421
+ await this.mutation("MKCOL", current, options, {}, true);
3422
+ } catch (error) {
3423
+ if (!isFsError(error, "EEXIST")) throw error;
3424
+ if ((await this.stat(`${current}/`, options)).type !== "directory") fail3("ENOTDIR", "mkdir", current);
3425
+ }
3426
+ }
3427
+ if (normalized === "/") await this.stat(normalized, options);
3428
+ }
3429
+ async rmdir(path, options = {}) {
3430
+ try {
3431
+ const normalized = normalize(path);
3432
+ if (options.signal?.aborted) fail3("ECANCELED", "rmdir", path);
3433
+ if (normalized === "/") fail3("EBUSY", "rmdir", path, "cannot remove WebDAV root");
3434
+ const stat = await this.stat(path, options);
3435
+ if (options.signal?.aborted) fail3("ECANCELED", "rmdir", path);
3436
+ if (stat.type !== "directory") fail3("ENOTDIR", "rmdir", path);
3437
+ if (this.atomicEmptyDirectory) {
3438
+ await this.atomicRmdir(normalized, path, options);
3439
+ return;
3440
+ }
3441
+ const entries2 = await this.readdir(path, options);
3442
+ if (options.signal?.aborted) fail3("ECANCELED", "rmdir", path);
3443
+ if (entries2.length) fail3("ENOTEMPTY", "rmdir", path);
3444
+ this.unsupported("rmdir", path);
3445
+ } catch (error) {
3446
+ if (isFsError(error) && (error.syscall !== "rmdir" || error.path !== path)) {
3447
+ throw new FsError(error.code, { syscall: "rmdir", path, cause: error });
3448
+ }
3449
+ throw error;
3450
+ }
3451
+ }
3452
+ async atomicRmdir(normalized, path, options) {
3453
+ const deadline = createRequestTimeout(this.timeoutMs);
3454
+ const timeout = deadline.signal;
3455
+ try {
3456
+ const signalScope = options.signal ? composeAbortSignals([timeout, options.signal]) : void 0;
3457
+ const signal = signalScope?.signal ?? timeout;
3458
+ try {
3459
+ signal.throwIfAborted();
3460
+ const receipt = await new Promise((resolve, reject) => {
3461
+ const abort = () => {
3462
+ signal.removeEventListener("abort", abort);
3463
+ reject(signal.reason);
3464
+ };
3465
+ signal.addEventListener("abort", abort, { once: true });
3466
+ try {
3467
+ Promise.resolve(this.atomicEmptyDirectory.removeEmptyDirectory(Object.freeze({
3468
+ operation: "atomic-empty-rmdir/v1",
3469
+ namespaceUrl: this.base.href,
3470
+ path: normalized,
3471
+ signal
3472
+ }))).then((result) => {
3473
+ signal.removeEventListener("abort", abort);
3474
+ resolve(result);
3475
+ }, (error) => {
3476
+ signal.removeEventListener("abort", abort);
3477
+ reject(error);
3478
+ });
3479
+ } catch (error) {
3480
+ signal.removeEventListener("abort", abort);
3481
+ reject(error);
3482
+ }
3483
+ });
3484
+ signal.throwIfAborted();
3485
+ if (!receipt || receipt.operation !== "atomic-empty-rmdir/v1" || receipt.namespaceUrl !== this.base.href || receipt.path !== normalized || receipt.outcome !== "removed") {
3486
+ fail3("EIO", "rmdir", path, "atomic empty-directory receipt mismatch; removal outcome is uncertain");
3487
+ }
3488
+ } catch (cause) {
3489
+ if (options.signal?.aborted) throw new FsError("ECANCELED", { syscall: "rmdir", path, cause });
3490
+ if (timeout.aborted) throw new FsError("ETIMEDOUT", { syscall: "rmdir", path, cause });
3491
+ if (isFsError(cause)) throw cause;
3492
+ const code = cause && typeof cause === "object" && "code" in cause && isErrnoCode(cause.code) ? cause.code : "EIO";
3493
+ throw new FsError(code, { syscall: "rmdir", path, cause });
3494
+ } finally {
3495
+ signalScope?.dispose();
3496
+ }
3497
+ } finally {
3498
+ deadline.dispose();
3499
+ }
3500
+ }
3501
+ async rm(path, options = {}) {
3502
+ const normalized = normalize(path);
3503
+ if (normalized === "/") fail3("EBUSY", "rm", path, "cannot remove WebDAV root");
3504
+ try {
3505
+ const stat = await this.stat(path, options);
3506
+ if (stat.type === "directory" && !options.recursive) this.unsupported("nonrecursive directory removal", path);
3507
+ await this.mutation("DELETE", normalized, options, {}, stat.type === "directory");
3508
+ } catch (error) {
3509
+ if (!(options.force && isFsError(error, "ENOENT"))) throw error;
3510
+ }
3511
+ }
3512
+ async guardTransferIdentity(source, destination, options) {
3513
+ if (!this.configuredComparison && this.compareEntry === originalWebDavComparison) return;
3514
+ const comparison = await compareEntries(this, source, this, destination, options);
3515
+ if (source === destination) {
3516
+ if (comparison === "distinct") fail3("EIO", "compareEntry", source, "comparison contradicts identical WebDAV paths");
3517
+ return;
3518
+ }
3519
+ if (comparison === "same") fail3("EINVAL", "compareEntry", destination, "source and destination refer to the same entry");
3520
+ if (comparison === "unknown") fail3("ENOTSUP", "compareEntry", destination, "configured authority cannot establish distinct entries");
3521
+ }
3522
+ async transfer(method, source, destination, options) {
3523
+ const from = normalize(source);
3524
+ const to = normalize(destination);
3525
+ if (from === "/" || to === "/") fail3("EBUSY", method, source, "cannot replace or transfer WebDAV root");
3526
+ const stat = await this.stat(source, options);
3527
+ if (method === "COPY" && stat.type === "directory") fail3("EISDIR", "copyFile", source);
3528
+ if (requiresCollection(destination) && stat.type !== "directory") fail3("ENOTDIR", method, destination);
3529
+ if (from === to) {
3530
+ await this.guardTransferIdentity(from, to, options);
3531
+ if (method === "MOVE") return;
3532
+ fail3(options.exclusive ? "EEXIST" : "EINVAL", "copyFile", source, "source and destination are identical");
3533
+ }
3534
+ if (stat.type === "directory" && to.startsWith(`${from}/`)) fail3("EINVAL", method, destination, "cannot move directory into itself");
3535
+ const existing = await this.maybeStat(destination, options);
3536
+ if (existing) await this.guardTransferIdentity(from, to, options);
3537
+ if (existing && options.exclusive) fail3("EEXIST", method, destination);
3538
+ const checkType = (target) => {
3539
+ if (target.type === "directory" && stat.type === "file") fail3("EISDIR", method, destination);
3540
+ if (target.type !== "directory" && stat.type === "directory") fail3("ENOTDIR", method, destination);
3541
+ };
3542
+ if (existing) checkType(existing);
3543
+ if (stat.type === "directory" && from.startsWith(`${to}/`)) fail3("EINVAL", method, destination, "cannot replace an ancestor of the source");
3544
+ const destinationUrl = this.url(to, stat.type === "directory");
3545
+ const headers = {
3546
+ Destination: destinationUrl,
3547
+ Overwrite: existing ? "T" : "F",
3548
+ Depth: method === "COPY" ? "0" : "infinity"
3549
+ };
3550
+ const sourceEtag = this.etags.get(stat);
3551
+ if (existing && sourceEtag && /^"[\x21\x23-\x7e\x80-\xff]*"$/.test(sourceEtag)) headers["If-Match"] = sourceEtag;
3552
+ const mutate = () => this.mutation(method, from, options, { headers }, stat.type === "directory");
3553
+ if (!existing) return mutate();
3554
+ if (this.overwritePolicy === "etag" && existing.type === "file") {
3555
+ headers.If = `<${destinationUrl}> ([${strongEtag(this.etags.get(existing), destination)}])`;
3556
+ return mutate();
3557
+ }
3558
+ await this.withDestinationLock(to, existing, options, async (token) => {
3559
+ const locked = await this.stat(destination, options);
3560
+ checkType(locked);
3561
+ if (locked.type === "directory" && (await this.readdir(destination, options)).length) fail3("ENOTEMPTY", method, destination);
3562
+ headers.If = `<${destinationUrl}> (${token})`;
3563
+ await mutate();
3564
+ });
3565
+ }
3566
+ async withDestinationLock(path, stat, options, operation) {
3567
+ let token;
3568
+ const collection = stat.type === "directory";
3569
+ const validator = this.etags.get(stat);
3570
+ const unlock = async (value) => {
3571
+ await this.request("UNLOCK", path, {}, { headers: { "Lock-Token": value } }, async (response) => {
3572
+ if (response.status !== 204) this.httpError(response.status, "UNLOCK", path);
3573
+ }, collection).catch(() => {
3574
+ });
3575
+ };
3576
+ try {
3577
+ await this.request("LOCK", path, options, {
3578
+ headers: {
3579
+ "Content-Type": "application/xml; charset=utf-8",
3580
+ Depth: "infinity",
3581
+ Timeout: "Second-60",
3582
+ "If-Match": validator && /^"[\x21\x23-\x7e\x80-\xff]*"$/.test(validator) ? validator : "*"
3583
+ },
3584
+ body: lockBody
3585
+ }, async (response, signal) => {
3586
+ if (response.status === 207) return this.lockFailure(response, signal, path);
3587
+ if (response.status !== 200) this.httpError(response.status, "LOCK", path);
3588
+ const root = await this.xml(response, signal);
3589
+ const discovery = davChild(root, "lockdiscovery");
3590
+ const active = discovery && davChild(discovery, "activelock");
3591
+ const scope = active && davChild(active, "lockscope");
3592
+ const type = active && davChild(active, "locktype");
3593
+ const depth = active && davChild(active, "depth");
3594
+ const lockToken = active && davChild(active, "locktoken");
3595
+ const href = lockToken && davChild(lockToken, "href");
3596
+ const lockRoot = active && davChild(active, "lockroot");
3597
+ const rootHref = lockRoot && davChild(lockRoot, "href");
3598
+ const timeout = active && davChild(active, "timeout");
3599
+ if (root.namespace !== "DAV:" || root.localName !== "prop" || !scope || !davChild(scope, "exclusive") || davChild(scope, "shared") || !type || !davChild(type, "write") || !depth || scalar(depth) !== "infinity" || !href || `<${scalar(href)}>` !== token || lockRoot !== void 0 && (!rootHref || this.hrefPath(scalar(rootHref)) !== path) || !timeout || !/^Second-[1-9]\d*$/i.test(scalar(timeout)) || Number(scalar(timeout).slice(7)) > 4294967295) {
3600
+ fail3("ENOTSUP", "LOCK", path, "server did not grant a finite exclusive depth-infinity write lock");
3601
+ }
3602
+ }, collection, (response, late) => {
3603
+ if (response.status !== 200) return;
3604
+ const header = response.headers.get("Lock-Token");
3605
+ if (!header || !/^<[a-z][a-z0-9+.-]*:[^<>\s\x00-\x20\x7f]+>$/i.test(header)) throw new Error("invalid LOCK token");
3606
+ if (late) void unlock(header);
3607
+ else token = header;
3608
+ });
3609
+ await operation(token);
3610
+ } finally {
3611
+ if (token) await unlock(token);
3612
+ }
3613
+ }
3614
+ async rename(source, destination, options = {}) {
3615
+ await this.transfer("MOVE", source, destination, options);
3616
+ }
3617
+ async copyFile(source, destination, options = {}) {
3618
+ await this.transfer("COPY", source, destination, options);
3619
+ }
3620
+ async realpath(path, options = {}) {
3621
+ const normalized = normalize(path);
3622
+ await this.stat(path, options);
3623
+ return normalized;
3624
+ }
3625
+ async access(path, mode2 = 0, options = {}) {
3626
+ if (!Number.isInteger(mode2) || mode2 < 0 || mode2 > 7) fail3("EINVAL", "access", path);
3627
+ if (options.signal?.aborted) fail3("ECANCELED", "access", path);
3628
+ if (mode2 & 2) this.unsupported("access write/execute permission checks", path);
3629
+ if (mode2 & 1) validateDirectoryAccessPath(path);
3630
+ const stat = await this.stat(path, options);
3631
+ if (options.signal?.aborted) fail3("ECANCELED", "access", path);
3632
+ if (mode2 & 1 && stat.type !== "directory") this.unsupported("access execute permission checks", path);
3633
+ if (mode2 & 4) {
3634
+ if (stat.type === "directory") await this.readdir(path, options);
3635
+ else await this.request("GET", normalize(path), options, { headers: { "Accept-Encoding": "identity" } }, async (response) => {
3636
+ if (response.status !== 200) this.httpError(response.status, "GET", path);
3637
+ });
3638
+ }
3639
+ if (options.signal?.aborted) fail3("ECANCELED", "access", path);
3640
+ }
3641
+ async readlink(path, _options = {}) {
3642
+ return this.unsupported("readlink", path);
3643
+ }
3644
+ async symlink(_target, path, _options = {}) {
3645
+ this.unsupported("symlink", path);
3646
+ }
3647
+ async link(_source, path, _options = {}) {
3648
+ this.unsupported("link", path);
3649
+ }
3650
+ async chmod(path, _mode, _options = {}) {
3651
+ this.unsupported("chmod", path);
3652
+ }
3653
+ async utimes(path, atimeMs, mtimeMs, options = {}) {
3654
+ const normalized = normalize(path);
3655
+ if (![atimeMs, mtimeMs].every((value) => Number.isFinite(value) && Math.abs(value) <= 864e13)) fail3("EINVAL", "utimes", path);
3656
+ const stat = await this.stat(path, options);
3657
+ const etag = strongEtag(this.etags.get(stat), path);
3658
+ const metadata = JSON.stringify({ version: 1, etag, type: stat.type, atimeMs, mtimeMs }).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3659
+ await this.request("PROPPATCH", normalized, options, {
3660
+ headers: { "Content-Type": "application/xml; charset=utf-8", "If-Match": etag },
3661
+ body: `<d:propertyupdate xmlns:d="DAV:" xmlns:v="${timestampNamespace}"><d:set><d:prop><v:timestamps>${metadata}</v:timestamps></d:prop></d:set></d:propertyupdate>`
3662
+ }, async (response, signal) => {
3663
+ if (response.status !== 207) this.httpError(response.status, "PROPPATCH", path);
3664
+ const entries2 = await this.multistatus(response, signal);
3665
+ if (entries2.length !== 1) throw new Error("PROPPATCH must report the requested resource");
3666
+ const entry = entries2[0];
3667
+ const href = davChild(entry, "href");
3668
+ if (!href || this.hrefPath(scalar(href)) !== normalized) fail3("EACCES", "PROPPATCH", path, "unexpected response resource");
3669
+ const status = davChild(entry, "status");
3670
+ if (status) this.httpError(statusCode(status), "PROPPATCH", path);
3671
+ const propstats = davChildren(entry, "propstat");
3672
+ if (propstats.length !== 1) throw new Error("PROPPATCH must report the requested property");
3673
+ const prop = davChild(propstats[0], "prop");
3674
+ const result = davChild(propstats[0], "status");
3675
+ const property = prop?.children[0];
3676
+ if (!result || prop?.children.length !== 1 || property?.namespace !== timestampNamespace || property.localName !== "timestamps") throw new Error("invalid PROPPATCH property result");
3677
+ const code = statusCode(result);
3678
+ if (code !== 200) this.httpError(code, "PROPPATCH", path);
3679
+ }, stat.type === "directory");
3680
+ const updated = await this.stat(path, options);
3681
+ if (updated.atimeMs !== atimeMs || updated.mtimeMs !== mtimeMs) {
3682
+ fail3("EAGAIN", "utimes", path, "server did not retain requested timestamps for the current representation");
3683
+ }
3684
+ }
3685
+ async truncate(path, _length, _options = {}) {
3686
+ this.unsupported("truncate", path);
3687
+ }
3688
+ };
3689
+ var originalWebDavComparison = WebDavFileSystem.prototype.compareEntry;
3690
+
3691
+ // packages/safe-fs/src/bridge/stats.ts
3692
+ function predicates(type) {
3693
+ return {
3694
+ isFile: () => type === "file",
3695
+ isDirectory: () => type === "directory",
3696
+ isSymbolicLink: () => type === "symlink",
3697
+ isBlockDevice: () => false,
3698
+ isCharacterDevice: () => false,
3699
+ isFIFO: () => false,
3700
+ isSocket: () => false
3701
+ };
3702
+ }
3703
+ function bridgeStats(stat) {
3704
+ return {
3705
+ dev: stat.dev ?? 0,
3706
+ ino: stat.ino ?? 0,
3707
+ mode: stat.mode & 4095 | (stat.type === "directory" ? 16384 : stat.type === "symlink" ? 40960 : 32768),
3708
+ nlink: stat.nlink ?? 1,
3709
+ uid: stat.uid ?? 0,
3710
+ gid: stat.gid ?? 0,
3711
+ rdev: 0,
3712
+ size: stat.size,
3713
+ blksize: 4096,
3714
+ blocks: Math.ceil(stat.size / 512),
3715
+ atimeMs: stat.atimeMs,
3716
+ mtimeMs: stat.mtimeMs,
3717
+ ctimeMs: stat.ctimeMs,
3718
+ birthtimeMs: stat.birthtimeMs ?? stat.ctimeMs,
3719
+ atime: new Date(stat.atimeMs),
3720
+ mtime: new Date(stat.mtimeMs),
3721
+ ctime: new Date(stat.ctimeMs),
3722
+ birthtime: new Date(stat.birthtimeMs ?? stat.ctimeMs),
3723
+ ...predicates(stat.type)
3724
+ };
3725
+ }
3726
+ function bridgeDirent(name, parentPath, type) {
3727
+ return { name, parentPath, path: parentPath, ...predicates(type) };
3728
+ }
3729
+
3730
+ // packages/safe-fs/src/bridge/values.ts
3731
+ function record(value, name) {
3732
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3733
+ throw new TypeError(`${name} must be a plain object`);
3734
+ }
3735
+ const prototype = Object.getPrototypeOf(value);
3736
+ if (prototype !== Object.prototype && prototype !== null) {
3737
+ throw new TypeError(`${name} must be a plain object`);
3738
+ }
3739
+ const result = {};
3740
+ for (const key of Reflect.ownKeys(value)) {
3741
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3742
+ if (typeof key !== "string" || descriptor === void 0 || !("value" in descriptor)) {
3743
+ throw new TypeError(`${name} must contain only string-keyed data properties`);
3744
+ }
3745
+ Object.defineProperty(result, key, {
3746
+ value: descriptor.value,
3747
+ enumerable: true,
3748
+ configurable: true,
3749
+ writable: true
3750
+ });
3751
+ }
3752
+ return result;
3753
+ }
3754
+ function onlyKeys(value, allowed) {
3755
+ for (const key of Object.keys(value)) {
3756
+ if (!allowed.includes(key)) throw new TypeError(`Unsupported option: ${key}`);
3757
+ }
3758
+ }
3759
+ function booleanValue(value, fallback = false) {
3760
+ if (value === void 0) return fallback;
3761
+ if (typeof value !== "boolean") throw new TypeError("Expected a boolean option");
3762
+ return value;
3763
+ }
3764
+ function abortError() {
3765
+ return Object.assign(new Error("The operation was aborted"), {
3766
+ name: "AbortError",
3767
+ code: "ABORT_ERR"
3768
+ });
3769
+ }
3770
+ function checkSignal(signal) {
3771
+ if (signal?.aborted) throw abortError();
3772
+ }
3773
+ async function withSignal(signal, operation) {
3774
+ checkSignal(signal);
3775
+ if (signal === void 0) return operation();
3776
+ let onAbort = () => void 0;
3777
+ const cancelled = new Promise((_resolve, reject) => {
3778
+ onAbort = () => reject(abortError());
3779
+ signal.addEventListener("abort", onAbort, { once: true });
3780
+ });
3781
+ try {
3782
+ return await Promise.race([Promise.resolve().then(() => {
3783
+ checkSignal(signal);
3784
+ return operation();
3785
+ }), cancelled]);
3786
+ } finally {
3787
+ signal.removeEventListener("abort", onAbort);
3788
+ }
3789
+ }
3790
+
3791
+ // packages/safe-fs/src/bridge/confinement.ts
3792
+ function denied(path) {
3793
+ throw new FsError("EACCES", { syscall: "resolve", path, message: "path escapes the bridge cwd" });
3794
+ }
3795
+ function relativeComponents(root, path) {
3796
+ if (typeof path !== "string" || !path.startsWith("/") || path.includes("\0")) denied(path);
3797
+ const boundary = root.split("/").filter(Boolean);
3798
+ const components = path.split("/").filter(Boolean);
3799
+ let index = 0;
3800
+ for (const component of boundary) {
3801
+ while (components[index] === ".") index++;
3802
+ if (components[index] !== component) denied(path);
3803
+ index++;
3804
+ }
3805
+ const relative = components.slice(index);
3806
+ let depth = 0;
3807
+ for (const component of relative) {
3808
+ if (component === ".") continue;
3809
+ if (component === "..") {
3810
+ if (depth === 0) denied(path);
3811
+ depth--;
3812
+ } else depth++;
3813
+ }
3814
+ if (path.endsWith("/")) relative.push(".");
3815
+ return relative;
3816
+ }
3817
+ function assertBridgePath(root, path) {
3818
+ if (root !== "/") relativeComponents(root, path);
3819
+ }
3820
+ async function assertCanonicalPath(fs, root, path, options) {
3821
+ checkSignal(options.signal);
3822
+ const canonical = await fs.realpath(path, options);
3823
+ checkSignal(options.signal);
3824
+ assertBridgePath(root, canonical);
3825
+ if (canonical !== path) denied(path);
3826
+ }
3827
+ async function assertCanonicalOperand(fs, root, path, expected, options) {
3828
+ let operand = path;
3829
+ let target = expected;
3830
+ while (true) {
3831
+ checkSignal(options.signal);
3832
+ try {
3833
+ const canonical = await fs.realpath(operand, options);
3834
+ checkSignal(options.signal);
3835
+ assertBridgePath(root, canonical);
3836
+ if (canonical !== target) denied(path);
3837
+ return;
3838
+ } catch (error) {
3839
+ checkSignal(options.signal);
3840
+ if (!isFsError(error) || error.code !== "ENOENT") throw error;
3841
+ let missing = false;
3842
+ try {
3843
+ await fs.lstat(operand, options);
3844
+ } catch (inspectionError) {
3845
+ checkSignal(options.signal);
3846
+ if (!isFsError(inspectionError) || inspectionError.code !== "ENOENT") throw inspectionError;
3847
+ missing = true;
3848
+ }
3849
+ checkSignal(options.signal);
3850
+ if (!missing || operand === root) denied(path);
3851
+ operand = dirname(operand);
3852
+ target = dirname(target);
3853
+ assertBridgePath(root, operand);
3854
+ assertBridgePath(root, target);
3855
+ }
3856
+ }
3857
+ }
3858
+ async function checkedBridgePath(fs, root, path, options, followFinal) {
3859
+ if (root === "/") return path;
3860
+ let pending = relativeComponents(root, path);
3861
+ let current = "";
3862
+ for (const component of root.split("/").filter(Boolean)) {
3863
+ current += `/${component}`;
3864
+ checkSignal(options.signal);
3865
+ try {
3866
+ const stat = await fs.lstat(current, options);
3867
+ checkSignal(options.signal);
3868
+ if (stat.type === "symlink") denied(current);
3869
+ if (stat.type !== "directory")
3870
+ throw new FsError("ENOTDIR", { syscall: "resolve", path: current });
3871
+ } catch (error) {
3872
+ checkSignal(options.signal);
3873
+ if (!isFsError(error) || error.code !== "ENOENT") throw error;
3874
+ if (current !== root) denied(path);
3875
+ if (pending.includes("..")) throw error;
3876
+ const parent = dirname(root);
3877
+ await assertCanonicalPath(fs, parent, parent, options);
3878
+ return path;
3879
+ }
3880
+ }
3881
+ await assertCanonicalPath(fs, root, root, options);
3882
+ let links = 0;
3883
+ let finalLink = false;
3884
+ let index = 0;
3885
+ while (index < pending.length) {
3886
+ const component = pending[index++];
3887
+ checkSignal(options.signal);
3888
+ if (component === ".") continue;
3889
+ if (component === "..") {
3890
+ if (current === root) denied(path);
3891
+ current = dirname(current);
3892
+ continue;
3893
+ }
3894
+ const candidate = `${current}/${component}`;
3895
+ let stat;
3896
+ try {
3897
+ stat = await fs.lstat(candidate, options);
3898
+ } catch (error) {
3899
+ checkSignal(options.signal);
3900
+ if (!isFsError(error) || error.code !== "ENOENT") throw error;
3901
+ if (pending.slice(index).includes("..")) throw error;
3902
+ await assertCanonicalPath(fs, root, current, options);
3903
+ const expected = [candidate, ...pending.slice(index)].join("/");
3904
+ await assertCanonicalOperand(fs, root, path, expected, options);
3905
+ return path;
3906
+ }
3907
+ checkSignal(options.signal);
3908
+ if (stat.type === "symlink" && (followFinal || index < pending.length)) {
3909
+ if (++links > 40) throw new FsError("ELOOP", { syscall: "resolve", path });
3910
+ if (typeof fs.readlink !== "function")
3911
+ throw new FsError("ENOTSUP", { syscall: "readlink", path: candidate });
3912
+ const target = await fs.readlink(candidate, options);
3913
+ checkSignal(options.signal);
3914
+ if (typeof target !== "string" || target.length === 0 || target.includes("\0"))
3915
+ denied(candidate);
3916
+ const absolute = target.startsWith("/") ? target : `${current}/${target}`;
3917
+ pending = [...relativeComponents(root, absolute), ...pending.slice(index)];
3918
+ index = 0;
3919
+ current = root;
3920
+ continue;
3921
+ }
3922
+ if (index < pending.length && stat.type !== "directory") {
3923
+ throw new FsError("ENOTDIR", { syscall: "resolve", path: candidate });
3924
+ }
3925
+ current = candidate;
3926
+ finalLink = stat.type === "symlink";
3927
+ }
3928
+ await assertCanonicalPath(fs, root, finalLink ? dirname(current) : current, options);
3929
+ await assertCanonicalOperand(
3930
+ fs,
3931
+ root,
3932
+ finalLink ? dirname(path) : path,
3933
+ finalLink ? dirname(current) : current,
3934
+ options
3935
+ );
3936
+ return path;
3937
+ }
3938
+
3939
+ // packages/safe-fs/src/bridge/filesystem.ts
3940
+ function fsError(code, syscall, path) {
3941
+ return Object.assign(new Error(`${code}: ${syscall}${path === void 0 ? "" : ` '${path}'`}`), {
3942
+ code,
3943
+ syscall,
3944
+ ...path === void 0 ? {} : { path }
3945
+ });
3946
+ }
3947
+ function unsupported(operation) {
3948
+ throw fsError("ENOTSUP", operation);
3949
+ }
3950
+ function hasCode2(error, code) {
3951
+ return error instanceof Error && "code" in error && error.code === code;
3952
+ }
3953
+ function childPath(parent, name) {
3954
+ return `${parent.replace(/\/$/u, "")}/${name}`;
3955
+ }
3956
+ function optionsRecord(value, allowed) {
3957
+ const options = value == null ? {} : typeof value === "string" ? { encoding: value } : record(value, "options");
3958
+ onlyKeys(options, allowed);
3959
+ return options;
3960
+ }
3961
+ function modeValue(value, fallback) {
3962
+ if (value === void 0) return fallback;
3963
+ const parsed = typeof value === "string" && /^[0-7]+$/u.test(value) ? Number.parseInt(value, 8) : value;
3964
+ if (typeof parsed !== "number" || !Number.isInteger(parsed) || parsed < 0 || parsed > 4095) {
3965
+ throw new TypeError("Invalid file mode");
3966
+ }
3967
+ return parsed;
3968
+ }
3969
+ function timeValue(value) {
3970
+ const number = value instanceof Date ? value.getTime() : Number(value) * 1e3;
3971
+ if (typeof value !== "string" && typeof value !== "number" && !(value instanceof Date) || !Number.isFinite(number)) {
3972
+ throw new TypeError("Invalid timestamp");
3973
+ }
3974
+ return number;
3975
+ }
3976
+ var FileSystemBridge = class {
3977
+ #fs;
3978
+ #cwd;
3979
+ #signal;
3980
+ #primitives;
3981
+ #codec;
3982
+ constructor(fs, options, primitives) {
3983
+ if (fs === void 0) throw new TypeError("An explicit filesystem is required");
3984
+ const cwd = options.cwd ?? "/";
3985
+ if (!primitives.paths.isAbsolute(cwd) || cwd.includes("\0")) throw new TypeError("cwd must be an absolute virtual path");
3986
+ const codec = primitives.codec;
3987
+ if (codec === void 0 || codec === null || typeof codec.isEncoding !== "function" || typeof codec.encode !== "function" || typeof codec.decode !== "function") {
3988
+ throw new TypeError("An explicit filesystem codec is required");
3989
+ }
3990
+ this.#primitives = primitives;
3991
+ this.#codec = Object.freeze({
3992
+ isEncoding: codec.isEncoding.bind(codec),
3993
+ encode: codec.encode.bind(codec),
3994
+ decode: codec.decode.bind(codec)
3995
+ });
3996
+ this.#fs = fs;
3997
+ this.#cwd = primitives.paths.resolve("/", cwd);
3998
+ this.#signal = options.signal;
3999
+ }
4000
+ #encoding(value, fallback, names = false) {
4001
+ const selected = value == null ? fallback : value;
4002
+ if (typeof selected !== "string" || selected !== "buffer" && !this.#codec.isEncoding(selected) || names && !this.#codec.isEncoding("utf8")) {
4003
+ throw new TypeError("Invalid encoding");
4004
+ }
4005
+ return selected;
4006
+ }
4007
+ #textBytes(value) {
4008
+ return this.#primitives.copyBytes(this.#codec.encode(value, "utf8"));
4009
+ }
4010
+ #path(value) {
4011
+ const path = this.#primitives.pathValue === void 0 ? value : this.#primitives.pathValue(value);
4012
+ if (typeof path !== "string" || path.includes("\0")) throw new TypeError("Expected a path without NUL bytes; file handles are unsupported");
4013
+ if (path.length === 0) throw fsError("ENOENT", "path", path);
4014
+ checkSignal(this.#signal);
4015
+ const absolute = this.#primitives.paths.isAbsolute(path) ? path : childPath(this.#cwd, path);
4016
+ assertBridgePath(this.#cwd, absolute);
4017
+ return absolute;
4018
+ }
4019
+ async #call(paths, operation, signal, noFollow = []) {
4020
+ if (signal !== void 0 && !(signal instanceof AbortSignal)) throw new TypeError("Invalid AbortSignal");
4021
+ const signalScope = signal !== void 0 && this.#signal !== void 0 && signal !== this.#signal ? composeAbortSignals([signal, this.#signal]) : void 0;
4022
+ const combined = signalScope?.signal ?? signal ?? this.#signal;
4023
+ try {
4024
+ checkSignal(combined);
4025
+ const options = combined === void 0 ? {} : { signal: combined };
4026
+ return await withSignal(combined, async () => {
4027
+ const absolute = paths.map((path) => this.#path(path));
4028
+ const resolved = [...absolute];
4029
+ if (this.#cwd !== "/") {
4030
+ for (let index = 0; index < absolute.length; index++) {
4031
+ resolved[index] = await checkedBridgePath(this.#fs, this.#cwd, absolute[index], options, !noFollow.includes(index));
4032
+ }
4033
+ }
4034
+ checkSignal(combined);
4035
+ return operation(options, resolved);
4036
+ });
4037
+ } finally {
4038
+ signalScope?.dispose();
4039
+ }
4040
+ }
4041
+ async readFile(path, value) {
4042
+ const options = optionsRecord(value, ["encoding", "flag", "signal"]);
4043
+ if (options.flag !== void 0 && options.flag !== "r") unsupported("readFile flag");
4044
+ if (options.encoding === "buffer") throw new TypeError("Invalid read encoding");
4045
+ const codec = this.#encoding(options.encoding, "buffer");
4046
+ const bytes = await this.#call([path], (signal, resolved) => this.#fs.readFile(resolved[0], signal), options.signal);
4047
+ const buffer = this.#primitives.copyBytes(bytes);
4048
+ return codec === "buffer" ? buffer : this.#codec.decode(buffer, codec);
4049
+ }
4050
+ async #write(path, data, value, fallback) {
4051
+ const options = optionsRecord(value, ["encoding", "flag", "mode", "flush", "signal"]);
4052
+ if (booleanValue(options.flush)) unsupported("writeFile flush");
4053
+ const codec = this.#encoding(options.encoding, "utf8");
4054
+ if (codec === "buffer") throw new TypeError("Invalid write encoding");
4055
+ const flag = options.flag ?? fallback;
4056
+ if (flag !== "w" && flag !== "wx" && flag !== "a" && flag !== "ax") unsupported("writeFile flag");
4057
+ const mode2 = modeValue(options.mode, 438);
4058
+ let bytes;
4059
+ if (typeof data === "string") bytes = this.#primitives.copyBytes(this.#codec.encode(data, codec));
4060
+ else if (ArrayBuffer.isView(data)) bytes = this.#primitives.copyBytes(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
4061
+ else throw new TypeError("writeFile accepts a string or ArrayBuffer view; streams and file handles are unsupported");
4062
+ await this.#call([path], (signal, resolved) => this.#fs.writeFile(resolved[0], bytes, { ...signal, flag, mode: mode2 }), options.signal);
4063
+ }
4064
+ async writeFile(path, data, options) {
4065
+ await this.#write(path, data, options, "w");
4066
+ }
4067
+ async appendFile(path, data, options) {
4068
+ await this.#write(path, data, options, "a");
4069
+ }
4070
+ async stat(path, value) {
4071
+ const options = optionsRecord(value, ["bigint"]);
4072
+ if (booleanValue(options.bigint)) unsupported("stat bigint");
4073
+ return bridgeStats(await this.#call([path], (signal, resolved) => this.#fs.stat(resolved[0], signal)));
4074
+ }
4075
+ async lstat(path, value) {
4076
+ const options = optionsRecord(value, ["bigint"]);
4077
+ if (booleanValue(options.bigint)) unsupported("lstat bigint");
4078
+ return bridgeStats(await this.#call([path], (signal, resolved) => this.#fs.lstat(resolved[0], signal), void 0, [0]));
4079
+ }
4080
+ async readdir(path, value) {
4081
+ const options = optionsRecord(value, ["encoding", "withFileTypes", "recursive"]);
4082
+ const codec = this.#encoding(options.encoding, "utf8", true);
4083
+ const withFileTypes = booleanValue(options.withFileTypes);
4084
+ const recursive = booleanValue(options.recursive);
4085
+ const root = this.#path(path);
4086
+ const entries2 = [];
4087
+ const pending = [root];
4088
+ while (pending.length > 0) {
4089
+ const directory = pending.shift();
4090
+ if (directory === void 0) break;
4091
+ for (const entry of await this.#call([directory], (signal, resolved) => this.#fs.readdir(resolved[0], signal))) {
4092
+ if (entry.name === "" || entry.name === "." || entry.name === ".." || entry.name.includes("/") || entry.name.includes("\0")) {
4093
+ throw new TypeError("Invalid directory entry from filesystem");
4094
+ }
4095
+ const fullPath = childPath(directory, entry.name);
4096
+ entries2.push({ ...entry, parentPath: directory, relative: this.#primitives.paths.relative(root, fullPath) });
4097
+ if (recursive && entry.type === "directory") pending.push(fullPath);
4098
+ }
4099
+ }
4100
+ if (withFileTypes) {
4101
+ return codec === "buffer" ? entries2.map((entry) => bridgeDirent(this.#textBytes(entry.name), entry.parentPath, entry.type)) : entries2.map((entry) => bridgeDirent(this.#codec.decode(this.#textBytes(entry.name), codec), entry.parentPath, entry.type));
4102
+ }
4103
+ return codec === "buffer" ? entries2.map((entry) => this.#textBytes(entry.relative)) : entries2.map((entry) => this.#codec.decode(this.#textBytes(entry.relative), codec));
4104
+ }
4105
+ async mkdir(path, value) {
4106
+ const options = typeof value === "number" || typeof value === "string" ? { mode: value } : optionsRecord(value, ["mode", "recursive"]);
4107
+ const mode2 = modeValue(options.mode, 511);
4108
+ const recursive = booleanValue(options.recursive);
4109
+ const target = this.#path(path);
4110
+ let firstCreated;
4111
+ if (recursive) {
4112
+ let candidate = target;
4113
+ while (true) {
4114
+ try {
4115
+ await this.#call([candidate], (signal, resolved) => this.#fs.stat(resolved[0], signal));
4116
+ break;
4117
+ } catch (error) {
4118
+ if (!hasCode2(error, "ENOENT")) throw error;
4119
+ firstCreated = candidate;
4120
+ if (candidate === this.#cwd) break;
4121
+ const parent = this.#primitives.paths.dirname(candidate);
4122
+ if (parent === candidate) break;
4123
+ candidate = parent;
4124
+ }
4125
+ }
4126
+ }
4127
+ await this.#call([target], (signal, resolved) => this.#fs.mkdir(resolved[0], { ...signal, mode: mode2, recursive }));
4128
+ return firstCreated;
4129
+ }
4130
+ async access(path, mode2 = 0) {
4131
+ if (!Number.isInteger(mode2) || mode2 < 0 || mode2 > 7) throw new TypeError("Invalid access mode");
4132
+ await this.#call([path], (signal, resolved) => this.#fs.access(resolved[0], mode2, signal));
4133
+ }
4134
+ async rm(path, value) {
4135
+ const options = optionsRecord(value, ["force", "recursive", "maxRetries", "retryDelay"]);
4136
+ if (options.maxRetries !== void 0 && options.maxRetries !== 0) unsupported("rm retries");
4137
+ if (options.retryDelay !== void 0) unsupported("rm retryDelay");
4138
+ const recursive = booleanValue(options.recursive);
4139
+ const force = booleanValue(options.force);
4140
+ await this.#call([path], (signal, resolved) => this.#fs.rm(resolved[0], { ...signal, recursive, force }), void 0, [0]);
4141
+ }
4142
+ async rmdir(path, value) {
4143
+ const options = optionsRecord(value, ["recursive", "maxRetries", "retryDelay"]);
4144
+ const stat = await this.lstat(path);
4145
+ if (!stat.isDirectory()) throw fsError("ENOTDIR", "rmdir", this.#path(path));
4146
+ if (!booleanValue(options.recursive)) {
4147
+ if ((await this.readdir(path)).length > 0) throw fsError("ENOTEMPTY", "rmdir", this.#path(path));
4148
+ if (options.maxRetries !== void 0 && options.maxRetries !== 0) unsupported("rmdir retries");
4149
+ if (options.retryDelay !== void 0) unsupported("rmdir retryDelay");
4150
+ const method = this.#fs.rmdir;
4151
+ if (method === void 0) unsupported("atomic rmdir");
4152
+ await this.#call([path], (signal, resolved) => method.call(this.#fs, resolved[0], signal), void 0, [0]);
4153
+ } else await this.rm(path, options);
4154
+ }
4155
+ async rename(source, destination) {
4156
+ await this.#call([source, destination], (signal, resolved) => this.#fs.rename(resolved[0], resolved[1], signal), void 0, [0, 1]);
4157
+ }
4158
+ async copyFile(source, destination, mode2 = 0) {
4159
+ if (mode2 !== 0 && mode2 !== 1) unsupported("copyFile mode");
4160
+ await this.#call([source, destination], (signal, resolved) => this.#fs.copyFile(resolved[0], resolved[1], { ...signal, exclusive: mode2 === 1 }));
4161
+ }
4162
+ async cp(source, destination, value) {
4163
+ const options = optionsRecord(value, ["recursive", "force", "errorOnExist", "mode", "dereference", "preserveTimestamps", "verbatimSymlinks"]);
4164
+ if (booleanValue(options.dereference) || booleanValue(options.preserveTimestamps) || booleanValue(options.verbatimSymlinks)) unsupported("cp options");
4165
+ const recursive = booleanValue(options.recursive);
4166
+ const force = booleanValue(options.force, true);
4167
+ const errorOnExist = booleanValue(options.errorOnExist);
4168
+ if (options.mode !== void 0 && options.mode !== 0 && options.mode !== 1) unsupported("cp mode");
4169
+ const from = this.#path(source);
4170
+ const to = this.#path(destination);
4171
+ const canonicalFrom = await this.realpath(from);
4172
+ let ancestor = to === this.#cwd ? to : this.#primitives.paths.dirname(to);
4173
+ let canonicalAncestor;
4174
+ while (true) {
4175
+ try {
4176
+ canonicalAncestor = await this.realpath(ancestor);
4177
+ break;
4178
+ } catch (error) {
4179
+ if (!hasCode2(error, "ENOENT")) throw error;
4180
+ const parent = this.#primitives.paths.dirname(ancestor);
4181
+ if (parent === ancestor) throw error;
4182
+ ancestor = parent;
4183
+ }
4184
+ }
4185
+ const canonicalTo = this.#primitives.paths.resolve(canonicalAncestor, this.#primitives.paths.relative(ancestor, to));
4186
+ if (canonicalFrom === "/" || canonicalTo === canonicalFrom || canonicalTo.startsWith(`${canonicalFrom}/`)) throw fsError("EINVAL", "cp", to);
4187
+ const copy = async (current, target) => {
4188
+ const sourceStat = await this.lstat(current);
4189
+ if (sourceStat.isSymbolicLink()) unsupported("cp symlink");
4190
+ let destinationStat;
4191
+ try {
4192
+ destinationStat = await this.lstat(target);
4193
+ } catch (error) {
4194
+ if (!hasCode2(error, "ENOENT")) throw error;
4195
+ }
4196
+ if (destinationStat?.isSymbolicLink()) unsupported("cp destination symlink");
4197
+ if (sourceStat.isDirectory()) {
4198
+ if (!recursive) throw fsError("EISDIR", "cp", current);
4199
+ if (destinationStat !== void 0 && !destinationStat.isDirectory()) throw fsError("ENOTDIR", "cp", target);
4200
+ await this.mkdir(target, { recursive: true, mode: sourceStat.mode & 4095 });
4201
+ for (const entry of await this.readdir(current)) await copy(childPath(current, entry), childPath(target, entry));
4202
+ } else {
4203
+ if (destinationStat?.isDirectory()) throw fsError("EISDIR", "cp", target);
4204
+ if (destinationStat !== void 0 && !force) {
4205
+ if (errorOnExist) throw fsError("EEXIST", "cp", target);
4206
+ return;
4207
+ }
4208
+ await this.mkdir(this.#primitives.paths.dirname(target), { recursive: true });
4209
+ await this.copyFile(current, target, options.mode === 1 || !force ? 1 : 0);
4210
+ }
4211
+ };
4212
+ await copy(from, to);
4213
+ }
4214
+ async readlink(path, value) {
4215
+ const codec = this.#encoding(optionsRecord(value, ["encoding"]).encoding, "utf8", true);
4216
+ const method = this.#fs.readlink;
4217
+ if (method === void 0) unsupported("readlink");
4218
+ const target = await this.#call([path], (signal, resolved) => method.call(this.#fs, resolved[0], signal), void 0, [0]);
4219
+ return codec === "buffer" ? this.#textBytes(target) : this.#codec.decode(this.#textBytes(target), codec);
4220
+ }
4221
+ async realpath(path, value) {
4222
+ const codec = this.#encoding(optionsRecord(value, ["encoding"]).encoding, "utf8", true);
4223
+ const target = await this.#call([path], (signal, resolved) => this.#fs.realpath(resolved[0], signal));
4224
+ assertBridgePath(this.#cwd, target);
4225
+ return codec === "buffer" ? this.#textBytes(target) : this.#codec.decode(this.#textBytes(target), codec);
4226
+ }
4227
+ async mkdtemp(prefix, value) {
4228
+ if (typeof prefix !== "string" || prefix.includes("\0")) throw new TypeError("Invalid mkdtemp prefix");
4229
+ const codec = this.#encoding(optionsRecord(value, ["encoding"]).encoding, "utf8", true);
4230
+ for (let attempt = 0; attempt < 10; attempt += 1) {
4231
+ const path = prefix + this.#primitives.randomSuffix();
4232
+ try {
4233
+ await this.mkdir(path, { mode: 448 });
4234
+ return codec === "buffer" ? this.#textBytes(path) : this.#codec.decode(this.#textBytes(path), codec);
4235
+ } catch (error) {
4236
+ if (!hasCode2(error, "EEXIST")) throw error;
4237
+ }
4238
+ }
4239
+ throw fsError("EEXIST", "mkdtemp", prefix);
4240
+ }
4241
+ async symlink(target, path, type) {
4242
+ if (type !== void 0 && type !== null && type !== "file" && type !== "dir") unsupported("symlink type");
4243
+ const linkTarget = this.#primitives.pathValue === void 0 ? target : this.#primitives.pathValue(target);
4244
+ if (typeof linkTarget !== "string" || linkTarget.includes("\0")) throw new TypeError("Invalid symlink target");
4245
+ const destination = this.#path(path);
4246
+ const absoluteTarget = this.#path(this.#primitives.paths.isAbsolute(linkTarget) ? linkTarget : childPath(this.#primitives.paths.dirname(destination), linkTarget));
4247
+ if (this.#cwd !== "/" && this.#primitives.paths.isAbsolute(linkTarget)) {
4248
+ throw fsError("ENOTSUP", "symlink", destination);
4249
+ }
4250
+ const method = this.#fs.symlink;
4251
+ if (method === void 0) unsupported("symlink");
4252
+ await this.#call([destination, absoluteTarget], (signal, resolved) => method.call(this.#fs, linkTarget, resolved[0], signal), void 0, [0]);
4253
+ }
4254
+ async link(existing, path) {
4255
+ const method = this.#fs.link;
4256
+ if (method === void 0) unsupported("link");
4257
+ await this.#call([existing, path], (signal, resolved) => method.call(this.#fs, resolved[0], resolved[1], signal), void 0, [1]);
4258
+ }
4259
+ async chmod(path, value) {
4260
+ const mode2 = modeValue(value, 0);
4261
+ const method = this.#fs.chmod;
4262
+ if (method === void 0) unsupported("chmod");
4263
+ await this.#call([path], (signal, resolved) => method.call(this.#fs, resolved[0], mode2, signal));
4264
+ }
4265
+ async utimes(path, atime, mtime) {
4266
+ const accessTime = timeValue(atime);
4267
+ const modificationTime = timeValue(mtime);
4268
+ const method = this.#fs.utimes;
4269
+ if (method === void 0) unsupported("utimes");
4270
+ await this.#call([path], (signal, resolved) => method.call(this.#fs, resolved[0], accessTime, modificationTime, signal));
4271
+ }
4272
+ async truncate(path, length = 0) {
4273
+ if (!Number.isInteger(length)) throw new TypeError("Invalid truncate length");
4274
+ const method = this.#fs.truncate;
4275
+ if (method === void 0) unsupported("truncate");
4276
+ await this.#call([path], (signal, resolved) => method.call(this.#fs, resolved[0], Math.max(0, length), signal));
4277
+ }
4278
+ };
4279
+
4280
+ // packages/safe-fs/src/bridge/index.ts
4281
+ function createFsBridge(fs, options) {
4282
+ if (options === void 0 || options === null) throw new TypeError("An explicit filesystem codec is required");
4283
+ return new FileSystemBridge(fs, options, {
4284
+ codec: options.codec,
4285
+ copyBytes(bytes) {
4286
+ return new Uint8Array(bytes);
4287
+ },
4288
+ randomSuffix() {
4289
+ return platform.randomUUID().slice(0, 6);
4290
+ },
4291
+ paths: {
4292
+ isAbsolute(path) {
4293
+ return typeof path === "string" && path.startsWith("/");
4294
+ },
4295
+ dirname,
4296
+ relative: relativePath,
4297
+ resolve: resolvePath
4298
+ }
4299
+ });
4300
+ }
4301
+
4302
+ export {
4303
+ FsError,
4304
+ isErrnoCode,
4305
+ isFsError,
4306
+ toFsError,
4307
+ ACCESS_MODES,
4308
+ toByteSource,
4309
+ collectBytes,
4310
+ readBytes,
4311
+ validatePath,
4312
+ resolvePath,
4313
+ normalizePath,
4314
+ relativePath,
4315
+ isPathWithin,
4316
+ assertPathWithin,
4317
+ basename,
4318
+ dirname2 as dirname,
4319
+ extname,
4320
+ isAbsolutePath,
4321
+ joinPath,
4322
+ posixPath,
4323
+ compareEntries,
4324
+ MemoryFileSystem,
4325
+ createMemoryFileSystem,
4326
+ ReadOnlyFileSystem,
4327
+ createReadOnlyFileSystem,
4328
+ MountFileSystem,
4329
+ createMountFileSystem,
4330
+ OverlayFileSystem,
4331
+ createOverlayFileSystem,
4332
+ WebDavFileSystem,
4333
+ createFsBridge
4334
+ };
4335
+ //# sourceMappingURL=chunk-QGN6DKT5.js.map