@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
@@ -1,7 +1,9 @@
1
+ import { type ExactFileIdentity } from "./exact-identity.js";
1
2
  import fs from "node:fs";
2
3
  import type { PathAliasPolicy } from "./path-policy.js";
3
4
  import { type SymlinkPolicy } from "./root-symlink-policy.js";
4
5
  import { type PinnedOpenSyncAllowedType, type PinnedOpenSyncFailureReason } from "./pinned-open.js";
6
+ import { type CanonicalRootObservation } from "./root-file-final-admission.js";
5
7
  type BoundaryReadFs = Pick<typeof fs, "closeSync" | "constants" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "realpathSync">;
6
8
  export type RootFileOpenFailureReason = PinnedOpenSyncFailureReason | "validation";
7
9
  export type RootFileOpenResult = {
@@ -9,6 +11,7 @@ export type RootFileOpenResult = {
9
11
  path: string;
10
12
  fd: number;
11
13
  stat: fs.Stats;
14
+ exactIdentity: ExactFileIdentity;
12
15
  rootRealPath: string;
13
16
  } | {
14
17
  ok: false;
@@ -42,5 +45,9 @@ export declare function matchRootFileOpenFailure<T>(failure: RootFileOpenFailure
42
45
  io?: (failure: RootFileOpenFailure) => T;
43
46
  fallback: (failure: RootFileOpenFailure) => T;
44
47
  }): T;
48
+ /** Internal retained-root adapter; the native opener owns component admission. */
49
+ export declare function openRootFileWithRetainedRootSync(params: OpenRootFileSyncParams, root: Extract<CanonicalRootObservation, {
50
+ ok: true;
51
+ }>, admittedFd: number): RootFileOpenResult;
45
52
  export declare function openRootFile(params: OpenRootFileParams): Promise<RootFileOpenResult>;
46
53
  export {};
@@ -1,3 +1,4 @@
1
+ import { exactFileIdentity } from "./exact-identity.js";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { resolveRootPathSyncWithCanonicalRootObservation, resolveRootPathWithCanonicalRootObservation, } from "./root-path.js";
@@ -82,6 +83,8 @@ function finalizeRootFileOpen(params) {
82
83
  maxBytes: params.maxBytes,
83
84
  allowedType: params.allowedType,
84
85
  ioFs: params.ioFs,
86
+ admittedFd: params.admittedFd,
87
+ rejectLeafSymlinks: params.rejectLeafSymlinks,
85
88
  finalAdmission: createRootFileFinalAdmission(params.ioFs, resolved.rootObservation, resolved.boundaryLabel, rejectHardlinks),
86
89
  });
87
90
  if (!opened.ok) {
@@ -92,9 +95,21 @@ function finalizeRootFileOpen(params) {
92
95
  path: opened.path,
93
96
  fd: opened.fd,
94
97
  stat: opened.stat,
98
+ exactIdentity: exactFileIdentity(opened.identity),
95
99
  rootRealPath: resolved.rootRealPath,
96
100
  };
97
101
  }
102
+ /** Internal retained-root adapter; the native opener owns component admission. */
103
+ export function openRootFileWithRetainedRootSync(params, root, admittedFd) {
104
+ return finalizeRootFileOpen({
105
+ resolved: {
106
+ absolutePath: params.absolutePath, resolvedPath: params.absolutePath,
107
+ rootRealPath: root.path, rootObservation: root, boundaryLabel: params.boundaryLabel,
108
+ },
109
+ maxBytes: params.maxBytes, rejectHardlinks: params.rejectHardlinks,
110
+ allowedType: params.allowedType, ioFs: fs, admittedFd, rejectLeafSymlinks: true,
111
+ });
112
+ }
98
113
  export async function openRootFile(params) {
99
114
  const ioFs = params.ioFs ?? fs;
100
115
  const rawAbsolutePath = params.absolutePath;
@@ -1,3 +1,4 @@
1
+ import { type ExactFileIdentity } from "./exact-identity.js";
1
2
  import type { BigIntStats, Stats } from "node:fs";
2
3
  import type { FileHandle } from "node:fs/promises";
3
4
  import type { ContainmentGuarantee } from "./containment.js";
@@ -14,6 +15,7 @@ export type OpenResult = {
14
15
  containment: ContainmentGuarantee;
15
16
  realPath: string;
16
17
  stat: Stats;
18
+ exactIdentity: ExactFileIdentity;
17
19
  [Symbol.asyncDispose](): Promise<void>;
18
20
  };
19
21
  export interface Root {
@@ -50,12 +50,13 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
+ import { exactFileIdentity, numberStats } from "./exact-identity.js";
53
54
  import { registerRootHandleContext } from "./root-handle-context.js";
54
55
  import fsSync, { constants as fsConstants } from "node:fs";
55
56
  import fs from "node:fs/promises";
56
57
  import path from "node:path";
57
58
  import { normalizeMaxBytes } from "./byte-budget.js";
58
- import { assertCopySourceCurrent, resolveFileCopyCloneMode } from "./copy-file-input.js";
59
+ import { copyFileInRoot } from "./root-copy.js";
59
60
  import { assertPrivateFileCreationAvailable, resolveCreationPermissions } from "./creation-boundary.js";
60
61
  import { assertAsyncDirectoryGuard, assertSyncDirectoryGuard, createAsyncDirectoryGuard, createNearestExistingDirectoryGuard } from "./directory-guard.js";
61
62
  import { FsSafeError } from "./errors.js";
@@ -68,6 +69,7 @@ import { openedPathResolutionError, recordExclusiveCreateFailure, recordOpenedFi
68
69
  import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy } from "./pinned-write.js";
69
70
  import { preparePinnedWriteMutationAdmission, snapshotPinnedMutationPolicy } from "./pinned-mutation-admission.js";
70
71
  import { getNativeBinding } from "./native.js";
72
+ import { getPinnedWriteNativeBinding } from "./native-pinned-write.js";
71
73
  import { isFsSafeNativeRequired } from "./native-config.js";
72
74
  import { validatePinnedRelativePath } from "./pinned-operation.js";
73
75
  import { PATH_ALIAS_POLICIES } from "./path-policy.js";
@@ -78,7 +80,8 @@ import { sameFileIdentity } from "./file-identity.js";
78
80
  import { removePathIfIdentityUnchanged } from "./replace-file-temp-owner.js";
79
81
  import { realpathSync } from "./realpath.js";
80
82
  import { mkdirPathFallback, prepareRootWriteTarget, tryMkdirAtExactParent } from "./root-directory-creation.js";
81
- import { buildAtomicWriteTempPath, rootWriteQueueKey, tryMkdirRootNative, tryOpenCreateRootNative } from "./root-create-native.js";
83
+ import { tryMkdirNativeParents } from "./root-mkdir-native.js";
84
+ import { buildAtomicWriteTempPath, rootWriteQueueKey, ensureNativeRootCurrent, isNativeCreateCollision, probeNativeCreationPath, tryMkdirNativeDirectChild, tryMkdirRootNative, tryOpenCreateRootNative } from "./root-create-native.js";
82
85
  import { isNonRegularWriteOpenError, resolveNonblockingWriteFlag } from "./write-open-flags.js";
83
86
  import { resolveRootPath, resolveRootPathSync, resolveRootPathForRemoval } from "./root-path.js";
84
87
  import { RemovalPathReceipts } from "./root-remove-receipt.js";
@@ -101,10 +104,10 @@ import { prepareSharedRootWriteTarget } from "./root-write-complete-parent.js";
101
104
  import { finishRootFallbackWrite } from "./root-write-publication.js";
102
105
  import { withRootFallbackCompatibilityLock } from "./root-write-compatibility.js";
103
106
  import { assertRootFallbackWritePath } from "./root-write-lock-binding.js";
104
- import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
107
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
105
108
  import { admitMoveSourceStat, movePathNative } from "./root-move-noreplace.js";
106
109
  import { admitRootReadHandle, inspectOpenedPathIdentitySync } from "./root-read-admission.js";
107
- import { createCopyPublicationObserver, onCopyPublication, onCopySourceAdmission } from "./copy-publication.js";
110
+ import { onCopyPublication, onCopySourceAdmission } from "./copy-publication.js";
108
111
  import { writeAllToFile } from "./write-file-handle.js";
109
112
  import { createInputOptions, rethrowCreateInputError, rootWriteInput, retainSidecarCreated } from "./root-create-input.js";
110
113
  import { assertFinalSymlinkRejected, mutationSymlinkResolution, readSymlinkResolution } from "./root-symlink-policy.js";
@@ -113,7 +116,7 @@ import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot } from "./wi
113
116
  import { resolvePinnedObservedPathInRoot } from "./root-observed-path.js";
114
117
  import { registerFileLockSyncRootAdapter } from "./file-lock-sync-root.js";
115
118
  import { mergeReadOptions, readDefaults, } from "./root-options.js";
116
- import { composeMutationAssertions, MutationAuthorityError, rethrowMutationAuthorityError } from "./mutation-authority.js";
119
+ import { composeMutationAssertions, rethrowMutationAuthorityError } from "./mutation-authority.js";
117
120
  export { DEFAULT_ROOT_MAX_BYTES } from "./root-options.js";
118
121
  export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
119
122
  function logWarn(message) {
@@ -141,6 +144,7 @@ function openResult(params) {
141
144
  containment: "best-effort",
142
145
  realPath: params.realPath,
143
146
  stat: params.stat,
147
+ exactIdentity: params.exactIdentity,
144
148
  [Symbol.asyncDispose]: () => params.handle.close().catch(() => undefined),
145
149
  };
146
150
  }
@@ -150,7 +154,7 @@ async function openVerifiedLocalFile(filePath, options) {
150
154
  try {
151
155
  const inspectPathIdentity = async (inspect) => {
152
156
  try {
153
- return await inspectFileIdentity(inspect, identity);
157
+ return inspectFileIdentitySync(inspect, identity);
154
158
  }
155
159
  catch (error) {
156
160
  const failure = isNotFoundPathError(error) ? openedPathResolutionError(fileNotFoundError()) : error;
@@ -160,8 +164,10 @@ async function openVerifiedLocalFile(filePath, options) {
160
164
  throw failure;
161
165
  }
162
166
  };
163
- await inspectPathIdentity(async () => inspectOpenedPathIdentitySync(filePath, options?.symlinks));
164
- await fsSafeTestHooks?.afterOpenedPathIdentityCheck?.(filePath, handle);
167
+ await inspectPathIdentity(() => inspectOpenedPathIdentitySync(filePath, options?.symlinks));
168
+ if (fsSafeTestHooks?.afterOpenedPathIdentityCheck) {
169
+ await fsSafeTestHooks.afterOpenedPathIdentityCheck(filePath, handle);
170
+ }
165
171
  const resolved = await resolveOpenedFileRealPathForFd(handle.fd, identity, filePath)
166
172
  .catch(async (error) => {
167
173
  if (stat.nlink <= 1 && (!preOpenStat || preOpenStat.nlink <= 1n)) {
@@ -172,7 +178,7 @@ async function openVerifiedLocalFile(filePath, options) {
172
178
  const { realPath } = resolved;
173
179
  assertNoWindowsPathAlias(realPath, "filesystem", "resolved file path uses a Windows filesystem namespace alias");
174
180
  let resolvedStat = resolved.stat;
175
- await inspectPathIdentity(async () => {
181
+ await inspectPathIdentity(() => {
176
182
  // Reuse the post-realpath observation; unknown Windows identities still
177
183
  // get a fresh observation on inspectFileIdentity's retry.
178
184
  const realStat = resolvedStat ?? fsSync.statSync(realPath, { bigint: true });
@@ -182,7 +188,7 @@ async function openVerifiedLocalFile(filePath, options) {
182
188
  }
183
189
  return realStat;
184
190
  });
185
- return { opened: openResult({ handle, realPath, stat }), identity };
191
+ return { opened: openResult({ handle, realPath, stat, exactIdentity: exactFileIdentity(identity) }), identity };
186
192
  }
187
193
  catch (err) {
188
194
  await handle.close().catch(() => { });
@@ -355,7 +361,7 @@ export class RootHandle {
355
361
  durable: options.durable ?? this.defaults.durable ?? true,
356
362
  verifyPublished: options[onCopyPublication],
357
363
  admitSource: options[onCopySourceAdmission],
358
- }).catch(rethrowMutationAuthorityError);
364
+ }, openVerifiedLocalFile).catch(rethrowMutationAuthorityError);
359
365
  }
360
366
  async exists(relativePath) {
361
367
  try {
@@ -464,7 +470,7 @@ async function openFileInRoot(root, params) {
464
470
  // The admission helper owns the descriptor until the complete root/file/root
465
471
  // fence succeeds, then transfers that still-open handle to the caller.
466
472
  const admitted = await admitRootReadHandle({
467
- root, filePath: resolved, opened: { handle, stat }, identity,
473
+ root, filePath: resolved, opened: { handle, stat, exactIdentity: exactFileIdentity(identity) }, identity,
468
474
  hardlinks: params.hardlinks, symlinks: params.symlinks,
469
475
  beforeFinalFence: fsSafeTestHooks?.beforeRootReadFinalFence,
470
476
  afterPathIdentityCheck: fsSafeTestHooks?.afterRootReadFinalPathIdentityCheck,
@@ -511,46 +517,61 @@ async function openWritableFileInRoot(root, params, options) {
511
517
  denyMutations: params.denyMutations,
512
518
  mutationSymlinks: params.mutationSymlinks,
513
519
  });
514
- const { resolved } = guardedTarget?.resolvedPath ?? await resolveGuardedWritePathInRoot(root, {
520
+ const creationProbe = requireNative && !guardedTarget && params.assertBeforeMutation === undefined && options?.createIfMissing !== false
521
+ ? probeNativeCreationPath(root, params.relativePath) : undefined;
522
+ const { resolved } = creationProbe ?? guardedTarget?.resolvedPath ?? await resolveGuardedWritePathInRoot(root, {
515
523
  relativePath: params.relativePath,
516
524
  });
517
525
  const resolveCurrent = async () => (await resolveGuardedWritePathInRoot(root, {
518
526
  relativePath: params.relativePath, ...policy,
519
527
  })).resolved;
520
- const nativeParent = requireNative && params.mkdir !== false && await tryMkdirRootNative({
521
- originalPath: params.relativePath,
522
- root, directory: path.dirname(resolved), target: resolved, mkdir: true, policy,
523
- resolveCurrent, assertBeforeMutation: params.assertBeforeMutation,
524
- });
525
- const prepared = !nativeParent && guardedTarget ? await prepareSharedRootWriteTarget(root, {
528
+ // Existing-file opens need no mkdir. Keep capability admission, and create
529
+ // missing parents only when the native exclusive-create route is needed.
530
+ const nativeCreatesParents = requireNative && params.mkdir !== false;
531
+ if (nativeCreatesParents) {
532
+ const binding = getNativeBinding("openBeneath", "mkdirChildBeneath", "closeOwnedFd");
533
+ if (!binding)
534
+ throw new FsSafeError("helper-unavailable", "native confined creation is unavailable");
535
+ // Policy selection retains a parent receipt before the existing-file open.
536
+ if (guardedTarget)
537
+ await tryMkdirRootNative({
538
+ originalPath: params.relativePath,
539
+ root, directory: path.dirname(resolved), target: resolved, mkdir: true, policy,
540
+ resolveCurrent, assertBeforeMutation: params.assertBeforeMutation,
541
+ });
542
+ }
543
+ const prepared = !nativeCreatesParents && guardedTarget ? await prepareSharedRootWriteTarget(root, {
526
544
  relativePath: params.relativePath, guardedTarget, mkdir: params.mkdir,
527
545
  assertBeforeMutation: params.assertBeforeMutation,
528
546
  }) : undefined;
529
547
  const preparedParent = prepared?.preparedParent;
530
- let ioPath = nativeParent ? resolved : prepared?.targetPath ?? (params.mkdir === false ? resolved :
548
+ let ioPath = nativeCreatesParents ? resolved : prepared?.targetPath ?? (params.mkdir === false ? resolved :
531
549
  await prepareRootWriteTarget(root, resolved, params.assertBeforeMutation));
532
550
  const operationTargetPath = ioPath;
533
- try {
534
- assertFinalSymlinkRejected(ioPath, params.mutationSymlinks !== undefined);
535
- const resolvedRealPath = params.mutationSymlinks === undefined ? realpathSync.native(ioPath) : ioPath;
536
- const admittedRealPath = admitPathInsideRoot({
537
- rootPath: root.rootReal,
538
- candidatePath: resolvedRealPath,
539
- rootIdentity: root.rootIdentity,
540
- });
541
- if (!admittedRealPath) {
542
- throw outsideWorkspaceError();
543
- }
544
- ioPath = admittedRealPath.path;
545
- }
546
- catch (err) {
547
- if (err instanceof FsSafeError) {
548
- throw err;
551
+ let observedMissing = Boolean(creationProbe);
552
+ if (!creationProbe)
553
+ try {
554
+ assertFinalSymlinkRejected(ioPath, params.mutationSymlinks !== undefined);
555
+ const resolvedRealPath = params.mutationSymlinks === undefined ? realpathSync.native(ioPath) : ioPath;
556
+ const admittedRealPath = admitPathInsideRoot({
557
+ rootPath: root.rootReal,
558
+ candidatePath: resolvedRealPath,
559
+ rootIdentity: root.rootIdentity,
560
+ });
561
+ if (!admittedRealPath) {
562
+ throw outsideWorkspaceError();
563
+ }
564
+ ioPath = admittedRealPath.path;
549
565
  }
550
- if (!isNotFoundPathError(err)) {
551
- throw err;
566
+ catch (err) {
567
+ if (err instanceof FsSafeError) {
568
+ throw err;
569
+ }
570
+ if (!isNotFoundPathError(err)) {
571
+ throw err;
572
+ }
573
+ observedMissing = true;
552
574
  }
553
- }
554
575
  assertRootFallbackWritePath(params.expectedWritePath, ioPath);
555
576
  const mode = params.mode ?? 0o600;
556
577
  let handle;
@@ -564,55 +585,82 @@ async function openWritableFileInRoot(root, params, options) {
564
585
  writePathSelection = guardedTarget
565
586
  ? await prepareGuardedRootWritePathSelection(guardedTarget, ioPath, operationTargetPath, preparedParent)
566
587
  : undefined;
567
- try {
568
- if (writePathSelection)
569
- assertRootWritePathSelectionSync(root, writePathSelection);
570
- handle = await fs.open(ioPath, existingFlags, mode);
571
- }
572
- catch (err) {
573
- if (await isNonRegularWriteOpenError(err, ioPath, existingFlags)) {
574
- throw new FsSafeError("not-file", "path is not a regular file under root");
588
+ const createNative = () => tryOpenCreateRootNative({
589
+ originalPath: params.relativePath,
590
+ root, directory: path.dirname(ioPath), target: ioPath, mkdir: params.mkdir !== false, policy,
591
+ selectedParent: creationProbe?.parent,
592
+ resolveCurrent,
593
+ assertBeforeMutation: params.assertBeforeMutation !== undefined || writePathSelection !== undefined ? () => {
594
+ params.assertBeforeMutation?.();
595
+ if (writePathSelection)
596
+ assertRootWritePathSelectionSync(root, writePathSelection);
597
+ } : undefined,
598
+ flags: createFlags, existingFlags, mode,
599
+ });
600
+ let precreated;
601
+ if (requireNative && observedMissing && !guardedTarget && params.assertBeforeMutation === undefined && options?.createIfMissing !== false) {
602
+ try {
603
+ precreated = await createNative();
575
604
  }
576
- if (!isNotFoundPathError(err)) {
577
- throw err;
605
+ catch (error) {
606
+ if (!hasNodeErrorCode(error, "EEXIST") || !isNativeCreateCollision(error))
607
+ throw error;
578
608
  }
579
- if (options?.createIfMissing === false) {
580
- const parentGuard = writePathSelection?.parentGuard ??
581
- await createAsyncDirectoryGuard(path.dirname(ioPath), { bigint: true });
582
- assertRootIdentityCurrentSync(root);
583
- if (!admitPathInsideRoot({
584
- rootPath: root.rootReal, candidatePath: parentGuard.realPath, rootIdentity: root.rootIdentity,
585
- }))
586
- throw outsideWorkspaceError();
587
- assertSyncDirectoryGuard(parentGuard);
588
- return { missing: true, targetPath: ioPath, parentGuard, writeSelection: writePathSelection };
609
+ }
610
+ if (precreated) {
611
+ handle = precreated.handle;
612
+ cleanupCreated = precreated.cleanupCreated;
613
+ releaseCreationParent = precreated.releaseCreationParent;
614
+ createdForWrite = true;
615
+ }
616
+ else {
617
+ try {
618
+ if (writePathSelection)
619
+ assertRootWritePathSelectionSync(root, writePathSelection);
620
+ handle = await fs.open(ioPath, existingFlags, mode);
589
621
  }
590
- if (writePathSelection)
591
- await refreshRootWritePathSelection(writePathSelection);
592
- const nativeCreated = requireNative ? await tryOpenCreateRootNative({
593
- originalPath: params.relativePath,
594
- root, directory: path.dirname(ioPath), target: ioPath, mkdir: false, policy,
595
- resolveCurrent, assertBeforeMutation() {
622
+ catch (err) {
623
+ if (await isNonRegularWriteOpenError(err, ioPath, existingFlags)) {
624
+ throw new FsSafeError("not-file", "path is not a regular file under root");
625
+ }
626
+ if (!isNotFoundPathError(err)) {
627
+ throw err;
628
+ }
629
+ if (options?.createIfMissing === false) {
630
+ if (nativeCreatesParents && !guardedTarget)
631
+ await tryMkdirRootNative({
632
+ originalPath: params.relativePath,
633
+ root, directory: path.dirname(ioPath), target: ioPath, mkdir: true, policy,
634
+ resolveCurrent, assertBeforeMutation: params.assertBeforeMutation,
635
+ });
636
+ const parentGuard = writePathSelection?.parentGuard ??
637
+ await createAsyncDirectoryGuard(path.dirname(ioPath), { bigint: true });
638
+ assertRootIdentityCurrentSync(root);
639
+ if (!admitPathInsideRoot({
640
+ rootPath: root.rootReal, candidatePath: parentGuard.realPath, rootIdentity: root.rootIdentity,
641
+ }))
642
+ throw outsideWorkspaceError();
643
+ assertSyncDirectoryGuard(parentGuard);
644
+ return { missing: true, targetPath: ioPath, parentGuard, writeSelection: writePathSelection };
645
+ }
646
+ if (writePathSelection)
647
+ await refreshRootWritePathSelection(writePathSelection);
648
+ const nativeCreated = requireNative ? await createNative() : undefined;
649
+ if (nativeCreated)
650
+ handle = nativeCreated.handle;
651
+ else {
596
652
  params.assertBeforeMutation?.();
597
653
  if (writePathSelection)
598
654
  assertRootWritePathSelectionSync(root, writePathSelection);
599
- },
600
- flags: createFlags, existingFlags, mode,
601
- }) : undefined;
602
- if (nativeCreated)
603
- handle = nativeCreated.handle;
604
- else {
605
- params.assertBeforeMutation?.();
606
- if (writePathSelection)
607
- assertRootWritePathSelectionSync(root, writePathSelection);
608
- // Windows can follow a dangling symlink even with O_EXCL.
609
- if (!SUPPORTS_NOFOLLOW)
610
- assertFinalSymlinkRejected(ioPath, true);
611
- handle = await fs.open(ioPath, createFlags, mode);
655
+ // Windows can follow a dangling symlink even with O_EXCL.
656
+ if (!SUPPORTS_NOFOLLOW)
657
+ assertFinalSymlinkRejected(ioPath, true);
658
+ handle = await fs.open(ioPath, createFlags, mode);
659
+ }
660
+ cleanupCreated = nativeCreated?.cleanupCreated;
661
+ releaseCreationParent = nativeCreated?.releaseCreationParent;
662
+ createdForWrite = true;
612
663
  }
613
- cleanupCreated = nativeCreated?.cleanupCreated;
614
- releaseCreationParent = nativeCreated?.releaseCreationParent;
615
- createdForWrite = true;
616
664
  }
617
665
  }
618
666
  catch (err) {
@@ -630,7 +678,6 @@ async function openWritableFileInRoot(root, params, options) {
630
678
  let realPathForCleanup = null;
631
679
  let createdIdentity = null;
632
680
  try {
633
- const stat = fsSync.fstatSync(handle.fd);
634
681
  const identity = inspectFileIdentitySync(() => {
635
682
  const observed = fsSync.fstatSync(handle.fd, { bigint: true });
636
683
  if (!observed.isFile()) {
@@ -640,6 +687,7 @@ async function openWritableFileInRoot(root, params, options) {
640
687
  throw hardlinkedPathNotAllowedError();
641
688
  return observed;
642
689
  });
690
+ const stat = numberStats(identity);
643
691
  if (createdForWrite)
644
692
  createdIdentity = identity;
645
693
  let observedIoPath = false;
@@ -721,6 +769,7 @@ async function openWritableFileInRoot(root, params, options) {
721
769
  createdForWrite,
722
770
  realPath,
723
771
  stat,
772
+ exactIdentity: exactFileIdentity(identity),
724
773
  [Symbol.asyncDispose]: () => handle.close().catch(() => undefined),
725
774
  };
726
775
  if (!params.keepCreationParent)
@@ -851,6 +900,16 @@ async function mkdirPathInRoot(root, params) {
851
900
  const policy = params.denyMutations === undefined && params.mutationSymlinks === undefined
852
901
  ? undefined
853
902
  : snapshotPinnedMutationPolicy(params.denyMutations, params.mutationSymlinks);
903
+ if (requireNative && !privateMode && !policy && !params.assertBeforeMutation) {
904
+ if (params.allowRoot && params.relativePath === "") {
905
+ ensureNativeRootCurrent(root);
906
+ return;
907
+ }
908
+ if (tryMkdirNativeDirectChild(root, params.relativePath))
909
+ return;
910
+ if (tryMkdirNativeParents(root, params.relativePath))
911
+ return;
912
+ }
854
913
  const resolution = { relativePath: params.relativePath, allowRoot: params.allowRoot, ...policy };
855
914
  const resolveCurrent = policy
856
915
  ? async () => await resolvePinnedPathInRoot(root, resolution)
@@ -897,7 +956,7 @@ async function writeFileInRoot(root, params) {
897
956
  const input = rootWriteInput(params);
898
957
  await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
899
958
  if (!params.private && input.kind === "buffer" && !input.stageBeforePublish && process.platform === "win32" &&
900
- (params.renameIdentity === "verify-content-with-lock" || !getNativeBinding())) {
959
+ (params.renameIdentity === "verify-content-with-lock" || !getPinnedWriteNativeBinding({ input }))) {
901
960
  await writeFileFallback(root, { ...params, data: input.data });
902
961
  return;
903
962
  }
@@ -965,93 +1024,6 @@ async function commitPinnedWriteInRoot(root, pinned, params, input) {
965
1024
  throw normalizePinnedWriteError(error);
966
1025
  }
967
1026
  }
968
- async function copyFileInRoot(root, params) {
969
- params.signal?.throwIfAborted();
970
- const clone = resolveFileCopyCloneMode(params.clone);
971
- let source;
972
- let sourceIdentity;
973
- if (typeof params.source === "string") {
974
- assertNoNulPathInput(params.source, "source path contains a NUL byte");
975
- assertNoWindowsPathAlias(params.source, "filesystem", "source path uses a Windows filesystem namespace alias");
976
- ({ opened: source, identity: sourceIdentity } = await openVerifiedLocalFile(params.source, {
977
- hardlinks: params.sourceHardlinks,
978
- }));
979
- }
980
- else {
981
- source = await params.source.root.open(params.source.relativePath, params.sourceHardlinks === undefined ? undefined : { hardlinks: params.sourceHardlinks });
982
- try {
983
- sourceIdentity = await inspectFileIdentity(() => fsSync.fstatSync(source.handle.fd, { bigint: true }));
984
- }
985
- catch (error) {
986
- await source.handle.close().catch(() => undefined);
987
- throw error;
988
- }
989
- }
990
- if (params.maxBytes !== undefined && source.stat.size > params.maxBytes) {
991
- await source.handle.close().catch(() => { });
992
- throw new FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
993
- }
994
- try {
995
- const sourceAdmission = params.admitSource?.(sourceIdentity, source.realPath);
996
- const mode = sourceAdmission?.mode ?? params.mode;
997
- await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
998
- const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, mode ?? (params.preserveSourceMode ? Number(sourceIdentity.mode & 4095n) : undefined), params.denyMutations, params.overwrite !== false, params.mutationSymlinks);
999
- await serializePathWrite(pinned.targetPath, async () => {
1000
- await assertCopySourceCurrent(source, sourceIdentity);
1001
- const verifySource = async () => {
1002
- params.signal?.throwIfAborted();
1003
- try {
1004
- sourceAdmission?.verify();
1005
- }
1006
- catch (error) {
1007
- throw new MutationAuthorityError(error);
1008
- }
1009
- if (typeof params.source !== "string") {
1010
- await params.source.root.stat(".");
1011
- }
1012
- await assertCopySourceCurrent(source, sourceIdentity);
1013
- };
1014
- const observer = createCopyPublicationObserver(pinned.targetPath, params.onDestinationPublished);
1015
- try {
1016
- await runPinnedWriteHelper({
1017
- rootPath: pinned.rootReal,
1018
- relativeParentPath: pinned.relativeParentPath,
1019
- basename: pinned.basename,
1020
- mkdir: params.mkdir !== false,
1021
- mode: pinned.mode,
1022
- overwrite: params.overwrite !== false,
1023
- rejectFinalSymlink: params.mutationSymlinks !== undefined,
1024
- maxBytes: params.maxBytes,
1025
- sync: params.durable !== false,
1026
- assertBeforeMutation: params.signal || params.assertBeforeMutation ? () => {
1027
- if (params.signal?.aborted)
1028
- throw new MutationAuthorityError(params.signal.reason);
1029
- params.assertBeforeMutation?.();
1030
- } : undefined,
1031
- verifyPublished: params.verifyPublished,
1032
- onPublished: observer.onPublished,
1033
- input: { kind: "file", handle: source.handle, size: source.stat.size, clone, signal: params.signal, verifySource },
1034
- rootIdentity: root.rootIdentity,
1035
- mutationAdmission: pinned.mutationAdmission,
1036
- });
1037
- }
1038
- catch (error) {
1039
- observer.rethrowObserverFailure(error);
1040
- if (params.signal?.aborted && error === params.signal.reason)
1041
- throw error;
1042
- if (isAlreadyExistsError(error)) {
1043
- throw new FsSafeError("already-exists", "copy destination already exists", { cause: error });
1044
- }
1045
- throw normalizePinnedWriteError(error);
1046
- }
1047
- await verifySource();
1048
- });
1049
- });
1050
- }
1051
- finally {
1052
- await source.handle.close().catch(() => { });
1053
- }
1054
- }
1055
1027
  async function resolvePinnedPathInRoot(root, params) {
1056
1028
  const resolved = await resolvePinnedRootPathInRoot(root, {
1057
1029
  relativePath: params.relativePath,
@@ -1203,10 +1175,15 @@ async function movePathFallback(root, params) {
1203
1175
  }
1204
1176
  const guardOptions = { bigint: sourceIdentity !== undefined };
1205
1177
  const sourceParentGuard = await createAsyncDirectoryGuard(path.dirname(source.resolved), guardOptions);
1206
- const targetParentGuard = await createNearestExistingDirectoryGuard(target.rootReal, path.dirname(target.resolved), guardOptions);
1207
- await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("move", target.resolved);
1178
+ const targetParentGuard = path.dirname(target.resolved) === sourceParentGuard.dir
1179
+ ? sourceParentGuard
1180
+ : await createNearestExistingDirectoryGuard(target.rootReal, path.dirname(target.resolved), guardOptions);
1181
+ const beforeMutation = getFsSafeTestHooks()?.beforeRootFallbackMutation;
1182
+ if (beforeMutation)
1183
+ await beforeMutation("move", target.resolved);
1208
1184
  await assertAsyncDirectoryGuard(sourceParentGuard);
1209
- await assertAsyncDirectoryGuard(targetParentGuard);
1185
+ if (targetParentGuard !== sourceParentGuard)
1186
+ await assertAsyncDirectoryGuard(targetParentGuard);
1210
1187
  try {
1211
1188
  assertFinalSymlinkRejected(source.resolved, params.mutationSymlinks !== undefined);
1212
1189
  assertFinalSymlinkRejected(target.resolved, params.mutationSymlinks !== undefined);
@@ -1214,7 +1191,8 @@ async function movePathFallback(root, params) {
1214
1191
  if (sourceIdentity) {
1215
1192
  assertRootIdentityCurrentSync(root);
1216
1193
  assertSyncDirectoryGuard(sourceParentGuard);
1217
- assertSyncDirectoryGuard(targetParentGuard);
1194
+ if (targetParentGuard !== sourceParentGuard)
1195
+ assertSyncDirectoryGuard(targetParentGuard);
1218
1196
  for (const [route, selected, expected, policy] of [
1219
1197
  [originalRoutes[0], source.resolved, pinnedSource.canonicalPath, PATH_ALIAS_POLICIES.strict],
1220
1198
  [originalRoutes[1], target.resolved, pinnedTarget.canonicalPath, PATH_ALIAS_POLICIES.unlinkTarget],
@@ -1249,7 +1227,8 @@ async function movePathFallback(root, params) {
1249
1227
  }
1250
1228
  try {
1251
1229
  await assertAsyncDirectoryGuard(sourceParentGuard);
1252
- await assertAsyncDirectoryGuard(targetParentGuard);
1230
+ if (targetParentGuard !== sourceParentGuard)
1231
+ await assertAsyncDirectoryGuard(targetParentGuard);
1253
1232
  }
1254
1233
  catch (error) {
1255
1234
  throw normalizePinnedPathError(error);
@@ -0,0 +1,2 @@
1
+ import { type RootContext } from "./root-context.js";
2
+ export declare function tryMkdirNativeParents(root: RootContext, relativePath: string): boolean;