@openclaw/fs-safe 0.10.0 → 0.12.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 (302) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +1 -0
  3. package/README.md +39 -6
  4. package/dist/absolute-path.d.ts.map +1 -1
  5. package/dist/absolute-path.js +3 -2
  6. package/dist/advanced.d.ts +5 -1
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/advanced.js +5 -1
  9. package/dist/archive-durability.d.ts +6 -6
  10. package/dist/archive-durability.d.ts.map +1 -1
  11. package/dist/archive-durability.js +1 -1
  12. package/dist/archive-entry.d.ts.map +1 -1
  13. package/dist/archive-entry.js +8 -8
  14. package/dist/archive-gzip-tail.d.ts +1 -0
  15. package/dist/archive-gzip-tail.d.ts.map +1 -1
  16. package/dist/archive-gzip-tail.js +16 -9
  17. package/dist/archive-input.d.ts.map +1 -1
  18. package/dist/archive-input.js +4 -2
  19. package/dist/archive-merge.d.ts +5 -1
  20. package/dist/archive-merge.d.ts.map +1 -1
  21. package/dist/archive-merge.js +15 -12
  22. package/dist/archive-native.js +4 -4
  23. package/dist/archive-parser.wasm +0 -0
  24. package/dist/archive-read.d.ts.map +1 -1
  25. package/dist/archive-read.js +54 -42
  26. package/dist/archive-staging.d.ts +6 -3
  27. package/dist/archive-staging.d.ts.map +1 -1
  28. package/dist/archive-staging.js +42 -22
  29. package/dist/archive-tar-stream.d.ts.map +1 -1
  30. package/dist/archive-tar-stream.js +7 -6
  31. package/dist/archive-tar-wasm.d.ts.map +1 -1
  32. package/dist/archive-tar-wasm.js +16 -13
  33. package/dist/archive-zip-admission.d.ts +1 -1
  34. package/dist/archive-zip-admission.d.ts.map +1 -1
  35. package/dist/archive-zip-admission.js +48 -12
  36. package/dist/archive-zip-loader.d.ts +6 -0
  37. package/dist/archive-zip-loader.d.ts.map +1 -0
  38. package/dist/archive-zip-loader.js +38 -0
  39. package/dist/archive-zip-names.d.ts.map +1 -1
  40. package/dist/archive-zip-names.js +26 -9
  41. package/dist/archive-zip-preflight.d.ts +2 -3
  42. package/dist/archive-zip-preflight.d.ts.map +1 -1
  43. package/dist/archive-zip-preflight.js +2 -34
  44. package/dist/archive.d.ts.map +1 -1
  45. package/dist/archive.js +12 -10
  46. package/dist/bounded-read.d.ts +5 -0
  47. package/dist/bounded-read.d.ts.map +1 -1
  48. package/dist/bounded-read.js +18 -11
  49. package/dist/copy-file-input.d.ts +0 -1
  50. package/dist/copy-file-input.d.ts.map +1 -1
  51. package/dist/copy-file-input.js +4 -26
  52. package/dist/copy-tree-portable.d.ts.map +1 -1
  53. package/dist/copy-tree-portable.js +57 -26
  54. package/dist/copy.d.ts +1 -1
  55. package/dist/copy.d.ts.map +1 -1
  56. package/dist/copy.js +3 -1
  57. package/dist/device-path.d.ts.map +1 -1
  58. package/dist/device-path.js +5 -3
  59. package/dist/directory-durability.d.ts.map +1 -1
  60. package/dist/directory-durability.js +5 -4
  61. package/dist/directory-guard.d.ts +11 -1
  62. package/dist/directory-guard.d.ts.map +1 -1
  63. package/dist/directory-guard.js +53 -11
  64. package/dist/durability.d.ts +1 -1
  65. package/dist/durability.d.ts.map +1 -1
  66. package/dist/durability.js +1 -1
  67. package/dist/file-handle-transfer.d.ts +14 -0
  68. package/dist/file-handle-transfer.d.ts.map +1 -0
  69. package/dist/file-handle-transfer.js +64 -0
  70. package/dist/file-hash.d.ts +3 -0
  71. package/dist/file-hash.d.ts.map +1 -1
  72. package/dist/file-hash.js +99 -32
  73. package/dist/file-lock-sync.d.ts.map +1 -1
  74. package/dist/file-lock-sync.js +8 -4
  75. package/dist/file-store-boundary.d.ts.map +1 -1
  76. package/dist/file-store-boundary.js +7 -5
  77. package/dist/file-store-path.d.ts +3 -0
  78. package/dist/file-store-path.d.ts.map +1 -0
  79. package/dist/file-store-path.js +27 -0
  80. package/dist/file-store-prune.d.ts.map +1 -1
  81. package/dist/file-store-prune.js +15 -5
  82. package/dist/file-store-sync-write.d.ts.map +1 -1
  83. package/dist/file-store-sync-write.js +56 -44
  84. package/dist/file-store.d.ts.map +1 -1
  85. package/dist/file-store.js +2 -18
  86. package/dist/filename.d.ts +1 -0
  87. package/dist/filename.d.ts.map +1 -1
  88. package/dist/filename.js +32 -14
  89. package/dist/guarded-mkdir.d.ts +2 -0
  90. package/dist/guarded-mkdir.d.ts.map +1 -1
  91. package/dist/guarded-mkdir.js +52 -16
  92. package/dist/guest-dispatch-python.d.ts +2 -0
  93. package/dist/guest-dispatch-python.d.ts.map +1 -0
  94. package/dist/guest-dispatch-python.js +117 -0
  95. package/dist/guest-native-python.d.ts +4 -0
  96. package/dist/guest-native-python.d.ts.map +1 -0
  97. package/dist/guest-native-python.js +135 -0
  98. package/dist/guest.d.ts +9 -0
  99. package/dist/guest.d.ts.map +1 -0
  100. package/dist/guest.js +421 -0
  101. package/dist/index.d.ts +1 -1
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/install-path.d.ts +6 -0
  104. package/dist/install-path.d.ts.map +1 -1
  105. package/dist/install-path.js +16 -2
  106. package/dist/json-durable-queue-directory.js +3 -3
  107. package/dist/json-durable-queue-ownership.d.ts +2 -0
  108. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  109. package/dist/json-durable-queue-ownership.js +47 -3
  110. package/dist/json-durable-queue-read.d.ts +6 -0
  111. package/dist/json-durable-queue-read.d.ts.map +1 -0
  112. package/dist/json-durable-queue-read.js +59 -0
  113. package/dist/json-durable-queue.d.ts +1 -1
  114. package/dist/json-durable-queue.d.ts.map +1 -1
  115. package/dist/json-durable-queue.js +43 -84
  116. package/dist/json.d.ts.map +1 -1
  117. package/dist/json.js +2 -1
  118. package/dist/local-roots.d.ts.map +1 -1
  119. package/dist/local-roots.js +2 -1
  120. package/dist/move-path-stage.d.ts.map +1 -1
  121. package/dist/move-path-stage.js +2 -1
  122. package/dist/move-path.d.ts.map +1 -1
  123. package/dist/move-path.js +4 -3
  124. package/dist/mutation-authority.d.ts +1 -0
  125. package/dist/mutation-authority.d.ts.map +1 -1
  126. package/dist/mutation-authority.js +4 -4
  127. package/dist/native-binding.d.ts +14 -2
  128. package/dist/native-binding.d.ts.map +1 -1
  129. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  130. package/dist/native-pinned-write-windows.js +3 -2
  131. package/dist/native-pinned-write.d.ts.map +1 -1
  132. package/dist/native-pinned-write.js +2 -1
  133. package/dist/opened-realpath.d.ts.map +1 -1
  134. package/dist/opened-realpath.js +5 -4
  135. package/dist/output.d.ts +2 -0
  136. package/dist/output.d.ts.map +1 -1
  137. package/dist/output.js +2 -0
  138. package/dist/overwrite-file-handle.d.ts +8 -0
  139. package/dist/overwrite-file-handle.d.ts.map +1 -0
  140. package/dist/overwrite-file-handle.js +42 -0
  141. package/dist/path-case.d.ts +7 -0
  142. package/dist/path-case.d.ts.map +1 -0
  143. package/dist/path-case.js +136 -0
  144. package/dist/path-scope-lexical.d.ts +14 -0
  145. package/dist/path-scope-lexical.d.ts.map +1 -0
  146. package/dist/path-scope-lexical.js +27 -0
  147. package/dist/path.d.ts.map +1 -1
  148. package/dist/path.js +22 -3
  149. package/dist/permissions-windows.d.ts +1 -1
  150. package/dist/permissions-windows.d.ts.map +1 -1
  151. package/dist/permissions-windows.js +48 -6
  152. package/dist/pinned-open.d.ts.map +1 -1
  153. package/dist/pinned-open.js +3 -1
  154. package/dist/pinned-write.d.ts +2 -2
  155. package/dist/pinned-write.d.ts.map +1 -1
  156. package/dist/pinned-write.js +3 -1
  157. package/dist/private-temp-workspace.d.ts.map +1 -1
  158. package/dist/private-temp-workspace.js +6 -4
  159. package/dist/realpath.d.ts +4 -0
  160. package/dist/realpath.d.ts.map +1 -0
  161. package/dist/realpath.js +43 -0
  162. package/dist/recursive-mkdir-path.d.ts +3 -0
  163. package/dist/recursive-mkdir-path.d.ts.map +1 -0
  164. package/dist/recursive-mkdir-path.js +8 -0
  165. package/dist/replace-directory.d.ts.map +1 -1
  166. package/dist/replace-directory.js +2 -1
  167. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  168. package/dist/replace-file-copy-fallback.js +23 -31
  169. package/dist/replace-file-copy-source.d.ts.map +1 -1
  170. package/dist/replace-file-copy-source.js +7 -12
  171. package/dist/replace-file-mode.d.ts +3 -0
  172. package/dist/replace-file-mode.d.ts.map +1 -0
  173. package/dist/replace-file-mode.js +10 -0
  174. package/dist/replace-file.d.ts +1 -0
  175. package/dist/replace-file.d.ts.map +1 -1
  176. package/dist/replace-file.js +14 -8
  177. package/dist/root-boundary.d.ts +25 -0
  178. package/dist/root-boundary.d.ts.map +1 -0
  179. package/dist/root-boundary.js +177 -0
  180. package/dist/root-context.d.ts.map +1 -1
  181. package/dist/root-context.js +40 -13
  182. package/dist/root-create-input.d.ts +10 -0
  183. package/dist/root-create-input.d.ts.map +1 -0
  184. package/dist/root-create-input.js +80 -0
  185. package/dist/root-directory-list.d.ts +3 -1
  186. package/dist/root-directory-list.d.ts.map +1 -1
  187. package/dist/root-directory-list.js +31 -5
  188. package/dist/root-entries.d.ts +11 -0
  189. package/dist/root-entries.d.ts.map +1 -0
  190. package/dist/root-entries.js +61 -0
  191. package/dist/root-errors.d.ts +5 -5
  192. package/dist/root-errors.d.ts.map +1 -1
  193. package/dist/root-errors.js +13 -12
  194. package/dist/root-impl.d.ts +13 -3
  195. package/dist/root-impl.d.ts.map +1 -1
  196. package/dist/root-impl.js +93 -70
  197. package/dist/root-move-preflight.d.ts +8 -0
  198. package/dist/root-move-preflight.d.ts.map +1 -0
  199. package/dist/root-move-preflight.js +16 -0
  200. package/dist/root-options.d.ts +12 -1
  201. package/dist/root-options.d.ts.map +1 -1
  202. package/dist/root-path-existing.d.ts +2 -0
  203. package/dist/root-path-existing.d.ts.map +1 -1
  204. package/dist/root-path-existing.js +14 -5
  205. package/dist/root-path-symlink.d.ts.map +1 -1
  206. package/dist/root-path-symlink.js +3 -2
  207. package/dist/root-path.d.ts +2 -0
  208. package/dist/root-path.d.ts.map +1 -1
  209. package/dist/root-path.js +54 -26
  210. package/dist/root-paths.d.ts +2 -6
  211. package/dist/root-paths.d.ts.map +1 -1
  212. package/dist/root-paths.js +24 -32
  213. package/dist/root-remove.d.ts +5 -0
  214. package/dist/root-remove.d.ts.map +1 -0
  215. package/dist/root-remove.js +286 -0
  216. package/dist/root-symlink-policy.d.ts +2 -1
  217. package/dist/root-symlink-policy.d.ts.map +1 -1
  218. package/dist/root-symlink-policy.js +2 -2
  219. package/dist/root-walk.d.ts.map +1 -1
  220. package/dist/root-walk.js +2 -1
  221. package/dist/root-write-mode.d.ts +2 -0
  222. package/dist/root-write-mode.d.ts.map +1 -1
  223. package/dist/root-write-mode.js +21 -7
  224. package/dist/root-write-verification.d.ts.map +1 -1
  225. package/dist/root-write-verification.js +12 -3
  226. package/dist/root.d.ts +2 -1
  227. package/dist/root.d.ts.map +1 -1
  228. package/dist/safe-path-segment.d.ts.map +1 -1
  229. package/dist/safe-path-segment.js +3 -1
  230. package/dist/secret-file.d.ts.map +1 -1
  231. package/dist/secret-file.js +2 -1
  232. package/dist/secret-read-async.d.ts.map +1 -1
  233. package/dist/secret-read-async.js +2 -1
  234. package/dist/secure-file-windows.d.ts +10 -0
  235. package/dist/secure-file-windows.d.ts.map +1 -0
  236. package/dist/secure-file-windows.js +186 -0
  237. package/dist/secure-file.d.ts.map +1 -1
  238. package/dist/secure-file.js +27 -7
  239. package/dist/secure-temp-dir.d.ts.map +1 -1
  240. package/dist/secure-temp-dir.js +2 -1
  241. package/dist/sibling-staged-file.d.ts +2 -0
  242. package/dist/sibling-staged-file.d.ts.map +1 -1
  243. package/dist/sibling-staged-file.js +49 -8
  244. package/dist/sibling-temp.d.ts +2 -0
  245. package/dist/sibling-temp.d.ts.map +1 -1
  246. package/dist/sibling-temp.js +8 -5
  247. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  248. package/dist/sidecar-lock-acquire.js +16 -5
  249. package/dist/sidecar-lock-policy.d.ts +2 -0
  250. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  251. package/dist/sidecar-lock-policy.js +17 -0
  252. package/dist/sidecar-lock.d.ts.map +1 -1
  253. package/dist/sidecar-lock.js +2 -3
  254. package/dist/staged-directory.d.ts.map +1 -1
  255. package/dist/staged-directory.js +4 -3
  256. package/dist/temp-target.d.ts +14 -12
  257. package/dist/temp-target.d.ts.map +1 -1
  258. package/dist/temp-target.js +15 -7
  259. package/dist/trash.d.ts.map +1 -1
  260. package/dist/trash.js +7 -5
  261. package/dist/unicode-path.d.ts +3 -0
  262. package/dist/unicode-path.d.ts.map +1 -0
  263. package/dist/unicode-path.js +13 -0
  264. package/dist/walk.d.ts.map +1 -1
  265. package/dist/walk.js +14 -12
  266. package/dist/write-file-handle.d.ts +1 -0
  267. package/dist/write-file-handle.d.ts.map +1 -1
  268. package/dist/write-file-handle.js +3 -2
  269. package/docs/advanced.md +7 -1
  270. package/docs/archive.md +31 -5
  271. package/docs/atomic.md +17 -1
  272. package/docs/config.md +1 -0
  273. package/docs/contributing.md +33 -2
  274. package/docs/copy.md +75 -6
  275. package/docs/directory-identity.md +85 -0
  276. package/docs/durability.md +40 -3
  277. package/docs/entries.md +109 -0
  278. package/docs/errors.md +3 -3
  279. package/docs/file-store.md +21 -0
  280. package/docs/filename.md +9 -2
  281. package/docs/guest.md +146 -0
  282. package/docs/in-place-write.md +81 -0
  283. package/docs/index.md +2 -0
  284. package/docs/install-path.md +59 -13
  285. package/docs/install.md +34 -0
  286. package/docs/native-helper.md +14 -5
  287. package/docs/native.md +18 -2
  288. package/docs/output.md +32 -6
  289. package/docs/path-case.md +64 -0
  290. package/docs/path-scope.md +1 -1
  291. package/docs/path.md +1 -1
  292. package/docs/permissions.md +37 -3
  293. package/docs/public-api.md +36 -2
  294. package/docs/root.md +33 -3
  295. package/docs/secure-file.md +17 -14
  296. package/docs/security-model.md +1 -1
  297. package/docs/sidecar-lock.md +12 -3
  298. package/docs/store.md +22 -1
  299. package/docs/temp.md +39 -6
  300. package/docs/types.md +1 -1
  301. package/docs/writing.md +153 -3
  302. package/package.json +14 -8
@@ -1 +1 @@
1
- {"version":3,"file":"directory-durability.d.ts","sourceRoot":"","sources":["../src/directory-durability.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE7E,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,GAAG;IACvD,UAAU,EAAE,oBAAoB,GAAG;QAAE,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB,CAAC,EAAE,gBAAgB,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAgKF,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,CAa1B;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAmB/B;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,oBAAoB,CAuCtB;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAMvE;AAuBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA8ElC"}
1
+ {"version":3,"file":"directory-durability.d.ts","sourceRoot":"","sources":["../src/directory-durability.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG7E,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,GAAG;IACvD,UAAU,EAAE,oBAAoB,GAAG;QAAE,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB,CAAC,EAAE,gBAAgB,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,CAAC;AAgKF,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,CAa1B;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAmB/B;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,oBAAoB,CAuCtB;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAMvE;AAuBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA8ElC"}
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { ensureAbsoluteDirectory } from "./absolute-path.js";
5
5
  import { FsSafeError } from "./errors.js";
6
6
  import { sameFileIdentity } from "./file-identity.js";
7
+ import { realpathSync } from "./realpath.js";
7
8
  function directoryOpenFlags() {
8
9
  if (process.platform === "win32") {
9
10
  return "r";
@@ -47,7 +48,7 @@ async function createDirectoryReceipt(directoryPath, label) {
47
48
  assertDirectory(identity, resolvedPath, label);
48
49
  return {
49
50
  path: resolvedPath,
50
- realPath: fsSync.realpathSync.native(resolvedPath),
51
+ realPath: realpathSync.native(resolvedPath),
51
52
  identity,
52
53
  };
53
54
  }
@@ -57,7 +58,7 @@ function createDirectoryReceiptSync(directoryPath, label) {
57
58
  assertDirectory(identity, resolvedPath, label);
58
59
  return {
59
60
  path: resolvedPath,
60
- realPath: fsSync.realpathSync(resolvedPath),
61
+ realPath: realpathSync(resolvedPath),
61
62
  identity,
62
63
  };
63
64
  }
@@ -65,7 +66,7 @@ async function assertDirectoryReceiptCurrent(receipt, label) {
65
66
  const currentIdentity = fsSync.lstatSync(receipt.path);
66
67
  assertDirectory(currentIdentity, receipt.path, label);
67
68
  if (!sameFileIdentity(receipt.identity, currentIdentity) ||
68
- (fsSync.realpathSync.native(receipt.path)) !== receipt.realPath) {
69
+ (realpathSync.native(receipt.path)) !== receipt.realPath) {
69
70
  throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
70
71
  }
71
72
  }
@@ -73,7 +74,7 @@ function assertDirectoryReceiptCurrentSync(receipt, label) {
73
74
  const currentIdentity = fsSync.lstatSync(receipt.path);
74
75
  assertDirectory(currentIdentity, receipt.path, label);
75
76
  if (!sameFileIdentity(receipt.identity, currentIdentity) ||
76
- fsSync.realpathSync(receipt.path) !== receipt.realPath) {
77
+ realpathSync(receipt.path) !== receipt.realPath) {
77
78
  throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
78
79
  }
79
80
  }
@@ -10,6 +10,15 @@ export type SyncDirectoryGuard = {
10
10
  realPath: string;
11
11
  stat: Stats;
12
12
  };
13
+ export type DirectoryIdentity = Readonly<{
14
+ dev: bigint;
15
+ ino: bigint;
16
+ realPath: string;
17
+ }>;
18
+ export declare function readDirectoryIdentity(dir: string): Promise<DirectoryIdentity>;
19
+ export declare function assertDirectoryIdentitySync(observedPath: string, expected: Pick<DirectoryIdentity, "dev" | "ino"> & {
20
+ realPath?: string;
21
+ }): void;
13
22
  export declare function createAsyncDirectoryGuard(dir: string, options: {
14
23
  bigint: true;
15
24
  }): Promise<AsyncDirectoryGuard<BigIntStats>>;
@@ -21,11 +30,12 @@ export declare function createAsyncDirectoryGuard(dir: string, options: {
21
30
  }): Promise<AnyAsyncDirectoryGuard>;
22
31
  export declare function assertAsyncDirectoryGuard(guard: AnyAsyncDirectoryGuard): Promise<void>;
23
32
  export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
24
- export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard): void;
33
+ export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard | AnyAsyncDirectoryGuard): void;
25
34
  export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
26
35
  export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
27
36
  bigint: boolean;
28
37
  }): Promise<AnyAsyncDirectoryGuard>;
29
38
  export declare function createNearestExistingSyncDirectoryGuard(rootReal: string, targetPath: string): SyncDirectoryGuard;
30
39
  export declare function inspectDirectoryIdentity(dir: string, expected?: Pick<BigIntStats, "dev" | "ino">): Promise<BigIntStats>;
40
+ export declare function inspectDirectoryIdentitySync(dir: string, expected?: Pick<BigIntStats, "dev" | "ino">): BigIntStats;
31
41
  //# sourceMappingURL=directory-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AASlD,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,KAAK,GAAG,WAAW,GAAG,KAAK,IAAI;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;AAC7H,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACnH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAStH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5F;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAMxE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAQxE;AAED,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACxH,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAqBzJ,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAcpB;AAGD,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAM7H"}
1
+ {"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUlD,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,KAAK,GAAG,WAAW,GAAG,KAAK,IAAI;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAGnF;AAED,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE,IAAI,CAKN;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;AAC7H,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACnH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAStH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5F;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAMxE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,GAAG,sBAAsB,GAAG,IAAI,CAajG;AAED,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACxH,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAqBzJ,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAcpB;AAGD,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAE7H;AAmBD,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,GAC1C,WAAW,CAQb"}
@@ -2,24 +2,37 @@ import fsSync from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { sameFileIdentity } from "./file-identity.js";
5
- import { inspectFileIdentity } from "./strict-file-identity.js";
5
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
6
  import { isNotFoundPathError } from "./path.js";
7
7
  import { directoryComponentNotDirectoryError } from "./root-errors.js";
8
+ import { realpathSync } from "./realpath.js";
9
+ export async function readDirectoryIdentity(dir) {
10
+ const guard = await createAsyncDirectoryGuard(dir, { bigint: true });
11
+ return Object.freeze({ dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
12
+ }
13
+ export function assertDirectoryIdentitySync(observedPath, expected) {
14
+ inspectDirectoryIdentitySync(observedPath, expected);
15
+ if (expected.realPath !== undefined && realpathSync.native(observedPath) !== expected.realPath) {
16
+ throw new FsSafeError("path-mismatch", "directory changed during operation");
17
+ }
18
+ }
8
19
  export async function createAsyncDirectoryGuard(dir, options) {
9
- const stat = options?.bigint ? await inspectDirectoryIdentity(dir) : fsSync.lstatSync(dir);
20
+ const stat = options?.bigint ? inspectDirectoryIdentitySync(dir) : fsSync.lstatSync(dir);
10
21
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
11
22
  throw directoryComponentNotDirectoryError();
12
23
  }
13
- return { dir, realPath: fsSync.realpathSync.native(dir), stat };
24
+ return { dir, realPath: realpathSync.native(dir), stat };
14
25
  }
15
26
  export async function assertAsyncDirectoryGuard(guard) {
16
- const stat = typeof guard.stat.dev === "bigint" && typeof guard.stat.ino === "bigint"
17
- ? await inspectDirectoryIdentity(guard.dir, { dev: guard.stat.dev, ino: guard.stat.ino })
18
- : fsSync.lstatSync(guard.dir);
27
+ if (typeof guard.stat.dev === "bigint" && typeof guard.stat.ino === "bigint") {
28
+ assertDirectoryIdentitySync(guard.dir, { dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
29
+ return;
30
+ }
31
+ const stat = fsSync.lstatSync(guard.dir);
19
32
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
20
33
  throw directoryComponentNotDirectoryError();
21
34
  }
22
- if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync.native(guard.dir) !== guard.realPath) {
35
+ if (!sameFileIdentity(stat, guard.stat) || realpathSync.native(guard.dir) !== guard.realPath) {
23
36
  throw new FsSafeError("path-mismatch", "directory changed during operation");
24
37
  }
25
38
  }
@@ -28,14 +41,19 @@ export function createSyncDirectoryGuard(dir) {
28
41
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
29
42
  throw directoryComponentNotDirectoryError();
30
43
  }
31
- return { dir, realPath: fsSync.realpathSync(dir), stat };
44
+ return { dir, realPath: realpathSync(dir), stat };
32
45
  }
33
46
  export function assertSyncDirectoryGuard(guard) {
47
+ if (typeof guard.stat.dev === "bigint" && typeof guard.stat.ino === "bigint") {
48
+ assertDirectoryIdentitySync(guard.dir, { dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
49
+ return;
50
+ }
34
51
  const stat = fsSync.lstatSync(guard.dir);
35
52
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
36
53
  throw directoryComponentNotDirectoryError();
37
54
  }
38
- if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync(guard.dir) !== guard.realPath) {
55
+ const realPath = realpathSync(guard.dir);
56
+ if (!sameFileIdentity(stat, guard.stat) || realPath !== guard.realPath) {
39
57
  throw new FsSafeError("path-mismatch", "directory changed during operation");
40
58
  }
41
59
  }
@@ -73,8 +91,32 @@ export function createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
73
91
  }
74
92
  // Recovery receipts must retain every identity bit, including on Windows.
75
93
  export async function inspectDirectoryIdentity(dir, expected) {
76
- return await inspectFileIdentity(async () => {
77
- const stat = fsSync.lstatSync(dir, { bigint: true });
94
+ return inspectDirectoryIdentitySync(dir, expected);
95
+ }
96
+ function directoryEntryPath(dir) {
97
+ const windows = process.platform === "win32";
98
+ if (typeof dir === "string") {
99
+ const last = dir[dir.length - 1];
100
+ if (last !== "/" && (!windows || last !== "\\"))
101
+ return dir;
102
+ }
103
+ let rootLength = (windows ? path.win32 : path.posix).parse(dir).root.length;
104
+ if (windows && /^[\\/]{2}[?.][\\/]UNC[\\/]/i.test(dir)) {
105
+ // Node parses the namespace prefix as the root; retain the complete UNC share instead.
106
+ const uncRoot = path.win32.parse(`\\\\${dir.slice(8)}`).root;
107
+ if (uncRoot.length > 1)
108
+ rootLength = Math.max(rootLength, uncRoot.length + 6);
109
+ }
110
+ let end = dir.length;
111
+ while (end > rootLength && (dir[end - 1] === "/" || (windows && dir[end - 1] === "\\")))
112
+ end--;
113
+ return end === rootLength || end === dir.length ? dir : dir.slice(0, end);
114
+ }
115
+ export function inspectDirectoryIdentitySync(dir, expected) {
116
+ // A trailing separator makes lstat follow a final directory symlink.
117
+ const entryPath = directoryEntryPath(dir);
118
+ return inspectFileIdentitySync(() => {
119
+ const stat = fsSync.lstatSync(entryPath, { bigint: true });
78
120
  if (stat.isSymbolicLink() || !stat.isDirectory())
79
121
  throw directoryComponentNotDirectoryError();
80
122
  return stat;
@@ -1,4 +1,4 @@
1
1
  export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, type DirectoryReceipt, type DirectorySyncOutcome, type DurableDirectoryReceipt, type EnsureDurableDirectoryOptions, type PinnedDirectory, } from "./directory-durability.js";
2
2
  export { isHardlinkFallbackError, publishFileExclusive, type PublishFileExclusiveResult, type PublishFileExclusiveStrategy, type PublishFileExclusiveCleanup, type PublishFileExclusiveDirectorySyncFailure, type PublishFileExclusiveFailureDetails, type PublishFileExclusiveFailurePhase, type PublishFileExclusiveSyncFailurePolicy, } from "./publish-file.js";
3
- export { sha256File, type Sha256FileInput, type Sha256FileOptions, type Sha256FileResult, } from "./file-hash.js";
3
+ export { sha256File, sha256FileSync, type Sha256FileInput, type Sha256FileSyncInput, type Sha256FileOptions, type Sha256FileResult, } from "./file-hash.js";
4
4
  //# sourceMappingURL=durability.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,uBAAuB,EACvB,2BAA2B,EAC3B,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,eAAe,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,wCAAwC,EAC7C,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,qCAAqC,GAC3C,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,uBAAuB,EACvB,2BAA2B,EAC3B,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,eAAe,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,wCAAwC,EAC7C,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,qCAAqC,GAC3C,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC"}
@@ -1,3 +1,3 @@
1
1
  export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, } from "./directory-durability.js";
2
2
  export { isHardlinkFallbackError, publishFileExclusive, } from "./publish-file.js";
3
- export { sha256File, } from "./file-hash.js";
3
+ export { sha256File, sha256FileSync, } from "./file-hash.js";
@@ -0,0 +1,14 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ export type CopyFileHandleOptions = {
3
+ maxBytes?: number;
4
+ signal?: AbortSignal;
5
+ onChunk?: (chunk: Uint8Array) => void;
6
+ assertBeforeMutation?: () => void;
7
+ };
8
+ /** Copies caller-owned regular files from offset zero without moving either cursor. */
9
+ export declare function copyFileHandle(source: FileHandle, target: FileHandle, options?: CopyFileHandleOptions): Promise<number>;
10
+ export declare function transferFileHandle(source: FileHandle, target: FileHandle | number, options: CopyFileHandleOptions & {
11
+ sizeHint: number;
12
+ targetPosition?: number;
13
+ }): Promise<number>;
14
+ //# sourceMappingURL=file-handle-transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAEF,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAGD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,64 @@
1
+ import { normalizeMaxBytes } from "./byte-budget.js";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { assertSynchronousCallbackResult, composeMutationAssertions, rethrowMutationAuthorityError, } from "./mutation-authority.js";
4
+ import { writeAllToFile } from "./write-file-handle.js";
5
+ import { inspectFileIdentity } from "./strict-file-identity.js";
6
+ /** Copies caller-owned regular files from offset zero without moving either cursor. */
7
+ export async function copyFileHandle(source, target, options = {}) {
8
+ options.signal?.throwIfAborted();
9
+ const maxBytes = normalizeMaxBytes(options.maxBytes);
10
+ const sourceStat = await inspectFileIdentity(() => {
11
+ options.signal?.throwIfAborted();
12
+ return source.stat({ bigint: true });
13
+ });
14
+ options.signal?.throwIfAborted();
15
+ if (!sourceStat.isFile())
16
+ throw new FsSafeError("not-file", "copy source handle must be a regular file");
17
+ if (maxBytes !== undefined && Number.isFinite(maxBytes) && sourceStat.size > BigInt(maxBytes)) {
18
+ throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
19
+ }
20
+ const targetStat = await inspectFileIdentity(() => {
21
+ options.signal?.throwIfAborted();
22
+ return target.stat({ bigint: true });
23
+ });
24
+ options.signal?.throwIfAborted();
25
+ if (!targetStat.isFile())
26
+ throw new FsSafeError("not-file", "copy target handle must be a regular file");
27
+ if (sourceStat.dev === targetStat.dev && sourceStat.ino === targetStat.ino) {
28
+ throw new FsSafeError("path-alias", "copy source and target handles refer to the same file");
29
+ }
30
+ return await transferFileHandle(source, target, {
31
+ ...options,
32
+ maxBytes,
33
+ sizeHint: Number(sourceStat.size),
34
+ targetPosition: 0,
35
+ assertBeforeMutation: composeMutationAssertions(undefined, options.assertBeforeMutation),
36
+ }).catch(rethrowMutationAuthorityError);
37
+ }
38
+ // Root.copyIn supplies already-admitted identities and retains its owned target cursor.
39
+ export async function transferFileHandle(source, target, options) {
40
+ const maxBytes = options.maxBytes ?? Infinity;
41
+ const buffer = Buffer.allocUnsafe(Math.min(512 * 1024, Math.max(64 * 1024, options.sizeHint), maxBytes + 1));
42
+ let position = 0;
43
+ while (true) {
44
+ options.signal?.throwIfAborted();
45
+ const { bytesRead } = await source.read(buffer, 0, Math.min(buffer.length, maxBytes - position + 1), position);
46
+ options.signal?.throwIfAborted();
47
+ if (bytesRead > maxBytes - position) {
48
+ throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
49
+ }
50
+ if (bytesRead === 0)
51
+ return position;
52
+ const chunk = buffer.subarray(0, bytesRead);
53
+ if (options.onChunk)
54
+ assertSynchronousCallbackResult(options.onChunk(chunk), "onChunk");
55
+ await writeAllToFile(target, chunk, {
56
+ position: options.targetPosition === undefined ? undefined : options.targetPosition + position,
57
+ assertBeforeMutation: () => {
58
+ options.signal?.throwIfAborted();
59
+ options.assertBeforeMutation?.();
60
+ },
61
+ });
62
+ position += bytesRead;
63
+ }
64
+ }
@@ -1,6 +1,7 @@
1
1
  import type { FileHandle } from "node:fs/promises";
2
2
  import { type NativeBinding } from "./native.js";
3
3
  export type Sha256FileInput = string | FileHandle;
4
+ export type Sha256FileSyncInput = string | number;
4
5
  export type Sha256FileOptions = {
5
6
  maxBytes?: number;
6
7
  signal?: AbortSignal;
@@ -11,4 +12,6 @@ export type Sha256FileResult = {
11
12
  };
12
13
  export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions): Promise<Sha256FileResult>;
13
14
  export declare function sha256File(input: Sha256FileInput, options?: Sha256FileOptions): Promise<Sha256FileResult>;
15
+ /** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
16
+ export declare function sha256FileSync(input: Sha256FileSyncInput, options?: Sha256FileOptions): Sha256FileResult;
14
17
  //# sourceMappingURL=file-hash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAoD3B;AAoDD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B"}
1
+ {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAwD3B;AAyDD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAuDD,8FAA8F;AAC9F,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAMlB"}
package/dist/file-hash.js CHANGED
@@ -5,7 +5,7 @@ import { normalizeMaxBytes } from "./byte-budget.js";
5
5
  import { FsSafeError } from "./errors.js";
6
6
  import { getNativeBinding } from "./native.js";
7
7
  import { resolveReadOpenFlags } from "./read-open-flags.js";
8
- import { inspectFileIdentity } from "./strict-file-identity.js";
8
+ import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
9
9
  export async function hashFileHandle(handle, native = getNativeBinding(), { maxBytes = Infinity, signal } = {}) {
10
10
  signal?.throwIfAborted();
11
11
  const stat = fsSync.fstatSync(handle.fd);
@@ -38,7 +38,7 @@ export async function hashFileHandle(handle, native = getNativeBinding(), { maxB
38
38
  }
39
39
  }
40
40
  const hash = createHash("sha256");
41
- const buffer = Buffer.allocUnsafe(Math.min(256 * 1024, Math.max(64 * 1024, stat.size)));
41
+ let buffer = Buffer.allocUnsafe(Math.min(256 * 1024, Math.max(1, stat.size + 1), maxBytes + 1));
42
42
  let position = 0;
43
43
  while (true) {
44
44
  signal?.throwIfAborted();
@@ -53,48 +53,54 @@ export async function hashFileHandle(handle, native = getNativeBinding(), { maxB
53
53
  }
54
54
  hash.update(buffer.subarray(0, bytesRead));
55
55
  position += bytesRead;
56
+ // A full small buffer can mean growth or a virtual file with an unhelpful size.
57
+ if (bytesRead === buffer.length && buffer.length < 64 * 1024) {
58
+ buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1));
59
+ }
60
+ }
61
+ }
62
+ function hashPathIdentity(filePath, afterOpen = false) {
63
+ const stat = fsSync.lstatSync(filePath, { bigint: true });
64
+ if (afterOpen && (stat.isSymbolicLink() || !stat.isFile())) {
65
+ throw new FsSafeError("path-mismatch", "SHA-256 path changed while opening");
66
+ }
67
+ if (stat.isSymbolicLink()) {
68
+ throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link");
56
69
  }
70
+ if (!stat.isFile()) {
71
+ throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
72
+ }
73
+ return stat;
74
+ }
75
+ function hashDescriptorIdentity(fd) {
76
+ const stat = fsSync.fstatSync(fd, { bigint: true });
77
+ if (!stat.isFile()) {
78
+ throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
79
+ }
80
+ return stat;
81
+ }
82
+ function rethrowHashOpenError(error) {
83
+ if (error?.code === "ELOOP") {
84
+ throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link", {
85
+ cause: error,
86
+ });
87
+ }
88
+ throw error;
57
89
  }
58
90
  async function hashPath(filePath, options) {
59
- const before = await inspectFileIdentity(async () => {
60
- const stat = fsSync.lstatSync(filePath, { bigint: true });
61
- if (stat.isSymbolicLink()) {
62
- throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link");
63
- }
64
- if (!stat.isFile()) {
65
- throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
66
- }
67
- return stat;
68
- });
91
+ const before = await inspectFileIdentity(() => hashPathIdentity(filePath));
69
92
  let handle;
70
93
  try {
71
94
  options.signal?.throwIfAborted();
72
95
  handle = await fs.open(filePath, resolveReadOpenFlags());
73
96
  }
74
97
  catch (error) {
75
- if (error?.code === "ELOOP") {
76
- throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link", {
77
- cause: error,
78
- });
79
- }
80
- throw error;
98
+ rethrowHashOpenError(error);
81
99
  }
82
100
  try {
83
101
  options.signal?.throwIfAborted();
84
- const opened = await inspectFileIdentity(async () => {
85
- const stat = fsSync.fstatSync(handle.fd, { bigint: true });
86
- if (!stat.isFile()) {
87
- throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
88
- }
89
- return stat;
90
- }, before);
91
- await inspectFileIdentity(async () => {
92
- const stat = fsSync.lstatSync(filePath, { bigint: true });
93
- if (stat.isSymbolicLink() || !stat.isFile()) {
94
- throw new FsSafeError("path-mismatch", "SHA-256 path changed while opening");
95
- }
96
- return stat;
97
- }, opened);
102
+ const opened = await inspectFileIdentity(() => hashDescriptorIdentity(handle.fd), before);
103
+ await inspectFileIdentity(() => hashPathIdentity(filePath, true), opened);
98
104
  return await hashFileHandle(handle, getNativeBinding(), options);
99
105
  }
100
106
  finally {
@@ -108,3 +114,64 @@ export async function sha256File(input, options = {}) {
108
114
  ? await hashPath(input, normalized)
109
115
  : await hashFileHandle(input, getNativeBinding(), normalized);
110
116
  }
117
+ function hashDescriptorSync(fd, { maxBytes = Infinity, signal }) {
118
+ signal?.throwIfAborted();
119
+ const stat = fsSync.fstatSync(fd);
120
+ if (!stat.isFile()) {
121
+ throw new FsSafeError("not-file", "SHA-256 input is not a regular file");
122
+ }
123
+ if (stat.size > maxBytes) {
124
+ throw new FsSafeError("too-large", `SHA-256 input exceeds ${maxBytes} bytes`);
125
+ }
126
+ const hash = createHash("sha256");
127
+ let buffer = Buffer.allocUnsafe(Math.min(256 * 1024, Math.max(1, stat.size + 1), maxBytes + 1));
128
+ let position = 0;
129
+ while (true) {
130
+ signal?.throwIfAborted();
131
+ const length = Math.min(buffer.length, maxBytes - position + 1);
132
+ const bytesRead = fsSync.readSync(fd, buffer, 0, length, position);
133
+ signal?.throwIfAborted();
134
+ if (bytesRead > maxBytes - position) {
135
+ throw new FsSafeError("too-large", `SHA-256 input exceeds ${maxBytes} bytes`);
136
+ }
137
+ if (bytesRead === 0) {
138
+ return { bytes: position, digest: hash.digest("hex") };
139
+ }
140
+ hash.update(buffer.subarray(0, bytesRead));
141
+ position += bytesRead;
142
+ if (bytesRead === buffer.length && buffer.length < 64 * 1024) {
143
+ buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1));
144
+ }
145
+ }
146
+ }
147
+ function hashPathSync(filePath, options) {
148
+ const before = inspectFileIdentitySync(() => hashPathIdentity(filePath));
149
+ let fd;
150
+ try {
151
+ options.signal?.throwIfAborted();
152
+ fd = fsSync.openSync(filePath, resolveReadOpenFlags());
153
+ }
154
+ catch (error) {
155
+ rethrowHashOpenError(error);
156
+ }
157
+ try {
158
+ options.signal?.throwIfAborted();
159
+ const opened = inspectFileIdentitySync(() => hashDescriptorIdentity(fd), before);
160
+ inspectFileIdentitySync(() => hashPathIdentity(filePath, true), opened);
161
+ return hashDescriptorSync(fd, options);
162
+ }
163
+ finally {
164
+ try {
165
+ fsSync.closeSync(fd);
166
+ }
167
+ catch { /* Preserve the hashing outcome. */ }
168
+ }
169
+ }
170
+ /** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
171
+ export function sha256FileSync(input, options = {}) {
172
+ options.signal?.throwIfAborted();
173
+ const normalized = { maxBytes: normalizeMaxBytes(options.maxBytes), signal: options.signal };
174
+ return typeof input === "string"
175
+ ? hashPathSync(input, normalized)
176
+ : hashDescriptorSync(input, normalized);
177
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"file-lock-sync.d.ts","sourceRoot":"","sources":["../src/file-lock-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EASL,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AASnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,iBAAiB,EAAE,KAAK,CAAC;KAC1B,KAAK,OAAO,CAAC;IACd,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC3D,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,IAAI,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;AAoJF,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC5C,kBAAkB,CAwMpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,EAC7C,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAOH"}
1
+ {"version":3,"file":"file-lock-sync.d.ts","sourceRoot":"","sources":["../src/file-lock-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EASL,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AAWnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,iBAAiB,EAAE,KAAK,CAAC;KAC1B,KAAK,OAAO,CAAC;IACd,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC3D,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,IAAI,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;AAoJF,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC5C,kBAAkB,CA0MpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,EAC7C,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAOH"}
@@ -2,9 +2,11 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { readSidecarLockSnapshotSync, readSidecarLockRawSnapshotSync, parseSidecarLockSnapshot, relativeSidecarLockPath, removeSidecarLockIfUnchangedSync, serializeSidecarLockPayload, sidecarLockSnapshotMatches, } from "./sidecar-lock-reclaim.js";
5
- import { computeSidecarLockDelayMs, isTransientLockFileDenial, maxTransientLockDenials, sidecarLockPayloadCreatedAtMs, validateSidecarLockRetryOptions, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
5
+ import { computeSidecarLockDelayMs, isTransientLockFileDenial, maxTransientLockDenials, sidecarLockPayloadCreatedAtMs, validateSidecarLockStaleMs, validateSidecarLockCompromiseCheckIntervalMs, validateSidecarLockRetryOptions, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
6
6
  import { getFsSafeLockConfig } from "./lock-config.js";
7
7
  import { sleepSync } from "./timing.js";
8
+ import { realpathSync } from "./realpath.js";
9
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
8
10
  const SYNC_HELD_LOCKS_KEY = Symbol.for("fsSafe.syncSidecarLocks");
9
11
  const SYNC_CLEANUP_REGISTERED_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupRegistered");
10
12
  const SYNC_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupHandler");
@@ -91,11 +93,11 @@ function createSyncHeldLockHandle(held) {
91
93
  }
92
94
  function canonicalLockParentSync(parent) {
93
95
  // Match Root's async realpath, including Windows short-name expansion.
94
- return process.platform === "win32" ? fs.realpathSync.native(parent) : fs.realpathSync(parent);
96
+ return process.platform === "win32" ? realpathSync.native(parent) : realpathSync(parent);
95
97
  }
96
98
  function normalizeTargetPath(targetPath) {
97
99
  const resolved = path.resolve(targetPath);
98
- fs.mkdirSync(path.dirname(resolved), { recursive: true });
100
+ fs.mkdirSync(recursiveMkdirPath(path.dirname(resolved)), { recursive: true });
99
101
  try {
100
102
  return path.join(canonicalLockParentSync(path.dirname(resolved)), path.basename(resolved));
101
103
  }
@@ -140,6 +142,9 @@ export function acquireFileLockSync(targetPath, options) {
140
142
  const timeoutMs = options.timeoutMs ?? defaults.timeoutMs;
141
143
  validateSidecarLockRetryOptions(retry);
142
144
  validateSidecarLockTimeoutMs(timeoutMs);
145
+ const staleMs = options.staleMs ?? defaults.staleMs ?? 30_000;
146
+ validateSidecarLockStaleMs(staleMs);
147
+ validateSidecarLockCompromiseCheckIntervalMs(options.compromiseCheckIntervalMs);
143
148
  const normalizedTargetPath = normalizeTargetPath(targetPath);
144
149
  const lockPath = boundedLockPath(options.lockPath ?? `${normalizedTargetPath}.lock`, options.lockRoot);
145
150
  const heldLocks = getSyncHeldLocks();
@@ -153,7 +158,6 @@ export function acquireFileLockSync(targetPath, options) {
153
158
  }
154
159
  // Process defaults fill the same fields here as withLockDefaults() fills for
155
160
  // the asynchronous manager, so both acquirers honor configureFsSafeLocks().
156
- const staleMs = options.staleMs ?? defaults.staleMs ?? 30_000;
157
161
  const staleRecovery = options.staleRecovery ?? defaults.staleRecovery;
158
162
  const startedAt = Date.now();
159
163
  let attempt = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAsCD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
1
+ {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAK1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAsCD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
@@ -13,6 +13,8 @@ import { ensureTrailingSep } from "./root-context.js";
13
13
  import { RootHandle } from "./root-impl.js";
14
14
  import { prepareSecretFileWrite } from "./secret-file.js";
15
15
  import { resolveSecureTempRoot } from "./secure-temp-dir.js";
16
+ import { realpathSync } from "./realpath.js";
17
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
16
18
  function parentRelativePath(relativePath) {
17
19
  const parent = path.posix.dirname(relativePath);
18
20
  return parent === "." ? "" : parent;
@@ -27,7 +29,7 @@ export async function ensureParentInRoot(scopedRoot, relativePath, mode) {
27
29
  }
28
30
  export async function openWritableStoreRoot(params) {
29
31
  const maxBytes = normalizeMaxBytes(params.maxBytes);
30
- await fs.mkdir(params.rootDir, { recursive: true, mode: params.dirMode });
32
+ await fs.mkdir(recursiveMkdirPath(params.rootDir), { recursive: true, mode: params.dirMode });
31
33
  await fs.chmod(params.rootDir, params.dirMode).catch(() => undefined);
32
34
  return await root(params.rootDir, { hardlinks: "reject", maxBytes });
33
35
  }
@@ -149,12 +151,12 @@ export function ensureStoreDirectorySync(params) {
149
151
  if (isPathRelativeEscape(relative)) {
150
152
  throw new FsSafeError("outside-workspace", "file path escapes store root");
151
153
  }
152
- syncFs.mkdirSync(rootDir, { recursive: true, mode: params.mode });
154
+ syncFs.mkdirSync(recursiveMkdirPath(rootDir), { recursive: true, mode: params.mode });
153
155
  const rootStat = syncFs.lstatSync(rootDir);
154
156
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
155
157
  throw new FsSafeError("not-file", `${params.messagePrefix} root must be a directory: ${rootDir}`);
156
158
  }
157
- const rootReal = syncFs.realpathSync(rootDir);
159
+ const rootReal = realpathSync(rootDir);
158
160
  chmodDirectorySyncBestEffort(rootDir, params.mode);
159
161
  let current = rootDir;
160
162
  for (const segment of path.relative(rootDir, dir).split(path.sep).filter(Boolean)) {
@@ -172,8 +174,8 @@ export function ensureStoreDirectorySync(params) {
172
174
  syncFs.mkdirSync(current, { mode: params.mode });
173
175
  }
174
176
  const currentRootStat = syncFs.lstatSync(rootDir);
175
- const currentRootReal = syncFs.realpathSync(rootDir);
176
- const currentReal = syncFs.realpathSync(current);
177
+ const currentRootReal = realpathSync(rootDir);
178
+ const currentReal = realpathSync(current);
177
179
  if (currentRootStat.isSymbolicLink() ||
178
180
  !currentRootStat.isDirectory() ||
179
181
  !sameFileIdentity(rootStat, currentRootStat) ||
@@ -0,0 +1,3 @@
1
+ export declare function assertRelativePath(relativePath: string): string;
2
+ export declare function resolveStorePath(rootDir: string, relativePath: string): string;
3
+ //# sourceMappingURL=file-store-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store-path.d.ts","sourceRoot":"","sources":["../src/file-store-path.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAe/D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAS9E"}
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { isPathInside, splitSafeRelativePath } from "./path.js";
4
+ export function assertRelativePath(relativePath) {
5
+ const raw = relativePath.trim();
6
+ if (!raw || raw !== relativePath) {
7
+ throw new FsSafeError("invalid-path", "store key must be non-empty and unpadded");
8
+ }
9
+ const segments = splitSafeRelativePath(raw);
10
+ if (segments.length === 0 ||
11
+ segments.join("/") !== raw ||
12
+ raw.normalize("NFC") !== raw ||
13
+ segments.some((segment) => /[ .]$/u.test(segment))) {
14
+ throw new FsSafeError("invalid-path", "store key must use one canonical relative spelling");
15
+ }
16
+ return raw;
17
+ }
18
+ export function resolveStorePath(rootDir, relativePath) {
19
+ const key = assertRelativePath(relativePath);
20
+ const root = path.resolve(rootDir);
21
+ // The immutable key already passed segment validation; keep the containment check.
22
+ const target = path.resolve(root, key);
23
+ if (!isPathInside(root, target)) {
24
+ throw new FsSafeError("outside-workspace", "relative path escapes root");
25
+ }
26
+ return target;
27
+ }