@openclaw/fs-safe 0.12.0 → 0.13.1

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 (395) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +12 -7
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +32 -11
  5. package/dist/advanced.d.ts +2 -0
  6. package/dist/advanced.d.ts.map +1 -1
  7. package/dist/advanced.js +2 -0
  8. package/dist/archive-input.d.ts.map +1 -1
  9. package/dist/archive-input.js +6 -2
  10. package/dist/archive-merge.d.ts.map +1 -1
  11. package/dist/archive-merge.js +15 -2
  12. package/dist/archive-native.d.ts.map +1 -1
  13. package/dist/archive-native.js +7 -19
  14. package/dist/archive-plan.js +1 -1
  15. package/dist/archive-read.d.ts.map +1 -1
  16. package/dist/archive-read.js +18 -13
  17. package/dist/archive-staging.d.ts.map +1 -1
  18. package/dist/archive-staging.js +43 -10
  19. package/dist/archive-tar-inspect.d.ts.map +1 -1
  20. package/dist/archive-tar-inspect.js +4 -1
  21. package/dist/archive-zip-admission.d.ts +1 -1
  22. package/dist/archive-zip-admission.d.ts.map +1 -1
  23. package/dist/archive-zip-admission.js +2 -2
  24. package/dist/archive-zip-directory.d.ts +3 -0
  25. package/dist/archive-zip-directory.d.ts.map +1 -1
  26. package/dist/archive-zip-directory.js +13 -2
  27. package/dist/archive-zip-loader.d.ts +3 -2
  28. package/dist/archive-zip-loader.d.ts.map +1 -1
  29. package/dist/archive-zip-loader.js +30 -4
  30. package/dist/archive-zip-manifest.d.ts +5 -0
  31. package/dist/archive-zip-manifest.d.ts.map +1 -0
  32. package/dist/archive-zip-manifest.js +22 -0
  33. package/dist/archive-zip-names.d.ts +6 -1
  34. package/dist/archive-zip-names.d.ts.map +1 -1
  35. package/dist/archive-zip-names.js +35 -14
  36. package/dist/archive-zip-preflight.d.ts.map +1 -1
  37. package/dist/archive-zip-preflight.js +3 -2
  38. package/dist/archive.d.ts.map +1 -1
  39. package/dist/archive.js +9 -4
  40. package/dist/copy-file-input.d.ts +1 -1
  41. package/dist/copy-file-input.d.ts.map +1 -1
  42. package/dist/copy-file-input.js +2 -0
  43. package/dist/darwin-acl.d.ts +4 -0
  44. package/dist/darwin-acl.d.ts.map +1 -0
  45. package/dist/darwin-acl.js +24 -0
  46. package/dist/deny-mutations.d.ts.map +1 -1
  47. package/dist/deny-mutations.js +8 -2
  48. package/dist/directory-durability.d.ts.map +1 -1
  49. package/dist/directory-durability.js +67 -23
  50. package/dist/directory-entry-path.d.ts +3 -0
  51. package/dist/directory-entry-path.d.ts.map +1 -0
  52. package/dist/directory-entry-path.js +21 -0
  53. package/dist/directory-guard.d.ts +17 -1
  54. package/dist/directory-guard.d.ts.map +1 -1
  55. package/dist/directory-guard.js +134 -48
  56. package/dist/directory-mode-node.d.ts +12 -0
  57. package/dist/directory-mode-node.d.ts.map +1 -1
  58. package/dist/directory-mode-node.js +102 -4
  59. package/dist/effective-uid.d.ts +2 -0
  60. package/dist/effective-uid.d.ts.map +1 -0
  61. package/dist/effective-uid.js +25 -0
  62. package/dist/file-handle-transfer.d.ts.map +1 -1
  63. package/dist/file-handle-transfer.js +98 -27
  64. package/dist/file-hash.d.ts.map +1 -1
  65. package/dist/file-hash.js +3 -0
  66. package/dist/file-lock-sync.d.ts.map +1 -1
  67. package/dist/file-lock-sync.js +36 -6
  68. package/dist/file-lock.d.ts.map +1 -1
  69. package/dist/file-lock.js +29 -9
  70. package/dist/file-observation.d.ts +1 -1
  71. package/dist/file-observation.d.ts.map +1 -1
  72. package/dist/file-store-boundary.d.ts +6 -2
  73. package/dist/file-store-boundary.d.ts.map +1 -1
  74. package/dist/file-store-boundary.js +20 -65
  75. package/dist/file-store-copy-source.d.ts +5 -0
  76. package/dist/file-store-copy-source.d.ts.map +1 -0
  77. package/dist/file-store-copy-source.js +31 -0
  78. package/dist/file-store-path.d.ts.map +1 -1
  79. package/dist/file-store-path.js +4 -1
  80. package/dist/file-store-sync-directory.d.ts +16 -0
  81. package/dist/file-store-sync-directory.d.ts.map +1 -0
  82. package/dist/file-store-sync-directory.js +349 -0
  83. package/dist/file-store.d.ts.map +1 -1
  84. package/dist/file-store.js +11 -28
  85. package/dist/filename.d.ts.map +1 -1
  86. package/dist/filename.js +65 -20
  87. package/dist/fs.d.ts.map +1 -1
  88. package/dist/fs.js +3 -2
  89. package/dist/guarded-mkdir.d.ts +8 -0
  90. package/dist/guarded-mkdir.d.ts.map +1 -1
  91. package/dist/guarded-mkdir.js +176 -27
  92. package/dist/guest.d.ts.map +1 -1
  93. package/dist/guest.js +4 -1
  94. package/dist/home-dir.d.ts.map +1 -1
  95. package/dist/home-dir.js +73 -10
  96. package/dist/install-path.d.ts.map +1 -1
  97. package/dist/install-path.js +54 -19
  98. package/dist/json-durable-queue-ownership.d.ts +6 -0
  99. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  100. package/dist/json-durable-queue-ownership.js +89 -42
  101. package/dist/json-durable-queue-paths.d.ts +11 -0
  102. package/dist/json-durable-queue-paths.d.ts.map +1 -0
  103. package/dist/json-durable-queue-paths.js +42 -0
  104. package/dist/json-durable-queue-read.d.ts.map +1 -1
  105. package/dist/json-durable-queue-read.js +2 -0
  106. package/dist/json-durable-queue.d.ts.map +1 -1
  107. package/dist/json-durable-queue.js +50 -58
  108. package/dist/json-store.d.ts.map +1 -1
  109. package/dist/json-store.js +5 -1
  110. package/dist/json.d.ts.map +1 -1
  111. package/dist/json.js +54 -22
  112. package/dist/local-file-access.d.ts.map +1 -1
  113. package/dist/local-file-access.js +4 -0
  114. package/dist/local-file-descriptor.d.ts +17 -0
  115. package/dist/local-file-descriptor.d.ts.map +1 -0
  116. package/dist/local-file-descriptor.js +84 -0
  117. package/dist/local-roots.d.ts.map +1 -1
  118. package/dist/local-roots.js +35 -7
  119. package/dist/move-path-cleanup.d.ts +2 -0
  120. package/dist/move-path-cleanup.d.ts.map +1 -1
  121. package/dist/move-path-cleanup.js +44 -18
  122. package/dist/move-path.d.ts.map +1 -1
  123. package/dist/move-path.js +31 -6
  124. package/dist/native-binding.d.ts +17 -0
  125. package/dist/native-binding.d.ts.map +1 -1
  126. package/dist/native-binding.js +8 -1
  127. package/dist/native-directory-observation.d.ts +17 -0
  128. package/dist/native-directory-observation.d.ts.map +1 -0
  129. package/dist/native-directory-observation.js +37 -0
  130. package/dist/native-operations.d.ts.map +1 -1
  131. package/dist/native-operations.js +6 -4
  132. package/dist/native-parent-admission.d.ts +32 -0
  133. package/dist/native-parent-admission.d.ts.map +1 -0
  134. package/dist/native-parent-admission.js +127 -0
  135. package/dist/native-pinned-write-windows.d.ts +0 -1
  136. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  137. package/dist/native-pinned-write-windows.js +8 -9
  138. package/dist/native-pinned-write.d.ts.map +1 -1
  139. package/dist/native-pinned-write.js +314 -42
  140. package/dist/native-staged-file.d.ts +4 -4
  141. package/dist/native-staged-file.d.ts.map +1 -1
  142. package/dist/native-staged-file.js +20 -10
  143. package/dist/native.d.ts +1 -1
  144. package/dist/native.d.ts.map +1 -1
  145. package/dist/native.js +7 -2
  146. package/dist/output.d.ts.map +1 -1
  147. package/dist/output.js +15 -5
  148. package/dist/overwrite-file-handle.d.ts.map +1 -1
  149. package/dist/overwrite-file-handle.js +5 -1
  150. package/dist/owner-dacl.d.ts.map +1 -1
  151. package/dist/owner-dacl.js +2 -0
  152. package/dist/path-policy.d.ts.map +1 -1
  153. package/dist/path-policy.js +7 -2
  154. package/dist/path-prefix.d.ts +7 -0
  155. package/dist/path-prefix.d.ts.map +1 -0
  156. package/dist/path-prefix.js +82 -0
  157. package/dist/path-scope-lexical.d.ts.map +1 -1
  158. package/dist/path-scope-lexical.js +18 -8
  159. package/dist/path-segment-route.d.ts +7 -0
  160. package/dist/path-segment-route.d.ts.map +1 -0
  161. package/dist/path-segment-route.js +24 -0
  162. package/dist/path-suffix-aliases.d.ts +10 -0
  163. package/dist/path-suffix-aliases.d.ts.map +1 -0
  164. package/dist/path-suffix-aliases.js +386 -0
  165. package/dist/path.d.ts.map +1 -1
  166. package/dist/path.js +16 -5
  167. package/dist/permissions-windows.d.ts.map +1 -1
  168. package/dist/permissions-windows.js +14 -3
  169. package/dist/permissions.d.ts.map +1 -1
  170. package/dist/permissions.js +37 -8
  171. package/dist/pinned-mutation-admission.d.ts +25 -0
  172. package/dist/pinned-mutation-admission.d.ts.map +1 -0
  173. package/dist/pinned-mutation-admission.js +425 -0
  174. package/dist/pinned-mutation-observation.d.ts +34 -0
  175. package/dist/pinned-mutation-observation.d.ts.map +1 -0
  176. package/dist/pinned-mutation-observation.js +142 -0
  177. package/dist/pinned-mutation-shared-route.d.ts +24 -0
  178. package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
  179. package/dist/pinned-mutation-shared-route.js +70 -0
  180. package/dist/pinned-open.d.ts +6 -0
  181. package/dist/pinned-open.d.ts.map +1 -1
  182. package/dist/pinned-open.js +28 -10
  183. package/dist/pinned-write-types.d.ts +75 -0
  184. package/dist/pinned-write-types.d.ts.map +1 -0
  185. package/dist/pinned-write-types.js +1 -0
  186. package/dist/pinned-write.d.ts +7 -33
  187. package/dist/pinned-write.d.ts.map +1 -1
  188. package/dist/pinned-write.js +151 -17
  189. package/dist/private-directory.d.ts.map +1 -1
  190. package/dist/private-directory.js +2 -0
  191. package/dist/private-producer-handoff.d.ts +16 -0
  192. package/dist/private-producer-handoff.d.ts.map +1 -0
  193. package/dist/private-producer-handoff.js +272 -0
  194. package/dist/private-temp-workspace.d.ts +2 -39
  195. package/dist/private-temp-workspace.d.ts.map +1 -1
  196. package/dist/private-temp-workspace.js +183 -77
  197. package/dist/publish-file.d.ts.map +1 -1
  198. package/dist/publish-file.js +38 -32
  199. package/dist/regular-file.d.ts.map +1 -1
  200. package/dist/regular-file.js +56 -45
  201. package/dist/replace-directory.d.ts.map +1 -1
  202. package/dist/replace-directory.js +12 -5
  203. package/dist/replace-file-temp-owner.d.ts +1 -0
  204. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  205. package/dist/replace-file-temp-owner.js +12 -1
  206. package/dist/replace-file.d.ts.map +1 -1
  207. package/dist/replace-file.js +24 -24
  208. package/dist/root-boundary.d.ts +4 -0
  209. package/dist/root-boundary.d.ts.map +1 -1
  210. package/dist/root-boundary.js +6 -1
  211. package/dist/root-context.d.ts +12 -1
  212. package/dist/root-context.d.ts.map +1 -1
  213. package/dist/root-context.js +57 -11
  214. package/dist/root-directory-creation.d.ts +13 -0
  215. package/dist/root-directory-creation.d.ts.map +1 -0
  216. package/dist/root-directory-creation.js +212 -0
  217. package/dist/root-directory-list.d.ts +16 -4
  218. package/dist/root-directory-list.d.ts.map +1 -1
  219. package/dist/root-directory-list.js +180 -39
  220. package/dist/root-directory.d.ts +23 -0
  221. package/dist/root-directory.d.ts.map +1 -0
  222. package/dist/root-directory.js +141 -0
  223. package/dist/root-errors.d.ts.map +1 -1
  224. package/dist/root-errors.js +12 -1
  225. package/dist/root-file-final-admission.d.ts +21 -0
  226. package/dist/root-file-final-admission.d.ts.map +1 -0
  227. package/dist/root-file-final-admission.js +83 -0
  228. package/dist/root-file.d.ts.map +1 -1
  229. package/dist/root-file.js +103 -24
  230. package/dist/root-impl.d.ts +1 -0
  231. package/dist/root-impl.d.ts.map +1 -1
  232. package/dist/root-impl.js +289 -317
  233. package/dist/root-move-noreplace.d.ts +14 -0
  234. package/dist/root-move-noreplace.d.ts.map +1 -0
  235. package/dist/root-move-noreplace.js +202 -0
  236. package/dist/root-observed-path.d.ts +9 -0
  237. package/dist/root-observed-path.d.ts.map +1 -0
  238. package/dist/root-observed-path.js +95 -0
  239. package/dist/root-path-errors.d.ts +12 -0
  240. package/dist/root-path-errors.d.ts.map +1 -0
  241. package/dist/root-path-errors.js +13 -0
  242. package/dist/root-path-existing.d.ts.map +1 -1
  243. package/dist/root-path-existing.js +53 -20
  244. package/dist/root-path-observation.d.ts +63 -0
  245. package/dist/root-path-observation.d.ts.map +1 -0
  246. package/dist/root-path-observation.js +180 -0
  247. package/dist/root-path-stat.d.ts +5 -0
  248. package/dist/root-path-stat.d.ts.map +1 -0
  249. package/dist/root-path-stat.js +101 -0
  250. package/dist/root-path-symlink.d.ts.map +1 -1
  251. package/dist/root-path-symlink.js +23 -4
  252. package/dist/root-path.d.ts +11 -0
  253. package/dist/root-path.d.ts.map +1 -1
  254. package/dist/root-path.js +215 -53
  255. package/dist/root-paths-lexical.d.ts +9 -0
  256. package/dist/root-paths-lexical.d.ts.map +1 -0
  257. package/dist/root-paths-lexical.js +22 -0
  258. package/dist/root-paths.d.ts +3 -25
  259. package/dist/root-paths.d.ts.map +1 -1
  260. package/dist/root-paths.js +77 -154
  261. package/dist/root-read-admission.d.ts +26 -0
  262. package/dist/root-read-admission.d.ts.map +1 -0
  263. package/dist/root-read-admission.js +94 -0
  264. package/dist/root-remove-identity.d.ts +15 -0
  265. package/dist/root-remove-identity.d.ts.map +1 -0
  266. package/dist/root-remove-identity.js +89 -0
  267. package/dist/root-remove-receipt.d.ts +17 -0
  268. package/dist/root-remove-receipt.d.ts.map +1 -0
  269. package/dist/root-remove-receipt.js +37 -0
  270. package/dist/root-remove.d.ts +2 -1
  271. package/dist/root-remove.d.ts.map +1 -1
  272. package/dist/root-remove.js +172 -10
  273. package/dist/root-write-admission.d.ts +68 -0
  274. package/dist/root-write-admission.d.ts.map +1 -0
  275. package/dist/root-write-admission.js +322 -0
  276. package/dist/root-write-compatibility.d.ts +13 -0
  277. package/dist/root-write-compatibility.d.ts.map +1 -0
  278. package/dist/root-write-compatibility.js +74 -0
  279. package/dist/root-write-complete-parent.d.ts +42 -0
  280. package/dist/root-write-complete-parent.d.ts.map +1 -0
  281. package/dist/root-write-complete-parent.js +195 -0
  282. package/dist/root-write-publication.d.ts +24 -0
  283. package/dist/root-write-publication.d.ts.map +1 -0
  284. package/dist/root-write-publication.js +85 -0
  285. package/dist/root-write-verification.d.ts.map +1 -1
  286. package/dist/root-write-verification.js +6 -4
  287. package/dist/secret-file.d.ts.map +1 -1
  288. package/dist/secret-file.js +62 -24
  289. package/dist/secret-read-async.d.ts.map +1 -1
  290. package/dist/secret-read-async.js +18 -13
  291. package/dist/secret-read-policy.d.ts.map +1 -1
  292. package/dist/secret-read-policy.js +5 -1
  293. package/dist/secure-file.d.ts.map +1 -1
  294. package/dist/secure-file.js +92 -7
  295. package/dist/secure-temp-dir.d.ts +6 -3
  296. package/dist/secure-temp-dir.d.ts.map +1 -1
  297. package/dist/secure-temp-dir.js +121 -101
  298. package/dist/secure-temp-repair.d.ts +35 -0
  299. package/dist/secure-temp-repair.d.ts.map +1 -0
  300. package/dist/secure-temp-repair.js +106 -0
  301. package/dist/sibling-staged-file.d.ts +3 -2
  302. package/dist/sibling-staged-file.d.ts.map +1 -1
  303. package/dist/sibling-staged-file.js +179 -55
  304. package/dist/sibling-temp.d.ts.map +1 -1
  305. package/dist/sibling-temp.js +27 -13
  306. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  307. package/dist/sidecar-lock-acquire.js +44 -14
  308. package/dist/sidecar-lock-root.d.ts.map +1 -1
  309. package/dist/sidecar-lock-root.js +4 -2
  310. package/dist/staged-directory.d.ts +14 -5
  311. package/dist/staged-directory.d.ts.map +1 -1
  312. package/dist/staged-directory.js +54 -3
  313. package/dist/standalone-publication-path.d.ts +2 -0
  314. package/dist/standalone-publication-path.d.ts.map +1 -0
  315. package/dist/standalone-publication-path.js +6 -0
  316. package/dist/stat-observation.d.ts +11 -0
  317. package/dist/stat-observation.d.ts.map +1 -0
  318. package/dist/stat-observation.js +64 -0
  319. package/dist/strict-file-identity.d.ts.map +1 -1
  320. package/dist/strict-file-identity.js +39 -2
  321. package/dist/symlink-parents.d.ts.map +1 -1
  322. package/dist/symlink-parents.js +7 -2
  323. package/dist/temp-target.d.ts +2 -0
  324. package/dist/temp-target.d.ts.map +1 -1
  325. package/dist/temp-target.js +130 -3
  326. package/dist/temp-workspace-admission.d.ts +22 -0
  327. package/dist/temp-workspace-admission.d.ts.map +1 -0
  328. package/dist/temp-workspace-admission.js +374 -0
  329. package/dist/temp-workspace-child-admission.d.ts +13 -0
  330. package/dist/temp-workspace-child-admission.d.ts.map +1 -0
  331. package/dist/temp-workspace-child-admission.js +95 -0
  332. package/dist/temp-workspace-descriptor.d.ts +48 -0
  333. package/dist/temp-workspace-descriptor.d.ts.map +1 -0
  334. package/dist/temp-workspace-descriptor.js +363 -0
  335. package/dist/temp-workspace-identity.d.ts +15 -0
  336. package/dist/temp-workspace-identity.d.ts.map +1 -0
  337. package/dist/temp-workspace-identity.js +41 -0
  338. package/dist/temp-workspace-owner.d.ts +7 -6
  339. package/dist/temp-workspace-owner.d.ts.map +1 -1
  340. package/dist/temp-workspace-owner.js +121 -61
  341. package/dist/temp-workspace-permissions.d.ts +4 -0
  342. package/dist/temp-workspace-permissions.d.ts.map +1 -0
  343. package/dist/temp-workspace-permissions.js +32 -0
  344. package/dist/temp-workspace-types.d.ts +40 -0
  345. package/dist/temp-workspace-types.d.ts.map +1 -0
  346. package/dist/temp-workspace-types.js +1 -0
  347. package/dist/temp.d.ts +1 -1
  348. package/dist/temp.d.ts.map +1 -1
  349. package/dist/temp.js +1 -1
  350. package/dist/test-hooks.d.ts +7 -0
  351. package/dist/test-hooks.d.ts.map +1 -1
  352. package/dist/text-atomic.d.ts.map +1 -1
  353. package/dist/text-atomic.js +3 -1
  354. package/dist/trash.d.ts.map +1 -1
  355. package/dist/trash.js +54 -8
  356. package/dist/walk.d.ts.map +1 -1
  357. package/dist/walk.js +9 -6
  358. package/dist/windows-owner.d.ts.map +1 -1
  359. package/dist/windows-owner.js +5 -0
  360. package/dist/windows-path-alias.d.ts +39 -0
  361. package/dist/windows-path-alias.d.ts.map +1 -0
  362. package/dist/windows-path-alias.js +153 -0
  363. package/docs/advanced.md +25 -0
  364. package/docs/archive.md +23 -2
  365. package/docs/atomic.md +27 -3
  366. package/docs/copy.md +3 -0
  367. package/docs/durability.md +14 -0
  368. package/docs/errors.md +14 -6
  369. package/docs/file-store.md +24 -3
  370. package/docs/filename.md +14 -7
  371. package/docs/install-path.md +2 -2
  372. package/docs/install.md +8 -7
  373. package/docs/json-store.md +5 -1
  374. package/docs/json.md +11 -0
  375. package/docs/mutation-policy-proof.md +65 -0
  376. package/docs/native-helper.md +41 -11
  377. package/docs/native.md +32 -8
  378. package/docs/output.md +33 -11
  379. package/docs/path-prefix.md +64 -0
  380. package/docs/path-suffix-aliases.md +159 -0
  381. package/docs/path.md +11 -0
  382. package/docs/private-file-store.md +14 -0
  383. package/docs/public-api.md +9 -0
  384. package/docs/quickstart.md +1 -1
  385. package/docs/reading.md +8 -1
  386. package/docs/root.md +18 -3
  387. package/docs/secret-file.md +3 -0
  388. package/docs/secure-file.md +6 -2
  389. package/docs/security-model.md +75 -8
  390. package/docs/sidecar-lock.md +28 -1
  391. package/docs/store.md +5 -1
  392. package/docs/temp.md +260 -36
  393. package/docs/test-hooks.md +14 -0
  394. package/docs/writing.md +38 -8
  395. package/package.json +10 -10
@@ -1,16 +1,20 @@
1
- import { randomUUID } from "node:crypto";
2
- import fsSync from "node:fs";
1
+ import { randomInt, randomUUID } from "node:crypto";
2
+ import fsSync, {} from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { fileStore, fileStoreSync, } from "./file-store.js";
6
6
  import { FsSafeError } from "./errors.js";
7
7
  import { isNotFoundPathError } from "./path.js";
8
- import { realpathSync } from "./realpath.js";
9
- import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
8
+ import { inspectDirectoryIdentitySync } from "./directory-guard.js";
10
9
  import { throwFsSafeReadError } from "./root-errors.js";
11
10
  import { matchRootFileOpenFailure, openRootFileSync, } from "./root-file.js";
12
11
  import { registerTempPathForExit, } from "./temp-cleanup.js";
13
12
  import { TempWorkspaceCleanupCapability, TempWorkspaceCleanupOwner, } from "./temp-workspace-owner.js";
13
+ import { assertNoWindowsPathAlias, hasWindowsPathAlias, } from "./windows-path-alias.js";
14
+ import { TempWorkspaceRetainedChild } from "./temp-workspace-descriptor.js";
15
+ import { admitRetainedTempWorkspaceChild, admitRetainedTempWorkspaceChildSync, validateInitialTempWorkspaceChild, } from "./temp-workspace-child-admission.js";
16
+ import { admitTempWorkspaceRoot, admitTempWorkspaceRootSync, } from "./temp-workspace-admission.js";
17
+ import { validateTempWorkspaceDirMode } from "./temp-workspace-permissions.js";
14
18
  function resolveTempWorkspaceCleanupSafety(value) {
15
19
  if (value === undefined || value === "compatible")
16
20
  return "compatible";
@@ -25,8 +29,43 @@ function sanitizeTempPrefix(prefix) {
25
29
  }
26
30
  return sanitized.endsWith("-") ? sanitized : `${sanitized}-`;
27
31
  }
32
+ const TEMP_WORKSPACE_SUFFIX_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
33
+ const TEMP_WORKSPACE_SUFFIX_SPACE = TEMP_WORKSPACE_SUFFIX_ALPHABET.length ** 6;
34
+ const TEMP_WORKSPACE_DIRECT_CREATE_ATTEMPTS = 64;
35
+ function randomTempWorkspaceChildPath(childPrefix) {
36
+ let encoded = randomInt(TEMP_WORKSPACE_SUFFIX_SPACE);
37
+ let suffix = "";
38
+ for (let index = 0; index < 6; index += 1) {
39
+ suffix = TEMP_WORKSPACE_SUFFIX_ALPHABET[encoded % TEMP_WORKSPACE_SUFFIX_ALPHABET.length] + suffix;
40
+ encoded = Math.floor(encoded / TEMP_WORKSPACE_SUFFIX_ALPHABET.length);
41
+ }
42
+ return `${childPrefix}${suffix}`;
43
+ }
44
+ function canCreateTempWorkspaceWithRequestedMode(dirMode) {
45
+ return (process.platform === "linux" || process.platform === "darwin") && dirMode !== 0o700 &&
46
+ (dirMode & 0o700) === 0o700 && (dirMode & 0o7000) === 0 && (dirMode & 0o022) === 0;
47
+ }
48
+ function createTempWorkspaceWithRequestedModeSync(childPrefix, dirMode, prepareChildCreation) {
49
+ let collision;
50
+ for (let attempt = 0; attempt < TEMP_WORKSPACE_DIRECT_CREATE_ATTEMPTS; attempt += 1) {
51
+ const candidate = randomTempWorkspaceChildPath(childPrefix);
52
+ prepareChildCreation();
53
+ try {
54
+ fsSync.mkdirSync(candidate, { mode: dirMode, recursive: false });
55
+ return candidate;
56
+ }
57
+ catch (error) {
58
+ if (error.code !== "EEXIST")
59
+ throw error;
60
+ collision = error;
61
+ }
62
+ }
63
+ throw collision;
64
+ }
28
65
  function resolveWorkspaceLeaf(dir, fileName) {
29
- return path.join(dir, assertWorkspaceFileName(fileName));
66
+ const resolved = path.join(dir, assertWorkspaceFileName(fileName));
67
+ assertNoWindowsPathAlias(resolved, "filesystem", "temp workspace path uses a Windows filesystem namespace alias");
68
+ return resolved;
30
69
  }
31
70
  function assertWorkspaceFileName(fileName) {
32
71
  const value = fileName.trim();
@@ -36,6 +75,7 @@ function assertWorkspaceFileName(fileName) {
36
75
  value.includes("\0") ||
37
76
  value.includes("/") ||
38
77
  value.includes("\\") ||
78
+ hasWindowsPathAlias(value, "relative") ||
39
79
  path.basename(value) !== value) {
40
80
  throw new Error(`Invalid temp workspace file name: ${JSON.stringify(fileName)}`);
41
81
  }
@@ -67,55 +107,60 @@ function throwTempWorkspaceOpenFailure(failure) {
67
107
  },
68
108
  });
69
109
  }
70
- async function ensurePrivateDirectory(dir, mode) {
71
- await fs.mkdir(recursiveMkdirPath(dir), { recursive: true, mode });
72
- const stat = fsSync.statSync(dir);
73
- if (!stat.isDirectory()) {
74
- throw new Error(`Temp root must be a directory: ${dir}`);
75
- }
76
- await fs.chmod(dir, mode).catch(() => undefined);
77
- }
78
- function ensurePrivateDirectorySync(dir, mode) {
79
- fsSync.mkdirSync(recursiveMkdirPath(dir), { recursive: true, mode });
80
- const stat = fsSync.statSync(dir);
81
- if (!stat.isDirectory()) {
82
- throw new Error(`Temp root must be a directory: ${dir}`);
83
- }
84
- try {
85
- fsSync.chmodSync(dir, mode);
86
- }
87
- catch {
88
- // Best-effort on platforms that do not enforce POSIX modes.
89
- }
90
- }
91
- async function createTempWorkspace(options) {
110
+ async function createTempWorkspace(options, scopedPrefix = false) {
111
+ const rootDir = options.rootDir;
112
+ assertNoWindowsPathAlias(rootDir, "filesystem", "temp workspace root uses a Windows filesystem namespace alias");
92
113
  const dirMode = options.dirMode ?? 0o700;
114
+ validateTempWorkspaceDirMode(dirMode);
93
115
  const mode = options.mode ?? 0o600;
94
116
  const cleanupSafety = resolveTempWorkspaceCleanupSafety(options.cleanupSafety);
95
- const requestedRoot = path.resolve(options.rootDir);
96
- let root = requestedRoot;
97
- try {
98
- root = realpathSync.native(requestedRoot);
99
- }
100
- catch {
101
- root = requestedRoot;
102
- }
103
- await ensurePrivateDirectory(root, dirMode);
104
- const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety);
117
+ const admission = await admitTempWorkspaceRoot(rootDir);
118
+ const root = admission.dir;
119
+ assertNoWindowsPathAlias(root, "filesystem", "temp workspace root uses a Windows filesystem namespace alias");
120
+ // Capture and sanitize caller-controlled prefix data before retaining descriptors.
121
+ const rawPrefix = options.prefix;
122
+ const workspacePrefix = scopedPrefix
123
+ ? `${sanitizeTempPrefix(rawPrefix)}${randomUUID()}-`
124
+ : rawPrefix;
125
+ const childPrefix = path.join(root, sanitizeTempPrefix(workspacePrefix));
126
+ assertNoWindowsPathAlias(childPrefix, "filesystem", "temp workspace path uses a Windows filesystem namespace alias");
127
+ const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety, admission, dirMode);
105
128
  let dir;
106
129
  let stat;
130
+ let retainedChild;
131
+ let retainChildDescriptor = false;
107
132
  let cleanupOwner;
108
133
  let unregisterTempDir;
109
134
  try {
110
- dir = await fs.mkdtemp(path.join(root, sanitizeTempPrefix(options.prefix)));
111
- await fs.chmod(dir, dirMode).catch(() => undefined);
112
- stat = fsSync.lstatSync(dir, { bigint: true });
113
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
114
- throw new Error(`Temp workspace must be a directory: ${dir}`);
115
- }
135
+ // Native capability discovery is complete before this synchronous
136
+ // boundary. The existing canonical-root route now captures the complete
137
+ // ancestry and associates its provisional parent descriptor here, then
138
+ // dispatches mkdtemp without yielding or invoking another probe.
139
+ capability.prepareChildCreation();
140
+ dir = await fs.mkdtemp(childPrefix);
141
+ assertNoWindowsPathAlias(dir, "filesystem", "temp workspace path uses a Windows filesystem namespace alias");
116
142
  if (capability.parent)
117
143
  capability.assertCurrent();
118
- cleanupOwner = new TempWorkspaceCleanupOwner(dir, stat, capability);
144
+ else
145
+ admission.assertCurrent();
146
+ stat = inspectDirectoryIdentitySync(dir);
147
+ const needsModeInitialization = validateInitialTempWorkspaceChild(stat, admission.ownerUid, dirMode);
148
+ // Retain while the child still has its private creation mode so an
149
+ // explicit dirMode such as 0 cannot make identity descriptor acquisition fail.
150
+ retainedChild = TempWorkspaceRetainedChild.retain(dir, stat);
151
+ const modeInitialization = admitRetainedTempWorkspaceChild(retainedChild, needsModeInitialization, admission, dirMode);
152
+ if (modeInitialization)
153
+ await modeInitialization;
154
+ retainChildDescriptor = capability.admitChildDescriptor(retainedChild.ensureReadable());
155
+ // Final adoption order is deliberate: complete ancestry, retained cleanup
156
+ // parent, then descriptor and named checks of the original child identity.
157
+ if (capability.parent)
158
+ capability.assertAncestryCurrent();
159
+ else
160
+ admission.assertAncestry();
161
+ stat = retainedChild.finalizeAdmission(admission.ownerUid, dirMode);
162
+ cleanupOwner = new TempWorkspaceCleanupOwner(retainedChild, capability, retainChildDescriptor);
163
+ retainedChild = undefined;
119
164
  unregisterTempDir = registerTempPathForExit(dir, {
120
165
  cleanupSync: () => cleanupOwner.cleanupSync(),
121
166
  });
@@ -124,8 +169,26 @@ async function createTempWorkspace(options) {
124
169
  try {
125
170
  if (cleanupOwner)
126
171
  cleanupOwner.cleanupSync();
127
- else
128
- capability.close();
172
+ else {
173
+ const closeErrors = [];
174
+ try {
175
+ retainedChild?.close();
176
+ }
177
+ catch (closeError) {
178
+ closeErrors.push(closeError);
179
+ }
180
+ try {
181
+ capability.close();
182
+ }
183
+ catch (closeError) {
184
+ closeErrors.push(closeError);
185
+ }
186
+ if (closeErrors.length === 1)
187
+ throw closeErrors[0];
188
+ if (closeErrors.length > 1) {
189
+ throw new AggregateError(closeErrors, "temp workspace admission descriptor close failed");
190
+ }
191
+ }
129
192
  }
130
193
  catch (cleanupError) {
131
194
  throw new AggregateError([error, cleanupError], "temp workspace creation and cleanup both failed");
@@ -178,10 +241,7 @@ export async function tempWorkspace(options) {
178
241
  return await createTempWorkspace(options);
179
242
  }
180
243
  export async function withTempWorkspace(options, run) {
181
- const workspace = await createTempWorkspace({
182
- ...options,
183
- prefix: `${sanitizeTempPrefix(options.prefix)}${randomUUID()}-`,
184
- });
244
+ const workspace = await createTempWorkspace(options, true);
185
245
  try {
186
246
  return await run(workspace);
187
247
  }
@@ -189,39 +249,67 @@ export async function withTempWorkspace(options, run) {
189
249
  await workspace.cleanup();
190
250
  }
191
251
  }
192
- export function tempWorkspaceSync(options) {
252
+ function createTempWorkspaceSync(options, scopedPrefix = false) {
253
+ const rootDir = options.rootDir;
254
+ assertNoWindowsPathAlias(rootDir, "filesystem", "temp workspace root uses a Windows filesystem namespace alias");
193
255
  const dirMode = options.dirMode ?? 0o700;
256
+ validateTempWorkspaceDirMode(dirMode);
194
257
  const mode = options.mode ?? 0o600;
195
258
  const cleanupSafety = resolveTempWorkspaceCleanupSafety(options.cleanupSafety);
196
- const requestedRoot = path.resolve(options.rootDir);
197
- let root = requestedRoot;
198
- try {
199
- root = realpathSync.native(requestedRoot);
200
- }
201
- catch {
202
- root = requestedRoot;
203
- }
204
- ensurePrivateDirectorySync(root, dirMode);
205
- const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety);
259
+ const admission = admitTempWorkspaceRootSync(rootDir);
260
+ const root = admission.dir;
261
+ assertNoWindowsPathAlias(root, "filesystem", "temp workspace root uses a Windows filesystem namespace alias");
262
+ // Capture and sanitize caller-controlled prefix data before retaining descriptors.
263
+ const rawPrefix = options.prefix;
264
+ const workspacePrefix = scopedPrefix
265
+ ? `${sanitizeTempPrefix(rawPrefix)}${randomUUID()}-`
266
+ : rawPrefix;
267
+ const childPrefix = path.join(root, sanitizeTempPrefix(workspacePrefix));
268
+ assertNoWindowsPathAlias(childPrefix, "filesystem", "temp workspace path uses a Windows filesystem namespace alias");
269
+ const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety, admission, dirMode);
206
270
  let dir;
207
271
  let stat;
272
+ let retainedChild;
273
+ let retainChildDescriptor = false;
274
+ let needsModeInitialization;
208
275
  let cleanupOwner;
209
276
  let unregisterTempDir;
210
277
  try {
211
- dir = fsSync.mkdtempSync(path.join(root, sanitizeTempPrefix(options.prefix)));
212
- try {
213
- fsSync.chmodSync(dir, dirMode);
278
+ const directRequestedMode = canCreateTempWorkspaceWithRequestedMode(dirMode);
279
+ if (directRequestedMode) {
280
+ dir = createTempWorkspaceWithRequestedModeSync(childPrefix, dirMode, () => capability.prepareChildCreation());
214
281
  }
215
- catch {
216
- // Best-effort on platforms that do not enforce POSIX modes.
217
- }
218
- stat = fsSync.lstatSync(dir, { bigint: true });
219
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
220
- throw new Error(`Temp workspace must be a directory: ${dir}`);
282
+ else {
283
+ capability.prepareChildCreation();
284
+ dir = fsSync.mkdtempSync(childPrefix);
221
285
  }
286
+ assertNoWindowsPathAlias(dir, "filesystem", "temp workspace path uses a Windows filesystem namespace alias");
222
287
  if (capability.parent)
223
288
  capability.assertCurrent();
224
- cleanupOwner = new TempWorkspaceCleanupOwner(dir, stat, capability);
289
+ else
290
+ admission.assertCurrent();
291
+ if (directRequestedMode) {
292
+ const created = TempWorkspaceRetainedChild.retainCreated(dir);
293
+ retainedChild = created.retained;
294
+ stat = created.stat;
295
+ needsModeInitialization = validateInitialTempWorkspaceChild(stat, admission.ownerUid, dirMode);
296
+ }
297
+ else {
298
+ stat = inspectDirectoryIdentitySync(dir);
299
+ needsModeInitialization = validateInitialTempWorkspaceChild(stat, admission.ownerUid, dirMode);
300
+ retainedChild = TempWorkspaceRetainedChild.retain(dir, stat);
301
+ }
302
+ admitRetainedTempWorkspaceChildSync(retainedChild, needsModeInitialization, admission, dirMode);
303
+ retainChildDescriptor = capability.admitChildDescriptor(retainedChild.ensureReadable());
304
+ // Match async adoption: complete ancestry and retained cleanup authority
305
+ // precede descriptor and named child security-state checks.
306
+ if (capability.parent)
307
+ capability.assertAncestryCurrent();
308
+ else
309
+ admission.assertAncestry();
310
+ stat = retainedChild.finalizeAdmission(admission.ownerUid, dirMode);
311
+ cleanupOwner = new TempWorkspaceCleanupOwner(retainedChild, capability, retainChildDescriptor);
312
+ retainedChild = undefined;
225
313
  unregisterTempDir = registerTempPathForExit(dir, {
226
314
  cleanupSync: () => cleanupOwner.cleanupSync(),
227
315
  });
@@ -230,8 +318,26 @@ export function tempWorkspaceSync(options) {
230
318
  try {
231
319
  if (cleanupOwner)
232
320
  cleanupOwner.cleanupSync();
233
- else
234
- capability.close();
321
+ else {
322
+ const closeErrors = [];
323
+ try {
324
+ retainedChild?.close();
325
+ }
326
+ catch (closeError) {
327
+ closeErrors.push(closeError);
328
+ }
329
+ try {
330
+ capability.close();
331
+ }
332
+ catch (closeError) {
333
+ closeErrors.push(closeError);
334
+ }
335
+ if (closeErrors.length === 1)
336
+ throw closeErrors[0];
337
+ if (closeErrors.length > 1) {
338
+ throw new AggregateError(closeErrors, "temp workspace admission descriptor close failed");
339
+ }
340
+ }
235
341
  }
236
342
  catch (cleanupError) {
237
343
  throw new AggregateError([error, cleanupError], "temp workspace creation and cleanup both failed");
@@ -293,11 +399,11 @@ export function tempWorkspaceSync(options) {
293
399
  },
294
400
  };
295
401
  }
402
+ export function tempWorkspaceSync(options) {
403
+ return createTempWorkspaceSync(options);
404
+ }
296
405
  export function withTempWorkspaceSync(options, run) {
297
- const workspace = tempWorkspaceSync({
298
- ...options,
299
- prefix: `${sanitizeTempPrefix(options.prefix)}${randomUUID()}-`,
300
- });
406
+ const workspace = createTempWorkspaceSync(options, true);
301
407
  try {
302
408
  return run(workspace);
303
409
  }
@@ -1 +1 @@
1
- {"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GACpC,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AA2ND,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,aAAa,CAAC,EAAE,qCAAqC,CAAC;IACtD,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4MtC"}
1
+ {"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAKnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GAAG,cAAc,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEjG,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAIF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AA0ND,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,aAAa,CAAC,EAAE,qCAAqC,CAAC;IACtD,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA6NtC"}
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import fsSync, {} from "node:fs";
3
- import fs from "node:fs/promises";
3
+ import fs, {} from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { pinDirectory, } from "./directory-durability.js";
6
6
  import { FsSafeError } from "./errors.js";
@@ -8,30 +8,18 @@ import { hashFileHandle } from "./file-hash.js";
8
8
  import { sameFileIdentity, sameFileIdentityForCleanup, } from "./file-identity.js";
9
9
  import { syncFileBestEffortSync } from "./file-sync.js";
10
10
  import { getNativeBinding, requireNativeBinding } from "./native.js";
11
+ import { captureNativeFdClose } from "./native-binding.js";
11
12
  import { resolveReadOpenFlags } from "./read-open-flags.js";
12
13
  import { directorySyncFailure, publicationFailure, rememberCreatedTarget, } from "./publish-file-failure.js";
14
+ import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
13
15
  import { getFsSafeTestHooks } from "./test-hooks.js";
14
- const HARDLINK_FALLBACK_CODES = new Set([
15
- "EPERM",
16
- "EXDEV",
17
- "ENOTSUP",
18
- "EOPNOTSUPP",
19
- "ENOSYS",
20
- ]);
21
- const NATIVE_COPY_FALLBACK_CODES = new Set([
22
- "EINVAL",
23
- "ENOSYS",
24
- "ENOTSUP",
25
- "EOPNOTSUPP",
26
- "EPERM",
27
- "EXDEV",
28
- ]);
16
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
17
+ const HARDLINK_FALLBACK_CODES = new Set(["EPERM", "EXDEV", "ENOTSUP", "EOPNOTSUPP", "ENOSYS"]);
18
+ const NATIVE_COPY_FALLBACK_CODES = new Set(["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EPERM", "EXDEV"]);
29
19
  export function isHardlinkFallbackError(error) {
30
20
  return HARDLINK_FALLBACK_CODES.has(error?.code ?? "");
31
21
  }
32
- function sourceOpenFlags() {
33
- return resolveReadOpenFlags();
34
- }
22
+ function sourceOpenFlags() { return resolveReadOpenFlags(); }
35
23
  function directoryOpenFlags() {
36
24
  return (fsSync.constants.O_RDONLY |
37
25
  (typeof fsSync.constants.O_DIRECTORY === "number" ? fsSync.constants.O_DIRECTORY : 0));
@@ -67,6 +55,7 @@ async function assertPinnedSourceCurrent(params) {
67
55
  }
68
56
  async function copyPinnedSource(params) {
69
57
  if (params.native && params.targetNativeParent) {
58
+ const closeFd = captureNativeFdClose(params.native);
70
59
  for (const method of ["clone", "copy-file-range"]) {
71
60
  let nativeFd;
72
61
  try {
@@ -108,7 +97,7 @@ async function copyPinnedSource(params) {
108
97
  const hashed = await hashFileHandle(target, params.native);
109
98
  const completedFd = nativeFd;
110
99
  nativeFd = undefined;
111
- fsSync.closeSync(completedFd);
100
+ closeFd(completedFd);
112
101
  return {
113
102
  handle: target,
114
103
  exactIdentity: opened,
@@ -122,7 +111,7 @@ async function copyPinnedSource(params) {
122
111
  }
123
112
  finally {
124
113
  if (nativeFd !== undefined)
125
- fsSync.closeSync(nativeFd);
114
+ closeFd(nativeFd);
126
115
  }
127
116
  }
128
117
  }
@@ -190,12 +179,27 @@ async function syncPublishedParent(params) {
190
179
  }
191
180
  }
192
181
  export async function publishFileExclusive(params) {
193
- const sourcePath = path.resolve(params.sourcePath);
194
- const targetPath = path.resolve(params.targetPath);
182
+ const sourcePathInput = admitStandalonePublicationPath(params.sourcePath, "publication source uses a Windows filesystem namespace alias");
183
+ const targetPathInput = admitStandalonePublicationPath(params.targetPath, "publication target uses a Windows filesystem namespace alias");
184
+ const parentReceiptInput = params.parentReceipt;
185
+ const parentReceipt = parentReceiptInput
186
+ ? { path: parentReceiptInput.path, realPath: parentReceiptInput.realPath,
187
+ identity: parentReceiptInput.identity }
188
+ : undefined;
189
+ if (parentReceipt) {
190
+ assertNoWindowsPathAlias(parentReceipt.path, "filesystem", "publication parent uses a Windows filesystem namespace alias");
191
+ }
192
+ const sourcePath = path.resolve(sourcePathInput);
193
+ const targetPath = path.resolve(targetPathInput);
194
+ assertNoWindowsPathAlias(sourcePath, "filesystem", "publication source uses a Windows filesystem namespace alias");
195
+ assertNoWindowsPathAlias(targetPath, "filesystem", "publication target uses a Windows filesystem namespace alias");
195
196
  const parentPath = path.dirname(targetPath);
196
- if (params.parentReceipt && path.resolve(params.parentReceipt.path) !== parentPath) {
197
+ if (parentReceipt && path.resolve(parentReceipt.path) !== parentPath) {
197
198
  throw new FsSafeError("path-mismatch", "publication parent receipt does not match target parent");
198
199
  }
200
+ if (parentReceipt) {
201
+ assertNoWindowsPathAlias(parentReceipt.realPath, "filesystem", "publication parent uses a Windows filesystem namespace alias");
202
+ }
199
203
  const sourcePathStat = fsSync.lstatSync(sourcePath);
200
204
  if (sourcePathStat.isSymbolicLink() || !sourcePathStat.isFile()) {
201
205
  throw new FsSafeError("not-file", "publication source must be a regular file");
@@ -204,38 +208,40 @@ export async function publishFileExclusive(params) {
204
208
  let parent;
205
209
  let sourceNativeParent;
206
210
  let targetNativeParent;
211
+ const strategy = params.strategy;
207
212
  const failure = {
208
- phase: params.strategy === "rename-noreplace" ? "rename-create" : "hardlink-create",
213
+ phase: strategy === "rename-noreplace" ? "rename-create" : "hardlink-create",
209
214
  targetCreated: false,
210
215
  preserveTarget: false,
211
216
  };
212
217
  try {
213
- parent = await pinDirectory(params.parentReceipt ?? parentPath, {
218
+ parent = await pinDirectory(parentReceipt ?? parentPath, {
214
219
  label: "publication parent",
215
220
  });
216
221
  const sourceIdentity = fsSync.fstatSync(source.fd);
217
222
  const sourceExactIdentity = fsSync.fstatSync(source.fd, { bigint: true });
218
223
  const sourcePathExactIdentity = fsSync.lstatSync(sourcePath, { bigint: true });
224
+ const expectedSourceIdentity = params.expectedSourceIdentity;
219
225
  if (sourcePathExactIdentity.isSymbolicLink() ||
220
226
  !sourcePathExactIdentity.isFile() ||
221
227
  !sameFileIdentity(sourcePathExactIdentity, sourceExactIdentity) ||
222
- (params.expectedSourceIdentity &&
223
- !sameFileIdentity(params.expectedSourceIdentity, typeof params.expectedSourceIdentity.dev === "bigint" ||
224
- typeof params.expectedSourceIdentity.ino === "bigint"
228
+ (expectedSourceIdentity &&
229
+ !sameFileIdentity(expectedSourceIdentity, typeof expectedSourceIdentity.dev === "bigint" ||
230
+ typeof expectedSourceIdentity.ino === "bigint"
225
231
  ? sourceExactIdentity
226
232
  : sourceIdentity))) {
227
233
  throw new FsSafeError("path-mismatch", "publication source identity did not match");
228
234
  }
229
235
  await parent.assertCurrent();
230
236
  await assertPinnedSourceCurrent({ sourcePath, handle: source, identity: sourceExactIdentity });
231
- const native = params.strategy === "rename-noreplace"
237
+ const native = strategy === "rename-noreplace"
232
238
  ? requireNativeBinding()
233
239
  : getNativeBinding();
234
240
  if (native) {
235
241
  sourceNativeParent = await openNativeParent(sourcePath);
236
242
  targetNativeParent = await openNativeParent(targetPath);
237
243
  }
238
- if (params.strategy === "rename-noreplace") {
244
+ if (strategy === "rename-noreplace") {
239
245
  const binding = requireNativeBinding();
240
246
  binding.renameNoReplace(sourceNativeParent.handle.fd, sourceNativeParent.basename, targetNativeParent.handle.fd, targetNativeParent.basename);
241
247
  rememberCreatedTarget(failure, sourceExactIdentity, "rename-verify");
@@ -301,7 +307,7 @@ export async function publishFileExclusive(params) {
301
307
  catch (error) {
302
308
  if (failure.targetCreated ||
303
309
  !isHardlinkFallbackError(error) ||
304
- params.strategy === "link-required") {
310
+ strategy === "link-required") {
305
311
  throw error;
306
312
  }
307
313
  }
@@ -1 +1 @@
1
- {"version":3,"file":"regular-file.d.ts","sourceRoot":"","sources":["../src/regular-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,MAAM,MAAM,SAAS,CAAC;AAmB7B,MAAM,MAAM,qBAAqB,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAExF,KAAK,8BAA8B,GAAG,IAAI,CACxC,OAAO,MAAM,CAAC,SAAS,EACvB,UAAU,GAAG,SAAS,GAAG,UAAU,CACpC,GACC,OAAO,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,8BAAiD,GAC3D,MAAM,CASR;AAaD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CActF;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,CAc3E;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CA6D3C;AA2DD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC;CACb,CAoCA;AAmBD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgFxF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAgF7E"}
1
+ {"version":3,"file":"regular-file.d.ts","sourceRoot":"","sources":["../src/regular-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,MAAM,MAAM,SAAS,CAAC;AAuB7B,MAAM,MAAM,qBAAqB,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAExF,KAAK,8BAA8B,GAAG,IAAI,CACxC,OAAO,MAAM,CAAC,SAAS,EACvB,UAAU,GAAG,SAAS,GAAG,UAAU,CACpC,GACC,OAAO,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,wBAAgB,6BAA6B,CAC3C,SAAS,GAAE,8BAAiD,GAC3D,MAAM,CASR;AAaD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAetF;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,CAe3E;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CA+D3C;AA2DD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC;CACb,CAsCA;AAmBD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFxF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAkF7E"}