@openclaw/openshell-sandbox 2026.10.5-beta.4 → 2026.10.10-beta.2

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 (240) hide show
  1. package/README.md +1 -1
  2. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +148 -0
  3. package/node_modules/@openclaw/fs-safe/README.md +20 -4
  4. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -1
  5. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  6. package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
  7. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  8. package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
  9. package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
  10. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
  11. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +2 -2
  12. package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +1 -0
  13. package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +9 -2
  14. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.d.ts +2 -0
  15. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +2 -1
  16. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +6 -0
  17. package/node_modules/@openclaw/fs-safe/dist/config.js +5 -0
  18. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +3 -1
  19. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +8 -7
  20. package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
  21. package/node_modules/@openclaw/fs-safe/dist/copy-link.js +170 -0
  22. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
  23. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
  24. package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +11 -2
  25. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.d.ts +2 -4
  26. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +4 -2
  27. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
  28. package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
  29. package/node_modules/@openclaw/fs-safe/dist/create.js +1 -1
  30. package/node_modules/@openclaw/fs-safe/dist/creation-boundary.js +7 -4
  31. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
  32. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
  33. package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +7 -4
  34. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +20 -5
  35. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
  36. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
  37. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.d.ts +7 -7
  38. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +9 -4
  39. package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +2 -1
  40. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +2 -2
  41. package/node_modules/@openclaw/fs-safe/dist/exact-identity.d.ts +17 -0
  42. package/node_modules/@openclaw/fs-safe/dist/exact-identity.js +29 -0
  43. package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
  44. package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +1 -1
  45. package/node_modules/@openclaw/fs-safe/dist/file-hash.js +11 -9
  46. package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +7 -1
  47. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +27 -5
  48. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +1 -2
  49. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -1
  50. package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
  51. package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
  52. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
  53. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
  54. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +1 -0
  55. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +22 -15
  56. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
  57. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
  58. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
  59. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -0
  60. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -0
  61. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
  62. package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.js +24 -13
  63. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +85 -18
  64. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +5 -0
  65. package/node_modules/@openclaw/fs-safe/dist/native-config.js +11 -0
  66. package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +6 -4
  67. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.d.ts +2 -1
  68. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.js +26 -2
  69. package/node_modules/@openclaw/fs-safe/dist/native-guidance.d.ts +3 -0
  70. package/node_modules/@openclaw/fs-safe/dist/native-guidance.js +21 -0
  71. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
  72. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
  73. package/node_modules/@openclaw/fs-safe/dist/native-operations.js +8 -6
  74. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +44 -4
  75. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +85 -15
  76. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +3 -3
  77. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +33 -6
  78. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
  79. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +22 -3
  80. package/node_modules/@openclaw/fs-safe/dist/native-policy-directory-observation.js +4 -3
  81. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
  82. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
  83. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.d.ts +3 -0
  84. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.js +16 -0
  85. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
  86. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +15 -5
  87. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.d.ts +3 -0
  88. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.js +7 -0
  89. package/node_modules/@openclaw/fs-safe/dist/native-status.d.ts +17 -0
  90. package/node_modules/@openclaw/fs-safe/dist/native-status.js +30 -0
  91. package/node_modules/@openclaw/fs-safe/dist/native.d.ts +31 -4
  92. package/node_modules/@openclaw/fs-safe/dist/native.js +107 -9
  93. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
  94. package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
  95. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +1 -1
  96. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -1
  97. package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +9 -1
  98. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
  99. package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
  100. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +83 -47
  101. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +2 -0
  102. package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +8 -3
  103. package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +13 -0
  104. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +10 -8
  105. package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
  106. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +7 -5
  107. package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
  108. package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
  109. package/node_modules/@openclaw/fs-safe/dist/private-directory.js +1 -1
  110. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
  111. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +3 -2
  112. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +38 -10
  113. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
  114. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +3 -0
  115. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +3 -0
  116. package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +5 -3
  117. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +26 -20
  118. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -0
  119. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +3 -4
  120. package/node_modules/@openclaw/fs-safe/dist/realpath.js +32 -0
  121. package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +4 -0
  122. package/node_modules/@openclaw/fs-safe/dist/regular-file.js +11 -15
  123. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +3 -2
  124. package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
  125. package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
  126. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +8 -13
  127. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +252 -27
  128. package/node_modules/@openclaw/fs-safe/dist/root-directory-creation.js +5 -1
  129. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +11 -8
  130. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +35 -13
  131. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.d.ts +8 -0
  132. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.js +108 -0
  133. package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +7 -0
  134. package/node_modules/@openclaw/fs-safe/dist/root-file.js +15 -0
  135. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +2 -0
  136. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +154 -175
  137. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.d.ts +2 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.js +239 -0
  139. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +37 -15
  140. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.d.ts +32 -0
  141. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.js +56 -0
  142. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
  143. package/node_modules/@openclaw/fs-safe/dist/root-public-types.d.ts +1 -0
  144. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +95 -16
  145. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +6 -4
  146. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +23 -19
  147. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -6
  148. package/node_modules/@openclaw/fs-safe/dist/root-write-publication.js +5 -1
  149. package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +3 -1
  150. package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +2 -0
  151. package/node_modules/@openclaw/fs-safe/dist/secure-file.js +10 -4
  152. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
  153. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +2 -2
  154. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
  155. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
  156. package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +13 -0
  157. package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +29 -0
  158. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +5 -3
  159. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.d.ts +8 -0
  160. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +13 -2
  161. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +71 -3
  162. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +2 -1
  163. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +22 -0
  164. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-types.d.ts +3 -0
  165. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
  166. package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
  167. package/node_modules/@openclaw/fs-safe/dist/trash.js +2 -2
  168. package/node_modules/@openclaw/fs-safe/dist/types.d.ts +4 -0
  169. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
  170. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.d.ts +5 -0
  171. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.js +39 -0
  172. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +1 -0
  173. package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +25 -36
  174. package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +24 -5
  175. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +2 -1
  176. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +4 -0
  177. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.d.ts +2 -0
  178. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
  179. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.js +7 -0
  180. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
  181. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
  182. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.d.ts +1 -0
  183. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.js +12 -10
  184. package/node_modules/@openclaw/fs-safe/docs/advanced.md +2 -1
  185. package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
  186. package/node_modules/@openclaw/fs-safe/docs/config.md +71 -1
  187. package/node_modules/@openclaw/fs-safe/docs/contributing.md +45 -5
  188. package/node_modules/@openclaw/fs-safe/docs/copy.md +45 -5
  189. package/node_modules/@openclaw/fs-safe/docs/durability.md +34 -7
  190. package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
  191. package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
  192. package/node_modules/@openclaw/fs-safe/docs/install.md +47 -27
  193. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -6
  194. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +1 -1
  195. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +79 -6
  196. package/node_modules/@openclaw/fs-safe/docs/native.md +279 -8
  197. package/node_modules/@openclaw/fs-safe/docs/permissions.md +64 -15
  198. package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
  199. package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
  200. package/node_modules/@openclaw/fs-safe/docs/public-api.md +10 -0
  201. package/node_modules/@openclaw/fs-safe/docs/queue-link-free-design.md +237 -0
  202. package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
  203. package/node_modules/@openclaw/fs-safe/docs/reading.md +56 -2
  204. package/node_modules/@openclaw/fs-safe/docs/regular-file.md +2 -0
  205. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
  206. package/node_modules/@openclaw/fs-safe/docs/secure-file.md +1 -1
  207. package/node_modules/@openclaw/fs-safe/docs/security-model.md +35 -0
  208. package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
  209. package/node_modules/@openclaw/fs-safe/docs/staged-file.md +18 -0
  210. package/node_modules/@openclaw/fs-safe/docs/store.md +15 -0
  211. package/node_modules/@openclaw/fs-safe/docs/temp.md +39 -0
  212. package/node_modules/@openclaw/fs-safe/docs/testing.md +13 -0
  213. package/node_modules/@openclaw/fs-safe/docs/types.md +51 -81
  214. package/node_modules/@openclaw/fs-safe/docs/walk.md +4 -1
  215. package/node_modules/@openclaw/fs-safe/docs/watch.md +3 -2
  216. package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
  217. package/node_modules/@openclaw/fs-safe/package.json +21 -12
  218. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  219. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  220. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  221. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  222. package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
  223. package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
  224. package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
  225. package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
  226. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  227. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  228. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  229. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  230. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  231. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  232. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  233. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  234. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
  235. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
  236. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  237. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  238. package/package.json +5 -5
  239. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +0 -336
  240. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +0 -90
@@ -0,0 +1,239 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import fs from "node:fs";
54
+ import path from "node:path";
55
+ import { inspectDirectoryIdentitySync } from "./directory-guard.js";
56
+ import { nodeDirectorySearchOnlyFlags } from "./directory-mode-node.js";
57
+ import { FsSafeError } from "./errors.js";
58
+ import { getNativeBinding } from "./native.js";
59
+ import { captureNativeFdClose } from "./native-binding.js";
60
+ import { openExactNativeRootSync } from "./native-parent-admission.js";
61
+ import { hasNodeErrorCode } from "./path.js";
62
+ import { realpathSync } from "./realpath.js";
63
+ import { assertRootIdentityCurrentSync } from "./root-context.js";
64
+ import { verifyMkdirDirectory } from "./root-create-native.js";
65
+ import { directoryComponentNotDirectoryError } from "./root-errors.js";
66
+ import { isSafePathSegment } from "./safe-path-segment.js";
67
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
68
+ import { getFsSafeTestHooks } from "./test-hooks.js";
69
+ import { pathForWindowsFilesystem } from "./windows-path-alias.js";
70
+ export function tryMkdirNativeParents(root, relativePath) {
71
+ try {
72
+ return mkdirOrdinaryNativeParents(root, relativePath);
73
+ }
74
+ catch (error) {
75
+ if (hasNodeErrorCode(error, "ENOTDIR"))
76
+ throw directoryComponentNotDirectoryError(error);
77
+ throw error;
78
+ }
79
+ }
80
+ function mkdirOrdinaryNativeParents(root, relativePath) {
81
+ if (getFsSafeTestHooks()?.beforeRootFallbackMutation)
82
+ return false;
83
+ const segments = relativePath.split(path.sep === "\\" ? /[\\/]/ : "/");
84
+ if (segments.length < 2 || segments.some(segment => !isSafePathSegment(segment, { allowDotPrefix: true }) || segment.endsWith(".")))
85
+ return false;
86
+ let rootStat;
87
+ assertRootIdentityCurrentSync(root, stat => { rootStat = stat; });
88
+ const target = path.join(root.rootReal, ...segments);
89
+ const parentPath = path.dirname(target);
90
+ try {
91
+ const child = fs.lstatSync(pathForWindowsFilesystem(target), { throwIfNoEntry: false });
92
+ if (child && (!child.isDirectory() || realpathSync.native(pathForWindowsFilesystem(target)) !== target))
93
+ return false;
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ let parentIdentity;
99
+ try {
100
+ parentIdentity = inspectDirectoryIdentitySync(parentPath);
101
+ }
102
+ catch (error) {
103
+ // Reuse this exact Root snapshot and failed parent observation without an
104
+ // intervening await. All opened ancestors still receive fresh native fences.
105
+ return hasNodeErrorCode(error, "ENOENT") ? mkdirMissingParents(root, segments, rootStat) : false;
106
+ }
107
+ try {
108
+ if (realpathSync.native(pathForWindowsFilesystem(parentPath)) !== parentPath)
109
+ return false;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ return mkdirExistingParent(root, segments, parentIdentity);
115
+ }
116
+ /** Ordinary missing suffixes need no policy callbacks or repeated path resolution. */
117
+ function mkdirMissingParents(root, segments, rootStat) {
118
+ const binding = getNativeBinding("openBeneath", "mkdirChildBeneath");
119
+ if (!binding?.openBeneath || !binding.mkdirOpenChildBeneath)
120
+ return false;
121
+ let parentDepth = segments.length - 2;
122
+ let parentPath;
123
+ let parentIdentity;
124
+ for (;;) {
125
+ parentPath = path.join(root.rootReal, ...segments.slice(0, parentDepth));
126
+ try {
127
+ parentIdentity = inspectDirectoryIdentitySync(parentPath, undefined, parentDepth === 0 ? rootStat : undefined);
128
+ if (realpathSync.native(pathForWindowsFilesystem(parentPath)) !== parentPath)
129
+ return false;
130
+ break;
131
+ }
132
+ catch (error) {
133
+ if (!hasNodeErrorCode(error, "ENOENT") || parentDepth === 0)
134
+ return false;
135
+ parentDepth--;
136
+ }
137
+ }
138
+ try {
139
+ const env_1 = { stack: [], error: void 0, hasError: false };
140
+ try {
141
+ const rootOwner = __addDisposableResource(env_1, openExactNativeRootSync(binding, {
142
+ rootPath: root.rootReal, rootIdentity: root.rootIdentity,
143
+ }), false);
144
+ const close = captureNativeFdClose(binding);
145
+ const directories = [];
146
+ const directoryOwner = __addDisposableResource(env_1, { [Symbol.dispose]() {
147
+ const errors = [];
148
+ for (const directory of directories.toReversed()) {
149
+ try {
150
+ close(directory.fd);
151
+ }
152
+ catch (error) {
153
+ errors.push(error);
154
+ }
155
+ }
156
+ if (errors.length)
157
+ throw new AggregateError(errors, "mkdir directory close failed");
158
+ } }, false);
159
+ const flags = (nodeDirectorySearchOnlyFlags()?.flags ?? fs.constants.O_RDONLY) |
160
+ (fs.constants.O_DIRECTORY ?? 0) | (fs.constants.O_NOFOLLOW ?? 0);
161
+ let current = rootOwner.fd;
162
+ if (parentDepth > 0) {
163
+ current = binding.openBeneath(rootOwner.fd, segments.slice(0, parentDepth).join("/"), flags).fd;
164
+ directories.push({ fd: current, pathname: parentPath });
165
+ inspectFileIdentitySync(() => fs.fstatSync(current, { bigint: true }), parentIdentity);
166
+ }
167
+ const assertCurrent = () => {
168
+ assertRootIdentityCurrentSync(root);
169
+ for (const directory of directories)
170
+ verifyMkdirDirectory(binding, directory.fd, directory.pathname);
171
+ verifyMkdirDirectory(binding, rootOwner.fd, root.rootReal);
172
+ };
173
+ assertCurrent();
174
+ for (const segment of segments.slice(parentDepth)) {
175
+ const child = binding.mkdirOpenChildBeneath(current, segment, 0o777, flags);
176
+ parentPath = path.join(parentPath, segment);
177
+ directories.push({ fd: child.fd, pathname: parentPath });
178
+ // Retain every ancestor until the complete suffix is admitted. The
179
+ // post-create fence also precedes the next synchronous child mutation.
180
+ assertCurrent();
181
+ current = child.fd;
182
+ }
183
+ return true;
184
+ }
185
+ catch (e_1) {
186
+ env_1.error = e_1;
187
+ env_1.hasError = true;
188
+ }
189
+ finally {
190
+ __disposeResources(env_1);
191
+ }
192
+ }
193
+ catch (error) {
194
+ if (hasNodeErrorCode(error, "ENOTDIR"))
195
+ throw directoryComponentNotDirectoryError(error);
196
+ throw error;
197
+ }
198
+ }
199
+ function mkdirExistingParent(root, segments, parentIdentity) {
200
+ const env_2 = { stack: [], error: void 0, hasError: false };
201
+ try {
202
+ const target = path.join(root.rootReal, ...segments);
203
+ const parentPath = path.dirname(target);
204
+ const binding = getNativeBinding("openBeneath", "mkdirChildBeneath");
205
+ if (!binding?.openBeneath || !binding.mkdirChildBeneath) {
206
+ throw new FsSafeError("helper-unavailable", "native confined creation is unavailable");
207
+ }
208
+ const rootOwner = __addDisposableResource(env_2, openExactNativeRootSync(binding, {
209
+ rootPath: root.rootReal, rootIdentity: root.rootIdentity,
210
+ }), false);
211
+ verifyMkdirDirectory(binding, rootOwner.fd, root.rootReal);
212
+ const flags = (nodeDirectorySearchOnlyFlags()?.flags ?? fs.constants.O_RDONLY) |
213
+ (fs.constants.O_DIRECTORY ?? 0) | (fs.constants.O_NOFOLLOW ?? 0);
214
+ const close = captureNativeFdClose(binding);
215
+ const parent = binding.openBeneath(rootOwner.fd, segments.slice(0, -1).join("/"), flags).fd;
216
+ const parentOwner = __addDisposableResource(env_2, { [Symbol.dispose]() { close(parent); } }, false);
217
+ inspectFileIdentitySync(() => fs.fstatSync(parent, { bigint: true }), parentIdentity);
218
+ verifyMkdirDirectory(binding, parent, parentPath);
219
+ assertRootIdentityCurrentSync(root);
220
+ const name = segments.at(-1);
221
+ const child = binding.mkdirOpenChildBeneath
222
+ ? binding.mkdirOpenChildBeneath(parent, name, 0o777, flags).fd
223
+ : (binding.mkdirChildBeneath(parent, name, 0o777), binding.openBeneath(parent, name, flags).fd);
224
+ const childOwner = __addDisposableResource(env_2, { [Symbol.dispose]() { close(child); } }, false);
225
+ assertRootIdentityCurrentSync(root);
226
+ verifyMkdirDirectory(binding, child, target);
227
+ inspectDirectoryIdentitySync(parentPath, parentIdentity);
228
+ verifyMkdirDirectory(binding, parent, parentPath);
229
+ verifyMkdirDirectory(binding, rootOwner.fd, root.rootReal);
230
+ return true;
231
+ }
232
+ catch (e_2) {
233
+ env_2.error = e_2;
234
+ env_2.hasError = true;
235
+ }
236
+ finally {
237
+ __disposeResources(env_2);
238
+ }
239
+ }
@@ -1,15 +1,15 @@
1
1
  import fsSync, {} from "node:fs";
2
2
  import path from "node:path";
3
- import { assertAsyncDirectoryGuard, assertSyncDirectoryGuard } from "./directory-guard.js";
3
+ import { assertDirectoryGuard, assertSyncDirectoryGuard } from "./directory-guard.js";
4
4
  import { FsSafeError } from "./errors.js";
5
5
  import { assertMutationNotDenied } from "./deny-mutations.js";
6
- import { openNativeParentAdmission, openNativeRootAdmission, } from "./native-parent-admission.js";
6
+ import { borrowNativeRootParent, openNativeParentAdmission, openExactNativeRootAdmissionSync, } from "./native-parent-admission.js";
7
7
  import { requireNativeBinding } from "./native.js";
8
8
  import { isNotFoundPathError } from "./path.js";
9
- import { assertRootIdentityCurrent, assertRootIdentityCurrentSync } from "./root-context.js";
9
+ import { assertRootIdentityCurrentSync } from "./root-context.js";
10
10
  import { resolveRootPathSync } from "./root-path.js";
11
11
  import { admitPathInsideRoot } from "./root-boundary.js";
12
- import { errorCauseOptions, fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, outsideWorkspaceError, } from "./root-errors.js";
12
+ import { errorCauseOptions, fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, outsideWorkspaceError, rootPathChangedError, } from "./root-errors.js";
13
13
  import { assertFinalSymlinkRejected, mutationSymlinkResolution } from "./root-symlink-policy.js";
14
14
  import { PATH_ALIAS_POLICIES } from "./path-policy.js";
15
15
  import { createSuppressedError } from "./suppressed-error.js";
@@ -80,15 +80,14 @@ export async function movePathNative(root, params, paths, overwrite = false) {
80
80
  throw error;
81
81
  // Advisory fast rejection only. renameNoReplace owns the collision decision.
82
82
  }
83
- const binding = requireNativeBinding();
83
+ const binding = requireNativeBinding("openBeneath", overwrite ? "renameReplaceWithIdentity" : "renameNoReplace");
84
84
  if (typeof (overwrite ? binding.renameReplaceWithIdentity : binding.renameNoReplace) !== "function") {
85
85
  throw new FsSafeError("helper-unavailable", "native move is unavailable");
86
86
  }
87
- const rootAdmission = await openNativeRootAdmission(binding, {
87
+ const rootAdmission = openExactNativeRootAdmissionSync(binding, {
88
88
  rootPath: root.rootReal,
89
89
  rootIdentity: root.rootIdentity,
90
90
  operation: "native move",
91
- searchOnly: true,
92
91
  });
93
92
  let sourceParent;
94
93
  let targetParent;
@@ -96,15 +95,18 @@ export async function movePathNative(root, params, paths, overwrite = false) {
96
95
  let operationError;
97
96
  try {
98
97
  const sourceParentRelativePath = nativeParentRelativePath(root.rootReal, paths.sourceParentPath);
99
- sourceParent = await openNativeParentAdmission(binding, rootAdmission, sourceParentRelativePath);
98
+ sourceParent = sourceParentRelativePath === "" ? borrowNativeRootParent(rootAdmission) : await openNativeParentAdmission(binding, rootAdmission, sourceParentRelativePath, "native-directory");
100
99
  const targetParentRelativePath = nativeParentRelativePath(root.rootReal, paths.targetParentPath);
101
100
  targetParent = targetParentRelativePath === sourceParentRelativePath
102
101
  ? sourceParent
103
- : await openNativeParentAdmission(binding, rootAdmission, targetParentRelativePath);
102
+ : targetParentRelativePath === "" ? borrowNativeRootParent(rootAdmission)
103
+ : await openNativeParentAdmission(binding, rootAdmission, targetParentRelativePath, "native-directory");
104
104
  const parentAdmissions = targetParent === sourceParent
105
105
  ? [sourceParent]
106
106
  : [sourceParent, targetParent];
107
- await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("move", paths.targetPath);
107
+ const beforeMutation = getFsSafeTestHooks()?.beforeRootFallbackMutation;
108
+ if (beforeMutation)
109
+ await beforeMutation("move", paths.targetPath);
108
110
  const admittedSourcePath = admitMovePath(root, sourceParent, path.basename(paths.sourcePath));
109
111
  const admittedTargetPath = admitMovePath(root, targetParent, path.basename(paths.targetPath));
110
112
  if (params.denyMutations) {
@@ -113,9 +115,29 @@ export async function movePathNative(root, params, paths, overwrite = false) {
113
115
  await assertMutationNotDenied(admittedSourcePath, params.denyMutations, { protectAncestors: true });
114
116
  await assertMutationNotDenied(admittedTargetPath, params.denyMutations, { protectAncestors: true });
115
117
  }
116
- await assertRootIdentityCurrent(root);
117
- for (const admission of parentAdmissions)
118
- assertSyncDirectoryGuard(admission.guard);
118
+ const rootParent = sourceParentRelativePath === "" ? sourceParent
119
+ : targetParentRelativePath === "" ? targetParent : undefined;
120
+ if (rootParent) {
121
+ // Borrowing Root already checked this guard in the same synchronous phase.
122
+ const borrowedRootCurrent = sourceParent === targetParent &&
123
+ sourceParentRelativePath === "" && !beforeMutation &&
124
+ params.denyMutations === undefined && params.assertBeforeMutation === undefined;
125
+ if (!borrowedRootCurrent) {
126
+ try {
127
+ assertSyncDirectoryGuard(rootParent.guard);
128
+ }
129
+ catch (error) {
130
+ throw rootPathChangedError(error instanceof Error ? error : undefined);
131
+ }
132
+ }
133
+ }
134
+ else {
135
+ assertRootIdentityCurrentSync(root);
136
+ }
137
+ for (const admission of parentAdmissions) {
138
+ if (admission !== rootParent)
139
+ assertSyncDirectoryGuard(admission.guard);
140
+ }
119
141
  // Linux auto fallback needs an exact source even without an authority callback.
120
142
  const sourceStat = overwrite || params.assertBeforeMutation || (process.platform === "linux" && !params.requireNative)
121
143
  ? inspectFileIdentitySync(() => admitMoveSourceStat(fsSync.lstatSync(admittedSourcePath, { bigint: true })), paths.expectedSourceIdentity)
@@ -185,7 +207,7 @@ export async function movePathNative(root, params, paths, overwrite = false) {
185
207
  }
186
208
  try {
187
209
  for (const admission of parentAdmissions)
188
- await assertAsyncDirectoryGuard(admission.guard);
210
+ assertDirectoryGuard(admission.guard, "native");
189
211
  if (overwrite)
190
212
  inspectFileIdentitySync(() => fsSync.lstatSync(admittedTargetPath, { bigint: true }), sourceStat);
191
213
  }
@@ -211,7 +233,7 @@ export async function movePathNative(root, params, paths, overwrite = false) {
211
233
  }
212
234
  }
213
235
  try {
214
- await rootAdmission.root.close();
236
+ rootAdmission.root.close();
215
237
  }
216
238
  catch (error) {
217
239
  closeErrors.push(error);
@@ -0,0 +1,32 @@
1
+ import fs from "node:fs";
2
+ import type { PinnedMutationPolicySnapshot } from "./pinned-mutation-admission.js";
3
+ import { type RootContext } from "./root-context.js";
4
+ export type Creation = {
5
+ root: RootContext;
6
+ directory: string;
7
+ target: string;
8
+ mkdir: boolean;
9
+ private?: boolean;
10
+ policy?: PinnedMutationPolicySnapshot;
11
+ resolveCurrent(): Promise<string>;
12
+ assertBeforeMutation?: () => void;
13
+ originalPath?: string;
14
+ selectedParent?: NonNullable<ReturnType<typeof existingCreationParent>>;
15
+ };
16
+ export declare function existingCreationParent(params: Creation): {
17
+ path: string;
18
+ identity: fs.BigIntStats;
19
+ relative: string;
20
+ flags: number;
21
+ } | undefined;
22
+ export declare function probeNativeCreationPath(root: RootContext, relativePath: string): {
23
+ rootReal: string;
24
+ rootWithSep: string;
25
+ resolved: string;
26
+ parent: {
27
+ path: string;
28
+ identity: fs.BigIntStats;
29
+ relative: string;
30
+ flags: number;
31
+ };
32
+ } | undefined;
@@ -0,0 +1,56 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { inspectDirectoryIdentitySync } from "./directory-guard.js";
4
+ import { nodeDirectorySearchOnlyFlags } from "./directory-mode-node.js";
5
+ import { assertRootIdentityCurrentSync } from "./root-context.js";
6
+ import { getFsSafeTestHooks } from "./test-hooks.js";
7
+ import { pathForWindowsFilesystem } from "./windows-path-alias.js";
8
+ import { realpathSync } from "./realpath.js";
9
+ import { isSafePathSegment } from "./safe-path-segment.js";
10
+ export function existingCreationParent(params) {
11
+ if (params.private || params.policy || params.assertBeforeMutation !== undefined ||
12
+ getFsSafeTestHooks()?.beforeRootFallbackMutation || !params.originalPath ||
13
+ params.directory !== path.dirname(params.target))
14
+ return undefined;
15
+ const segments = params.originalPath.split(path.sep === "\\" ? /[\\/]/ : "/");
16
+ if (segments.some(segment => !isSafePathSegment(segment, { allowDotPrefix: true }) || segment.endsWith(".")) ||
17
+ path.join(params.root.rootReal, ...segments) !== params.target)
18
+ return undefined;
19
+ let rootStat;
20
+ assertRootIdentityCurrentSync(params.root, stat => { rootStat = stat; });
21
+ try {
22
+ const identity = inspectDirectoryIdentitySync(params.directory, undefined, params.directory === params.root.rootReal ? rootStat : undefined);
23
+ if (realpathSync.native(pathForWindowsFilesystem(params.directory)) !== params.directory)
24
+ return undefined;
25
+ return {
26
+ path: params.directory, identity,
27
+ relative: path.relative(params.root.rootReal, params.directory).split(path.sep).join("/"),
28
+ flags: (nodeDirectorySearchOnlyFlags()?.flags ?? fs.constants.O_RDONLY) |
29
+ (fs.constants.O_DIRECTORY ?? 0) | (fs.constants.O_NOFOLLOW ?? 0),
30
+ };
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ export function probeNativeCreationPath(root, relativePath) {
37
+ if (typeof relativePath !== "string")
38
+ return undefined;
39
+ const segments = relativePath.split(path.sep === "\\" ? /[\\/]/ : "/");
40
+ if (segments.some(segment => !isSafePathSegment(segment, { allowDotPrefix: true }) || segment.endsWith(".")))
41
+ return undefined;
42
+ const target = path.join(root.rootReal, ...segments);
43
+ assertRootIdentityCurrentSync(root);
44
+ try {
45
+ if (fs.lstatSync(pathForWindowsFilesystem(target), { throwIfNoEntry: false }))
46
+ return undefined;
47
+ }
48
+ catch {
49
+ return undefined;
50
+ }
51
+ const parent = existingCreationParent({ root, target, directory: path.dirname(target), mkdir: true,
52
+ originalPath: relativePath, resolveCurrent: async () => target });
53
+ if (!parent)
54
+ return undefined;
55
+ return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved: target, parent };
56
+ }
@@ -38,6 +38,8 @@ export type RootCopyOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyM
38
38
  clone?: CopyCloneMode;
39
39
  signal?: AbortSignal;
40
40
  preserveSourceMode?: boolean;
41
+ preserveMetadata?: boolean;
42
+ sourceSymlinks?: "reject" | "copy-link";
41
43
  onDestinationPublished?: (receipt: RootCopyPublicationReceipt) => void;
42
44
  };
43
45
  export type RootCopySource = string | {
@@ -9,3 +9,4 @@ export type { CopyCloneMode } from "./copy-policy.js";
9
9
  export type { RootEntriesOptions } from "./root-entries.js";
10
10
  export type { ContainmentGuarantee } from "./containment.js";
11
11
  export type { RootWalkDataEntry, RootWalkDataEntryKind, RootWalkDirectoryErrorBehavior, RootWalkEntry, RootWalkEntryFilter, RootWalkEntryFilterResult, RootWalkEntryKind, RootWalkLimitBehavior, RootWalkOptions, RootWalkSymlinkPolicy, } from "./root-walk.js";
12
+ export type { ExactFileIdentity } from "./exact-identity.js";
@@ -1,16 +1,16 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { scheduler } from "node:timers/promises";
4
- import { assertSyncDirectoryGuard } from "./directory-guard.js";
5
4
  import { assertMutationNotDenied } from "./deny-mutations.js";
6
5
  import { FsSafeError } from "./errors.js";
7
6
  import { MutationAuthorityError } from "./mutation-authority.js";
8
7
  import { getNativeBinding } from "./native.js";
9
- import { openNativeParentAdmission, openNativeRootAdmission } from "./native-parent-admission.js";
8
+ import { borrowNativeRootParent, openNativeParentAdmission, openExactNativeRootAdmissionSync } from "./native-parent-admission.js";
10
9
  import { isNotFoundPathError, isPathInside } from "./path.js";
11
10
  import { assertRootIdentityCurrentSync } from "./root-context.js";
12
11
  import { normalizeRemoveGuardError, normalizeRemovePathError } from "./root-errors.js";
13
12
  import { captureNonrecursiveRemovalAdmission, nonrecursiveRemovalKind } from "./root-remove.js";
13
+ import { assertRemovalDirectoryCurrent, createRemovalDirectoryAssertion } from "./root-remove-identity.js";
14
14
  import { createSuppressedError } from "./suppressed-error.js";
15
15
  import { getFsSafeTestHooks } from "./test-hooks.js";
16
16
  function unavailable() {
@@ -28,8 +28,73 @@ function normalize(error, details) {
28
28
  }
29
29
  return normalizeRemovePathError(error, details);
30
30
  }
31
+ async function removeNativeLeaf(binding, parent, target, options, boundary, rootGuardCurrent) {
32
+ const assertNotAborted = () => {
33
+ try {
34
+ options.signal?.throwIfAborted();
35
+ }
36
+ catch (error) {
37
+ throw new MutationAuthorityError(error);
38
+ }
39
+ };
40
+ assertNotAborted();
41
+ const before = getFsSafeTestHooks()?.beforeRootFallbackMutation;
42
+ if (before) {
43
+ try {
44
+ await before("remove", target);
45
+ }
46
+ catch (error) {
47
+ throw normalizeRemoveGuardError(error);
48
+ }
49
+ }
50
+ if (options.denyMutations)
51
+ await assertMutationNotDenied(target, options.denyMutations, { protectAncestors: true });
52
+ const name = path.basename(target);
53
+ const observe = (expected) => {
54
+ assertNotAborted();
55
+ if (!rootGuardCurrent)
56
+ boundary.assertCurrent();
57
+ rootGuardCurrent = false;
58
+ try {
59
+ const entry = binding.rootRemovalStat(parent.fd, name);
60
+ if (expected && (entry.dev !== expected.dev || entry.ino !== expected.ino || entry.directory !== expected.directory)) {
61
+ throw new FsSafeError("path-mismatch", "removal entry changed");
62
+ }
63
+ if (entry.symlink && options.mutationSymlinks !== undefined)
64
+ throw new FsSafeError("symlink", "symlink not allowed", {
65
+ details: { operation: "remove", phase: expected ? "remove" : "inspect", relativePath: "" },
66
+ });
67
+ return entry;
68
+ }
69
+ catch (error) {
70
+ if (options.force && isNotFoundPathError(error))
71
+ return undefined;
72
+ throw normalize(error, { operation: "remove", phase: expected ? "remove" : "inspect", relativePath: "" });
73
+ }
74
+ };
75
+ const entry = observe();
76
+ if (!entry)
77
+ return;
78
+ if (!entry.directory && options[nonrecursiveRemovalKind] === "directory") {
79
+ throw new FsSafeError("path-mismatch", "store entry is no longer a directory");
80
+ }
81
+ if (options.assertBeforeMutation) {
82
+ options.assertBeforeMutation();
83
+ if (!observe(entry))
84
+ return;
85
+ }
86
+ try {
87
+ binding.rootRemovalUnlink(parent.fd, name, entry.dev, entry.ino, entry.directory);
88
+ }
89
+ catch (error) {
90
+ if (!(options.force && isNotFoundPathError(error)))
91
+ throw normalize(error, { operation: "remove", phase: "remove", relativePath: "" });
92
+ }
93
+ boundary.assertAfterMutation();
94
+ assertNotAborted();
95
+ }
31
96
  export async function removePathInRootNative(root, target, options, receipts) {
32
- const binding = getNativeBinding();
97
+ const binding = getNativeBinding("openBeneath", "rootRemovalStat", "rootRemovalUnlink");
33
98
  if (!binding?.rootRemovalStat || !binding.rootRemovalUnlink) {
34
99
  unavailable();
35
100
  }
@@ -39,9 +104,22 @@ export async function removePathInRootNative(root, target, options, receipts) {
39
104
  const boundary = await captureNonrecursiveRemovalAdmission(root, target, options, receipts);
40
105
  if (!boundary)
41
106
  return;
42
- const rootAdmission = await openNativeRootAdmission(binding, {
43
- rootPath: root.rootReal, rootIdentity: root.rootIdentity, operation: "remove", reportCloseErrors: true, searchOnly: true,
44
- }).catch(error => { throw normalizeRemoveGuardError(error); });
107
+ if (options.recursive && options.maxEntries === 0) {
108
+ options.signal?.throwIfAborted();
109
+ boundary.assertCurrent();
110
+ throw new FsSafeError("too-large", "recursive removal budget exceeded", {
111
+ details: { operation: "remove", phase: "inspect", relativePath: "" },
112
+ });
113
+ }
114
+ let rootAdmission;
115
+ try {
116
+ rootAdmission = openExactNativeRootAdmissionSync(binding, {
117
+ rootPath: root.rootReal, rootIdentity: root.rootIdentity, operation: "remove", reportCloseErrors: true,
118
+ });
119
+ }
120
+ catch (error) {
121
+ throw normalizeRemoveGuardError(error);
122
+ }
45
123
  let parent;
46
124
  let initialEntry;
47
125
  let initialDirectory;
@@ -53,7 +131,7 @@ export async function removePathInRootNative(root, target, options, receipts) {
53
131
  if (!isPathInside(root.rootReal, parentPath))
54
132
  throw new FsSafeError("outside-workspace", "removal parent is outside root");
55
133
  try {
56
- parent = await openNativeParentAdmission(binding, rootAdmission, path.relative(root.rootReal, parentPath).split(path.sep).join("/"));
134
+ parent = parentPath === root.rootReal ? borrowNativeRootParent(rootAdmission) : await openNativeParentAdmission(binding, rootAdmission, path.relative(root.rootReal, parentPath).split(path.sep).join("/"), "native-directory");
57
135
  }
58
136
  catch (error) {
59
137
  assertRootIdentityCurrentSync(root);
@@ -95,7 +173,13 @@ export async function removePathInRootNative(root, target, options, receipts) {
95
173
  }
96
174
  }
97
175
  }
98
- if (!options.recursive || initialEntry)
176
+ if (!options.recursive)
177
+ await removeNativeLeaf(binding, parent, target, options, boundary,
178
+ // The borrowed Root guard is the complete parent fence. Without an
179
+ // intervening callback or await, the first leaf sample shares its phase.
180
+ parentPath === root.rootReal && !options.signal && !options.denyMutations &&
181
+ !options.assertBeforeMutation && !getFsSafeTestHooks()?.beforeRootFallbackMutation);
182
+ else if (initialEntry)
99
183
  await removeEntries(parent);
100
184
  }
101
185
  }
@@ -159,7 +243,7 @@ export async function removePathInRootNative(root, target, options, receipts) {
159
243
  function assertTraversalCurrent() {
160
244
  for (const guard of guards) {
161
245
  try {
162
- assertSyncDirectoryGuard(guard);
246
+ assertRemovalDirectoryCurrent(guard);
163
247
  }
164
248
  catch (cause) {
165
249
  throw new FsSafeError("path-mismatch", "removal ancestor changed", { cause });
@@ -196,8 +280,6 @@ export async function removePathInRootNative(root, target, options, receipts) {
196
280
  }
197
281
  if (!counted)
198
282
  examined++;
199
- if (!options.recursive)
200
- await beforeMutation(entryPath);
201
283
  await assertMutationNotDenied(entryPath, options.denyMutations, { protectAncestors: true });
202
284
  const initial = observe(fd, name, entryPath, depth === 0 ? initialEntry : undefined);
203
285
  if (!initial)
@@ -234,7 +316,7 @@ export async function removePathInRootNative(root, target, options, receipts) {
234
316
  throw normalize(error, details(entryPath, "enumerate"));
235
317
  }
236
318
  };
237
- guards.push({ dir: entryPath, realPath: entryPath, stat });
319
+ guards.push(createRemovalDirectoryAssertion(entryPath, stat, entryPath));
238
320
  try {
239
321
  assertCurrent();
240
322
  if (options.order === "sorted") {
@@ -299,10 +381,7 @@ export async function removePathInRootNative(root, target, options, receipts) {
299
381
  if (!(options.force && isNotFoundPathError(error)))
300
382
  throw normalize(error, details(entryPath, "remove"));
301
383
  }
302
- if (options.recursive)
303
- boundary.assertCurrent();
304
- else
305
- boundary.assertAfterMutation();
384
+ boundary.assertCurrent();
306
385
  assertTraversalCurrent();
307
386
  assertNotAborted();
308
387
  }