@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,118 @@
1
+ import fs, {} from "node:fs";
2
+ import fsAsync from "node:fs/promises";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { sameFileIdentityForCleanup } from "./file-identity.js";
5
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
+ import { hasUnsettledWindowsSecurityCommand } from "./windows-security-command.js";
7
+ import { assertDarwinCreationAcl } from "./creation-darwin.js";
8
+ export function assertCreationFile(fd, pathname, expected) {
9
+ const opened = inspectFileIdentitySync(() => fs.fstatSync(fd, { bigint: true }), expected);
10
+ const named = inspectFileIdentitySync(() => fs.lstatSync(pathname, { bigint: true }), opened);
11
+ if (!opened.isFile() || !named.isFile() || named.isSymbolicLink() || opened.nlink !== 1n || named.nlink !== 1n) {
12
+ throw new FsSafeError("path-mismatch", "created file is not the expected single-linked regular file");
13
+ }
14
+ return opened;
15
+ }
16
+ export function assertPrivateCreationFile(stat, fd) {
17
+ if (process.platform === "win32")
18
+ return;
19
+ if (typeof process.getuid !== "function" || stat.uid !== BigInt(process.getuid())) {
20
+ throw new FsSafeError("not-owned", "created private file must belong to the current user");
21
+ }
22
+ if ((stat.mode & 3647n) !== 0n) {
23
+ throw new FsSafeError("insecure-permissions", "created private file is not owner-only");
24
+ }
25
+ assertDarwinCreationAcl(fd);
26
+ }
27
+ function assertRecordedFile(current, identity) {
28
+ if (!current.isFile() || current.isSymbolicLink() || !sameFileIdentityForCleanup(current, identity)) {
29
+ throw new FsSafeError("path-mismatch", "created file cleanup preserved a replacement");
30
+ }
31
+ }
32
+ export function removeRecordedCreationFileSync(pathname, identity, assertParent) {
33
+ assertParent();
34
+ let current;
35
+ try {
36
+ current = fs.lstatSync(pathname, { bigint: true });
37
+ }
38
+ catch (error) {
39
+ if (error?.code === "ENOENT")
40
+ return;
41
+ throw error;
42
+ }
43
+ assertRecordedFile(current, identity);
44
+ fs.unlinkSync(pathname);
45
+ }
46
+ export async function removeRecordedCreationFile(pathname, identity, assertParent) {
47
+ assertParent();
48
+ let current;
49
+ try {
50
+ current = await fsAsync.lstat(pathname, { bigint: true });
51
+ }
52
+ catch (error) {
53
+ if (error?.code === "ENOENT")
54
+ return;
55
+ throw error;
56
+ }
57
+ assertParent();
58
+ assertRecordedFile(current, identity);
59
+ // Recheck after the awaited observation before dispatching the unlink.
60
+ assertRecordedFile(fs.lstatSync(pathname, { bigint: true }), identity);
61
+ await fsAsync.unlink(pathname);
62
+ }
63
+ export function creationPublicationAfterFailure(error, recorded) {
64
+ if (error instanceof FsSafeError) {
65
+ const publication = error.details?.publication;
66
+ if (publication && typeof publication === "object" && "status" in publication &&
67
+ publication.status === "indeterminate")
68
+ return "indeterminate";
69
+ }
70
+ return recorded;
71
+ }
72
+ export function hasPreservedCreationArtifacts(error) {
73
+ if (!(error instanceof FsSafeError))
74
+ return false;
75
+ if (error.details?.cleanup === "preserved" || error.details?.cleanup === "failed")
76
+ return true;
77
+ const publication = error.details?.publication;
78
+ return publication !== null && typeof publication === "object" && "status" in publication &&
79
+ (publication.status === "published" || publication.status === "indeterminate");
80
+ }
81
+ export function rethrowPrivateStageCreationFailure(error, path, stageDirectory) {
82
+ if (error instanceof FsSafeError && error.code === "already-exists") {
83
+ throw new FsSafeError("helper-failed", "private file staging path already exists", {
84
+ cause: error, details: { publication: { status: "not-published" }, path, stageDirectory },
85
+ });
86
+ }
87
+ const publication = error instanceof FsSafeError ? error.details?.publication : undefined;
88
+ const created = error instanceof FsSafeError && error.details?.path === stageDirectory &&
89
+ publication !== null && typeof publication === "object" && "status" in publication &&
90
+ publication.status === "published";
91
+ let unconfirmed = hasUnsettledWindowsSecurityCommand(error);
92
+ if (error !== null && (typeof error === "object" || typeof error === "function")) {
93
+ try {
94
+ const outcome = Object.getOwnPropertyDescriptor(error, "creationOutcome");
95
+ unconfirmed ||= Boolean(outcome && "value" in outcome && outcome.value === "unconfirmed");
96
+ }
97
+ catch {
98
+ unconfirmed = true;
99
+ }
100
+ }
101
+ if (created || unconfirmed) {
102
+ throw privateFileSettlementFailure({
103
+ primary: error, cleanup: [], publication: "not-published", path, stageDirectory, preserved: true,
104
+ });
105
+ }
106
+ throw error;
107
+ }
108
+ export function privateFileSettlementFailure(params) {
109
+ return new FsSafeError("helper-failed", "file creation or staging settlement failed", {
110
+ cause: params.cleanup.length
111
+ ? new AggregateError([params.primary, ...params.cleanup], "file creation and cleanup failed")
112
+ : params.primary,
113
+ details: {
114
+ publication: { status: params.publication }, path: params.path, stageDirectory: params.stageDirectory,
115
+ cleanup: params.preserved || params.publication === "indeterminate" ? "preserved" : params.cleanup.length ? "failed" : "removed",
116
+ },
117
+ });
118
+ }
@@ -0,0 +1,21 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ import { type AsyncDirectoryGuard, type AnyAsyncDirectoryGuard } from "./directory-guard.js";
3
+ export type CreationParentIdentity = Readonly<{
4
+ dev: bigint;
5
+ ino: bigint;
6
+ realPath?: string;
7
+ }>;
8
+ export type CreationPath = {
9
+ target: string;
10
+ parent: AsyncDirectoryGuard<BigIntStats>;
11
+ assertParent(): void;
12
+ };
13
+ export declare function creationAdmissionFromParent(parent: AnyAsyncDirectoryGuard): {
14
+ expectedParentIdentity: CreationParentIdentity;
15
+ };
16
+ export declare function prepareCreationPath(input: string, expectedParent?: CreationParentIdentity): CreationPath;
17
+ export declare function creationCollision(error: unknown): unknown;
18
+ /** Remove only the recorded empty stage; never traverse a replaced directory. */
19
+ export declare function removeCreationDirectory(directory: string, identity: BigIntStats, assertParent: () => void): void;
20
+ export declare function removeCreationDirectoryAsync(directory: string, identity: BigIntStats, assertParent: () => void): Promise<void>;
21
+ //# sourceMappingURL=creation-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"creation-path.d.ts","sourceRoot":"","sources":["../src/creation-path.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAM9B,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACzC,YAAY,IAAI,IAAI,CAAC;CACtB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,sBAAsB,GAAG;IAAE,sBAAsB,EAAE,sBAAsB,CAAA;CAAE,CAO9H;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,sBAAsB,GAAG,YAAY,CAuBxG;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKzD;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,IAAI,GAAG,IAAI,CAQhH;AAED,wBAAsB,4BAA4B,CAChD,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,IAAI,GACjE,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,71 @@
1
+ import fs, {} from "node:fs";
2
+ import fsAsync from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertDirectoryIdentitySync, inspectDirectoryIdentitySync, } from "./directory-guard.js";
5
+ import { FsSafeError } from "./errors.js";
6
+ import { assertNoNulPathInput } from "./path.js";
7
+ import { realpathSync } from "./realpath.js";
8
+ import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
9
+ export function creationAdmissionFromParent(parent) {
10
+ const { dev, ino } = parent.stat;
11
+ if (typeof dev !== "bigint" || typeof ino !== "bigint") {
12
+ throw new FsSafeError("path-mismatch", "private creation requires an exact parent identity");
13
+ }
14
+ const identity = inspectDirectoryIdentitySync(parent.dir, { dev, ino });
15
+ return { expectedParentIdentity: { dev: identity.dev, ino: identity.ino, realPath: parent.realPath } };
16
+ }
17
+ export function prepareCreationPath(input, expectedParent) {
18
+ expectedParent = expectedParent && { dev: expectedParent.dev, ino: expectedParent.ino, realPath: expectedParent.realPath };
19
+ assertNoNulPathInput(input);
20
+ assertNoWindowsPathAlias(input, "filesystem");
21
+ if (process.platform === "win32" && input.replaceAll("/", "\\").split("\\")
22
+ .some(component => component.endsWith(".") || component.endsWith(" "))) {
23
+ throw new FsSafeError("invalid-path", "creation path contains an ambiguous Windows component");
24
+ }
25
+ const target = resolvePathPreservingWindowsRoot(input);
26
+ const directory = path.dirname(target);
27
+ if (target === directory || input.length === 0) {
28
+ throw new FsSafeError("invalid-path", "creation requires a child name");
29
+ }
30
+ if (expectedParent)
31
+ assertDirectoryIdentitySync(directory, expectedParent);
32
+ const stat = inspectDirectoryIdentitySync(directory);
33
+ const realPath = realpathSync.native(directory);
34
+ const parent = { dir: directory, realPath, stat };
35
+ const assertParent = () => {
36
+ assertDirectoryIdentitySync(directory, { dev: stat.dev, ino: stat.ino, realPath });
37
+ if (expectedParent)
38
+ assertDirectoryIdentitySync(directory, expectedParent);
39
+ };
40
+ assertParent();
41
+ return { target, parent, assertParent };
42
+ }
43
+ export function creationCollision(error) {
44
+ if (error?.code === "EEXIST") {
45
+ return new FsSafeError("already-exists", "creation target already exists", { cause: error });
46
+ }
47
+ return error;
48
+ }
49
+ /** Remove only the recorded empty stage; never traverse a replaced directory. */
50
+ export function removeCreationDirectory(directory, identity, assertParent) {
51
+ assertParent();
52
+ try {
53
+ assertDirectoryIdentitySync(directory, identity);
54
+ fs.rmdirSync(directory);
55
+ }
56
+ catch (error) {
57
+ if (error?.code !== "ENOENT")
58
+ throw error;
59
+ }
60
+ }
61
+ export async function removeCreationDirectoryAsync(directory, identity, assertParent) {
62
+ assertParent();
63
+ try {
64
+ assertDirectoryIdentitySync(directory, identity);
65
+ await fsAsync.rmdir(directory);
66
+ }
67
+ catch (error) {
68
+ if (error?.code !== "ENOENT")
69
+ throw error;
70
+ }
71
+ }
@@ -0,0 +1,19 @@
1
+ export type CreationPermissions = {
2
+ private?: boolean;
3
+ mode?: number;
4
+ };
5
+ export declare function resolveCreationPermissions(options: CreationPermissions, directory: boolean): {
6
+ private: boolean;
7
+ mode: number | undefined;
8
+ };
9
+ export declare function assertPrivateFileCreationAvailable(): void;
10
+ /** Full Windows identity, with security validation when requested; no repairs. */
11
+ export declare function inspectCreationDirectorySync(targetPath: string, privatePath: boolean): string;
12
+ export declare function inspectCreationDirectory(targetPath: string, privatePath: boolean): Promise<string>;
13
+ export declare function assertPrivateDirectorySync(targetPath: string): void;
14
+ export declare function assertPrivateDirectory(targetPath: string): Promise<void>;
15
+ export declare function protectCreatedFileSync(fd: number, targetPath: string, parentIdentity: string): string;
16
+ export declare function protectCreatedFile(fd: number, targetPath: string, parentIdentity: string): Promise<string>;
17
+ export declare function verifyCreatedFileSync(fd: number, targetPath: string, identity: string, parentIdentity: string, links?: number): void;
18
+ export declare function verifyCreatedFile(fd: number, targetPath: string, identity: string, parentIdentity: string, links?: number): Promise<void>;
19
+ //# sourceMappingURL=creation-permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"creation-permissions.d.ts","sourceRoot":"","sources":["../src/creation-permissions.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,mBAAmB,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,OAAO,GACjB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAehD;AAeD,wBAAgB,kCAAkC,IAAI,IAAI,CAOzD;AAOD,kFAAkF;AAClF,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAQ7F;AAED,wBAAsB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxG;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAQnE;AAED,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9E;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAOrG;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOhH;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,SAAI,GAClF,IAAI,CAQN;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,SAAI,GAClF,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,125 @@
1
+ import { inspectDirectoryIdentitySync } from "./directory-guard.js";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { getNativeBinding } from "./native.js";
4
+ import { getFsSafeNativeConfig } from "./native-config.js";
5
+ import { warnNativeFallback } from "./native-fallback-warning.js";
6
+ import { inspectWindowsDirectoryCommand, inspectWindowsDirectoryCommandSync, protectPrivateWindowsFileCommand, protectPrivateWindowsFileCommandSync, verifyPrivateWindowsFileCommand, verifyPrivateWindowsFileCommandSync, } from "./windows-security-command.js";
7
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
8
+ import { assertDarwinCreationDirectoryAcl, assertDarwinPrivateCreationAvailable, assertDarwinPrivateDirectoryMode } from "./creation-darwin.js";
9
+ export function resolveCreationPermissions(options, directory) {
10
+ const privatePath = options.private;
11
+ const mode = options.mode;
12
+ if (privatePath !== undefined && typeof privatePath !== "boolean") {
13
+ throw new TypeError("private must be a boolean");
14
+ }
15
+ if (mode !== undefined && (!Number.isInteger(mode) || mode < 0 || mode > 0o7777)) {
16
+ throw new RangeError("mode must contain only permission bits");
17
+ }
18
+ if (privatePath && mode !== undefined && (mode & 0o7077) !== 0) {
19
+ throw new FsSafeError("insecure-permissions", "private creation requires owner-only permission bits");
20
+ }
21
+ if (privatePath)
22
+ assertDarwinPrivateCreationAvailable();
23
+ if (privatePath && directory)
24
+ assertDarwinPrivateDirectoryMode(mode ?? 0o700);
25
+ return { private: privatePath === true, mode: mode ?? (privatePath ? directory ? 0o700 : 0o600 : undefined) };
26
+ }
27
+ function assertPrivatePosixDirectory(stat) {
28
+ if (typeof process.getuid !== "function" || stat.uid !== BigInt(process.getuid())) {
29
+ throw new FsSafeError("not-owned", "private directory must belong to the current user");
30
+ }
31
+ if ((stat.mode & 3647n) !== 0n) {
32
+ throw new FsSafeError("insecure-permissions", "existing directory is not private");
33
+ }
34
+ }
35
+ function unavailable() {
36
+ throw new FsSafeError("helper-unavailable", "private Windows creation requires an up-to-date native helper");
37
+ }
38
+ export function assertPrivateFileCreationAvailable() {
39
+ const native = getNativeBinding();
40
+ if (process.platform !== "win32" || getFsSafeNativeConfig().mode !== "require")
41
+ return;
42
+ if (typeof native?.createPrivateDirectoryWithParentIdentity !== "function" ||
43
+ typeof native.inspectWindowsDirectory !== "function" ||
44
+ typeof native.protectPrivateWindowsFile !== "function" ||
45
+ typeof native.verifyPrivateWindowsFile !== "function")
46
+ unavailable();
47
+ }
48
+ function commandFallback() {
49
+ if (getFsSafeNativeConfig().mode === "require")
50
+ unavailable();
51
+ warnNativeFallback("windows-private-creation", "Private Windows creation uses a slower built-in system command.");
52
+ }
53
+ /** Full Windows identity, with security validation when requested; no repairs. */
54
+ export function inspectCreationDirectorySync(targetPath, privatePath) {
55
+ assertNoWindowsPathAlias(targetPath, "filesystem");
56
+ const native = getNativeBinding();
57
+ if (typeof native?.inspectWindowsDirectory === "function") {
58
+ return native.inspectWindowsDirectory(targetPath, privatePath).identity;
59
+ }
60
+ commandFallback();
61
+ return inspectWindowsDirectoryCommandSync(targetPath, privatePath).identity;
62
+ }
63
+ export async function inspectCreationDirectory(targetPath, privatePath) {
64
+ assertNoWindowsPathAlias(targetPath, "filesystem");
65
+ const native = getNativeBinding();
66
+ if (typeof native?.inspectWindowsDirectory === "function") {
67
+ return native.inspectWindowsDirectory(targetPath, privatePath).identity;
68
+ }
69
+ commandFallback();
70
+ return (await inspectWindowsDirectoryCommand(targetPath, privatePath)).identity;
71
+ }
72
+ export function assertPrivateDirectorySync(targetPath) {
73
+ const stat = inspectDirectoryIdentitySync(targetPath);
74
+ if (process.platform === "win32")
75
+ inspectCreationDirectorySync(targetPath, true);
76
+ else {
77
+ assertPrivatePosixDirectory(stat);
78
+ assertDarwinCreationDirectoryAcl(targetPath, stat);
79
+ }
80
+ inspectDirectoryIdentitySync(targetPath, stat);
81
+ }
82
+ export async function assertPrivateDirectory(targetPath) {
83
+ const stat = inspectDirectoryIdentitySync(targetPath);
84
+ if (process.platform === "win32")
85
+ await inspectCreationDirectory(targetPath, true);
86
+ else {
87
+ assertPrivatePosixDirectory(stat);
88
+ assertDarwinCreationDirectoryAcl(targetPath, stat);
89
+ }
90
+ inspectDirectoryIdentitySync(targetPath, stat);
91
+ }
92
+ export function protectCreatedFileSync(fd, targetPath, parentIdentity) {
93
+ const native = getNativeBinding();
94
+ if (typeof native?.protectPrivateWindowsFile === "function") {
95
+ return native.protectPrivateWindowsFile(fd, targetPath, parentIdentity).identity;
96
+ }
97
+ commandFallback();
98
+ return protectPrivateWindowsFileCommandSync(fd, targetPath, parentIdentity).identity;
99
+ }
100
+ export async function protectCreatedFile(fd, targetPath, parentIdentity) {
101
+ const native = getNativeBinding();
102
+ if (typeof native?.protectPrivateWindowsFile === "function") {
103
+ return native.protectPrivateWindowsFile(fd, targetPath, parentIdentity).identity;
104
+ }
105
+ commandFallback();
106
+ return (await protectPrivateWindowsFileCommand(fd, targetPath, parentIdentity)).identity;
107
+ }
108
+ export function verifyCreatedFileSync(fd, targetPath, identity, parentIdentity, links = 1) {
109
+ const native = getNativeBinding();
110
+ if (typeof native?.verifyPrivateWindowsFile === "function") {
111
+ native.verifyPrivateWindowsFile(fd, targetPath, identity, parentIdentity, links);
112
+ return;
113
+ }
114
+ commandFallback();
115
+ verifyPrivateWindowsFileCommandSync(fd, targetPath, identity, parentIdentity, links);
116
+ }
117
+ export async function verifyCreatedFile(fd, targetPath, identity, parentIdentity, links = 1) {
118
+ const native = getNativeBinding();
119
+ if (typeof native?.verifyPrivateWindowsFile === "function") {
120
+ native.verifyPrivateWindowsFile(fd, targetPath, identity, parentIdentity, links);
121
+ return;
122
+ }
123
+ commandFallback();
124
+ await verifyPrivateWindowsFileCommand(fd, targetPath, identity, parentIdentity, links);
125
+ }
@@ -1,5 +1,5 @@
1
1
  import { type Stats } from "node:fs";
2
- import { type FileIdentityStat } from "./file-identity.js";
2
+ import type { FileIdentityStat } from "./file-identity.js";
3
3
  export type DirectorySyncOutcome = {
4
4
  status: "synced";
5
5
  } | {
@@ -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;AAI7E,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;AAwOF,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,CAe1B;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,CAqB/B;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,oBAAoB,CAyCtB;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAMvE;AAiCD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA2FlC"}
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,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAa3D,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;AAkHF,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,eAAe,CAAC,CAe1B;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,CAqB/B;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,gBAAgB,EACpC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,oBAAoB,CAkCtB;AAED,wBAAsB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAMvE;AAiCD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA6FlC"}
@@ -3,7 +3,8 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { ensureAbsoluteDirectory } from "./absolute-path.js";
5
5
  import { FsSafeError } from "./errors.js";
6
- import { sameFileIdentity } from "./file-identity.js";
6
+ import { assertDirectoryReceiptCurrentSync, copyRetainedDirectoryReceipt, createDirectoryReceiptSync, directoryReceiptAuthority, directoryReceiptIdentity, ownDirectoryReceipt, } from "./directory-receipt.js";
7
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
7
8
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
8
9
  import { realpathSync } from "./realpath.js";
9
10
  function directoryOpenFlags() {
@@ -38,81 +39,23 @@ function unsupportedOutcome(error) {
38
39
  const code = error.code;
39
40
  return code ? { status: "unsupported", code } : { status: "unsupported" };
40
41
  }
41
- function assertDirectory(identity, pathname, label) {
42
- if (identity.isSymbolicLink() || !identity.isDirectory()) {
43
- throw new FsSafeError("not-file", `${label} must be a real directory: ${pathname}`);
44
- }
45
- }
46
- function ownDirectoryReceipt(receipt) {
47
- const pathname = receipt.path;
48
- const realPath = receipt.realPath;
49
- const identity = receipt.identity;
50
- return { path: pathname, realPath, identity };
51
- }
52
- function assertDirectoryReceiptPaths(receipt, label) {
53
- assertNoWindowsPathAlias(receipt.path, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
54
- assertNoWindowsPathAlias(receipt.realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
55
- }
56
42
  async function createDirectoryReceipt(directoryPath, label) {
57
- assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
58
- const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
59
- assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
60
- const operationPath = pathForWindowsFilesystem(resolvedPath);
61
- const identity = fsSync.lstatSync(operationPath);
62
- assertDirectory(identity, resolvedPath, label);
63
- const realPath = realpathSync.native(operationPath);
64
- assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
65
- return {
66
- path: resolvedPath,
67
- realPath,
68
- identity,
69
- };
70
- }
71
- function createDirectoryReceiptSync(directoryPath, label) {
72
- assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
73
- const resolvedPath = resolvePathPreservingWindowsRoot(directoryPath);
74
- assertNoWindowsPathAlias(resolvedPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
75
- const operationPath = pathForWindowsFilesystem(resolvedPath);
76
- const identity = fsSync.lstatSync(operationPath);
77
- assertDirectory(identity, resolvedPath, label);
78
- const realPath = realpathSync(operationPath);
79
- assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
80
- return {
81
- path: resolvedPath,
82
- realPath,
83
- identity,
84
- };
43
+ return createDirectoryReceiptSync(directoryPath, label, realpathSync.native);
85
44
  }
86
45
  async function assertDirectoryReceiptCurrent(receipt, label) {
87
- assertDirectoryReceiptPaths(receipt, label);
88
- const operationPath = pathForWindowsFilesystem(receipt.path);
89
- const currentIdentity = fsSync.lstatSync(operationPath);
90
- assertDirectory(currentIdentity, receipt.path, label);
91
- const realPath = realpathSync.native(operationPath);
92
- assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
93
- if (!sameFileIdentity(receipt.identity, currentIdentity) ||
94
- realPath !== receipt.realPath) {
95
- throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
96
- }
46
+ assertDirectoryReceiptCurrentSync(receipt, label, realpathSync.native);
97
47
  }
98
- function assertDirectoryReceiptCurrentSync(receipt, label) {
99
- assertDirectoryReceiptPaths(receipt, label);
100
- const operationPath = pathForWindowsFilesystem(receipt.path);
101
- const currentIdentity = fsSync.lstatSync(operationPath);
102
- assertDirectory(currentIdentity, receipt.path, label);
103
- const realPath = realpathSync(operationPath);
104
- assertNoWindowsPathAlias(realPath, "filesystem", `${label} real path uses a Windows filesystem namespace alias`);
105
- if (!sameFileIdentity(receipt.identity, currentIdentity) ||
106
- realPath !== receipt.realPath) {
107
- throw new FsSafeError("path-mismatch", `${label} changed during durable directory operation: ${receipt.path}`);
108
- }
48
+ function assertOpenDirectoryIdentity(descriptor, receipt, label) {
49
+ inspectFileIdentitySync(() => {
50
+ const opened = fsSync.fstatSync(descriptor, { bigint: true });
51
+ if (!opened.isDirectory()) {
52
+ throw new FsSafeError("not-file", `${label} must be a real directory: ${receipt.path}`);
53
+ }
54
+ return opened;
55
+ }, directoryReceiptAuthority(receipt).identity);
109
56
  }
110
57
  async function assertOpenDirectoryCurrent(handle, receipt, label) {
111
- const openedIdentity = fsSync.fstatSync(handle.fd);
112
- assertDirectory(openedIdentity, receipt.path, label);
113
- if (!sameFileIdentity(receipt.identity, openedIdentity)) {
114
- throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
115
- }
58
+ assertOpenDirectoryIdentity(handle.fd, receipt, label);
116
59
  await assertDirectoryReceiptCurrent(receipt, label);
117
60
  }
118
61
  class PinnedDirectoryImpl {
@@ -124,7 +67,7 @@ class PinnedDirectoryImpl {
124
67
  constructor(handle, receipt, label) {
125
68
  this.#handle = handle;
126
69
  this.#authority = receipt;
127
- this.receipt = ownDirectoryReceipt(receipt);
70
+ this.receipt = copyRetainedDirectoryReceipt(receipt);
128
71
  this.#label = label;
129
72
  }
130
73
  async assertCurrent() {
@@ -213,11 +156,7 @@ export function syncDirectorySync(directory, options = {}) {
213
156
  return unsupportedOutcome(error);
214
157
  }
215
158
  try {
216
- const openedIdentity = fsSync.fstatSync(descriptor);
217
- assertDirectory(openedIdentity, receipt.path, label);
218
- if (!sameFileIdentity(receipt.identity, openedIdentity)) {
219
- throw new FsSafeError("path-mismatch", `${label} handle changed during directory sync: ${receipt.path}`);
220
- }
159
+ assertOpenDirectoryIdentity(descriptor, receipt, label);
221
160
  assertDirectoryReceiptCurrentSync(receipt, label);
222
161
  try {
223
162
  fsSync.fsyncSync(descriptor);
@@ -273,13 +212,16 @@ export async function ensureDurableDirectory(options) {
273
212
  assertNoWindowsPathAlias(requestedDirectoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
274
213
  const directoryPath = resolvePathPreservingWindowsRoot(requestedDirectoryPath);
275
214
  assertNoWindowsPathAlias(directoryPath, "filesystem", `${label} path uses a Windows filesystem namespace alias`);
215
+ const expectedInput = options.expectedExistingIdentity;
216
+ const expectedIdentity = expectedInput && directoryReceiptIdentity(expectedInput);
276
217
  const ancestorReceipt = await findExistingAncestorReceipt(directoryPath, label);
277
218
  const targetExists = ancestorReceipt.path === directoryPath;
278
- const expectedExistingIdentity = options.expectedExistingIdentity;
279
- if (expectedExistingIdentity &&
280
- (!targetExists || !sameFileIdentity(expectedExistingIdentity, ancestorReceipt.identity))) {
219
+ if (expectedIdentity && !targetExists) {
281
220
  throw new FsSafeError("path-mismatch", `${label} changed before durable directory pinning: ${directoryPath}`);
282
221
  }
222
+ if (expectedIdentity) {
223
+ inspectFileIdentitySync(() => directoryReceiptAuthority(ancestorReceipt).identity, expectedIdentity);
224
+ }
283
225
  const ancestor = await pinDirectory(ancestorReceipt, { label });
284
226
  const pinnedDirectories = [ancestor];
285
227
  try {
@@ -336,7 +278,7 @@ export async function ensureDurableDirectory(options) {
336
278
  }
337
279
  await ancestor.assertCurrent();
338
280
  await assertDirectoryReceiptCurrent(finalReceipt, label);
339
- return { ...finalReceipt, parentSync };
281
+ return Object.assign(copyRetainedDirectoryReceipt(finalReceipt), { parentSync });
340
282
  }
341
283
  finally {
342
284
  await Promise.all(pinnedDirectories.toReversed().map(async (directory) => directory.close()));
@@ -34,6 +34,9 @@ export declare function assertAsyncDirectoryGuard(guard: AnyAsyncDirectoryGuard)
34
34
  export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
35
35
  export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard | AnyAsyncDirectoryGuard): void;
36
36
  export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
37
+ export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
38
+ bigint: true;
39
+ }): Promise<AsyncDirectoryGuard<BigIntStats>>;
37
40
  export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
38
41
  bigint: boolean;
39
42
  }): Promise<AnyAsyncDirectoryGuard>;
@@ -1 +1 @@
1
- {"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { type BigIntStats } from "node:fs";
2
2
  import { type DirectoryModeOwner } from "./directory-mode-owner.js";
3
+ /** Darwin descriptor inspection avoids requesting directory-content reads. */
4
+ export declare function nodeDarwinDirectoryMetadataFlags(): number;
3
5
  export declare function nodeDirectorySearchOnlyFlags(): {
4
6
  flags: number;
5
7
  proc: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
1
+ {"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE5G,8EAA8E;AAC9E,wBAAgB,gCAAgC,IAAI,MAAM,CAMzD;AAED,wBAAgB,4BAA4B,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAO3F;AAED,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAsFlE"}
@@ -4,6 +4,14 @@ import { FsSafeError } from "./errors.js";
4
4
  import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
5
5
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
6
  import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
7
+ /** Darwin descriptor inspection avoids requesting directory-content reads. */
8
+ export function nodeDarwinDirectoryMetadataFlags() {
9
+ if (process.platform !== "darwin" || (process.arch !== "x64" && process.arch !== "arm64")) {
10
+ throw new FsSafeError("helper-unavailable", "Darwin directory metadata descriptors are unavailable");
11
+ }
12
+ // Darwin SDK O_EVTONLY; Node omits this flag from its exported constants.
13
+ return 0x8000 | constants.O_DIRECTORY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
14
+ }
7
15
  export function nodeDirectorySearchOnlyFlags() {
8
16
  if (process.arch !== "x64" && process.arch !== "arm64")
9
17
  return undefined;
@@ -43,10 +43,10 @@ export function ownDirectoryMode(params) {
43
43
  // Hooks/ancestor checks can yield; recheck the original named association.
44
44
  await params.inspect();
45
45
  checks.check?.();
46
- let dispatchDeadlineError;
46
+ let dispatchDeadlineFailure;
47
47
  if (currentMode !== mode) {
48
+ checks.check?.();
48
49
  try {
49
- checks.check?.();
50
50
  await params.chmod(mode);
51
51
  }
52
52
  catch (error) {
@@ -58,13 +58,13 @@ export function ownDirectoryMode(params) {
58
58
  checks.check?.();
59
59
  }
60
60
  catch (error) {
61
- dispatchDeadlineError = error;
61
+ dispatchDeadlineFailure = { error };
62
62
  }
63
63
  await params.verifyChmod?.();
64
64
  }
65
65
  const finalMode = await params.inspect();
66
- if (dispatchDeadlineError)
67
- throw dispatchDeadlineError;
66
+ if (dispatchDeadlineFailure)
67
+ throw dispatchDeadlineFailure.error;
68
68
  checks.check?.();
69
69
  if (!params.ignoreChmodError && finalMode !== mode) {
70
70
  throw new FsSafeError("path-mismatch", "directory final mode could not be verified");