@openclaw/fs-safe 0.14.0 → 0.16.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 (370) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +38 -10
  3. package/dist/advanced.d.ts +2 -1
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +2 -1
  6. package/dist/archive-kind.d.ts +0 -1
  7. package/dist/archive-kind.d.ts.map +1 -1
  8. package/dist/archive-kind.js +5 -17
  9. package/dist/archive-merge.d.ts.map +1 -1
  10. package/dist/archive-merge.js +113 -46
  11. package/dist/archive-parser.wasm +0 -0
  12. package/dist/archive-read.d.ts.map +1 -1
  13. package/dist/archive-read.js +10 -11
  14. package/dist/archive-tar-stream.d.ts +3 -0
  15. package/dist/archive-tar-stream.d.ts.map +1 -1
  16. package/dist/archive-tar-stream.js +56 -37
  17. package/dist/archive-tar-wasm.d.ts +16 -4
  18. package/dist/archive-tar-wasm.d.ts.map +1 -1
  19. package/dist/archive-tar-wasm.js +134 -34
  20. package/dist/archive-zip-directory.d.ts +4 -0
  21. package/dist/archive-zip-directory.d.ts.map +1 -1
  22. package/dist/archive-zip-directory.js +2 -0
  23. package/dist/archive-zip-entry.d.ts +6 -2
  24. package/dist/archive-zip-entry.d.ts.map +1 -1
  25. package/dist/archive-zip-entry.js +23 -8
  26. package/dist/archive-zip-integrity.d.ts.map +1 -1
  27. package/dist/archive-zip-integrity.js +3 -4
  28. package/dist/archive-zip-loader.d.ts.map +1 -1
  29. package/dist/archive-zip-loader.js +107 -31
  30. package/dist/archive-zip-names.d.ts +1 -0
  31. package/dist/archive-zip-names.d.ts.map +1 -1
  32. package/dist/archive-zip-names.js +6 -0
  33. package/dist/archive.d.ts.map +1 -1
  34. package/dist/archive.js +11 -11
  35. package/dist/bounded-read-stream.d.ts +0 -1
  36. package/dist/bounded-read-stream.d.ts.map +1 -1
  37. package/dist/bounded-read-stream.js +0 -6
  38. package/dist/clone-metadata.d.ts +1 -0
  39. package/dist/clone-metadata.d.ts.map +1 -1
  40. package/dist/clone-metadata.js +6 -2
  41. package/dist/copy-publication.d.ts +6 -0
  42. package/dist/copy-publication.d.ts.map +1 -1
  43. package/dist/copy-publication.js +3 -0
  44. package/dist/copy-tree-portable.d.ts.map +1 -1
  45. package/dist/copy-tree-portable.js +44 -24
  46. package/dist/copy.d.ts.map +1 -1
  47. package/dist/copy.js +29 -11
  48. package/dist/create-directory.d.ts +20 -0
  49. package/dist/create-directory.d.ts.map +1 -0
  50. package/dist/create-directory.js +130 -0
  51. package/dist/create-file-async.d.ts +7 -0
  52. package/dist/create-file-async.d.ts.map +1 -0
  53. package/dist/create-file-async.js +121 -0
  54. package/dist/create-file.d.ts +8 -0
  55. package/dist/create-file.d.ts.map +1 -0
  56. package/dist/create-file.js +190 -0
  57. package/dist/create-owned-file.d.ts +8 -0
  58. package/dist/create-owned-file.d.ts.map +1 -0
  59. package/dist/create-owned-file.js +16 -0
  60. package/dist/create.d.ts +4 -0
  61. package/dist/create.d.ts.map +1 -0
  62. package/dist/create.js +2 -0
  63. package/dist/creation-darwin.d.ts +7 -0
  64. package/dist/creation-darwin.d.ts.map +1 -0
  65. package/dist/creation-darwin.js +79 -0
  66. package/dist/creation-file-state.d.ts +19 -0
  67. package/dist/creation-file-state.d.ts.map +1 -0
  68. package/dist/creation-file-state.js +118 -0
  69. package/dist/creation-path.d.ts +21 -0
  70. package/dist/creation-path.d.ts.map +1 -0
  71. package/dist/creation-path.js +71 -0
  72. package/dist/creation-permissions.d.ts +19 -0
  73. package/dist/creation-permissions.d.ts.map +1 -0
  74. package/dist/creation-permissions.js +125 -0
  75. package/dist/directory-durability.d.ts +1 -1
  76. package/dist/directory-durability.d.ts.map +1 -1
  77. package/dist/directory-durability.js +22 -80
  78. package/dist/directory-guard.d.ts +3 -0
  79. package/dist/directory-guard.d.ts.map +1 -1
  80. package/dist/directory-mode-node.d.ts +2 -0
  81. package/dist/directory-mode-node.d.ts.map +1 -1
  82. package/dist/directory-mode-node.js +8 -0
  83. package/dist/directory-mode-owner.js +5 -5
  84. package/dist/directory-receipt.d.ts +24 -0
  85. package/dist/directory-receipt.d.ts.map +1 -0
  86. package/dist/directory-receipt.js +127 -0
  87. package/dist/file-cleanup.d.ts +19 -0
  88. package/dist/file-cleanup.d.ts.map +1 -0
  89. package/dist/file-cleanup.js +78 -0
  90. package/dist/file-handle-transfer.d.ts +2 -0
  91. package/dist/file-handle-transfer.d.ts.map +1 -1
  92. package/dist/file-handle-transfer.js +57 -2
  93. package/dist/file-identity.d.ts.map +1 -1
  94. package/dist/file-identity.js +18 -4
  95. package/dist/file-lock-sync-admission.d.ts +19 -0
  96. package/dist/file-lock-sync-admission.d.ts.map +1 -0
  97. package/dist/file-lock-sync-admission.js +93 -0
  98. package/dist/file-lock-sync-root-acquire.d.ts +4 -0
  99. package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
  100. package/dist/file-lock-sync-root-acquire.js +370 -0
  101. package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
  102. package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
  103. package/dist/file-lock-sync-root-arbitration.js +66 -0
  104. package/dist/file-lock-sync-root-held.d.ts +34 -0
  105. package/dist/file-lock-sync-root-held.d.ts.map +1 -0
  106. package/dist/file-lock-sync-root-held.js +393 -0
  107. package/dist/file-lock-sync-root-io.d.ts +44 -0
  108. package/dist/file-lock-sync-root-io.d.ts.map +1 -0
  109. package/dist/file-lock-sync-root-io.js +209 -0
  110. package/dist/file-lock-sync-root-mutation.d.ts +17 -0
  111. package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
  112. package/dist/file-lock-sync-root-mutation.js +277 -0
  113. package/dist/file-lock-sync-root-options.d.ts +20 -0
  114. package/dist/file-lock-sync-root-options.d.ts.map +1 -0
  115. package/dist/file-lock-sync-root-options.js +58 -0
  116. package/dist/file-lock-sync-root-registration.d.ts +2 -0
  117. package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
  118. package/dist/file-lock-sync-root-registration.js +90 -0
  119. package/dist/file-lock-sync-root.d.ts +36 -0
  120. package/dist/file-lock-sync-root.d.ts.map +1 -0
  121. package/dist/file-lock-sync-root.js +361 -0
  122. package/dist/file-lock-sync-stale-admission.d.ts +24 -0
  123. package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
  124. package/dist/file-lock-sync-stale-admission.js +205 -0
  125. package/dist/file-lock-sync.d.ts.map +1 -1
  126. package/dist/file-lock-sync.js +245 -205
  127. package/dist/file-observation.d.ts +1 -1
  128. package/dist/file-observation.d.ts.map +1 -1
  129. package/dist/file-store-boundary.d.ts +2 -6
  130. package/dist/file-store-boundary.d.ts.map +1 -1
  131. package/dist/file-store-boundary.js +3 -9
  132. package/dist/file-store-prune.d.ts.map +1 -1
  133. package/dist/file-store-prune.js +5 -1
  134. package/dist/file-store-sync-write.d.ts.map +1 -1
  135. package/dist/file-store-sync-write.js +5 -8
  136. package/dist/file-store.d.ts.map +1 -1
  137. package/dist/file-store.js +47 -12
  138. package/dist/guarded-mkdir.d.ts +1 -0
  139. package/dist/guarded-mkdir.d.ts.map +1 -1
  140. package/dist/guarded-mkdir.js +36 -7
  141. package/dist/json-document-store.d.ts.map +1 -1
  142. package/dist/json-document-store.js +22 -15
  143. package/dist/json-durable-queue-ownership.d.ts +0 -1
  144. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  145. package/dist/json-durable-queue-ownership.js +0 -6
  146. package/dist/move-path.js +1 -1
  147. package/dist/native-binding.d.ts +13 -1
  148. package/dist/native-binding.d.ts.map +1 -1
  149. package/dist/native-fallback-warning.d.ts +4 -0
  150. package/dist/native-fallback-warning.d.ts.map +1 -0
  151. package/dist/native-fallback-warning.js +11 -0
  152. package/dist/native-operations.d.ts +0 -2
  153. package/dist/native-operations.d.ts.map +1 -1
  154. package/dist/native-operations.js +0 -24
  155. package/dist/native-parent-admission.d.ts +5 -2
  156. package/dist/native-parent-admission.d.ts.map +1 -1
  157. package/dist/native-parent-admission.js +27 -7
  158. package/dist/native-pinned-write-windows.d.ts +1 -1
  159. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  160. package/dist/native-pinned-write-windows.js +174 -29
  161. package/dist/native-pinned-write.d.ts.map +1 -1
  162. package/dist/native-pinned-write.js +26 -14
  163. package/dist/native-policy-parent-windows.d.ts +14 -0
  164. package/dist/native-policy-parent-windows.d.ts.map +1 -0
  165. package/dist/native-policy-parent-windows.js +209 -0
  166. package/dist/native-rename-outcome.d.ts +4 -0
  167. package/dist/native-rename-outcome.d.ts.map +1 -0
  168. package/dist/native-rename-outcome.js +8 -0
  169. package/dist/native-staged-file.d.ts +3 -2
  170. package/dist/native-staged-file.d.ts.map +1 -1
  171. package/dist/native-staged-file.js +121 -72
  172. package/dist/output.d.ts.map +1 -1
  173. package/dist/output.js +12 -8
  174. package/dist/owner-dacl.d.ts.map +1 -1
  175. package/dist/owner-dacl.js +10 -4
  176. package/dist/path-prefix.d.ts.map +1 -1
  177. package/dist/path-prefix.js +30 -8
  178. package/dist/path-suffix-aliases.d.ts +2 -0
  179. package/dist/path-suffix-aliases.d.ts.map +1 -1
  180. package/dist/path-suffix-aliases.js +25 -17
  181. package/dist/permission-exec.d.ts +2 -0
  182. package/dist/permission-exec.d.ts.map +1 -1
  183. package/dist/permission-exec.js +150 -21
  184. package/dist/permissions-windows.js +1 -1
  185. package/dist/pinned-mutation-admission.d.ts.map +1 -1
  186. package/dist/pinned-mutation-admission.js +10 -5
  187. package/dist/pinned-mutation-observation.d.ts +0 -1
  188. package/dist/pinned-mutation-observation.d.ts.map +1 -1
  189. package/dist/pinned-mutation-observation.js +0 -19
  190. package/dist/pinned-mutation-shared-route.d.ts +1 -0
  191. package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
  192. package/dist/pinned-mutation-shared-route.js +1 -1
  193. package/dist/pinned-write-input.d.ts +4 -0
  194. package/dist/pinned-write-input.d.ts.map +1 -0
  195. package/dist/pinned-write-input.js +25 -0
  196. package/dist/pinned-write-mode.d.ts +5 -0
  197. package/dist/pinned-write-mode.d.ts.map +1 -0
  198. package/dist/pinned-write-mode.js +24 -0
  199. package/dist/pinned-write-staged.d.ts +6 -0
  200. package/dist/pinned-write-staged.d.ts.map +1 -0
  201. package/dist/pinned-write-staged.js +187 -0
  202. package/dist/pinned-write-types.d.ts +5 -0
  203. package/dist/pinned-write-types.d.ts.map +1 -1
  204. package/dist/pinned-write.d.ts.map +1 -1
  205. package/dist/pinned-write.js +35 -145
  206. package/dist/private-directory.d.ts.map +1 -1
  207. package/dist/private-directory.js +18 -4
  208. package/dist/private-producer-handoff-sync.d.ts +14 -0
  209. package/dist/private-producer-handoff-sync.d.ts.map +1 -0
  210. package/dist/private-producer-handoff-sync.js +114 -0
  211. package/dist/private-producer-handoff.d.ts +22 -4
  212. package/dist/private-producer-handoff.d.ts.map +1 -1
  213. package/dist/private-producer-handoff.js +140 -77
  214. package/dist/private-temp-workspace.d.ts.map +1 -1
  215. package/dist/private-temp-workspace.js +75 -121
  216. package/dist/publish-copy-stage.d.ts +2 -1
  217. package/dist/publish-copy-stage.d.ts.map +1 -1
  218. package/dist/publish-copy-stage.js +16 -7
  219. package/dist/publish-file.d.ts.map +1 -1
  220. package/dist/publish-file.js +2 -2
  221. package/dist/regular-file.d.ts.map +1 -1
  222. package/dist/regular-file.js +1 -15
  223. package/dist/replace-directory.d.ts.map +1 -1
  224. package/dist/replace-directory.js +256 -18
  225. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  226. package/dist/replace-file-copy-fallback.js +62 -70
  227. package/dist/replace-file-copy-source.d.ts.map +1 -1
  228. package/dist/replace-file-copy-source.js +10 -12
  229. package/dist/replace-file-temp-owner.d.ts +5 -9
  230. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  231. package/dist/replace-file-temp-owner.js +56 -72
  232. package/dist/replace-file.js +6 -6
  233. package/dist/retained-directory-replacement.d.ts +26 -0
  234. package/dist/retained-directory-replacement.d.ts.map +1 -0
  235. package/dist/retained-directory-replacement.js +193 -0
  236. package/dist/root-boundary.d.ts +1 -0
  237. package/dist/root-boundary.d.ts.map +1 -1
  238. package/dist/root-boundary.js +4 -0
  239. package/dist/root-context.d.ts +0 -8
  240. package/dist/root-context.d.ts.map +1 -1
  241. package/dist/root-context.js +0 -3
  242. package/dist/root-create-input.d.ts +8 -1
  243. package/dist/root-create-input.d.ts.map +1 -1
  244. package/dist/root-create-input.js +17 -4
  245. package/dist/root-directory-creation.d.ts +3 -3
  246. package/dist/root-directory-creation.d.ts.map +1 -1
  247. package/dist/root-directory-creation.js +15 -3
  248. package/dist/root-directory-list.d.ts +1 -0
  249. package/dist/root-directory-list.d.ts.map +1 -1
  250. package/dist/root-directory-list.js +1 -0
  251. package/dist/root-impl.d.ts.map +1 -1
  252. package/dist/root-impl.js +46 -17
  253. package/dist/root-move-noreplace.d.ts.map +1 -1
  254. package/dist/root-move-noreplace.js +24 -15
  255. package/dist/root-options.d.ts +12 -4
  256. package/dist/root-options.d.ts.map +1 -1
  257. package/dist/root-path-errors.d.ts +1 -0
  258. package/dist/root-path-errors.d.ts.map +1 -1
  259. package/dist/root-path-errors.js +11 -2
  260. package/dist/root-path-existing.d.ts.map +1 -1
  261. package/dist/root-path-existing.js +11 -35
  262. package/dist/root-path-stat.d.ts.map +1 -1
  263. package/dist/root-path-stat.js +59 -7
  264. package/dist/root-path.js +1 -13
  265. package/dist/root-remove.d.ts +1 -0
  266. package/dist/root-remove.d.ts.map +1 -1
  267. package/dist/root-remove.js +4 -0
  268. package/dist/root-walk.d.ts +1 -1
  269. package/dist/root-walk.d.ts.map +1 -1
  270. package/dist/root-walk.js +17 -2
  271. package/dist/root-write-admission.d.ts +0 -2
  272. package/dist/root-write-admission.d.ts.map +1 -1
  273. package/dist/root-write-admission.js +1 -15
  274. package/dist/root-write-complete-parent.d.ts.map +1 -1
  275. package/dist/root-write-complete-parent.js +7 -23
  276. package/dist/root-write-publication.js +1 -1
  277. package/dist/root-write-verification.d.ts.map +1 -1
  278. package/dist/root-write-verification.js +29 -42
  279. package/dist/secret-file.d.ts.map +1 -1
  280. package/dist/secret-file.js +3 -24
  281. package/dist/secret-read-async.d.ts.map +1 -1
  282. package/dist/secret-read-async.js +3 -24
  283. package/dist/secret-read-policy.d.ts +6 -2
  284. package/dist/secret-read-policy.d.ts.map +1 -1
  285. package/dist/secret-read-policy.js +26 -2
  286. package/dist/secure-file-windows.d.ts +6 -0
  287. package/dist/secure-file-windows.d.ts.map +1 -1
  288. package/dist/secure-file-windows.js +34 -117
  289. package/dist/secure-file.js +2 -2
  290. package/dist/sibling-temp.d.ts.map +1 -1
  291. package/dist/sibling-temp.js +23 -12
  292. package/dist/sidecar-lock-acquire.d.ts +2 -28
  293. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  294. package/dist/sidecar-lock-acquire.js +288 -199
  295. package/dist/sidecar-lock-admission-context.d.ts +19 -0
  296. package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
  297. package/dist/sidecar-lock-admission-context.js +60 -0
  298. package/dist/sidecar-lock-admission-parser.d.ts +43 -0
  299. package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
  300. package/dist/sidecar-lock-admission-parser.js +113 -0
  301. package/dist/sidecar-lock-admission.d.ts +35 -0
  302. package/dist/sidecar-lock-admission.d.ts.map +1 -0
  303. package/dist/sidecar-lock-admission.js +7 -0
  304. package/dist/sidecar-lock-reclaim.d.ts +9 -4
  305. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  306. package/dist/sidecar-lock-reclaim.js +80 -25
  307. package/dist/sidecar-lock-root.d.ts.map +1 -1
  308. package/dist/sidecar-lock-root.js +2 -1
  309. package/dist/sidecar-lock-stale-admission.d.ts +39 -0
  310. package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
  311. package/dist/sidecar-lock-stale-admission.js +232 -0
  312. package/dist/sidecar-lock-target.d.ts +8 -0
  313. package/dist/sidecar-lock-target.d.ts.map +1 -0
  314. package/dist/sidecar-lock-target.js +55 -0
  315. package/dist/sidecar-lock.d.ts.map +1 -1
  316. package/dist/sidecar-lock.js +100 -16
  317. package/dist/staged-directory.d.ts.map +1 -1
  318. package/dist/staged-directory.js +6 -6
  319. package/dist/staged-file-settlement.d.ts +17 -0
  320. package/dist/staged-file-settlement.d.ts.map +1 -0
  321. package/dist/staged-file-settlement.js +57 -0
  322. package/dist/temp-workspace-descriptor.d.ts.map +1 -1
  323. package/dist/temp-workspace-descriptor.js +9 -27
  324. package/dist/temp-workspace-owner.d.ts.map +1 -1
  325. package/dist/temp-workspace-owner.js +8 -8
  326. package/dist/walk.d.ts +5 -1
  327. package/dist/walk.d.ts.map +1 -1
  328. package/dist/walk.js +19 -6
  329. package/dist/windows-owner.d.ts.map +1 -1
  330. package/dist/windows-owner.js +4 -3
  331. package/dist/windows-security-bridge.cs +336 -0
  332. package/dist/windows-security-bridge.ps1 +15 -0
  333. package/dist/windows-security-command.d.ts +26 -0
  334. package/dist/windows-security-command.d.ts.map +1 -0
  335. package/dist/windows-security-command.js +363 -0
  336. package/dist/windows-security-facts.d.ts +6 -0
  337. package/dist/windows-security-facts.d.ts.map +1 -0
  338. package/dist/windows-security-facts.js +108 -0
  339. package/docs/advanced.md +4 -2
  340. package/docs/archive.md +97 -46
  341. package/docs/atomic.md +85 -8
  342. package/docs/config.md +6 -2
  343. package/docs/contributing.md +44 -4
  344. package/docs/copy.md +37 -0
  345. package/docs/creation.md +128 -0
  346. package/docs/durability.md +24 -0
  347. package/docs/file-store.md +19 -0
  348. package/docs/install.md +31 -7
  349. package/docs/json-store.md +5 -0
  350. package/docs/migrating-to-0.5.md +15 -6
  351. package/docs/migrating-to-0.6.md +9 -4
  352. package/docs/native-helper.md +32 -12
  353. package/docs/native.md +38 -7
  354. package/docs/output.md +6 -0
  355. package/docs/path-prefix.md +10 -0
  356. package/docs/path-suffix-aliases.md +51 -6
  357. package/docs/permissions.md +50 -14
  358. package/docs/public-api.md +3 -2
  359. package/docs/root.md +56 -3
  360. package/docs/secret-file.md +11 -2
  361. package/docs/secure-file.md +9 -4
  362. package/docs/sidecar-lock.md +114 -8
  363. package/docs/staged-file.md +12 -3
  364. package/docs/temp.md +20 -3
  365. package/docs/walk.md +67 -1
  366. package/docs/writing.md +76 -6
  367. package/package.json +19 -16
  368. package/dist/darwin-acl.d.ts +0 -4
  369. package/dist/darwin-acl.d.ts.map +0 -1
  370. package/dist/darwin-acl.js +0 -24
@@ -0,0 +1,209 @@
1
+ import fsSync, {} from "node:fs";
2
+ import path from "node:path";
3
+ import { assertDirectoryIdentitySync, inspectDirectoryIdentitySync, } from "./directory-guard.js";
4
+ import { FsSafeError } from "./errors.js";
5
+ import { openNativeParentAdmission } from "./native-parent-admission.js";
6
+ import { captureNativeFdClose } from "./native-binding.js";
7
+ import { inspectNativeDirectoryObservation } from "./native-directory-observation.js";
8
+ import { createPathSegmentRoute, joinPathSegmentRoute } from "./path-segment-route.js";
9
+ import { isSymlinkOpenError } from "./path.js";
10
+ import { checkedMutationDirectory } from "./pinned-mutation-observation.js";
11
+ import { canReuseParentWithMutationAssertion } from "./root-write-lock-binding.js";
12
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
13
+ import { createSuppressedError } from "./suppressed-error.js";
14
+ function assertParentCurrent(parent) {
15
+ const current = inspectFileIdentitySync(() => fsSync.fstatSync(parent.fd, { bigint: true }), parent.guard.stat);
16
+ assertDirectoryIdentitySync(parent.guard.dir, {
17
+ dev: parent.guard.stat.dev,
18
+ ino: parent.guard.stat.ino,
19
+ realPath: parent.guard.realPath,
20
+ });
21
+ return current;
22
+ }
23
+ function closeAfterFailure(closeFd, fd, failure, report = false) {
24
+ if (fd === undefined)
25
+ return;
26
+ try {
27
+ closeFd(fd);
28
+ }
29
+ catch (error) {
30
+ if (report)
31
+ throw failure
32
+ ? createSuppressedError(error, failure.error, "native parent admission and close failed")
33
+ : error;
34
+ }
35
+ }
36
+ export async function capturePolicyAwareWindowsParent(binding, params, rootAdmission) {
37
+ const closeFd = captureNativeFdClose(binding);
38
+ let session;
39
+ function parentObservation(fd, guard) {
40
+ return checkedMutationDirectory(guard.dir, guard.realPath, guard.stat, typeof binding.observeDirectory === "function" ? () => {
41
+ const stat = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), guard.stat);
42
+ let observed;
43
+ try {
44
+ observed = inspectNativeDirectoryObservation(binding, guard.dir, stat);
45
+ }
46
+ catch (error) {
47
+ if (error?.code === "OBSERVATION_UNAVAILABLE")
48
+ return undefined;
49
+ throw error;
50
+ }
51
+ return { canonicalPath: observed.realPath,
52
+ identity: { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink } };
53
+ } : undefined);
54
+ }
55
+ async function openParent(fd, parentPath, relativePath) {
56
+ try {
57
+ const admitted = await openNativeParentAdmission(binding, {
58
+ ...rootAdmission,
59
+ root: { fd },
60
+ rootPath: parentPath,
61
+ // Policy fences retain every identity bit even with a legacy numeric root.
62
+ exactRoot: true,
63
+ }, relativePath, "native-directory");
64
+ const guard = { ...admitted.guard, stat: admitted.guard.stat };
65
+ return { fd: admitted.fd, guard, observation: parentObservation(admitted.fd, guard) };
66
+ }
67
+ catch (error) {
68
+ if (!isSymlinkOpenError(error))
69
+ throw error;
70
+ throw new FsSafeError(params.mutationAdmission.rejectParentSymlinks ? "symlink" : "path-mismatch", "native write parent changed during policy admission", { cause: error instanceof Error ? error : undefined });
71
+ }
72
+ }
73
+ async function authorize(targetPath, mutationPath = targetPath, phase = "parent") {
74
+ const request = Object.freeze({ targetPath, mutationPath, phase });
75
+ return await (session ? session.authorize(request) : params.mutationAdmission.authorize(request));
76
+ }
77
+ // Existing complete parents need one open and one target admission. Exact
78
+ // parent denies apply only to creation, never to merely using a directory.
79
+ let complete;
80
+ try {
81
+ complete = await openParent(rootAdmission.root.fd, params.rootPath, params.relativeParentPath);
82
+ }
83
+ catch (error) {
84
+ if (error?.code !== "ENOENT" || !params.mkdir)
85
+ throw error;
86
+ }
87
+ if (complete) {
88
+ try {
89
+ await authorize(path.join(complete.guard.realPath, params.basename));
90
+ assertParentCurrent(complete);
91
+ return complete;
92
+ }
93
+ catch (error) {
94
+ closeAfterFailure(closeFd, complete.fd, { error }, rootAdmission.reportCloseErrors);
95
+ throw error;
96
+ }
97
+ }
98
+ const segments = params.relativeParentPath.split("/").filter(Boolean);
99
+ const route = createPathSegmentRoute(segments);
100
+ const rootStat = inspectDirectoryIdentitySync(params.rootPath, inspectFileIdentitySync(() => fsSync.fstatSync(rootAdmission.root.fd, { bigint: true })));
101
+ let current = {
102
+ fd: rootAdmission.root.fd,
103
+ guard: { dir: params.rootPath, realPath: params.rootPath, stat: rootStat },
104
+ observation: parentObservation(rootAdmission.root.fd, {
105
+ dir: params.rootPath, realPath: params.rootPath, stat: rootStat,
106
+ }),
107
+ };
108
+ let ownedFd;
109
+ let failure;
110
+ const initialTarget = joinPathSegmentRoute(params.rootPath, route, 0, params.basename);
111
+ if (canReuseParentWithMutationAssertion(params.assertBeforeMutation, params.rootPath, initialTarget)) {
112
+ session = params.mutationAdmission?.beginNativeParentWalk?.() ??
113
+ params.mutationAdmission?.beginSharedParentWalk?.();
114
+ if (session && session.retainedTargetPath !== initialTarget) {
115
+ session.dispose();
116
+ session = undefined;
117
+ }
118
+ }
119
+ try {
120
+ for (let index = 0; index < segments.length; index += 1) {
121
+ const segment = segments[index];
122
+ const parentPath = current.guard.realPath;
123
+ let child;
124
+ let createdByMkdir = false;
125
+ let createReceipt;
126
+ try {
127
+ child = await openParent(current.fd, parentPath, segment);
128
+ }
129
+ catch (error) {
130
+ if (error?.code !== "ENOENT")
131
+ throw error;
132
+ // The request admits both the prospective leaf and this missing direct
133
+ // child. A deeper denial can leave already admitted ancestors intact.
134
+ const targetPath = session?.retainedTargetPath ??
135
+ joinPathSegmentRoute(parentPath, route, index, params.basename);
136
+ const mutationPath = path.join(parentPath, segment);
137
+ const request = Object.freeze({ targetPath, mutationPath, phase: "parent-create" });
138
+ createReceipt = session?.tryAuthorizeAtParent(request, current.observation);
139
+ if (!createReceipt) {
140
+ createReceipt = await authorize(targetPath, mutationPath, "parent-create");
141
+ assertParentCurrent(current);
142
+ }
143
+ if (params.assertBeforeMutation) {
144
+ // Opaque callbacks retain their post-callback descriptor/path fence.
145
+ params.assertBeforeMutation();
146
+ assertParentCurrent(current);
147
+ }
148
+ // Synchronous session admission already ends at the exact parent fence.
149
+ const mkdirChild = binding.mkdirChildBeneath;
150
+ if (typeof mkdirChild !== "function") {
151
+ throw new FsSafeError("helper-unavailable", "native direct-child parent creation is unavailable");
152
+ }
153
+ createdByMkdir = mkdirChild.call(binding, current.fd, segment, 0o777) === true;
154
+ child = await openParent(current.fd, parentPath, segment);
155
+ }
156
+ try {
157
+ const targetPath = joinPathSegmentRoute(child.guard.realPath, route, index + 1, params.basename);
158
+ if (session && targetPath !== session.retainedTargetPath) {
159
+ session.dispose();
160
+ session = undefined;
161
+ }
162
+ let evidence;
163
+ if (session && createdByMkdir && createReceipt) {
164
+ try {
165
+ evidence = Object.freeze({
166
+ admission: createReceipt,
167
+ parent: parentObservation(current.fd, { ...current.guard,
168
+ stat: inspectFileIdentitySync(() => fsSync.fstatSync(current.fd, { bigint: true }), current.guard.stat),
169
+ }),
170
+ child: child.observation,
171
+ });
172
+ }
173
+ catch {
174
+ // Incomplete optional evidence returns to ordered admission below.
175
+ }
176
+ }
177
+ // Advancement refreshes both named directories after all Root/policy
178
+ // observations; these receipts supply expectations, not cached authority.
179
+ const advanced = evidence && session.advanceCreatedDirectory(evidence);
180
+ if (!advanced) {
181
+ await authorize(targetPath);
182
+ assertParentCurrent(child);
183
+ }
184
+ }
185
+ catch (error) {
186
+ closeAfterFailure(closeFd, child.fd, { error }, rootAdmission.reportCloseErrors);
187
+ throw error;
188
+ }
189
+ const previousFd = ownedFd;
190
+ current = child;
191
+ ownedFd = child.fd;
192
+ if (previousFd !== undefined)
193
+ closeFd(previousFd);
194
+ }
195
+ if (ownedFd === undefined) {
196
+ throw new FsSafeError("path-mismatch", "native write parent admission did not complete");
197
+ }
198
+ ownedFd = undefined;
199
+ return current;
200
+ }
201
+ catch (error) {
202
+ failure = { error };
203
+ throw error;
204
+ }
205
+ finally {
206
+ session?.dispose();
207
+ closeAfterFailure(closeFd, ownedFd, failure, rootAdmission.reportCloseErrors);
208
+ }
209
+ }
@@ -0,0 +1,4 @@
1
+ export declare const NATIVE_RENAME_SOURCE_IDENTITY_MISMATCH = "FS_SAFE_INTERNAL_RENAME_SOURCE_IDENTITY_MISMATCH";
2
+ export type NativeRenameFailureOutcome = "uncommitted" | "indeterminate";
3
+ export declare function classifyNativeRenameFailure(error: unknown): NativeRenameFailureOutcome;
4
+ //# sourceMappingURL=native-rename-outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-rename-outcome.d.ts","sourceRoot":"","sources":["../src/native-rename-outcome.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sCAAsC,qDACC,CAAC;AAErD,MAAM,MAAM,0BAA0B,GAAG,aAAa,GAAG,eAAe,CAAC;AAEzE,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,CAMtF"}
@@ -0,0 +1,8 @@
1
+ export const NATIVE_RENAME_SOURCE_IDENTITY_MISMATCH = "FS_SAFE_INTERNAL_RENAME_SOURCE_IDENTITY_MISMATCH";
2
+ export function classifyNativeRenameFailure(error) {
3
+ const code = error?.code ?? "";
4
+ // Ordinary errno can follow a committed remote rename whose reply was lost.
5
+ return code === NATIVE_RENAME_SOURCE_IDENTITY_MISMATCH
6
+ ? "uncommitted"
7
+ : "indeterminate";
8
+ }
@@ -6,10 +6,11 @@ import type { PinnedWriteInput, PinnedWriteParams } from "./pinned-write.js";
6
6
  import type { PublishedFileReceipt, StagedFile, StagedFileCleanupReceipt, StagedFileReceipt } from "./staged-file-types.js";
7
7
  export type NativeStagingBinding = NativeBinding & Required<Pick<NativeBinding, "createStagedFile" | "stagedFileMatches" | "removeStagedFile">>;
8
8
  export declare function assertNativeStaging(binding: NativeBinding): asserts binding is NativeStagingBinding;
9
+ type StagedPermissionPolicy = "private-creation" | "mode-only";
9
10
  declare class NativeStagedFile implements StagedFile {
10
11
  #private;
11
- constructor(binding: NativeStagingBinding, parentFd: number, closeParentFd: (fd: number) => void, directory: StagedFileReceipt["directory"], portableNames: boolean, publishedMode: number, sync: boolean, assertBeforeMutation?: () => void);
12
- static create(binding: NativeStagingBinding, parentFd: number, closeParentFd: (fd: number) => void, directory: StagedFileReceipt["directory"], input: PinnedWriteInput, mode: number, maxBytes?: number, portableNames?: boolean, sync?: boolean, assertBeforeMutation?: () => void): Promise<NativeStagedFile>;
12
+ constructor(binding: NativeStagingBinding, parentFd: number, closeParentFd: (fd: number) => void, directory: StagedFileReceipt["directory"], portableNames: boolean, publishedMode: number, sync: boolean, assertBeforeMutation?: () => void, name?: string, strictFileSync?: boolean, permissionPolicy?: StagedPermissionPolicy);
13
+ static create(binding: NativeStagingBinding, parentFd: number, closeParentFd: (fd: number) => void, directory: StagedFileReceipt["directory"], input: PinnedWriteInput, mode: number, maxBytes?: number, portableNames?: boolean, sync?: boolean, assertBeforeMutation?: () => void, exclusiveBasename?: string, strictFileSync?: boolean, permissionPolicy?: StagedPermissionPolicy): Promise<NativeStagedFile>;
13
14
  static write(binding: NativeStagingBinding, parentFd: number, closeParentFd: (fd: number) => void, directory: StagedFileReceipt["directory"], params: PinnedWriteParams, parentGuard: AnyAsyncDirectoryGuard): Promise<FileIdentityStat>;
14
15
  get receipt(): StagedFileReceipt;
15
16
  assertCurrent(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"native-staged-file.d.ts","sourceRoot":"","sources":["../src/native-staged-file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"native-staged-file.d.ts","sourceRoot":"","sources":["../src/native-staged-file.ts"],"names":[],"mappings":"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"}
@@ -50,18 +50,22 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
+ import { assertDarwinCreationAcl, privateFileMutationAssertion } from "./creation-darwin.js";
54
+ import { assertPrivateCreationFile } from "./creation-file-state.js";
53
55
  import { requireNativeBinding } from "./native.js";
54
56
  import { syncFileBestEffortSync } from "./file-sync.js";
55
57
  import { randomUUID } from "node:crypto";
56
58
  import fs from "node:fs";
57
59
  import path from "node:path";
58
60
  import { FsSafeError } from "./errors.js";
59
- import { MutationAuthorityError } from "./mutation-authority.js";
61
+ import { assertSynchronousCallbackResult, MutationAuthorityError } from "./mutation-authority.js";
60
62
  import { captureNativeFdClose } from "./native-binding.js";
61
- import { writeNativeInput } from "./native-operations.js";
63
+ import { writePinnedInput } from "./pinned-write-input.js";
62
64
  import { assertNativeCopyCompleted, createNativeCopyFile } from "./copy-file-input.js";
63
65
  import { assertStagedDirectoryCurrent, openStagedDirectory } from "./staged-directory.js";
64
66
  import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
67
+ import { classifyNativeRenameFailure } from "./native-rename-outcome.js";
68
+ import { createStagedFileReceipt, stagedFileFailure as failure } from "./staged-file-settlement.js";
65
69
  export function assertNativeStaging(binding) {
66
70
  if ([
67
71
  binding.closeOwnedFd,
@@ -81,17 +85,6 @@ function assertBasename(name, portable) {
81
85
  }
82
86
  }
83
87
  const NOT_PUBLISHED = Object.freeze({ status: "not-published" });
84
- const UNCOMMITTED_RENAME_ERRORS = new Set([
85
- "EACCES", "EBADF", "EBUSY", "EEXIST", "EINVAL", "EISDIR", "ELOOP", "EMLINK",
86
- "ENAMETOOLONG", "ENOENT", "ENOSPC", "ENOSYS", "ENOTDIR", "ENOTEMPTY", "ENOTSUP",
87
- "EPERM", "EROFS", "ETXTBSY", "EXDEV",
88
- ]);
89
- function failure(error, details) {
90
- const code = error instanceof FsSafeError
91
- ? error.code
92
- : error?.code === "EEXIST" ? "already-exists" : "helper-failed";
93
- return new FsSafeError(code, `staged file ${details.phase} failed`, { cause: error, details });
94
- }
95
88
  class NativeStagedFile {
96
89
  #binding;
97
90
  #closeFd;
@@ -101,12 +94,17 @@ class NativeStagedFile {
101
94
  #portableNames;
102
95
  #publishedMode;
103
96
  #sync;
97
+ #strictFileSync;
98
+ #private;
99
+ #verifyMode;
104
100
  #assertBeforeMutation;
105
- #name = `.fs-safe-${randomUUID()}.tmp`;
101
+ #name;
106
102
  #state = { status: "open", publication: NOT_PUBLISHED };
107
103
  #receipt;
108
104
  #rejectFinalSymlink = false;
109
- constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation) {
105
+ constructor(binding, parentFd, closeParentFd, directory, portableNames, publishedMode, sync, assertBeforeMutation, name = `.fs-safe-${randomUUID()}.tmp`, strictFileSync = false, permissionPolicy) {
106
+ assertBasename(name, portableNames);
107
+ this.#name = name;
110
108
  this.#closeFd = captureNativeFdClose(binding);
111
109
  this.#binding = binding;
112
110
  this.#parentFd = parentFd;
@@ -115,16 +113,19 @@ class NativeStagedFile {
115
113
  this.#portableNames = portableNames;
116
114
  this.#publishedMode = publishedMode;
117
115
  this.#sync = sync;
116
+ this.#strictFileSync = strictFileSync;
117
+ this.#private = permissionPolicy === "private-creation";
118
+ this.#verifyMode = permissionPolicy !== undefined;
118
119
  this.#assertBeforeMutation = assertBeforeMutation;
119
120
  }
120
121
  // Public staging supplies a Node parent; pinned writes supply a native parent.
121
122
  // The supplied closer owns parentFd even on construction or preparation failure.
122
123
  static async create(binding, parentFd, closeParentFd, directory, input, mode, maxBytes,
123
124
  // Public staging uses portable names; existing POSIX writes accept literal names.
124
- portableNames = true, sync = true, assertBeforeMutation) {
125
+ portableNames = true, sync = true, assertBeforeMutation, exclusiveBasename, strictFileSync = false, permissionPolicy) {
125
126
  let staged;
126
127
  try {
127
- staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation);
128
+ staged = new NativeStagedFile(binding, parentFd, closeParentFd, directory, portableNames, mode, sync, assertBeforeMutation, exclusiveBasename, strictFileSync, permissionPolicy);
128
129
  }
129
130
  catch (error) {
130
131
  try {
@@ -141,15 +142,32 @@ class NativeStagedFile {
141
142
  static async write(binding, parentFd, closeParentFd, directory, params, parentGuard) {
142
143
  const env_1 = { stack: [], error: void 0, hasError: false };
143
144
  try {
145
+ const exclusive = params.overwrite === false && params.input.kind === "buffer" && params.input.stageBeforePublish === false;
144
146
  // This owner never escapes. Only the internal verifier borrows its fd;
145
147
  // public descriptor methods remain await-free and cannot race disposal.
146
- const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation), true);
148
+ const staged = __addDisposableResource(env_1, await NativeStagedFile.create(binding, parentFd, closeParentFd, directory, params.input, params.mode, params.maxBytes, false, params.sync, params.assertBeforeMutation, exclusive ? params.basename : undefined, params.strictFileSync, params.private ? "private-creation" : params.verifyPosixMode === true ? "mode-only" : undefined), true);
147
149
  staged.#rejectFinalSymlink = params.rejectFinalSymlink === true;
148
150
  if (params.input.kind === "file")
149
151
  await params.input.verifySource();
150
- const published = await staged.publish(params.basename, { overwrite: params.overwrite !== false }, params.onPublished);
152
+ if (exclusive) {
153
+ staged.#assertCurrent();
154
+ params.assertBeforeMutation?.();
155
+ if (staged.#verifyMode)
156
+ staged.#assertCurrent();
157
+ }
158
+ const published = exclusive
159
+ ? staged.#completePublication(params.basename, false, params.onPublished)
160
+ : await staged.publish(params.basename, { overwrite: params.overwrite !== false }, params.onPublished);
151
161
  const identity = published.staged.identity;
152
- await params.verifyPublished?.(staged.#file(), identity, parentGuard);
162
+ try {
163
+ await params.verifyPublished?.(staged.#file(), identity, parentGuard);
164
+ }
165
+ catch (error) {
166
+ if (params.overwrite === false && params.input.kind !== "file" && params.input.stageBeforePublish === true) {
167
+ throw failure(error, { phase: "publish", publication: published });
168
+ }
169
+ throw error;
170
+ }
153
171
  return { dev: identity.dev, ino: identity.ino };
154
172
  }
155
173
  catch (e_1) {
@@ -181,13 +199,14 @@ class NativeStagedFile {
181
199
  }
182
200
  return state.fileFd;
183
201
  }
184
- #assertNamed(name) {
202
+ #assertNamed(name, expectedMode) {
185
203
  const fd = this.#file();
186
204
  const stat = this.#binding.stagedFileMatches(this.#parentFd, name, fd)
187
205
  ? fs.fstatSync(fd, { bigint: true }) : undefined;
188
206
  if (!stat || stat.nlink !== 1n) {
189
207
  throw new FsSafeError("path-mismatch", "staged entry no longer names the exclusive created file");
190
208
  }
209
+ this.#assertPermissions(fd, stat, expectedMode);
191
210
  return Number(stat.mode & 4095n);
192
211
  }
193
212
  #assertCurrent() {
@@ -195,7 +214,17 @@ class NativeStagedFile {
195
214
  throw new FsSafeError("helper-failed", "staged file publication has already been attempted");
196
215
  }
197
216
  assertStagedDirectoryCurrent(this.#directory);
198
- this.#assertNamed(this.#name);
217
+ this.#assertNamed(this.#name, 0o600);
218
+ }
219
+ #assertPermissions(fd, stat, expectedMode) {
220
+ if (this.#private)
221
+ assertPrivateCreationFile(stat, fd);
222
+ if (this.#verifyMode && Number(stat.mode & 4095n) !== expectedMode) {
223
+ throw new FsSafeError("insecure-permissions", "filesystem did not enforce the staged file mode");
224
+ }
225
+ }
226
+ #assertStagePermissions(fd) {
227
+ this.#assertPermissions(fd, fs.fstatSync(fd, { bigint: true }), 0o600);
199
228
  }
200
229
  async #prepare(input, maxBytes) {
201
230
  try {
@@ -203,44 +232,60 @@ class NativeStagedFile {
203
232
  if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
204
233
  throw new FsSafeError("invalid-path", "invalid staged file mode");
205
234
  }
235
+ const fileSignal = input.kind === "file" && this.#verifyMode ? input.signal : undefined;
236
+ if (input.kind === "file" && this.#verifyMode) {
237
+ fileSignal?.throwIfAborted();
238
+ if (input.clone === "always") {
239
+ throw new FsSafeError("helper-unavailable", "required cloning cannot verify staged permissions before copying");
240
+ }
241
+ }
206
242
  assertStagedDirectoryCurrent(this.#directory);
207
243
  // The exclusive open performs no fallible post-open checks. Store its fd
208
244
  // before every subsequent operation, including the first metadata read.
209
245
  const state = this.#open();
246
+ if (this.#private)
247
+ assertDarwinCreationAcl(this.#parentFd, "file");
210
248
  this.#assertBeforeMutation?.();
211
- const copied = input.kind === "file"
249
+ if (this.#private)
250
+ assertDarwinCreationAcl(this.#parentFd, "file");
251
+ // Native copying populates its new file before returning its descriptor.
252
+ const copied = input.kind === "file" && !this.#verifyMode
212
253
  ? await createNativeCopyFile(this.#binding, input, this.#parentFd, this.#name, maxBytes, false)
213
254
  : undefined;
214
255
  state.fileFd = copied?.fd;
215
256
  if (state.fileFd === undefined) {
216
257
  this.#assertBeforeMutation?.();
258
+ if (this.#private)
259
+ assertDarwinCreationAcl(this.#parentFd, "file");
217
260
  state.fileFd = this.#binding.createStagedFile(this.#parentFd, this.#name);
218
261
  }
219
262
  const fd = state.fileFd;
220
263
  if (input.kind === "file")
221
264
  assertNativeCopyCompleted(input, copied);
222
- this.#assertBeforeMutation?.();
265
+ const assertBeforeChmod = this.#private
266
+ ? privateFileMutationAssertion(fd, this.#assertBeforeMutation) : this.#assertBeforeMutation;
267
+ assertBeforeChmod?.();
223
268
  fs.fchmodSync(fd, 0o600);
269
+ // Some filesystems report successful chmod without enforcing its mode.
270
+ if (this.#verifyMode)
271
+ this.#assertStagePermissions(fd);
272
+ const assertBeforeMutation = this.#verifyMode ? () => {
273
+ assertSynchronousCallbackResult(this.#assertBeforeMutation?.(), "assertBeforeMutation");
274
+ fileSignal?.throwIfAborted();
275
+ this.#assertStagePermissions(fd);
276
+ } : this.#assertBeforeMutation;
224
277
  if (!copied)
225
- await writeNativeInput(fd, input, maxBytes, this.#assertBeforeMutation);
226
- if (this.#sync)
227
- syncFileBestEffortSync(fd);
278
+ await writePinnedInput(fd, input, maxBytes, assertBeforeMutation);
279
+ if (this.#verifyMode)
280
+ this.#assertStagePermissions(fd);
281
+ if (this.#sync) {
282
+ if (this.#strictFileSync)
283
+ fs.fsyncSync(fd);
284
+ else
285
+ syncFileBestEffortSync(fd);
286
+ }
228
287
  const stat = fs.fstatSync(fd, { bigint: true });
229
- this.#receipt = Object.freeze({
230
- directory: this.#directory,
231
- temporaryBasename: this.#name,
232
- identity: Object.freeze({
233
- dev: stat.dev,
234
- ino: stat.ino,
235
- mode: Number(stat.mode & 4095n),
236
- nlink: stat.nlink,
237
- size: stat.size,
238
- uid: Number(stat.uid),
239
- gid: Number(stat.gid),
240
- mtimeNs: stat.mtimeNs,
241
- ctimeNs: stat.ctimeNs,
242
- }),
243
- });
288
+ this.#receipt = createStagedFileReceipt(this.#directory, this.#name, stat);
244
289
  this.#assertCurrent();
245
290
  }
246
291
  catch (error) {
@@ -270,6 +315,10 @@ class NativeStagedFile {
270
315
  this.#assertCurrent();
271
316
  assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
272
317
  this.#assertBeforeMutation?.();
318
+ if (this.#assertBeforeMutation) {
319
+ this.#assertCurrent();
320
+ assertFinalSymlinkRejected(path.join(this.#directory.realPath, basename), this.#rejectFinalSymlink);
321
+ }
273
322
  try {
274
323
  if (overwrite) {
275
324
  this.#binding.renameReplace(this.#parentFd, this.#name, this.#parentFd, basename);
@@ -279,39 +328,13 @@ class NativeStagedFile {
279
328
  }
280
329
  }
281
330
  catch (error) {
282
- // Unknown rename errors can be indeterminate on remote filesystems.
283
- if (!UNCOMMITTED_RENAME_ERRORS.has(error?.code ?? "")) {
331
+ // Only explicit pre-dispatch provenance can rule out a committed rename.
332
+ if (classifyNativeRenameFailure(error) === "indeterminate") {
284
333
  state.publication = Object.freeze({ status: "indeterminate", basename, overwrite });
285
334
  }
286
335
  throw error;
287
336
  }
288
- // Commit is recorded synchronously before any post-rename operation.
289
- const receipt = Object.freeze({
290
- status: "published",
291
- staged: this.receipt,
292
- basename,
293
- overwrite,
294
- });
295
- state.publication = receipt;
296
- onPublished?.(receipt.staged.identity);
297
- const stagedMode = this.#assertNamed(basename);
298
- assertStagedDirectoryCurrent(this.#directory);
299
- // Keep contents private until the published name passes its identity
300
- // fence. Mode changes use the owned fd, including for final mode 000.
301
- const fd = this.#file();
302
- if (this.#publishedMode !== 0o600 || stagedMode !== 0o600)
303
- fs.fchmodSync(fd, this.#publishedMode);
304
- // With sync enabled, content was synced before rename. A lost chmod leaves staged 0600,
305
- // no wider than modes retaining owner rw. Restrictive modes and observed
306
- // widening of the stage still need the permission correction made durable.
307
- if (this.#sync && ((this.#publishedMode & 0o600) !== 0o600 || (stagedMode & ~this.#publishedMode) !== 0)) {
308
- syncFileBestEffortSync(fd);
309
- }
310
- if (this.#sync)
311
- syncFileBestEffortSync(this.#parentFd);
312
- this.#assertNamed(basename);
313
- assertStagedDirectoryCurrent(this.#directory);
314
- return receipt;
337
+ return this.#completePublication(basename, overwrite, onPublished);
315
338
  }
316
339
  catch (error) {
317
340
  if (error instanceof MutationAuthorityError)
@@ -323,6 +346,32 @@ class NativeStagedFile {
323
346
  throw failure(error, { phase: "publish", publication });
324
347
  }
325
348
  }
349
+ #completePublication(basename, overwrite, onPublished) {
350
+ // Record complete content before fallible post-publication verification.
351
+ const receipt = Object.freeze({ status: "published", staged: this.receipt, basename, overwrite });
352
+ this.#open().publication = receipt;
353
+ onPublished?.(receipt.staged.identity);
354
+ const stagedMode = this.#assertNamed(basename, 0o600);
355
+ assertStagedDirectoryCurrent(this.#directory);
356
+ // Keep contents private until the name passes its identity fence. Mode
357
+ // changes use the owned fd, including for final mode 000.
358
+ const fd = this.#file();
359
+ if (this.#publishedMode !== 0o600 || stagedMode !== 0o600)
360
+ fs.fchmodSync(fd, this.#publishedMode);
361
+ // Content was synced during preparation. A lost chmod leaves 0600, no wider
362
+ // than modes retaining owner rw; restrictive modes still need a durable correction.
363
+ if (this.#sync && ((this.#publishedMode & 0o600) !== 0o600 || (stagedMode & ~this.#publishedMode) !== 0)) {
364
+ if (this.#strictFileSync)
365
+ fs.fsyncSync(fd);
366
+ else
367
+ syncFileBestEffortSync(fd);
368
+ }
369
+ if (this.#sync)
370
+ syncFileBestEffortSync(this.#parentFd);
371
+ this.#assertNamed(basename, this.#publishedMode);
372
+ assertStagedDirectoryCurrent(this.#directory);
373
+ return receipt;
374
+ }
326
375
  #finalize() {
327
376
  if (this.#state.status === "closed") {
328
377
  return this.#state;
@@ -395,5 +444,5 @@ export async function stageFileInDirectory(options) {
395
444
  const input = { kind: "buffer", data: Buffer.from(options.content) };
396
445
  const mode = options.mode ?? 0o600;
397
446
  const parent = openStagedDirectory(options.directory);
398
- return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode);
447
+ return await createNativeStage(binding, parent.fd, fs.closeSync, parent.receipt, input, mode, undefined, true, true, undefined, undefined, false, "mode-only");
399
448
  }
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,wBAAwB,CAAC,CAAC,GAAG,IAAI,IAAI;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAClC,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,IAAI,IAAI;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAyDF,wBAAsB,2BAA2B,CAAC,CAAC,GAAG,IAAI,EACxD,OAAO,EAAE,wBAAwB,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CA2DrC"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,wBAAwB,CAAC,CAAC,GAAG,IAAI,IAAI;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAClC,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,IAAI,IAAI;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAyDF,wBAAsB,2BAA2B,CAAC,CAAC,GAAG,IAAI,EACxD,OAAO,EAAE,wBAAwB,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CA+DrC"}
package/dist/output.js CHANGED
@@ -57,6 +57,10 @@ export async function writeExternalFileWithinRoot(options) {
57
57
  const requestedTargetPath = options.path;
58
58
  assertNoWindowsPathAlias(path.dirname(requestedTargetPath), "filesystem", "output target parent uses a Windows filesystem namespace alias");
59
59
  const producerIsolation = options.producerIsolation;
60
+ const write = options.write;
61
+ const mode = options.mode;
62
+ const staging = options.staging;
63
+ const fallbackFileName = options.fallbackFileName;
60
64
  const targetRoot = await root(rootDir);
61
65
  if (requestedTargetPath.length === 0) {
62
66
  throw new FsSafeError("invalid-path", "target path is required");
@@ -68,11 +72,11 @@ export async function writeExternalFileWithinRoot(options) {
68
72
  targetPath: requestedTargetPath,
69
73
  });
70
74
  assertFileTargetPath(rawTargetPath);
71
- const targetPath = sanitizedTargetPath(rawTargetPath, options.fallbackFileName);
75
+ const targetPath = sanitizedTargetPath(rawTargetPath, fallbackFileName);
72
76
  assertNoWindowsPathAlias(targetPath, "filesystem", "output target uses a Windows filesystem namespace alias");
73
77
  const finalPath = await targetRoot.resolve(targetPath);
74
78
  assertNoWindowsPathAlias(finalPath, "filesystem", "output target uses a Windows filesystem namespace alias");
75
- if (options.staging === "sibling") {
79
+ if (staging === "sibling") {
76
80
  const parentPath = path.dirname(targetPath);
77
81
  if (parentPath !== ".") {
78
82
  await targetRoot.mkdir(parentPath);
@@ -80,23 +84,23 @@ export async function writeExternalFileWithinRoot(options) {
80
84
  const siblingFinalPath = await targetRoot.resolve(targetPath);
81
85
  const result = await writeExternalFileViaSibling({
82
86
  finalPath: siblingFinalPath,
83
- write: options.write,
87
+ write,
84
88
  producerIsolation,
85
- fallbackFileName: options.fallbackFileName,
89
+ fallbackFileName,
86
90
  maxBytes,
87
- mode: options.mode,
91
+ mode,
88
92
  });
89
93
  return { path: siblingFinalPath, result };
90
94
  }
91
95
  const staged = await tempFile({
92
96
  prefix: "fs-safe-output",
93
- fileName: tempFileNameForTarget(targetPath, options.fallbackFileName),
97
+ fileName: tempFileNameForTarget(targetPath, fallbackFileName),
94
98
  });
95
99
  try {
96
- const result = await options.write(staged.path);
100
+ const result = await Function.prototype.call.call(write, options, staged.path);
97
101
  await targetRoot.copyIn(targetPath, staged.path, {
98
102
  maxBytes,
99
- mode: options.mode,
103
+ mode,
100
104
  mkdir: true,
101
105
  sourceHardlinks: "reject",
102
106
  });
@@ -1 +1 @@
1
- {"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}
1
+ {"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}