@openclaw/fs-safe 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (705) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +8 -1
  3. package/dist/absolute-path.d.ts +0 -1
  4. package/dist/absolute-path.js +2 -8
  5. package/dist/advanced.d.ts +2 -1
  6. package/dist/advanced.js +2 -0
  7. package/dist/archive-crc32.d.ts +0 -1
  8. package/dist/archive-deadline.d.ts +0 -1
  9. package/dist/archive-deadline.js +22 -23
  10. package/dist/archive-durability.d.ts +0 -1
  11. package/dist/archive-entry.d.ts +0 -1
  12. package/dist/archive-errors.d.ts +0 -1
  13. package/dist/archive-gzip-tail.d.ts +0 -1
  14. package/dist/archive-input.d.ts +0 -1
  15. package/dist/archive-kind.d.ts +0 -1
  16. package/dist/archive-limits.d.ts +0 -1
  17. package/dist/archive-merge.d.ts +3 -4
  18. package/dist/archive-merge.js +17 -7
  19. package/dist/archive-native.d.ts +3 -14
  20. package/dist/archive-native.js +46 -70
  21. package/dist/archive-options.d.ts +9 -2
  22. package/dist/archive-parser-errors.d.ts +0 -1
  23. package/dist/archive-parser.wasm +0 -0
  24. package/dist/archive-plan.d.ts +10 -1
  25. package/dist/archive-plan.js +31 -21
  26. package/dist/archive-policy.d.ts +0 -1
  27. package/dist/archive-read.d.ts +0 -1
  28. package/dist/archive-staging.d.ts +0 -1
  29. package/dist/archive-tar-inspect.d.ts +0 -1
  30. package/dist/archive-tar-stream.d.ts +0 -1
  31. package/dist/archive-tar-wasm.d.ts +0 -1
  32. package/dist/archive-tar.d.ts +0 -1
  33. package/dist/archive-zip-admission.d.ts +0 -1
  34. package/dist/archive-zip-count.d.ts +0 -1
  35. package/dist/archive-zip-count.js +21 -1
  36. package/dist/archive-zip-directory.d.ts +0 -1
  37. package/dist/archive-zip-directory.js +23 -1
  38. package/dist/archive-zip-entry.d.ts +0 -1
  39. package/dist/archive-zip-integrity.d.ts +0 -1
  40. package/dist/archive-zip-loader.d.ts +2 -1
  41. package/dist/archive-zip-loader.js +7 -0
  42. package/dist/archive-zip-manifest.d.ts +0 -1
  43. package/dist/archive-zip-names.d.ts +0 -1
  44. package/dist/archive-zip-names.js +7 -2
  45. package/dist/archive-zip-preflight.d.ts +0 -1
  46. package/dist/archive.d.ts +0 -1
  47. package/dist/archive.js +93 -164
  48. package/dist/async-lock.d.ts +0 -1
  49. package/dist/atomic.d.ts +0 -1
  50. package/dist/bounded-read-stream.d.ts +0 -1
  51. package/dist/bounded-read.d.ts +0 -1
  52. package/dist/byte-budget.d.ts +0 -1
  53. package/dist/byte-view.d.ts +2 -0
  54. package/dist/byte-view.js +13 -0
  55. package/dist/clone-metadata.d.ts +0 -1
  56. package/dist/config.d.ts +0 -1
  57. package/dist/containment.d.ts +0 -1
  58. package/dist/copy-file-input.d.ts +0 -1
  59. package/dist/copy-policy.d.ts +0 -1
  60. package/dist/copy-publication.d.ts +0 -1
  61. package/dist/copy-tree-portable.d.ts +0 -1
  62. package/dist/copy-tree-portable.js +9 -6
  63. package/dist/copy.d.ts +0 -1
  64. package/dist/copy.js +5 -5
  65. package/dist/create-directory.d.ts +0 -2
  66. package/dist/create-directory.js +3 -22
  67. package/dist/create-file-async.d.ts +0 -1
  68. package/dist/create-file.d.ts +0 -1
  69. package/dist/create-file.js +3 -14
  70. package/dist/create-owned-file.d.ts +0 -1
  71. package/dist/create.d.ts +1 -2
  72. package/dist/create.js +1 -1
  73. package/dist/creation-darwin.d.ts +0 -2
  74. package/dist/creation-darwin.js +0 -9
  75. package/dist/creation-file-state.d.ts +0 -1
  76. package/dist/creation-path.d.ts +2 -1
  77. package/dist/creation-path.js +12 -0
  78. package/dist/creation-permissions.d.ts +0 -1
  79. package/dist/creation-permissions.js +28 -38
  80. package/dist/deny-mutations.d.ts +2 -3
  81. package/dist/deny-mutations.js +55 -35
  82. package/dist/device-path.d.ts +0 -1
  83. package/dist/directory-durability.d.ts +6 -7
  84. package/dist/directory-entry-path.d.ts +0 -1
  85. package/dist/directory-guard.d.ts +2 -7
  86. package/dist/directory-guard.js +25 -56
  87. package/dist/directory-mode-node.d.ts +0 -1
  88. package/dist/directory-mode-owner.d.ts +0 -1
  89. package/dist/directory-receipt.d.ts +2 -3
  90. package/dist/directory-receipt.js +15 -19
  91. package/dist/durability.d.ts +0 -1
  92. package/dist/effective-uid.d.ts +0 -1
  93. package/dist/error-detail.d.ts +0 -1
  94. package/dist/errors.d.ts +0 -1
  95. package/dist/file-cleanup.d.ts +1 -1
  96. package/dist/file-cleanup.js +7 -4
  97. package/dist/file-contents.d.ts +5 -0
  98. package/dist/file-contents.js +40 -0
  99. package/dist/file-handle-transfer.d.ts +0 -1
  100. package/dist/file-hash.d.ts +0 -1
  101. package/dist/file-hash.js +186 -70
  102. package/dist/file-identity.d.ts +0 -1
  103. package/dist/file-lock-sync-acquisition.d.ts +22 -0
  104. package/dist/file-lock-sync-acquisition.js +98 -0
  105. package/dist/file-lock-sync-admission.d.ts +0 -2
  106. package/dist/file-lock-sync-admission.js +9 -48
  107. package/dist/file-lock-sync-root-acquire.d.ts +0 -1
  108. package/dist/file-lock-sync-root-acquire.js +35 -98
  109. package/dist/file-lock-sync-root-arbitration.d.ts +0 -1
  110. package/dist/file-lock-sync-root-held.d.ts +1 -3
  111. package/dist/file-lock-sync-root-held.js +7 -5
  112. package/dist/file-lock-sync-root-io.d.ts +0 -1
  113. package/dist/file-lock-sync-root-mutation.d.ts +4 -5
  114. package/dist/file-lock-sync-root-mutation.js +7 -9
  115. package/dist/file-lock-sync-root-options.d.ts +0 -1
  116. package/dist/file-lock-sync-root-registration.d.ts +0 -1
  117. package/dist/file-lock-sync-root.d.ts +1 -2
  118. package/dist/file-lock-sync-root.js +14 -87
  119. package/dist/file-lock-sync-stale-admission.d.ts +2 -7
  120. package/dist/file-lock-sync-stale-admission.js +24 -23
  121. package/dist/file-lock-sync.d.ts +0 -1
  122. package/dist/file-lock-sync.js +46 -128
  123. package/dist/file-lock.d.ts +0 -1
  124. package/dist/file-observation.d.ts +0 -1
  125. package/dist/file-store-boundary.d.ts +0 -2
  126. package/dist/file-store-boundary.js +6 -26
  127. package/dist/file-store-copy-source.d.ts +1 -2
  128. package/dist/file-store-copy-source.js +1 -1
  129. package/dist/file-store-limit.d.ts +1 -2
  130. package/dist/file-store-limit.js +2 -3
  131. package/dist/file-store-path.d.ts +0 -1
  132. package/dist/file-store-prune.d.ts +0 -1
  133. package/dist/file-store-sync-directory.d.ts +0 -1
  134. package/dist/file-store-sync-directory.js +194 -231
  135. package/dist/file-store-sync-write.d.ts +0 -1
  136. package/dist/file-store-sync-write.js +16 -26
  137. package/dist/file-store.d.ts +0 -1
  138. package/dist/file-store.js +6 -7
  139. package/dist/file-sync.d.ts +0 -1
  140. package/dist/filename.d.ts +0 -1
  141. package/dist/filename.js +1 -7
  142. package/dist/fs.d.ts +0 -1
  143. package/dist/guarded-mkdir.d.ts +0 -1
  144. package/dist/guarded-mkdir.js +6 -27
  145. package/dist/guarded-mutation.d.ts +0 -7
  146. package/dist/guarded-mutation.js +6 -11
  147. package/dist/guest-dispatch-python.d.ts +0 -1
  148. package/dist/guest-dispatch-python.js +41 -90
  149. package/dist/guest-native-python.d.ts +0 -1
  150. package/dist/guest.d.ts +0 -1
  151. package/dist/home-dir.d.ts +0 -1
  152. package/dist/index.d.ts +0 -1
  153. package/dist/install-path.d.ts +0 -1
  154. package/dist/install-path.js +2 -5
  155. package/dist/json-document-store.d.ts +0 -1
  156. package/dist/json-durable-queue-directory.d.ts +13 -1
  157. package/dist/json-durable-queue-directory.js +134 -0
  158. package/dist/json-durable-queue-ownership.d.ts +0 -1
  159. package/dist/json-durable-queue-ownership.js +43 -66
  160. package/dist/json-durable-queue-read.d.ts +0 -1
  161. package/dist/json-durable-queue-read.js +6 -6
  162. package/dist/json-durable-queue.d.ts +0 -1
  163. package/dist/json-durable-queue.js +17 -139
  164. package/dist/json-store.d.ts +0 -1
  165. package/dist/json-stringify.d.ts +0 -1
  166. package/dist/json.d.ts +0 -1
  167. package/dist/json.js +38 -90
  168. package/dist/local-file-access.d.ts +0 -1
  169. package/dist/local-file-access.js +3 -10
  170. package/dist/local-file-descriptor.d.ts +0 -1
  171. package/dist/local-roots.d.ts +0 -1
  172. package/dist/local-roots.js +19 -21
  173. package/dist/lock-config.d.ts +0 -1
  174. package/dist/move-path-cleanup.d.ts +5 -20
  175. package/dist/move-path-cleanup.js +57 -21
  176. package/dist/move-path-stage.d.ts +0 -1
  177. package/dist/move-path.d.ts +0 -1
  178. package/dist/move-path.js +62 -39
  179. package/dist/mutation-authority.d.ts +0 -1
  180. package/dist/native-binding.d.ts +0 -1
  181. package/dist/native-config.d.ts +0 -1
  182. package/dist/native-directory-observation.d.ts +0 -1
  183. package/dist/native-fallback-warning.d.ts +0 -1
  184. package/dist/native-operations.d.ts +1 -5
  185. package/dist/native-operations.js +2 -6
  186. package/dist/native-parent-admission.d.ts +0 -1
  187. package/dist/native-pinned-write-windows.d.ts +0 -1
  188. package/dist/native-pinned-write.d.ts +0 -1
  189. package/dist/native-pinned-write.js +4 -10
  190. package/dist/native-policy-directory-observation.d.ts +0 -1
  191. package/dist/native-policy-parent-windows.d.ts +0 -1
  192. package/dist/native-rename-outcome.d.ts +0 -1
  193. package/dist/native-staged-file.d.ts +17 -4
  194. package/dist/native-staged-file.js +63 -68
  195. package/dist/native.d.ts +0 -1
  196. package/dist/native.js +2 -2
  197. package/dist/opened-file-failure.d.ts +0 -1
  198. package/dist/opened-realpath.d.ts +0 -1
  199. package/dist/opened-realpath.js +11 -2
  200. package/dist/output.d.ts +0 -1
  201. package/dist/overwrite-file-handle.d.ts +0 -1
  202. package/dist/owner-dacl.d.ts +0 -1
  203. package/dist/path-case.d.ts +0 -1
  204. package/dist/path-policy.d.ts +0 -1
  205. package/dist/path-prefix.d.ts +0 -1
  206. package/dist/path-scope-lexical.d.ts +0 -1
  207. package/dist/path-segment-route.d.ts +0 -1
  208. package/dist/path-suffix-aliases.d.ts +0 -1
  209. package/dist/path.d.ts +0 -1
  210. package/dist/path.js +2 -1
  211. package/dist/permission-exec.d.ts +0 -1
  212. package/dist/permission-exec.js +19 -25
  213. package/dist/permissions-public.d.ts +0 -1
  214. package/dist/permissions-windows.d.ts +2 -10
  215. package/dist/permissions-windows.js +10 -30
  216. package/dist/permissions.d.ts +0 -1
  217. package/dist/permissions.js +11 -33
  218. package/dist/pinned-mutation-admission.d.ts +0 -1
  219. package/dist/pinned-mutation-observation.d.ts +0 -1
  220. package/dist/pinned-mutation-shared-route.d.ts +0 -1
  221. package/dist/pinned-open.d.ts +1 -1
  222. package/dist/pinned-open.js +1 -1
  223. package/dist/pinned-operation.d.ts +0 -1
  224. package/dist/pinned-write-input.d.ts +0 -1
  225. package/dist/pinned-write-input.js +11 -1
  226. package/dist/pinned-write-mode.d.ts +3 -4
  227. package/dist/pinned-write-mode.js +15 -8
  228. package/dist/pinned-write-staged.d.ts +0 -1
  229. package/dist/pinned-write-staged.js +10 -11
  230. package/dist/pinned-write-types.d.ts +9 -21
  231. package/dist/pinned-write.d.ts +0 -1
  232. package/dist/pinned-write.js +17 -13
  233. package/dist/positional-read.d.ts +0 -1
  234. package/dist/private-directory.d.ts +0 -1
  235. package/dist/private-producer-handoff-sync.d.ts +0 -1
  236. package/dist/private-producer-handoff-sync.js +9 -51
  237. package/dist/private-producer-handoff.d.ts +20 -1
  238. package/dist/private-producer-handoff.js +69 -46
  239. package/dist/private-temp-workspace.d.ts +0 -1
  240. package/dist/private-temp-workspace.js +11 -36
  241. package/dist/publish-copy-stage.d.ts +0 -1
  242. package/dist/publish-file-failure.d.ts +0 -1
  243. package/dist/publish-file.d.ts +2 -3
  244. package/dist/publish-file.js +56 -96
  245. package/dist/read-open-flags.d.ts +0 -1
  246. package/dist/read-opened-file.d.ts +0 -1
  247. package/dist/realpath.d.ts +0 -1
  248. package/dist/recursive-mkdir-path.d.ts +0 -1
  249. package/dist/regular-file.d.ts +0 -1
  250. package/dist/regular-file.js +107 -178
  251. package/dist/replace-directory.d.ts +0 -1
  252. package/dist/replace-file-copy-fallback.d.ts +0 -1
  253. package/dist/replace-file-copy-fallback.js +42 -29
  254. package/dist/replace-file-copy-source.d.ts +0 -1
  255. package/dist/replace-file-copy-source.js +19 -23
  256. package/dist/replace-file-descriptor.d.ts +0 -1
  257. package/dist/replace-file-descriptor.js +16 -17
  258. package/dist/replace-file-mode.d.ts +0 -1
  259. package/dist/replace-file-rename-policy.d.ts +0 -1
  260. package/dist/replace-file-temp-owner.d.ts +12 -13
  261. package/dist/replace-file-temp-owner.js +72 -91
  262. package/dist/replace-file.d.ts +0 -1
  263. package/dist/replace-file.js +9 -13
  264. package/dist/retained-directory-replacement.d.ts +0 -1
  265. package/dist/root-boundary.d.ts +0 -1
  266. package/dist/root-context.d.ts +0 -1
  267. package/dist/root-create-input.d.ts +0 -1
  268. package/dist/root-directory-creation.d.ts +0 -1
  269. package/dist/root-directory-list.d.ts +0 -1
  270. package/dist/root-directory-list.js +20 -3
  271. package/dist/root-directory.d.ts +0 -1
  272. package/dist/root-entries.d.ts +0 -1
  273. package/dist/root-errors.d.ts +0 -1
  274. package/dist/root-file-final-admission.d.ts +1 -2
  275. package/dist/root-file-final-admission.js +5 -2
  276. package/dist/root-file.d.ts +0 -1
  277. package/dist/root-file.js +3 -2
  278. package/dist/root-impl.d.ts +2 -10
  279. package/dist/root-impl.js +164 -111
  280. package/dist/root-move-noreplace.d.ts +2 -1
  281. package/dist/root-move-noreplace.js +2 -2
  282. package/dist/root-move-preflight.d.ts +0 -1
  283. package/dist/root-observed-path.d.ts +0 -1
  284. package/dist/root-options.d.ts +0 -1
  285. package/dist/root-path-errors.d.ts +0 -1
  286. package/dist/root-path-existing.d.ts +3 -1
  287. package/dist/root-path-existing.js +39 -18
  288. package/dist/root-path-observation.d.ts +0 -1
  289. package/dist/root-path-stat.d.ts +0 -1
  290. package/dist/root-path.d.ts +0 -1
  291. package/dist/root-path.js +60 -188
  292. package/dist/root-paths-lexical.d.ts +0 -1
  293. package/dist/root-paths.d.ts +0 -1
  294. package/dist/root-read-admission.d.ts +0 -1
  295. package/dist/root-read-admission.js +31 -39
  296. package/dist/root-remove-identity.d.ts +0 -1
  297. package/dist/root-remove-receipt.d.ts +0 -1
  298. package/dist/root-remove.d.ts +0 -1
  299. package/dist/root-remove.js +15 -1
  300. package/dist/root-symlink-policy.d.ts +0 -1
  301. package/dist/root-walk.d.ts +0 -1
  302. package/dist/root-write-admission.d.ts +0 -1
  303. package/dist/root-write-compatibility.d.ts +0 -1
  304. package/dist/root-write-complete-parent.d.ts +0 -1
  305. package/dist/root-write-lock-binding.d.ts +0 -1
  306. package/dist/root-write-mode.d.ts +0 -1
  307. package/dist/root-write-publication.d.ts +0 -1
  308. package/dist/root-write-verification.d.ts +0 -1
  309. package/dist/root.d.ts +0 -1
  310. package/dist/safe-path-segment.d.ts +1 -2
  311. package/dist/safe-path-segment.js +2 -5
  312. package/dist/secret-file.d.ts +0 -1
  313. package/dist/secret-file.js +7 -17
  314. package/dist/secret-read-async.d.ts +0 -1
  315. package/dist/secret-read-async.js +5 -5
  316. package/dist/secret-read-policy.d.ts +0 -1
  317. package/dist/secret.d.ts +0 -1
  318. package/dist/secure-file-windows.d.ts +0 -6
  319. package/dist/secure-file-windows.js +13 -28
  320. package/dist/secure-file.d.ts +0 -1
  321. package/dist/secure-file.js +9 -9
  322. package/dist/secure-temp-dir.d.ts +0 -1
  323. package/dist/secure-temp-repair.d.ts +0 -1
  324. package/dist/sibling-staged-file.d.ts +0 -1
  325. package/dist/sibling-staged-file.js +32 -39
  326. package/dist/sibling-temp.d.ts +0 -1
  327. package/dist/sidecar-lock-acquire.d.ts +1 -2
  328. package/dist/sidecar-lock-acquire.js +35 -58
  329. package/dist/sidecar-lock-admission-context.d.ts +0 -1
  330. package/dist/sidecar-lock-admission-parser.d.ts +0 -1
  331. package/dist/sidecar-lock-admission.d.ts +1 -5
  332. package/dist/sidecar-lock-admission.js +1 -7
  333. package/dist/sidecar-lock-handle.d.ts +3 -1
  334. package/dist/sidecar-lock-handle.js +6 -0
  335. package/dist/sidecar-lock-policy.d.ts +3 -1
  336. package/dist/sidecar-lock-policy.js +16 -0
  337. package/dist/sidecar-lock-reclaim.d.ts +1 -2
  338. package/dist/sidecar-lock-reclaim.js +11 -8
  339. package/dist/sidecar-lock-registration.d.ts +13 -0
  340. package/dist/sidecar-lock-registration.js +47 -0
  341. package/dist/sidecar-lock-root.d.ts +0 -1
  342. package/dist/sidecar-lock-stale-admission.d.ts +0 -1
  343. package/dist/sidecar-lock-target.d.ts +0 -1
  344. package/dist/sidecar-lock-types.d.ts +0 -1
  345. package/dist/sidecar-lock.d.ts +0 -1
  346. package/dist/sidecar-lock.js +19 -88
  347. package/dist/staged-directory.d.ts +2 -3
  348. package/dist/staged-file-settlement.d.ts +0 -1
  349. package/dist/staged-file-types.d.ts +0 -1
  350. package/dist/standalone-publication-path.d.ts +0 -1
  351. package/dist/stat-observation.d.ts +0 -1
  352. package/dist/store.d.ts +0 -1
  353. package/dist/strict-file-identity.d.ts +1 -2
  354. package/dist/strict-file-identity.js +9 -9
  355. package/dist/string-coerce.d.ts +0 -1
  356. package/dist/suppressed-error.d.ts +0 -1
  357. package/dist/symlink-parents.d.ts +0 -1
  358. package/dist/symlink-parents.js +2 -27
  359. package/dist/temp-cleanup.d.ts +0 -1
  360. package/dist/temp-target.d.ts +0 -1
  361. package/dist/temp-target.js +7 -46
  362. package/dist/temp-workspace-admission.d.ts +0 -8
  363. package/dist/temp-workspace-admission.js +0 -4
  364. package/dist/temp-workspace-child-admission.d.ts +0 -4
  365. package/dist/temp-workspace-child-admission.js +0 -12
  366. package/dist/temp-workspace-descriptor.d.ts +0 -6
  367. package/dist/temp-workspace-descriptor.js +0 -2
  368. package/dist/temp-workspace-identity.d.ts +0 -1
  369. package/dist/temp-workspace-owner.d.ts +1 -1
  370. package/dist/temp-workspace-owner.js +49 -26
  371. package/dist/temp-workspace-permissions.d.ts +0 -1
  372. package/dist/temp-workspace-types.d.ts +0 -1
  373. package/dist/temp.d.ts +0 -1
  374. package/dist/test-hooks.d.ts +0 -1
  375. package/dist/text-atomic.d.ts +0 -1
  376. package/dist/timing.d.ts +0 -1
  377. package/dist/trash.d.ts +0 -1
  378. package/dist/types.d.ts +0 -1
  379. package/dist/unicode-path.d.ts +0 -1
  380. package/dist/unicode-path.js +3 -0
  381. package/dist/walk.d.ts +0 -1
  382. package/dist/walk.js +4 -2
  383. package/dist/windows-command.d.ts +0 -1
  384. package/dist/windows-owner.d.ts +0 -1
  385. package/dist/windows-owner.js +4 -1
  386. package/dist/windows-path-alias.d.ts +0 -1
  387. package/dist/windows-path-alias.js +7 -43
  388. package/dist/windows-path-syntax.d.ts +5 -0
  389. package/dist/windows-path-syntax.js +21 -0
  390. package/dist/windows-security-command.d.ts +0 -1
  391. package/dist/windows-security-facts.d.ts +0 -1
  392. package/dist/write-file-handle.d.ts +7 -1
  393. package/dist/write-file-handle.js +23 -0
  394. package/dist/write-open-flags.d.ts +0 -1
  395. package/dist/write-open-flags.js +1 -8
  396. package/dist/write-queue.d.ts +0 -1
  397. package/dist/write-queue.js +1 -4
  398. package/docs/advanced.md +68 -1
  399. package/docs/archive.md +41 -2
  400. package/docs/atomic.md +43 -5
  401. package/docs/contributing.md +4 -0
  402. package/docs/copy.md +2 -0
  403. package/docs/creation.md +8 -4
  404. package/docs/durability.md +35 -0
  405. package/docs/file-contents.md +68 -0
  406. package/docs/json.md +5 -4
  407. package/docs/local-roots.md +2 -0
  408. package/docs/mutation-policy-proof.md +11 -7
  409. package/docs/native.md +9 -8
  410. package/docs/path.md +4 -4
  411. package/docs/permissions.md +6 -0
  412. package/docs/public-api.md +5 -0
  413. package/docs/quickstart.md +1 -1
  414. package/docs/reading.md +2 -2
  415. package/docs/regular-file.md +3 -0
  416. package/docs/root.md +19 -0
  417. package/docs/secret-file.md +4 -0
  418. package/docs/sidecar-lock.md +9 -1
  419. package/docs/staged-file.md +4 -3
  420. package/docs/store.md +3 -1
  421. package/docs/temp.md +4 -1
  422. package/docs/types.md +18 -2
  423. package/docs/walk.md +7 -0
  424. package/docs/writing.md +18 -5
  425. package/package.json +8 -9
  426. package/dist/absolute-path.d.ts.map +0 -1
  427. package/dist/advanced.d.ts.map +0 -1
  428. package/dist/archive-crc32.d.ts.map +0 -1
  429. package/dist/archive-deadline.d.ts.map +0 -1
  430. package/dist/archive-durability.d.ts.map +0 -1
  431. package/dist/archive-entry.d.ts.map +0 -1
  432. package/dist/archive-errors.d.ts.map +0 -1
  433. package/dist/archive-gzip-tail.d.ts.map +0 -1
  434. package/dist/archive-input.d.ts.map +0 -1
  435. package/dist/archive-kind.d.ts.map +0 -1
  436. package/dist/archive-limits.d.ts.map +0 -1
  437. package/dist/archive-merge.d.ts.map +0 -1
  438. package/dist/archive-native.d.ts.map +0 -1
  439. package/dist/archive-options.d.ts.map +0 -1
  440. package/dist/archive-parser-errors.d.ts.map +0 -1
  441. package/dist/archive-plan.d.ts.map +0 -1
  442. package/dist/archive-policy.d.ts.map +0 -1
  443. package/dist/archive-read.d.ts.map +0 -1
  444. package/dist/archive-staging.d.ts.map +0 -1
  445. package/dist/archive-tar-inspect.d.ts.map +0 -1
  446. package/dist/archive-tar-stream.d.ts.map +0 -1
  447. package/dist/archive-tar-wasm.d.ts.map +0 -1
  448. package/dist/archive-tar.d.ts.map +0 -1
  449. package/dist/archive-zip-admission.d.ts.map +0 -1
  450. package/dist/archive-zip-count.d.ts.map +0 -1
  451. package/dist/archive-zip-directory.d.ts.map +0 -1
  452. package/dist/archive-zip-entry.d.ts.map +0 -1
  453. package/dist/archive-zip-integrity.d.ts.map +0 -1
  454. package/dist/archive-zip-loader.d.ts.map +0 -1
  455. package/dist/archive-zip-manifest.d.ts.map +0 -1
  456. package/dist/archive-zip-names.d.ts.map +0 -1
  457. package/dist/archive-zip-preflight.d.ts.map +0 -1
  458. package/dist/archive.d.ts.map +0 -1
  459. package/dist/async-lock.d.ts.map +0 -1
  460. package/dist/atomic.d.ts.map +0 -1
  461. package/dist/bounded-read-stream.d.ts.map +0 -1
  462. package/dist/bounded-read.d.ts.map +0 -1
  463. package/dist/byte-budget.d.ts.map +0 -1
  464. package/dist/clone-metadata.d.ts.map +0 -1
  465. package/dist/config.d.ts.map +0 -1
  466. package/dist/containment.d.ts.map +0 -1
  467. package/dist/copy-file-input.d.ts.map +0 -1
  468. package/dist/copy-policy.d.ts.map +0 -1
  469. package/dist/copy-publication.d.ts.map +0 -1
  470. package/dist/copy-tree-portable.d.ts.map +0 -1
  471. package/dist/copy.d.ts.map +0 -1
  472. package/dist/create-directory.d.ts.map +0 -1
  473. package/dist/create-file-async.d.ts.map +0 -1
  474. package/dist/create-file.d.ts.map +0 -1
  475. package/dist/create-owned-file.d.ts.map +0 -1
  476. package/dist/create.d.ts.map +0 -1
  477. package/dist/creation-darwin.d.ts.map +0 -1
  478. package/dist/creation-file-state.d.ts.map +0 -1
  479. package/dist/creation-path.d.ts.map +0 -1
  480. package/dist/creation-permissions.d.ts.map +0 -1
  481. package/dist/deny-mutations.d.ts.map +0 -1
  482. package/dist/device-path.d.ts.map +0 -1
  483. package/dist/directory-durability.d.ts.map +0 -1
  484. package/dist/directory-entry-path.d.ts.map +0 -1
  485. package/dist/directory-guard.d.ts.map +0 -1
  486. package/dist/directory-mode-node.d.ts.map +0 -1
  487. package/dist/directory-mode-owner.d.ts.map +0 -1
  488. package/dist/directory-receipt.d.ts.map +0 -1
  489. package/dist/durability.d.ts.map +0 -1
  490. package/dist/effective-uid.d.ts.map +0 -1
  491. package/dist/error-detail.d.ts.map +0 -1
  492. package/dist/errors.d.ts.map +0 -1
  493. package/dist/file-cleanup.d.ts.map +0 -1
  494. package/dist/file-handle-transfer.d.ts.map +0 -1
  495. package/dist/file-hash.d.ts.map +0 -1
  496. package/dist/file-identity.d.ts.map +0 -1
  497. package/dist/file-lock-sync-admission.d.ts.map +0 -1
  498. package/dist/file-lock-sync-root-acquire.d.ts.map +0 -1
  499. package/dist/file-lock-sync-root-arbitration.d.ts.map +0 -1
  500. package/dist/file-lock-sync-root-held.d.ts.map +0 -1
  501. package/dist/file-lock-sync-root-io.d.ts.map +0 -1
  502. package/dist/file-lock-sync-root-mutation.d.ts.map +0 -1
  503. package/dist/file-lock-sync-root-options.d.ts.map +0 -1
  504. package/dist/file-lock-sync-root-registration.d.ts.map +0 -1
  505. package/dist/file-lock-sync-root.d.ts.map +0 -1
  506. package/dist/file-lock-sync-stale-admission.d.ts.map +0 -1
  507. package/dist/file-lock-sync.d.ts.map +0 -1
  508. package/dist/file-lock.d.ts.map +0 -1
  509. package/dist/file-observation.d.ts.map +0 -1
  510. package/dist/file-store-boundary.d.ts.map +0 -1
  511. package/dist/file-store-copy-source.d.ts.map +0 -1
  512. package/dist/file-store-limit.d.ts.map +0 -1
  513. package/dist/file-store-path.d.ts.map +0 -1
  514. package/dist/file-store-prune.d.ts.map +0 -1
  515. package/dist/file-store-sync-directory.d.ts.map +0 -1
  516. package/dist/file-store-sync-write.d.ts.map +0 -1
  517. package/dist/file-store.d.ts.map +0 -1
  518. package/dist/file-sync.d.ts.map +0 -1
  519. package/dist/filename.d.ts.map +0 -1
  520. package/dist/fs.d.ts.map +0 -1
  521. package/dist/guarded-mkdir.d.ts.map +0 -1
  522. package/dist/guarded-mutation.d.ts.map +0 -1
  523. package/dist/guest-dispatch-python.d.ts.map +0 -1
  524. package/dist/guest-native-python.d.ts.map +0 -1
  525. package/dist/guest.d.ts.map +0 -1
  526. package/dist/home-dir.d.ts.map +0 -1
  527. package/dist/index.d.ts.map +0 -1
  528. package/dist/install-path.d.ts.map +0 -1
  529. package/dist/json-document-store.d.ts.map +0 -1
  530. package/dist/json-durable-queue-directory.d.ts.map +0 -1
  531. package/dist/json-durable-queue-ownership.d.ts.map +0 -1
  532. package/dist/json-durable-queue-paths.d.ts +0 -11
  533. package/dist/json-durable-queue-paths.d.ts.map +0 -1
  534. package/dist/json-durable-queue-paths.js +0 -42
  535. package/dist/json-durable-queue-read.d.ts.map +0 -1
  536. package/dist/json-durable-queue.d.ts.map +0 -1
  537. package/dist/json-store.d.ts.map +0 -1
  538. package/dist/json-stringify.d.ts.map +0 -1
  539. package/dist/json.d.ts.map +0 -1
  540. package/dist/local-file-access.d.ts.map +0 -1
  541. package/dist/local-file-descriptor.d.ts.map +0 -1
  542. package/dist/local-roots.d.ts.map +0 -1
  543. package/dist/lock-config.d.ts.map +0 -1
  544. package/dist/move-path-cleanup.d.ts.map +0 -1
  545. package/dist/move-path-stage.d.ts.map +0 -1
  546. package/dist/move-path.d.ts.map +0 -1
  547. package/dist/mutation-authority.d.ts.map +0 -1
  548. package/dist/native-binding.d.ts.map +0 -1
  549. package/dist/native-config.d.ts.map +0 -1
  550. package/dist/native-directory-observation.d.ts.map +0 -1
  551. package/dist/native-fallback-warning.d.ts.map +0 -1
  552. package/dist/native-operations.d.ts.map +0 -1
  553. package/dist/native-parent-admission.d.ts.map +0 -1
  554. package/dist/native-pinned-write-windows.d.ts.map +0 -1
  555. package/dist/native-pinned-write.d.ts.map +0 -1
  556. package/dist/native-policy-directory-observation.d.ts.map +0 -1
  557. package/dist/native-policy-parent-windows.d.ts.map +0 -1
  558. package/dist/native-rename-outcome.d.ts.map +0 -1
  559. package/dist/native-staged-file.d.ts.map +0 -1
  560. package/dist/native.d.ts.map +0 -1
  561. package/dist/opened-file-failure.d.ts.map +0 -1
  562. package/dist/opened-realpath.d.ts.map +0 -1
  563. package/dist/output.d.ts.map +0 -1
  564. package/dist/overwrite-file-handle.d.ts.map +0 -1
  565. package/dist/owner-dacl.d.ts.map +0 -1
  566. package/dist/path-case.d.ts.map +0 -1
  567. package/dist/path-policy.d.ts.map +0 -1
  568. package/dist/path-prefix.d.ts.map +0 -1
  569. package/dist/path-scope-lexical.d.ts.map +0 -1
  570. package/dist/path-segment-route.d.ts.map +0 -1
  571. package/dist/path-suffix-aliases.d.ts.map +0 -1
  572. package/dist/path.d.ts.map +0 -1
  573. package/dist/permission-exec.d.ts.map +0 -1
  574. package/dist/permissions-public.d.ts.map +0 -1
  575. package/dist/permissions-windows.d.ts.map +0 -1
  576. package/dist/permissions.d.ts.map +0 -1
  577. package/dist/pinned-mutation-admission.d.ts.map +0 -1
  578. package/dist/pinned-mutation-observation.d.ts.map +0 -1
  579. package/dist/pinned-mutation-shared-route.d.ts.map +0 -1
  580. package/dist/pinned-open.d.ts.map +0 -1
  581. package/dist/pinned-operation.d.ts.map +0 -1
  582. package/dist/pinned-write-input.d.ts.map +0 -1
  583. package/dist/pinned-write-mode.d.ts.map +0 -1
  584. package/dist/pinned-write-staged.d.ts.map +0 -1
  585. package/dist/pinned-write-types.d.ts.map +0 -1
  586. package/dist/pinned-write.d.ts.map +0 -1
  587. package/dist/positional-read.d.ts.map +0 -1
  588. package/dist/private-directory.d.ts.map +0 -1
  589. package/dist/private-producer-handoff-sync.d.ts.map +0 -1
  590. package/dist/private-producer-handoff.d.ts.map +0 -1
  591. package/dist/private-temp-workspace.d.ts.map +0 -1
  592. package/dist/publish-copy-stage.d.ts.map +0 -1
  593. package/dist/publish-file-failure.d.ts.map +0 -1
  594. package/dist/publish-file.d.ts.map +0 -1
  595. package/dist/read-open-flags.d.ts.map +0 -1
  596. package/dist/read-opened-file.d.ts.map +0 -1
  597. package/dist/realpath.d.ts.map +0 -1
  598. package/dist/recursive-mkdir-path.d.ts.map +0 -1
  599. package/dist/regular-file.d.ts.map +0 -1
  600. package/dist/replace-directory.d.ts.map +0 -1
  601. package/dist/replace-file-copy-fallback.d.ts.map +0 -1
  602. package/dist/replace-file-copy-source.d.ts.map +0 -1
  603. package/dist/replace-file-descriptor.d.ts.map +0 -1
  604. package/dist/replace-file-mode.d.ts.map +0 -1
  605. package/dist/replace-file-rename-policy.d.ts.map +0 -1
  606. package/dist/replace-file-temp-owner.d.ts.map +0 -1
  607. package/dist/replace-file.d.ts.map +0 -1
  608. package/dist/retained-directory-replacement.d.ts.map +0 -1
  609. package/dist/root-boundary.d.ts.map +0 -1
  610. package/dist/root-context.d.ts.map +0 -1
  611. package/dist/root-create-input.d.ts.map +0 -1
  612. package/dist/root-directory-creation.d.ts.map +0 -1
  613. package/dist/root-directory-list.d.ts.map +0 -1
  614. package/dist/root-directory.d.ts.map +0 -1
  615. package/dist/root-entries.d.ts.map +0 -1
  616. package/dist/root-errors.d.ts.map +0 -1
  617. package/dist/root-file-final-admission.d.ts.map +0 -1
  618. package/dist/root-file.d.ts.map +0 -1
  619. package/dist/root-impl.d.ts.map +0 -1
  620. package/dist/root-move-noreplace.d.ts.map +0 -1
  621. package/dist/root-move-preflight.d.ts.map +0 -1
  622. package/dist/root-observed-path.d.ts.map +0 -1
  623. package/dist/root-options.d.ts.map +0 -1
  624. package/dist/root-path-errors.d.ts.map +0 -1
  625. package/dist/root-path-existing.d.ts.map +0 -1
  626. package/dist/root-path-observation.d.ts.map +0 -1
  627. package/dist/root-path-stat.d.ts.map +0 -1
  628. package/dist/root-path-symlink.d.ts +0 -7
  629. package/dist/root-path-symlink.d.ts.map +0 -1
  630. package/dist/root-path-symlink.js +0 -55
  631. package/dist/root-path.d.ts.map +0 -1
  632. package/dist/root-paths-lexical.d.ts.map +0 -1
  633. package/dist/root-paths.d.ts.map +0 -1
  634. package/dist/root-read-admission.d.ts.map +0 -1
  635. package/dist/root-remove-identity.d.ts.map +0 -1
  636. package/dist/root-remove-receipt.d.ts.map +0 -1
  637. package/dist/root-remove.d.ts.map +0 -1
  638. package/dist/root-symlink-policy.d.ts.map +0 -1
  639. package/dist/root-walk.d.ts.map +0 -1
  640. package/dist/root-write-admission.d.ts.map +0 -1
  641. package/dist/root-write-compatibility.d.ts.map +0 -1
  642. package/dist/root-write-complete-parent.d.ts.map +0 -1
  643. package/dist/root-write-lock-binding.d.ts.map +0 -1
  644. package/dist/root-write-mode.d.ts.map +0 -1
  645. package/dist/root-write-publication.d.ts.map +0 -1
  646. package/dist/root-write-verification.d.ts.map +0 -1
  647. package/dist/root.d.ts.map +0 -1
  648. package/dist/safe-path-segment.d.ts.map +0 -1
  649. package/dist/secret-file.d.ts.map +0 -1
  650. package/dist/secret-read-async.d.ts.map +0 -1
  651. package/dist/secret-read-policy.d.ts.map +0 -1
  652. package/dist/secret.d.ts.map +0 -1
  653. package/dist/secure-file-windows.d.ts.map +0 -1
  654. package/dist/secure-file.d.ts.map +0 -1
  655. package/dist/secure-temp-dir.d.ts.map +0 -1
  656. package/dist/secure-temp-repair.d.ts.map +0 -1
  657. package/dist/sibling-staged-file.d.ts.map +0 -1
  658. package/dist/sibling-temp.d.ts.map +0 -1
  659. package/dist/sidecar-lock-acquire.d.ts.map +0 -1
  660. package/dist/sidecar-lock-admission-context.d.ts.map +0 -1
  661. package/dist/sidecar-lock-admission-parser.d.ts.map +0 -1
  662. package/dist/sidecar-lock-admission.d.ts.map +0 -1
  663. package/dist/sidecar-lock-handle.d.ts.map +0 -1
  664. package/dist/sidecar-lock-policy.d.ts.map +0 -1
  665. package/dist/sidecar-lock-reclaim.d.ts.map +0 -1
  666. package/dist/sidecar-lock-root.d.ts.map +0 -1
  667. package/dist/sidecar-lock-stale-admission.d.ts.map +0 -1
  668. package/dist/sidecar-lock-target.d.ts.map +0 -1
  669. package/dist/sidecar-lock-types.d.ts.map +0 -1
  670. package/dist/sidecar-lock.d.ts.map +0 -1
  671. package/dist/staged-directory.d.ts.map +0 -1
  672. package/dist/staged-file-settlement.d.ts.map +0 -1
  673. package/dist/staged-file-types.d.ts.map +0 -1
  674. package/dist/standalone-publication-path.d.ts.map +0 -1
  675. package/dist/stat-observation.d.ts.map +0 -1
  676. package/dist/store.d.ts.map +0 -1
  677. package/dist/strict-file-identity.d.ts.map +0 -1
  678. package/dist/string-coerce.d.ts.map +0 -1
  679. package/dist/suppressed-error.d.ts.map +0 -1
  680. package/dist/symlink-parents.d.ts.map +0 -1
  681. package/dist/temp-cleanup.d.ts.map +0 -1
  682. package/dist/temp-target.d.ts.map +0 -1
  683. package/dist/temp-workspace-admission.d.ts.map +0 -1
  684. package/dist/temp-workspace-child-admission.d.ts.map +0 -1
  685. package/dist/temp-workspace-descriptor.d.ts.map +0 -1
  686. package/dist/temp-workspace-identity.d.ts.map +0 -1
  687. package/dist/temp-workspace-owner.d.ts.map +0 -1
  688. package/dist/temp-workspace-permissions.d.ts.map +0 -1
  689. package/dist/temp-workspace-types.d.ts.map +0 -1
  690. package/dist/temp.d.ts.map +0 -1
  691. package/dist/test-hooks.d.ts.map +0 -1
  692. package/dist/text-atomic.d.ts.map +0 -1
  693. package/dist/timing.d.ts.map +0 -1
  694. package/dist/trash.d.ts.map +0 -1
  695. package/dist/types.d.ts.map +0 -1
  696. package/dist/unicode-path.d.ts.map +0 -1
  697. package/dist/walk.d.ts.map +0 -1
  698. package/dist/windows-command.d.ts.map +0 -1
  699. package/dist/windows-owner.d.ts.map +0 -1
  700. package/dist/windows-path-alias.d.ts.map +0 -1
  701. package/dist/windows-security-command.d.ts.map +0 -1
  702. package/dist/windows-security-facts.d.ts.map +0 -1
  703. package/dist/write-file-handle.d.ts.map +0 -1
  704. package/dist/write-open-flags.d.ts.map +0 -1
  705. package/dist/write-queue.d.ts.map +0 -1
@@ -2,9 +2,8 @@ import { syncFileBestEffort, syncFileBestEffortSync } from "./file-sync.js";
2
2
  import syncFs, {} from "node:fs";
3
3
  import fs, {} from "node:fs/promises";
4
4
  import { FsSafeError } from "./errors.js";
5
- import { sameFileIdentity } from "./file-identity.js";
6
5
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
7
- import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
6
+ import { ownDirectoryMode } from "./directory-mode-owner.js";
8
7
  export async function syncDirectoryBestEffort(fsModule, dirPath) {
9
8
  let handle;
10
9
  try {
@@ -15,7 +14,12 @@ export async function syncDirectoryBestEffort(fsModule, dirPath) {
15
14
  // Best-effort on platforms/filesystems that do not support directory fsync.
16
15
  }
17
16
  finally {
18
- await handle?.close().catch(() => undefined);
17
+ try {
18
+ await handle?.close();
19
+ }
20
+ catch {
21
+ // Best-effort close also covers synchronous adapter throws.
22
+ }
19
23
  }
20
24
  }
21
25
  export function syncDirectoryBestEffortSync(fsModule, dirPath) {
@@ -48,12 +52,7 @@ function assertDirectory(identity, dirPath) {
48
52
  if (identity.isSymbolicLink() || !identity.isDirectory()) {
49
53
  throw new FsSafeError("not-file", `Atomic replace parent must be a real directory: ${dirPath}`);
50
54
  }
51
- }
52
- function assertSameDirectory(expected, opened, dirPath) {
53
- assertDirectory(opened, dirPath);
54
- if (!sameFileIdentity(expected, opened)) {
55
- throw new FsSafeError("path-mismatch", `Atomic replace parent changed before its mode could be applied: ${dirPath}`);
56
- }
55
+ return identity;
57
56
  }
58
57
  export async function pinDirectoryForMode(params) {
59
58
  // Node does not enforce POSIX directory modes on Windows, and its directory
@@ -63,15 +62,16 @@ export async function pinDirectoryForMode(params) {
63
62
  return;
64
63
  }
65
64
  const expected = params.fsModule === fs
66
- ? syncFs.lstatSync(params.dirPath) : await params.fsModule.lstat(params.dirPath);
67
- assertDirectory(expected, params.dirPath);
65
+ ? inspectFileIdentitySync(() => assertDirectory(syncFs.lstatSync(params.dirPath, { bigint: true }), params.dirPath))
66
+ : await inspectFileIdentity(async () => assertDirectory(await params.fsModule.lstat(params.dirPath, { bigint: true }), params.dirPath));
68
67
  const handle = await params.fsModule.open(params.dirPath, directoryOpenFlags());
69
68
  try {
70
69
  const owner = ownDirectoryMode({
71
70
  async inspect() {
72
- const opened = params.fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
73
- assertOwnedDirectory(expected, opened);
74
- return opened.mode & 0o7777;
71
+ const opened = params.fsModule === fs
72
+ ? inspectFileIdentitySync(() => assertDirectory(syncFs.fstatSync(handle.fd, { bigint: true }), params.dirPath), expected)
73
+ : await inspectFileIdentity(async () => assertDirectory(await handle.stat({ bigint: true }), params.dirPath), expected);
74
+ return Number(opened.mode & 4095n);
75
75
  },
76
76
  chmod: (mode) => handle.chmod(mode),
77
77
  close: () => handle.close(),
@@ -98,11 +98,10 @@ export function applyDirectoryModeSync(params) {
98
98
  if (process.platform === "win32") {
99
99
  return;
100
100
  }
101
- const expected = params.fsModule.lstatSync(params.dirPath);
102
- assertDirectory(expected, params.dirPath);
101
+ const expected = inspectFileIdentitySync(() => assertDirectory(params.fsModule.lstatSync(params.dirPath, { bigint: true }), params.dirPath));
103
102
  const fd = params.fsModule.openSync(params.dirPath, directoryOpenFlags());
104
103
  try {
105
- assertSameDirectory(expected, params.fsModule.fstatSync(fd), params.dirPath);
104
+ inspectFileIdentitySync(() => assertDirectory(params.fsModule.fstatSync(fd, { bigint: true }), params.dirPath), expected);
106
105
  // chmod ignores file-type bits; mask so raw stat modes are tolerated.
107
106
  params.fchmodSync?.(fd, params.mode & 0o7777);
108
107
  }
@@ -1,3 +1,2 @@
1
1
  import type { Stats } from "node:fs";
2
2
  export declare function inheritedRegularFileMode(stat: Stats): number;
3
- //# sourceMappingURL=replace-file-mode.d.ts.map
@@ -4,4 +4,3 @@ export declare function validateRenameIdentity(policy: RenameIdentityPolicy | un
4
4
  export declare function atomicExpectedContentHash(policy: RenameIdentityPolicy | undefined, content: string | Uint8Array): string | undefined;
5
5
  export declare function withAtomicRenameIdentityLock<T>(filePath: string, fn: () => Promise<T>): Promise<T>;
6
6
  export declare function withAtomicRenameIdentityLockSync<T>(filePath: string, fn: () => T): T;
7
- //# sourceMappingURL=replace-file-rename-policy.d.ts.map
@@ -1,45 +1,45 @@
1
1
  import syncFs, { type BigIntStats } from "node:fs";
2
2
  import fs, { type FileHandle } from "node:fs/promises";
3
+ import { type TempPathRegistration } from "./temp-cleanup.js";
3
4
  type AsyncOwnerFileSystem = Pick<typeof fs, "lstat" | "open" | "unlink">;
4
5
  type SyncOwnerFileSystem = Pick<typeof syncFs, "closeSync" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "unlinkSync">;
5
6
  export type AtomicTempFailure = Readonly<{
6
7
  error: unknown;
7
8
  }>;
8
9
  export declare function removePathIfIdentityUnchanged(targetPath: string, identity: Pick<BigIntStats, "dev" | "ino">): Promise<void>;
9
- export declare class AsyncAtomicTempOwner {
10
- #private;
10
+ declare class AtomicTempOwner<Resource> {
11
11
  readonly pathname: string;
12
+ protected resource: Resource | undefined;
13
+ protected recordedIdentity: BigIntStats | undefined;
14
+ protected exists: boolean;
15
+ protected unregister: TempPathRegistration;
12
16
  constructor(pathname: string);
13
17
  start(): void;
14
18
  readonly onIdentity: (identity: BigIntStats) => void;
19
+ get identity(): BigIntStats;
20
+ markRenamed(): void;
21
+ protected takeResource(): Resource | undefined;
22
+ }
23
+ export declare class AsyncAtomicTempOwner extends AtomicTempOwner<FileHandle> {
15
24
  adopt(temp: {
16
25
  handle: FileHandle;
17
26
  identity: BigIntStats;
18
27
  }): void;
19
- get identity(): BigIntStats;
20
28
  assertCurrent(fsModule: AsyncOwnerFileSystem, pathname?: string): Promise<void>;
21
29
  assertPublished(fsModule: AsyncOwnerFileSystem, pathname: string, expectedHash?: string): Promise<void>;
22
- markRenamed(): void;
23
30
  finish(params: {
24
31
  fsModule: AsyncOwnerFileSystem;
25
32
  originalFailure?: AtomicTempFailure;
26
33
  throwOnCleanupError: boolean;
27
34
  }): Promise<void>;
28
35
  }
29
- export declare class SyncAtomicTempOwner {
30
- #private;
31
- readonly pathname: string;
32
- constructor(pathname: string);
33
- start(): void;
34
- readonly onIdentity: (identity: BigIntStats) => void;
36
+ export declare class SyncAtomicTempOwner extends AtomicTempOwner<number> {
35
37
  adopt(temp: {
36
38
  fd: number;
37
39
  identity: BigIntStats;
38
40
  }): void;
39
- get identity(): BigIntStats;
40
41
  assertCurrent(fsModule: SyncOwnerFileSystem, pathname?: string): void;
41
42
  assertPublished(fsModule: SyncOwnerFileSystem, pathname: string, expectedHash?: string): void;
42
- markRenamed(): void;
43
43
  finish(params: {
44
44
  fsModule: SyncOwnerFileSystem;
45
45
  originalFailure?: AtomicTempFailure;
@@ -47,4 +47,3 @@ export declare class SyncAtomicTempOwner {
47
47
  }): void;
48
48
  }
49
49
  export {};
50
- //# sourceMappingURL=replace-file-temp-owner.d.ts.map
@@ -57,6 +57,15 @@ function cleanupFailure(originalFailure, cleanupError) {
57
57
  }
58
58
  return isErrorValue(cleanupError) ? cleanupError : new Error(describeFailure(cleanupError));
59
59
  }
60
+ function closeFailure(closeError, params, cleanupFailure) {
61
+ return {
62
+ error: cleanupFailure
63
+ ? new AggregateError([cleanupFailure.error, closeError], "Atomic temp cleanup and close failed")
64
+ : params.originalFailure
65
+ ? new AggregateError([params.originalFailure.error, closeError], "Atomic file replace and close failed")
66
+ : closeError,
67
+ };
68
+ }
60
69
  async function cleanupOwnedPath(params) {
61
70
  try {
62
71
  await removeOwnedPath(params);
@@ -92,36 +101,48 @@ function cleanupOwnedPathSync(params) {
92
101
  return false;
93
102
  }
94
103
  }
95
- export class AsyncAtomicTempOwner {
104
+ class AtomicTempOwner {
96
105
  pathname;
97
- #handle;
98
- #identity;
99
- #exists = false;
100
- #unregister;
106
+ resource;
107
+ recordedIdentity;
108
+ exists = false;
109
+ unregister;
101
110
  constructor(pathname) {
102
111
  this.pathname = pathname;
103
- this.#unregister = registerTempPathForExit(pathname, { singleLinkFile: true });
112
+ this.unregister = registerTempPathForExit(pathname, { singleLinkFile: true });
104
113
  }
105
114
  start() {
106
- this.#exists = true;
115
+ this.exists = true;
107
116
  }
108
117
  onIdentity = (identity) => {
109
- this.#identity = identity;
110
- this.#unregister.setIdentity(identity);
118
+ this.recordedIdentity = identity;
119
+ this.unregister.setIdentity(identity);
111
120
  };
112
- adopt(temp) {
113
- this.#handle = temp.handle;
114
- this.onIdentity(temp.identity);
115
- }
116
121
  get identity() {
117
- if (!this.#identity)
122
+ if (!this.recordedIdentity)
118
123
  throw new Error("Atomic temp owner has no identity");
119
- return this.#identity;
124
+ return this.recordedIdentity;
125
+ }
126
+ markRenamed() {
127
+ this.exists = false;
128
+ this.unregister();
129
+ }
130
+ takeResource() {
131
+ // A throwing close may already have released the descriptor for reuse.
132
+ const resource = this.resource;
133
+ this.resource = undefined;
134
+ return resource;
135
+ }
136
+ }
137
+ export class AsyncAtomicTempOwner extends AtomicTempOwner {
138
+ adopt(temp) {
139
+ this.resource = temp.handle;
140
+ this.onIdentity(temp.identity);
120
141
  }
121
142
  async assertCurrent(fsModule, pathname = this.pathname) {
122
143
  const opened = await inspectFileIdentity(async () => {
123
- const stat = fsModule === fs ? syncFs.fstatSync(this.#handle.fd, { bigint: true })
124
- : await this.#handle.stat({ bigint: true });
144
+ const stat = fsModule === fs ? syncFs.fstatSync(this.resource.fd, { bigint: true })
145
+ : await this.resource.stat({ bigint: true });
125
146
  assertOwnedFile(stat, pathname, false);
126
147
  return stat;
127
148
  }, this.identity);
@@ -134,7 +155,7 @@ export class AsyncAtomicTempOwner {
134
155
  }, opened);
135
156
  }
136
157
  catch (error) {
137
- if (error.code === "ENOENT") {
158
+ if (hasErrorCode(error, "ENOENT")) {
138
159
  throw missingOwnedFile(pathname, error);
139
160
  }
140
161
  throw error;
@@ -146,7 +167,7 @@ export class AsyncAtomicTempOwner {
146
167
  return;
147
168
  }
148
169
  catch (error) {
149
- if (!(error instanceof FsSafeError) || error.code !== "path-mismatch" || !expectedHash) {
170
+ if (!(error instanceof FsSafeError) || !hasErrorCode(error, "path-mismatch") || !expectedHash) {
150
171
  throw error;
151
172
  }
152
173
  }
@@ -156,7 +177,7 @@ export class AsyncAtomicTempOwner {
156
177
  published = await fsModule.open(pathname, PUBLISHED_READ_FLAGS);
157
178
  }
158
179
  catch (error) {
159
- if (error.code === "ELOOP") {
180
+ if (hasErrorCode(error, "ELOOP")) {
160
181
  throw new FsSafeError("symlink", `Atomic replace published file became a symlink: ${pathname}`, {
161
182
  cause: error,
162
183
  });
@@ -178,30 +199,30 @@ export class AsyncAtomicTempOwner {
178
199
  if (sha256Hex(await published.readFile()) !== expectedHash) {
179
200
  throw new FsSafeError("path-mismatch", `Atomic replace published content changed: ${pathname}`);
180
201
  }
181
- const previousHandle = this.#handle;
182
- this.#handle = undefined;
202
+ const previousHandle = this.takeResource();
183
203
  await previousHandle?.close();
184
- this.#handle = published;
185
- this.#identity = identity;
204
+ this.resource = published;
205
+ this.recordedIdentity = identity;
186
206
  published = undefined;
187
207
  }
188
208
  finally {
189
- await published?.close().catch(() => undefined);
209
+ try {
210
+ await published?.close();
211
+ }
212
+ catch {
213
+ // Preserve the selected verification or previous-handle close failure.
214
+ }
190
215
  }
191
216
  }
192
- markRenamed() {
193
- this.#exists = false;
194
- this.#unregister();
195
- }
196
217
  async finish(params) {
197
218
  let deferredFailure;
198
- let cleanupComplete = !this.#exists;
199
- if (this.#exists) {
219
+ let cleanupComplete = !this.exists;
220
+ if (this.exists) {
200
221
  try {
201
222
  cleanupComplete = await cleanupOwnedPath({
202
223
  fsModule: params.fsModule,
203
224
  pathname: this.pathname,
204
- identity: this.#identity,
225
+ identity: this.recordedIdentity,
205
226
  originalFailure: params.originalFailure,
206
227
  throwOnCleanupError: params.throwOnCleanupError,
207
228
  });
@@ -211,54 +232,26 @@ export class AsyncAtomicTempOwner {
211
232
  }
212
233
  }
213
234
  if (cleanupComplete)
214
- this.#unregister();
215
- const handle = this.#handle;
216
- this.#handle = undefined;
235
+ this.unregister();
236
+ const handle = this.takeResource();
217
237
  try {
218
238
  await handle?.close();
219
239
  }
220
240
  catch (closeError) {
221
- deferredFailure = {
222
- error: deferredFailure
223
- ? new AggregateError([deferredFailure.error, closeError], "Atomic temp cleanup and close failed")
224
- : params.originalFailure
225
- ? new AggregateError([params.originalFailure.error, closeError], "Atomic file replace and close failed")
226
- : closeError,
227
- };
241
+ deferredFailure = closeFailure(closeError, params, deferredFailure);
228
242
  }
229
243
  if (deferredFailure)
230
244
  throw deferredFailure.error;
231
245
  }
232
246
  }
233
- export class SyncAtomicTempOwner {
234
- pathname;
235
- #fd;
236
- #identity;
237
- #exists = false;
238
- #unregister;
239
- constructor(pathname) {
240
- this.pathname = pathname;
241
- this.#unregister = registerTempPathForExit(pathname, { singleLinkFile: true });
242
- }
243
- start() {
244
- this.#exists = true;
245
- }
246
- onIdentity = (identity) => {
247
- this.#identity = identity;
248
- this.#unregister.setIdentity(identity);
249
- };
247
+ export class SyncAtomicTempOwner extends AtomicTempOwner {
250
248
  adopt(temp) {
251
- this.#fd = temp.fd;
249
+ this.resource = temp.fd;
252
250
  this.onIdentity(temp.identity);
253
251
  }
254
- get identity() {
255
- if (!this.#identity)
256
- throw new Error("Atomic temp owner has no identity");
257
- return this.#identity;
258
- }
259
252
  assertCurrent(fsModule, pathname = this.pathname) {
260
253
  const opened = inspectFileIdentitySync(() => {
261
- const stat = fsModule.fstatSync(this.#fd, { bigint: true });
254
+ const stat = fsModule.fstatSync(this.resource, { bigint: true });
262
255
  assertOwnedFile(stat, pathname, false);
263
256
  return stat;
264
257
  }, this.identity);
@@ -270,7 +263,7 @@ export class SyncAtomicTempOwner {
270
263
  }, opened);
271
264
  }
272
265
  catch (error) {
273
- if (error.code === "ENOENT") {
266
+ if (hasErrorCode(error, "ENOENT")) {
274
267
  throw missingOwnedFile(pathname, error);
275
268
  }
276
269
  throw error;
@@ -282,7 +275,7 @@ export class SyncAtomicTempOwner {
282
275
  return;
283
276
  }
284
277
  catch (error) {
285
- if (!(error instanceof FsSafeError) || error.code !== "path-mismatch" || !expectedHash) {
278
+ if (!(error instanceof FsSafeError) || !hasErrorCode(error, "path-mismatch") || !expectedHash) {
286
279
  throw error;
287
280
  }
288
281
  }
@@ -292,7 +285,7 @@ export class SyncAtomicTempOwner {
292
285
  publishedFd = fsModule.openSync(pathname, PUBLISHED_READ_FLAGS);
293
286
  }
294
287
  catch (error) {
295
- if (error.code === "ELOOP") {
288
+ if (hasErrorCode(error, "ELOOP")) {
296
289
  throw new FsSafeError("symlink", `Atomic replace published file became a symlink: ${pathname}`, {
297
290
  cause: error,
298
291
  });
@@ -312,11 +305,10 @@ export class SyncAtomicTempOwner {
312
305
  if (sha256Hex(fsModule.readFileSync(publishedFd)) !== expectedHash) {
313
306
  throw new FsSafeError("path-mismatch", `Atomic replace published content changed: ${pathname}`);
314
307
  }
315
- const previousFd = this.#fd;
316
- this.#fd = undefined;
308
+ const previousFd = this.takeResource();
317
309
  fsModule.closeSync(previousFd);
318
- this.#fd = publishedFd;
319
- this.#identity = identity;
310
+ this.resource = publishedFd;
311
+ this.recordedIdentity = identity;
320
312
  publishedFd = undefined;
321
313
  }
322
314
  finally {
@@ -330,19 +322,15 @@ export class SyncAtomicTempOwner {
330
322
  }
331
323
  }
332
324
  }
333
- markRenamed() {
334
- this.#exists = false;
335
- this.#unregister();
336
- }
337
325
  finish(params) {
338
326
  let deferredFailure;
339
- let cleanupComplete = !this.#exists;
340
- if (this.#exists) {
327
+ let cleanupComplete = !this.exists;
328
+ if (this.exists) {
341
329
  try {
342
330
  cleanupComplete = cleanupOwnedPathSync({
343
331
  fsModule: params.fsModule,
344
332
  pathname: this.pathname,
345
- identity: this.#identity,
333
+ identity: this.recordedIdentity,
346
334
  originalFailure: params.originalFailure,
347
335
  throwOnCleanupError: params.throwOnCleanupError,
348
336
  });
@@ -352,21 +340,14 @@ export class SyncAtomicTempOwner {
352
340
  }
353
341
  }
354
342
  if (cleanupComplete)
355
- this.#unregister();
356
- if (this.#fd !== undefined) {
357
- const fd = this.#fd;
358
- this.#fd = undefined;
343
+ this.unregister();
344
+ const fd = this.takeResource();
345
+ if (fd !== undefined) {
359
346
  try {
360
347
  params.fsModule.closeSync(fd);
361
348
  }
362
349
  catch (closeError) {
363
- deferredFailure = {
364
- error: deferredFailure
365
- ? new AggregateError([deferredFailure.error, closeError], "Atomic temp cleanup and close failed")
366
- : params.originalFailure
367
- ? new AggregateError([params.originalFailure.error, closeError], "Atomic file replace and close failed")
368
- : closeError,
369
- };
350
+ deferredFailure = closeFailure(closeError, params, deferredFailure);
370
351
  }
371
352
  }
372
353
  if (deferredFailure)
@@ -56,4 +56,3 @@ export type ReplaceFileAtomicResult = {
56
56
  export declare function replaceFileAtomic(options: ReplaceFileAtomicOptions): Promise<ReplaceFileAtomicResult>;
57
57
  export declare function replaceFileAtomicWithDirectorySync(options: ReplaceFileAtomicOptions, syncParent?: (directoryPath: string) => Promise<unknown>): Promise<ReplaceFileAtomicResult>;
58
58
  export declare function replaceFileAtomicSync(options: ReplaceFileAtomicSyncOptions): ReplaceFileAtomicResult;
59
- //# sourceMappingURL=replace-file.d.ts.map
@@ -12,13 +12,7 @@ import { assertSafePathPrefix } from "./safe-path-segment.js";
12
12
  import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
13
13
  import { sleep, sleepSync } from "./timing.js";
14
14
  import { serializePathWrite } from "./write-queue.js";
15
- function isRetryableRenameError(error) {
16
- return error.code === "EBUSY";
17
- }
18
- function isPermissionRenameError(error) {
19
- const code = error.code;
20
- return code === "EPERM" || code === "EEXIST";
21
- }
15
+ import { hasErrorCode, readErrorCode } from "./file-cleanup.js";
22
16
  async function renameWithRetry(params) {
23
17
  for (let attempt = 0; attempt <= params.maxRetries; attempt++) {
24
18
  if (attempt > 0)
@@ -28,11 +22,12 @@ async function renameWithRetry(params) {
28
22
  return { method: "rename" };
29
23
  }
30
24
  catch (error) {
31
- if (isRetryableRenameError(error) && attempt < params.maxRetries) {
25
+ const code = readErrorCode(error);
26
+ if (code === "EBUSY" && attempt < params.maxRetries) {
32
27
  await sleep(params.baseDelayMs * 2 ** attempt);
33
28
  continue;
34
29
  }
35
- if (params.copyFallbackOnPermissionError && isPermissionRenameError(error)) {
30
+ if (params.copyFallbackOnPermissionError && (code === "EPERM" || code === "EEXIST")) {
36
31
  await copyFallbackReplace({
37
32
  fsModule: params.fsModule,
38
33
  src: params.src,
@@ -59,11 +54,12 @@ function renameWithRetrySync(params) {
59
54
  return { method: "rename" };
60
55
  }
61
56
  catch (error) {
62
- if (isRetryableRenameError(error) && attempt < params.maxRetries) {
57
+ const code = readErrorCode(error);
58
+ if (code === "EBUSY" && attempt < params.maxRetries) {
63
59
  sleepSync(params.baseDelayMs * 2 ** attempt);
64
60
  continue;
65
61
  }
66
- if (params.copyFallbackOnPermissionError && isPermissionRenameError(error)) {
62
+ if (params.copyFallbackOnPermissionError && (code === "EPERM" || code === "EEXIST")) {
67
63
  copyFallbackReplaceSync({
68
64
  fsModule: params.fsModule,
69
65
  src: params.src,
@@ -114,7 +110,7 @@ async function resolveMode(options, filePath) {
114
110
  stat = fsModule === fs ? syncFs.lstatSync(filePath) : await fsModule.lstat(filePath);
115
111
  }
116
112
  catch (error) {
117
- if (error.code === "ENOENT") {
113
+ if (hasErrorCode(error, "ENOENT")) {
118
114
  return defaultMode;
119
115
  }
120
116
  throw error;
@@ -132,7 +128,7 @@ function resolveModeSync(options, filePath) {
132
128
  stat = fsModule.lstatSync(filePath);
133
129
  }
134
130
  catch (error) {
135
- if (error.code !== "ENOENT") {
131
+ if (!hasErrorCode(error, "ENOENT")) {
136
132
  throw error;
137
133
  }
138
134
  }
@@ -23,4 +23,3 @@ export declare class RetainedDirectoryReplacement {
23
23
  close(): void;
24
24
  }
25
25
  export {};
26
- //# sourceMappingURL=retained-directory-replacement.d.ts.map
@@ -27,4 +27,3 @@ export declare function admitPathInsideRoot(params: {
27
27
  ino: bigint;
28
28
  }>) => void;
29
29
  }): AdmittedRootPath | undefined;
30
- //# sourceMappingURL=root-boundary.d.ts.map
@@ -41,4 +41,3 @@ export declare function resolvePathInRoot(root: RootContext, relativePath: strin
41
41
  rootWithSep: string;
42
42
  resolved: string;
43
43
  }>;
44
- //# sourceMappingURL=root-context.d.ts.map
@@ -14,4 +14,3 @@ export declare function rethrowCreateInputError(error: unknown): never;
14
14
  export declare function createInputOptions(data: RootWriteParams["data"], options: RootCreateOptions & RootCreateStreamOptions, defaultMaxBytes?: number): RootCreateOptions & RootCreateStreamOptions;
15
15
  export declare function rootWriteInput(params: RootWriteParams): PinnedWriteInput;
16
16
  export {};
17
- //# sourceMappingURL=root-create-input.d.ts.map
@@ -10,4 +10,3 @@ export declare function mkdirPathFallback(root: RootContext, resolved: {
10
10
  rootReal: string;
11
11
  resolved: string;
12
12
  }, assertBeforeMutation?: () => void, rejectSymlinks?: boolean, mutationAdmission?: PinnedWriteMutationAdmission, privateMode?: boolean): Promise<void>;
13
- //# sourceMappingURL=root-directory-creation.d.ts.map
@@ -36,4 +36,3 @@ export type RootDirectoryListingOptions = {
36
36
  admitEntry(): boolean;
37
37
  };
38
38
  export declare function openRootDirectoryListing(root: RootContext, directory: string, options: RootDirectoryListingOptions): Promise<RootDirectoryListing>;
39
- //# sourceMappingURL=root-directory-list.d.ts.map
@@ -2,7 +2,7 @@ import fsSync from "node:fs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { setImmediate as yieldToEventLoop } from "node:timers/promises";
5
- import { assertAsyncDirectoryGuard, assertDirectoryObservationGuardSync, assertDirectoryObservationSync, createAsyncDirectoryGuard, } from "./directory-guard.js";
5
+ import { assertAsyncDirectoryGuard, assertDirectoryObservationGuardSync, assertDirectoryObservationSync, createAsyncDirectoryGuard, extendDirectoryObservationGuard, } from "./directory-guard.js";
6
6
  import { FsSafeError } from "./errors.js";
7
7
  import { inspectNativeDirectoryObservation, isNativeDirectoryObservationGuard, } from "./native-directory-observation.js";
8
8
  import { isNotFoundPathError } from "./path.js";
@@ -73,7 +73,18 @@ export async function assertRootDirectoryObservationGuard(root, guard) {
73
73
  }
74
74
  return;
75
75
  }
76
- await assertRootIdentityCurrent(root);
76
+ if ("identity" in guard && typeof root.rootIdentity.dev === "bigint" &&
77
+ typeof root.rootIdentity.ino === "bigint") {
78
+ try {
79
+ assertDirectoryObservationSync(root.rootReal, root.rootIdentity);
80
+ }
81
+ catch (error) {
82
+ throw rootPathChangedError(error instanceof Error ? error : undefined);
83
+ }
84
+ }
85
+ else {
86
+ await assertRootIdentityCurrent(root);
87
+ }
77
88
  try {
78
89
  if ("identity" in guard)
79
90
  assertDirectoryObservationGuardSync(guard);
@@ -215,9 +226,15 @@ async function listGuardedDirectoryPath(root, guard, withFileTypes, receipt) {
215
226
  return entries;
216
227
  }
217
228
  export async function openRootDirectoryListing(root, directory, options) {
218
- const guard = await createRootDirectoryObservationGuard(root, directory).catch((error) => {
229
+ const admitted = await createRootDirectoryObservationGuard(root, directory).catch((error) => {
219
230
  throw normalizeDirectoryError(error);
220
231
  });
232
+ // Retain exact admission identities; metadata rechecks can use numeric Stats
233
+ // only when every identity component is losslessly representable.
234
+ const guard = extendDirectoryObservationGuard({
235
+ stat: admitted.stat,
236
+ identity: { dev: admitted.stat.dev, ino: admitted.stat.ino },
237
+ }, admitted.dir, admitted.realPath);
221
238
  const assertCurrent = async () => {
222
239
  options.signal?.throwIfAborted();
223
240
  await assertRootDirectoryObservationGuard(root, guard);
@@ -20,4 +20,3 @@ export declare function ensureDirectoryWithinRoot(params: {
20
20
  defaultDirName?: string;
21
21
  mode?: number;
22
22
  }): Promise<DirectoryResult>;
23
- //# sourceMappingURL=root-directory.d.ts.map
@@ -8,4 +8,3 @@ export type RootEntriesOptions = {
8
8
  symlinks?: SymlinkPolicy;
9
9
  };
10
10
  export declare function entriesInRoot(root: RootContext, relativePath: string, options: RootEntriesOptions): AsyncGenerator<DirEntry>;
11
- //# sourceMappingURL=root-entries.d.ts.map
@@ -10,4 +10,3 @@ export declare function normalizePinnedPathError(error: unknown, details?: FsSaf
10
10
  export declare function normalizeRemoveGuardError(error: unknown, details?: FsSafeErrorDetails): Error;
11
11
  export declare function normalizeRemovePathError(error: unknown, details?: FsSafeErrorDetails): Error;
12
12
  export declare function throwFsSafeReadError(error: unknown, label: string): never;
13
- //# sourceMappingURL=root-errors.d.ts.map
@@ -16,6 +16,5 @@ export type CanonicalRootObservation = {
16
16
  export declare function observeCanonicalRoot(ioFs: RootFileAdmissionFs, rootPath: string): CanonicalRootObservation;
17
17
  export declare function createRootFileFinalAdmission(ioFs: RootFileAdmissionFs, root: Extract<CanonicalRootObservation, {
18
18
  ok: true;
19
- }>, boundaryLabel: string): PinnedOpenSyncFinalAdmission;
19
+ }>, boundaryLabel: string, rejectHardlinks?: boolean): PinnedOpenSyncFinalAdmission;
20
20
  export {};
21
- //# sourceMappingURL=root-file-final-admission.d.ts.map
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import { FsSafeError } from "./errors.js";
3
3
  import { realpathSync } from "./realpath.js";
4
4
  import { admitPathInsideRoot } from "./root-boundary.js";
5
+ import { hardlinkedPathNotAllowedError } from "./root-errors.js";
5
6
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
7
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem } from "./windows-path-alias.js";
7
8
  function rootIdentityMismatch(cause) {
@@ -51,7 +52,7 @@ export function observeCanonicalRoot(ioFs, rootPath) {
51
52
  function resolveConsumedPath(ioFs, filePath) {
52
53
  return ioFs === fs ? realpathSync.native(filePath) : ioFs.realpathSync(filePath);
53
54
  }
54
- export function createRootFileFinalAdmission(ioFs, root, boundaryLabel) {
55
+ export function createRootFileFinalAdmission(ioFs, root, boundaryLabel, rejectHardlinks = true) {
55
56
  const inspectRoot = (candidateRootPath, expected) => {
56
57
  inspectCanonicalRoot(ioFs, candidateRootPath, expected);
57
58
  };
@@ -69,7 +70,9 @@ export function createRootFileFinalAdmission(ioFs, root, boundaryLabel) {
69
70
  throw new FsSafeError("outside-workspace", `resolved path escapes ${boundaryLabel}`);
70
71
  }
71
72
  try {
72
- inspectFileIdentitySync(() => ioFs.lstatSync(admitted.path, { bigint: true }), descriptorIdentity);
73
+ const current = inspectFileIdentitySync(() => ioFs.lstatSync(admitted.path, { bigint: true }), descriptorIdentity);
74
+ if (rejectHardlinks && current.isFile() && current.nlink > 1n)
75
+ throw hardlinkedPathNotAllowedError();
73
76
  }
74
77
  catch (error) {
75
78
  if (isExpectedPathError(error)) {