@openclaw/fs-safe 0.12.0 → 0.13.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 (383) hide show
  1. package/CHANGELOG.md +61 -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/darwin-acl.d.ts +4 -0
  41. package/dist/darwin-acl.d.ts.map +1 -0
  42. package/dist/darwin-acl.js +24 -0
  43. package/dist/deny-mutations.d.ts.map +1 -1
  44. package/dist/deny-mutations.js +8 -2
  45. package/dist/directory-durability.d.ts.map +1 -1
  46. package/dist/directory-durability.js +67 -23
  47. package/dist/directory-entry-path.d.ts +3 -0
  48. package/dist/directory-entry-path.d.ts.map +1 -0
  49. package/dist/directory-entry-path.js +21 -0
  50. package/dist/directory-guard.d.ts +17 -1
  51. package/dist/directory-guard.d.ts.map +1 -1
  52. package/dist/directory-guard.js +134 -48
  53. package/dist/directory-mode-node.d.ts +12 -0
  54. package/dist/directory-mode-node.d.ts.map +1 -1
  55. package/dist/directory-mode-node.js +102 -4
  56. package/dist/effective-uid.d.ts +2 -0
  57. package/dist/effective-uid.d.ts.map +1 -0
  58. package/dist/effective-uid.js +25 -0
  59. package/dist/file-handle-transfer.d.ts.map +1 -1
  60. package/dist/file-handle-transfer.js +98 -27
  61. package/dist/file-hash.d.ts.map +1 -1
  62. package/dist/file-hash.js +3 -0
  63. package/dist/file-lock-sync.d.ts.map +1 -1
  64. package/dist/file-lock-sync.js +36 -6
  65. package/dist/file-lock.d.ts.map +1 -1
  66. package/dist/file-lock.js +29 -9
  67. package/dist/file-observation.d.ts +1 -1
  68. package/dist/file-observation.d.ts.map +1 -1
  69. package/dist/file-store-boundary.d.ts +6 -2
  70. package/dist/file-store-boundary.d.ts.map +1 -1
  71. package/dist/file-store-boundary.js +20 -65
  72. package/dist/file-store-copy-source.d.ts +5 -0
  73. package/dist/file-store-copy-source.d.ts.map +1 -0
  74. package/dist/file-store-copy-source.js +31 -0
  75. package/dist/file-store-path.d.ts.map +1 -1
  76. package/dist/file-store-path.js +4 -1
  77. package/dist/file-store-sync-directory.d.ts +16 -0
  78. package/dist/file-store-sync-directory.d.ts.map +1 -0
  79. package/dist/file-store-sync-directory.js +349 -0
  80. package/dist/file-store.d.ts.map +1 -1
  81. package/dist/file-store.js +11 -28
  82. package/dist/filename.d.ts.map +1 -1
  83. package/dist/filename.js +65 -20
  84. package/dist/fs.d.ts.map +1 -1
  85. package/dist/fs.js +3 -2
  86. package/dist/guarded-mkdir.d.ts +8 -0
  87. package/dist/guarded-mkdir.d.ts.map +1 -1
  88. package/dist/guarded-mkdir.js +176 -27
  89. package/dist/guest.d.ts.map +1 -1
  90. package/dist/guest.js +4 -1
  91. package/dist/home-dir.d.ts.map +1 -1
  92. package/dist/home-dir.js +73 -10
  93. package/dist/install-path.d.ts.map +1 -1
  94. package/dist/install-path.js +54 -19
  95. package/dist/json-durable-queue-ownership.d.ts +6 -0
  96. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  97. package/dist/json-durable-queue-ownership.js +89 -42
  98. package/dist/json-durable-queue-paths.d.ts +11 -0
  99. package/dist/json-durable-queue-paths.d.ts.map +1 -0
  100. package/dist/json-durable-queue-paths.js +42 -0
  101. package/dist/json-durable-queue-read.d.ts.map +1 -1
  102. package/dist/json-durable-queue-read.js +2 -0
  103. package/dist/json-durable-queue.d.ts.map +1 -1
  104. package/dist/json-durable-queue.js +50 -58
  105. package/dist/json-store.d.ts.map +1 -1
  106. package/dist/json-store.js +5 -1
  107. package/dist/json.d.ts.map +1 -1
  108. package/dist/json.js +54 -22
  109. package/dist/local-file-access.d.ts.map +1 -1
  110. package/dist/local-file-access.js +4 -0
  111. package/dist/local-file-descriptor.d.ts +17 -0
  112. package/dist/local-file-descriptor.d.ts.map +1 -0
  113. package/dist/local-file-descriptor.js +84 -0
  114. package/dist/local-roots.d.ts.map +1 -1
  115. package/dist/local-roots.js +35 -7
  116. package/dist/move-path-cleanup.d.ts +2 -0
  117. package/dist/move-path-cleanup.d.ts.map +1 -1
  118. package/dist/move-path-cleanup.js +44 -18
  119. package/dist/move-path.d.ts.map +1 -1
  120. package/dist/move-path.js +31 -6
  121. package/dist/native-binding.d.ts +14 -0
  122. package/dist/native-binding.d.ts.map +1 -1
  123. package/dist/native-directory-observation.d.ts +17 -0
  124. package/dist/native-directory-observation.d.ts.map +1 -0
  125. package/dist/native-directory-observation.js +37 -0
  126. package/dist/native-parent-admission.d.ts +31 -0
  127. package/dist/native-parent-admission.d.ts.map +1 -0
  128. package/dist/native-parent-admission.js +124 -0
  129. package/dist/native-pinned-write-windows.d.ts +0 -1
  130. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  131. package/dist/native-pinned-write-windows.js +0 -3
  132. package/dist/native-pinned-write.d.ts.map +1 -1
  133. package/dist/native-pinned-write.js +309 -40
  134. package/dist/output.d.ts.map +1 -1
  135. package/dist/output.js +15 -5
  136. package/dist/overwrite-file-handle.d.ts.map +1 -1
  137. package/dist/overwrite-file-handle.js +5 -1
  138. package/dist/owner-dacl.d.ts.map +1 -1
  139. package/dist/owner-dacl.js +2 -0
  140. package/dist/path-policy.d.ts.map +1 -1
  141. package/dist/path-policy.js +7 -2
  142. package/dist/path-prefix.d.ts +7 -0
  143. package/dist/path-prefix.d.ts.map +1 -0
  144. package/dist/path-prefix.js +82 -0
  145. package/dist/path-scope-lexical.d.ts.map +1 -1
  146. package/dist/path-scope-lexical.js +18 -8
  147. package/dist/path-segment-route.d.ts +7 -0
  148. package/dist/path-segment-route.d.ts.map +1 -0
  149. package/dist/path-segment-route.js +24 -0
  150. package/dist/path-suffix-aliases.d.ts +10 -0
  151. package/dist/path-suffix-aliases.d.ts.map +1 -0
  152. package/dist/path-suffix-aliases.js +386 -0
  153. package/dist/path.d.ts.map +1 -1
  154. package/dist/path.js +16 -5
  155. package/dist/permissions-windows.d.ts.map +1 -1
  156. package/dist/permissions-windows.js +14 -3
  157. package/dist/permissions.d.ts.map +1 -1
  158. package/dist/permissions.js +37 -8
  159. package/dist/pinned-mutation-admission.d.ts +25 -0
  160. package/dist/pinned-mutation-admission.d.ts.map +1 -0
  161. package/dist/pinned-mutation-admission.js +425 -0
  162. package/dist/pinned-mutation-observation.d.ts +34 -0
  163. package/dist/pinned-mutation-observation.d.ts.map +1 -0
  164. package/dist/pinned-mutation-observation.js +142 -0
  165. package/dist/pinned-mutation-shared-route.d.ts +24 -0
  166. package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
  167. package/dist/pinned-mutation-shared-route.js +70 -0
  168. package/dist/pinned-open.d.ts +6 -0
  169. package/dist/pinned-open.d.ts.map +1 -1
  170. package/dist/pinned-open.js +28 -10
  171. package/dist/pinned-write-types.d.ts +75 -0
  172. package/dist/pinned-write-types.d.ts.map +1 -0
  173. package/dist/pinned-write-types.js +1 -0
  174. package/dist/pinned-write.d.ts +7 -33
  175. package/dist/pinned-write.d.ts.map +1 -1
  176. package/dist/pinned-write.js +151 -17
  177. package/dist/private-directory.d.ts.map +1 -1
  178. package/dist/private-directory.js +2 -0
  179. package/dist/private-producer-handoff.d.ts +16 -0
  180. package/dist/private-producer-handoff.d.ts.map +1 -0
  181. package/dist/private-producer-handoff.js +272 -0
  182. package/dist/private-temp-workspace.d.ts +2 -39
  183. package/dist/private-temp-workspace.d.ts.map +1 -1
  184. package/dist/private-temp-workspace.js +183 -77
  185. package/dist/publish-file.d.ts.map +1 -1
  186. package/dist/publish-file.js +33 -29
  187. package/dist/regular-file.d.ts.map +1 -1
  188. package/dist/regular-file.js +56 -45
  189. package/dist/replace-directory.d.ts.map +1 -1
  190. package/dist/replace-directory.js +12 -5
  191. package/dist/replace-file-temp-owner.d.ts +1 -0
  192. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  193. package/dist/replace-file-temp-owner.js +12 -1
  194. package/dist/replace-file.d.ts.map +1 -1
  195. package/dist/replace-file.js +24 -24
  196. package/dist/root-boundary.d.ts +4 -0
  197. package/dist/root-boundary.d.ts.map +1 -1
  198. package/dist/root-boundary.js +6 -1
  199. package/dist/root-context.d.ts +12 -1
  200. package/dist/root-context.d.ts.map +1 -1
  201. package/dist/root-context.js +57 -11
  202. package/dist/root-directory-creation.d.ts +13 -0
  203. package/dist/root-directory-creation.d.ts.map +1 -0
  204. package/dist/root-directory-creation.js +212 -0
  205. package/dist/root-directory-list.d.ts +16 -4
  206. package/dist/root-directory-list.d.ts.map +1 -1
  207. package/dist/root-directory-list.js +180 -39
  208. package/dist/root-directory.d.ts +23 -0
  209. package/dist/root-directory.d.ts.map +1 -0
  210. package/dist/root-directory.js +141 -0
  211. package/dist/root-errors.d.ts.map +1 -1
  212. package/dist/root-errors.js +12 -1
  213. package/dist/root-file-final-admission.d.ts +21 -0
  214. package/dist/root-file-final-admission.d.ts.map +1 -0
  215. package/dist/root-file-final-admission.js +83 -0
  216. package/dist/root-file.d.ts.map +1 -1
  217. package/dist/root-file.js +103 -24
  218. package/dist/root-impl.d.ts +1 -0
  219. package/dist/root-impl.d.ts.map +1 -1
  220. package/dist/root-impl.js +289 -317
  221. package/dist/root-move-noreplace.d.ts +14 -0
  222. package/dist/root-move-noreplace.d.ts.map +1 -0
  223. package/dist/root-move-noreplace.js +202 -0
  224. package/dist/root-observed-path.d.ts +9 -0
  225. package/dist/root-observed-path.d.ts.map +1 -0
  226. package/dist/root-observed-path.js +95 -0
  227. package/dist/root-path-errors.d.ts +12 -0
  228. package/dist/root-path-errors.d.ts.map +1 -0
  229. package/dist/root-path-errors.js +13 -0
  230. package/dist/root-path-existing.d.ts.map +1 -1
  231. package/dist/root-path-existing.js +53 -20
  232. package/dist/root-path-observation.d.ts +63 -0
  233. package/dist/root-path-observation.d.ts.map +1 -0
  234. package/dist/root-path-observation.js +180 -0
  235. package/dist/root-path-stat.d.ts +5 -0
  236. package/dist/root-path-stat.d.ts.map +1 -0
  237. package/dist/root-path-stat.js +101 -0
  238. package/dist/root-path-symlink.d.ts.map +1 -1
  239. package/dist/root-path-symlink.js +23 -4
  240. package/dist/root-path.d.ts +11 -0
  241. package/dist/root-path.d.ts.map +1 -1
  242. package/dist/root-path.js +215 -53
  243. package/dist/root-paths-lexical.d.ts +9 -0
  244. package/dist/root-paths-lexical.d.ts.map +1 -0
  245. package/dist/root-paths-lexical.js +22 -0
  246. package/dist/root-paths.d.ts +3 -25
  247. package/dist/root-paths.d.ts.map +1 -1
  248. package/dist/root-paths.js +77 -154
  249. package/dist/root-read-admission.d.ts +26 -0
  250. package/dist/root-read-admission.d.ts.map +1 -0
  251. package/dist/root-read-admission.js +94 -0
  252. package/dist/root-remove-identity.d.ts +15 -0
  253. package/dist/root-remove-identity.d.ts.map +1 -0
  254. package/dist/root-remove-identity.js +89 -0
  255. package/dist/root-remove-receipt.d.ts +17 -0
  256. package/dist/root-remove-receipt.d.ts.map +1 -0
  257. package/dist/root-remove-receipt.js +37 -0
  258. package/dist/root-remove.d.ts +2 -1
  259. package/dist/root-remove.d.ts.map +1 -1
  260. package/dist/root-remove.js +172 -10
  261. package/dist/root-write-admission.d.ts +68 -0
  262. package/dist/root-write-admission.d.ts.map +1 -0
  263. package/dist/root-write-admission.js +322 -0
  264. package/dist/root-write-compatibility.d.ts +13 -0
  265. package/dist/root-write-compatibility.d.ts.map +1 -0
  266. package/dist/root-write-compatibility.js +74 -0
  267. package/dist/root-write-complete-parent.d.ts +42 -0
  268. package/dist/root-write-complete-parent.d.ts.map +1 -0
  269. package/dist/root-write-complete-parent.js +195 -0
  270. package/dist/root-write-publication.d.ts +24 -0
  271. package/dist/root-write-publication.d.ts.map +1 -0
  272. package/dist/root-write-publication.js +85 -0
  273. package/dist/root-write-verification.d.ts.map +1 -1
  274. package/dist/root-write-verification.js +6 -4
  275. package/dist/secret-file.d.ts.map +1 -1
  276. package/dist/secret-file.js +62 -24
  277. package/dist/secret-read-async.d.ts.map +1 -1
  278. package/dist/secret-read-async.js +18 -13
  279. package/dist/secret-read-policy.d.ts.map +1 -1
  280. package/dist/secret-read-policy.js +5 -1
  281. package/dist/secure-file.d.ts.map +1 -1
  282. package/dist/secure-file.js +92 -7
  283. package/dist/secure-temp-dir.d.ts +6 -3
  284. package/dist/secure-temp-dir.d.ts.map +1 -1
  285. package/dist/secure-temp-dir.js +121 -101
  286. package/dist/secure-temp-repair.d.ts +35 -0
  287. package/dist/secure-temp-repair.d.ts.map +1 -0
  288. package/dist/secure-temp-repair.js +106 -0
  289. package/dist/sibling-staged-file.d.ts +3 -2
  290. package/dist/sibling-staged-file.d.ts.map +1 -1
  291. package/dist/sibling-staged-file.js +179 -55
  292. package/dist/sibling-temp.d.ts.map +1 -1
  293. package/dist/sibling-temp.js +27 -13
  294. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  295. package/dist/sidecar-lock-acquire.js +44 -14
  296. package/dist/sidecar-lock-root.d.ts.map +1 -1
  297. package/dist/sidecar-lock-root.js +4 -2
  298. package/dist/staged-directory.d.ts +14 -5
  299. package/dist/staged-directory.d.ts.map +1 -1
  300. package/dist/staged-directory.js +54 -3
  301. package/dist/standalone-publication-path.d.ts +2 -0
  302. package/dist/standalone-publication-path.d.ts.map +1 -0
  303. package/dist/standalone-publication-path.js +6 -0
  304. package/dist/stat-observation.d.ts +11 -0
  305. package/dist/stat-observation.d.ts.map +1 -0
  306. package/dist/stat-observation.js +64 -0
  307. package/dist/strict-file-identity.d.ts.map +1 -1
  308. package/dist/strict-file-identity.js +39 -2
  309. package/dist/symlink-parents.d.ts.map +1 -1
  310. package/dist/symlink-parents.js +7 -2
  311. package/dist/temp-target.d.ts +2 -0
  312. package/dist/temp-target.d.ts.map +1 -1
  313. package/dist/temp-target.js +130 -3
  314. package/dist/temp-workspace-admission.d.ts +22 -0
  315. package/dist/temp-workspace-admission.d.ts.map +1 -0
  316. package/dist/temp-workspace-admission.js +374 -0
  317. package/dist/temp-workspace-child-admission.d.ts +13 -0
  318. package/dist/temp-workspace-child-admission.d.ts.map +1 -0
  319. package/dist/temp-workspace-child-admission.js +95 -0
  320. package/dist/temp-workspace-descriptor.d.ts +48 -0
  321. package/dist/temp-workspace-descriptor.d.ts.map +1 -0
  322. package/dist/temp-workspace-descriptor.js +363 -0
  323. package/dist/temp-workspace-identity.d.ts +15 -0
  324. package/dist/temp-workspace-identity.d.ts.map +1 -0
  325. package/dist/temp-workspace-identity.js +41 -0
  326. package/dist/temp-workspace-owner.d.ts +7 -6
  327. package/dist/temp-workspace-owner.d.ts.map +1 -1
  328. package/dist/temp-workspace-owner.js +121 -61
  329. package/dist/temp-workspace-permissions.d.ts +4 -0
  330. package/dist/temp-workspace-permissions.d.ts.map +1 -0
  331. package/dist/temp-workspace-permissions.js +32 -0
  332. package/dist/temp-workspace-types.d.ts +40 -0
  333. package/dist/temp-workspace-types.d.ts.map +1 -0
  334. package/dist/temp-workspace-types.js +1 -0
  335. package/dist/temp.d.ts +1 -1
  336. package/dist/temp.d.ts.map +1 -1
  337. package/dist/temp.js +1 -1
  338. package/dist/test-hooks.d.ts +7 -0
  339. package/dist/test-hooks.d.ts.map +1 -1
  340. package/dist/text-atomic.d.ts.map +1 -1
  341. package/dist/text-atomic.js +3 -1
  342. package/dist/trash.d.ts.map +1 -1
  343. package/dist/trash.js +54 -8
  344. package/dist/walk.d.ts.map +1 -1
  345. package/dist/walk.js +9 -6
  346. package/dist/windows-owner.d.ts.map +1 -1
  347. package/dist/windows-owner.js +5 -0
  348. package/dist/windows-path-alias.d.ts +39 -0
  349. package/dist/windows-path-alias.d.ts.map +1 -0
  350. package/dist/windows-path-alias.js +153 -0
  351. package/docs/advanced.md +25 -0
  352. package/docs/archive.md +23 -2
  353. package/docs/atomic.md +27 -3
  354. package/docs/copy.md +3 -0
  355. package/docs/durability.md +14 -0
  356. package/docs/errors.md +14 -6
  357. package/docs/file-store.md +24 -3
  358. package/docs/filename.md +14 -7
  359. package/docs/install-path.md +2 -2
  360. package/docs/install.md +8 -7
  361. package/docs/json-store.md +5 -1
  362. package/docs/json.md +11 -0
  363. package/docs/mutation-policy-proof.md +65 -0
  364. package/docs/native-helper.md +26 -9
  365. package/docs/native.md +32 -8
  366. package/docs/output.md +33 -11
  367. package/docs/path-prefix.md +64 -0
  368. package/docs/path-suffix-aliases.md +159 -0
  369. package/docs/path.md +11 -0
  370. package/docs/private-file-store.md +14 -0
  371. package/docs/public-api.md +9 -0
  372. package/docs/quickstart.md +1 -1
  373. package/docs/reading.md +8 -1
  374. package/docs/root.md +18 -3
  375. package/docs/secret-file.md +3 -0
  376. package/docs/secure-file.md +6 -2
  377. package/docs/security-model.md +75 -8
  378. package/docs/sidecar-lock.md +28 -1
  379. package/docs/store.md +5 -1
  380. package/docs/temp.md +260 -36
  381. package/docs/test-hooks.md +14 -0
  382. package/docs/writing.md +38 -8
  383. package/package.json +10 -10
@@ -0,0 +1,106 @@
1
+ import fs, {} from "node:fs";
2
+ export function captureSecureTempRepairAdapter(supplied, hasLegacyFsHooks) {
3
+ if (supplied === undefined && hasLegacyFsHooks)
4
+ return undefined;
5
+ if (supplied !== undefined && (supplied === null || (typeof supplied !== "object" && typeof supplied !== "function")))
6
+ return undefined;
7
+ const source = supplied === undefined ? fs : supplied;
8
+ const { lstatSync, fstatSync, openSync, fchmodSync, closeSync, constants } = source;
9
+ const { O_RDONLY, O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK } = constants ?? {};
10
+ if ([lstatSync, fstatSync, openSync, fchmodSync, closeSync].some((fn) => typeof fn !== "function") ||
11
+ O_RDONLY !== 0 || [O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK].some((flag) => !Number.isSafeInteger(flag) || flag <= 0 || flag > 0x7fff_ffff) ||
12
+ (O_DIRECTORY & O_NOFOLLOW) !== 0 || (O_DIRECTORY & O_NONBLOCK) !== 0 || (O_NOFOLLOW & O_NONBLOCK) !== 0) {
13
+ return undefined;
14
+ }
15
+ return { lstatSync, fstatSync, openSync, fchmodSync, closeSync, flags: O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK };
16
+ }
17
+ export function secureTempDirectoryReceipt(stat, uid) {
18
+ const { dev, ino, uid: owner, mode } = stat;
19
+ if (typeof dev !== "bigint" || dev < 0n || typeof ino !== "bigint" || ino <= 0n ||
20
+ typeof owner !== "bigint" || owner !== BigInt(uid) || typeof mode !== "bigint" ||
21
+ mode < 0n || mode > 0xffffffffn || (mode & 61440n) !== 16384n ||
22
+ stat.isDirectory() !== true || stat.isSymbolicLink() !== false) {
23
+ throw new Error("Secure temp directory identity, owner, or type could not be verified.");
24
+ }
25
+ return { dev, ino, uid: owner, mode };
26
+ }
27
+ function assertSameDirectory(stat, expected, uid) {
28
+ const observed = secureTempDirectoryReceipt(stat, uid);
29
+ if (observed.dev !== expected.dev || observed.ino !== expected.ino || observed.uid !== expected.uid) {
30
+ throw new Error("Secure temp directory changed during repair.");
31
+ }
32
+ return observed;
33
+ }
34
+ function mayHaveConcurrentRepair(error) {
35
+ if (typeof error !== "object" || error === null || !("code" in error))
36
+ return false;
37
+ const code = error.code;
38
+ return code === "EPERM" || code === "EACCES" || code === "ENOENT";
39
+ }
40
+ /** No pathname chmod, search-only descriptors, procfs, or reopening fallback. */
41
+ export function repairSecureTempDirectory(candidate, expected, uid, adapter, access, repaired, finalize = false) {
42
+ assertSameDirectory(adapter.lstatSync(candidate, { bigint: true }), expected, uid);
43
+ const fd = adapter.openSync(candidate, adapter.flags);
44
+ if (!Number.isSafeInteger(fd) || fd < 0)
45
+ throw new Error("Secure temp directory descriptor is invalid.");
46
+ const errors = [];
47
+ try {
48
+ const inspect = () => {
49
+ const opened = assertSameDirectory(adapter.fstatSync(fd, { bigint: true }), expected, uid);
50
+ const named = assertSameDirectory(adapter.lstatSync(candidate, { bigint: true }), expected, uid);
51
+ return { opened, named };
52
+ };
53
+ const verify = (requireExactMode) => {
54
+ const check = () => {
55
+ const { opened, named } = inspect();
56
+ for (const stat of [opened, named]) {
57
+ if ((stat.mode & 18n) !== 0n || (requireExactMode && (stat.mode & 4095n) !== 448n)) {
58
+ throw new Error("Secure temp directory permissions remain unsafe.");
59
+ }
60
+ }
61
+ };
62
+ check();
63
+ access();
64
+ check();
65
+ };
66
+ const { opened } = inspect();
67
+ const needsChmod = finalize ? (opened.mode & 4095n) !== 448n :
68
+ (opened.mode & 18n) !== 0n || (opened.mode & 448n) !== 448n;
69
+ let chmodError;
70
+ let chmodFailed = false;
71
+ if (needsChmod) {
72
+ try {
73
+ adapter.fchmodSync(fd, 0o700);
74
+ }
75
+ catch (error) {
76
+ if (!mayHaveConcurrentRepair(error))
77
+ throw error;
78
+ chmodFailed = true;
79
+ chmodError = error;
80
+ }
81
+ if (!chmodFailed)
82
+ repaired();
83
+ }
84
+ try {
85
+ verify(finalize || (needsChmod && !chmodFailed));
86
+ }
87
+ catch (error) {
88
+ if (chmodFailed)
89
+ throw new AggregateError([chmodError, error], "Secure temp directory chmod and verification failed.");
90
+ throw error;
91
+ }
92
+ }
93
+ catch (error) {
94
+ errors.push(error);
95
+ }
96
+ try {
97
+ adapter.closeSync(fd);
98
+ }
99
+ catch (error) {
100
+ errors.push(error);
101
+ }
102
+ if (errors.length > 1)
103
+ throw new AggregateError(errors, "Secure temp directory repair and close failed.");
104
+ if (errors.length === 1)
105
+ throw errors[0];
106
+ }
@@ -1,6 +1,7 @@
1
- export declare function assertCallbackTempPathDeviceSafe(tempPath: string): void;
1
+ export declare function resolveCallbackTempPath(workspaceDir: string, component: string): string;
2
2
  export declare function writeCallbackSibling<T>(params: {
3
- tempPath: string;
3
+ tempDir: string;
4
+ tempName: string;
4
5
  write: (tempPath: string) => Promise<T>;
5
6
  producerIsolation?: "private-directory";
6
7
  resolveFinalPath: (result: T) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"sibling-staged-file.d.ts","sourceRoot":"","sources":["../src/sibling-staged-file.ts"],"names":[],"mappings":"AAuCA,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIvE;AAmCD,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAgH3C"}
1
+ {"version":3,"file":"sibling-staged-file.d.ts","sourceRoot":"","sources":["../src/sibling-staged-file.ts"],"names":[],"mappings":"AAsCA,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAoBvF;AAsHD,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,MAAM,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAsK3C"}
@@ -6,12 +6,41 @@ import { assertAsyncDirectoryGuard, assertDirectoryIdentitySync, createAsyncDire
6
6
  import { syncDirectoryBestEffort } from "./directory-durability.js";
7
7
  import { isWindowsReservedDeviceName } from "./device-path.js";
8
8
  import { FsSafeError } from "./errors.js";
9
+ import { getNativeBinding } from "./native.js";
10
+ import { handoffPrivateProducerFile, } from "./private-producer-handoff.js";
9
11
  import { resolveReadOpenFlags } from "./read-open-flags.js";
10
- import { root } from "./root.js";
12
+ import { rootFromDirectoryGuard } from "./root-impl.js";
11
13
  import { inspectFileIdentity } from "./strict-file-identity.js";
12
14
  import { registerTempPathForExit } from "./temp-cleanup.js";
13
15
  import { createOwnedTempFile } from "./temp-target.js";
14
16
  import { serializePathWrite } from "./write-queue.js";
17
+ import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
18
+ const INVALID_CALLBACK_COMPONENT_CHARACTERS = /[\u0000-\u001f\u007f-\u009f<>:"/\\|?*]/u;
19
+ function resolveCallbackTempDirectory(workspaceDir) {
20
+ assertNoWindowsPathAlias(workspaceDir, "filesystem", "sibling temp parent uses a Windows filesystem namespace alias");
21
+ const dir = resolvePathPreservingWindowsRoot(workspaceDir);
22
+ assertNoWindowsPathAlias(dir, "filesystem", "sibling temp parent uses a Windows filesystem namespace alias");
23
+ return dir;
24
+ }
25
+ export function resolveCallbackTempPath(workspaceDir, component) {
26
+ const dir = resolveCallbackTempDirectory(workspaceDir);
27
+ if (typeof component !== "string" ||
28
+ component === "" ||
29
+ component === "." ||
30
+ component === ".." ||
31
+ INVALID_CALLBACK_COMPONENT_CHARACTERS.test(component) ||
32
+ component.endsWith(".") ||
33
+ component.endsWith(" ") ||
34
+ isWindowsReservedDeviceName(component)) {
35
+ throw new FsSafeError("invalid-path", "callback temp name must be one path component");
36
+ }
37
+ const joined = path.join(dir, component);
38
+ assertNoWindowsPathAlias(joined, "filesystem", "sibling temp path uses a Windows filesystem namespace alias");
39
+ if (path.dirname(joined) !== dir) {
40
+ throw new FsSafeError("invalid-path", "callback temp path must be a direct workspace child");
41
+ }
42
+ return joined;
43
+ }
15
44
  function assertRegularFile(stat) {
16
45
  if (stat.isSymbolicLink()) {
17
46
  throw new FsSafeError("symlink", "symlink sibling temp not allowed");
@@ -30,48 +59,102 @@ async function inspectStage(inspect, expected) {
30
59
  return stat;
31
60
  }, expected);
32
61
  }
33
- export function assertCallbackTempPathDeviceSafe(tempPath) {
34
- if (isWindowsReservedDeviceName(tempPath)) {
35
- throw new FsSafeError("invalid-path", "callback temp path uses a reserved Windows device name");
36
- }
62
+ function aggregateErrors(errors, message) {
63
+ if (errors.length === 1)
64
+ return errors[0];
65
+ return new AggregateError(errors, message);
37
66
  }
38
67
  // Own the workspace before the producer can leave partial output. The finished
39
68
  // file still enters the ordinary sibling admission and publication lifecycle.
40
69
  async function writeIsolatedProducer(params) {
41
- const targetRoot = await root(path.dirname(params.tempPath));
70
+ const tempPath = params.tempPath;
71
+ const write = params.write;
72
+ const writeReceiver = params.writeReceiver;
73
+ const parentGuard = params.parentGuard;
74
+ const assertParent = params.assertParent;
75
+ const syncTempFile = params.syncTempFile;
76
+ assertParent();
77
+ const targetRoot = rootFromDirectoryGuard(parentGuard);
78
+ // Select the handoff before invoking the producer. Missing required native
79
+ // support must not leave producer output behind as a discovery side effect.
80
+ const native = getNativeBinding();
81
+ const cleanupErrors = [];
42
82
  const { target, identity } = await createOwnedTempFile({
43
83
  rootDir: targetRoot.rootReal,
44
84
  prefix: "fs-safe-output",
45
- fileName: path.basename(params.tempPath),
85
+ fileName: path.basename(tempPath),
86
+ onCleanupError: (error) => cleanupErrors.push(error),
46
87
  });
47
- const assertCurrent = () => {
48
- params.assertParent();
88
+ let cleanupStarted = false;
89
+ const cleanupWorkspace = async () => {
90
+ if (cleanupStarted)
91
+ return;
92
+ cleanupStarted = true;
93
+ try {
94
+ await target.cleanup();
95
+ }
96
+ catch (error) {
97
+ cleanupErrors.push(error);
98
+ }
99
+ if (cleanupErrors.length > 0) {
100
+ throw aggregateErrors(cleanupErrors, "isolated producer workspace cleanup failed");
101
+ }
102
+ };
103
+ const assertWorkspace = () => {
49
104
  assertDirectoryIdentitySync(target.dir, { ...identity, realPath: target.dir });
50
105
  };
106
+ const assertCurrent = () => {
107
+ assertParent();
108
+ assertWorkspace();
109
+ };
51
110
  try {
111
+ const producerPath = resolveCallbackTempPath(target.dir, path.basename(target.path));
52
112
  assertCurrent();
53
- const result = await params.write(target.path);
113
+ const result = await Reflect.apply(write, writeReceiver, [producerPath]);
54
114
  assertCurrent();
55
- await targetRoot.move(path.relative(targetRoot.rootReal, target.path), path.basename(params.tempPath), {
56
- assertBeforeMutation: assertCurrent,
115
+ if (native) {
116
+ await targetRoot.move(path.relative(targetRoot.rootReal, producerPath), path.basename(tempPath), { assertBeforeMutation: assertCurrent });
117
+ return { cleanupWorkspace, result };
118
+ }
119
+ const handoff = await handoffPrivateProducerFile({
120
+ sourcePath: producerPath,
121
+ targetPath: tempPath,
122
+ assertSourceParent: assertWorkspace,
123
+ assertTargetParent: assertParent,
124
+ readWrite: syncTempFile,
57
125
  });
58
- return result;
126
+ return { cleanupWorkspace, handoff, result };
59
127
  }
60
- finally {
61
- await target.cleanup();
128
+ catch (error) {
129
+ try {
130
+ await cleanupWorkspace();
131
+ }
132
+ catch (cleanupError) {
133
+ throw new AggregateError([error, cleanupError], "isolated producer operation and workspace cleanup failed");
134
+ }
135
+ throw error;
62
136
  }
63
137
  }
64
138
  // Callback paths are not owned until all three admission observations agree.
65
139
  // Keep one descriptor and one exact identity through mode, sync, rename and cleanup.
66
140
  // Read/write access is needed only when the caller requests file synchronization.
67
141
  export async function writeCallbackSibling(params) {
68
- assertCallbackTempPathDeviceSafe(params.tempPath);
69
- const parent = path.dirname(params.tempPath);
142
+ const parent = resolveCallbackTempDirectory(params.tempDir);
143
+ const tempPath = resolveCallbackTempPath(parent, params.tempName);
144
+ const write = params.write;
145
+ const producerIsolation = params.producerIsolation;
146
+ const resolveFinalPath = params.resolveFinalPath;
147
+ const mode = params.mode;
148
+ const ignoreModeError = params.ignoreModeError;
149
+ const maxBytes = params.maxBytes;
150
+ const syncTempFile = params.syncTempFile;
151
+ const syncParentDir = params.syncParentDir;
70
152
  const guard = await createAsyncDirectoryGuard(parent, { bigint: true });
71
153
  const assertParent = () => assertAsyncDirectoryGuard(guard);
72
154
  let handle;
73
155
  let identity;
74
156
  let unregister;
157
+ let cleanupWorkspace;
75
158
  let renamed = false;
76
159
  let failure;
77
160
  const inspectPath = (pathname, expected) => inspectStage(() => fsSync.lstatSync(pathname, { bigint: true }), expected);
@@ -79,70 +162,98 @@ export async function writeCallbackSibling(params) {
79
162
  await assertParent();
80
163
  const opened = await inspectStage(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity);
81
164
  const current = await inspectPath(pathname, opened);
82
- if (params.maxBytes !== undefined &&
83
- (opened.size > params.maxBytes || current.size > params.maxBytes)) {
84
- throw new FsSafeError("too-large", `sibling temp exceeds maxBytes (${params.maxBytes})`);
165
+ if (maxBytes !== undefined &&
166
+ (opened.size > maxBytes || current.size > maxBytes)) {
167
+ throw new FsSafeError("too-large", `sibling temp exceeds maxBytes (${maxBytes})`);
85
168
  }
86
169
  };
87
170
  try {
88
- const result = params.producerIsolation === "private-directory"
89
- ? await writeIsolatedProducer({
90
- tempPath: params.tempPath,
91
- write: params.write,
171
+ let result;
172
+ if (producerIsolation === "private-directory") {
173
+ const isolated = await writeIsolatedProducer({
174
+ tempPath: tempPath,
175
+ write,
176
+ writeReceiver: params,
177
+ parentGuard: guard,
178
+ syncTempFile: syncTempFile,
92
179
  assertParent: () => assertDirectoryIdentitySync(parent, {
93
180
  dev: guard.stat.dev,
94
181
  ino: guard.stat.ino,
95
182
  realPath: guard.realPath,
96
183
  }),
97
- })
98
- : await params.write(params.tempPath);
184
+ });
185
+ result = isolated.result;
186
+ cleanupWorkspace = isolated.cleanupWorkspace;
187
+ if (isolated.handoff) {
188
+ handle = isolated.handoff.handle;
189
+ identity = isolated.handoff.identity;
190
+ unregister = isolated.handoff.unregister;
191
+ }
192
+ }
193
+ else {
194
+ result = await Reflect.apply(write, params, [tempPath]);
195
+ }
99
196
  await assertParent();
100
- const before = await inspectPath(params.tempPath);
101
- try {
102
- // No create/truncate flags; O_NONBLOCK also bounds a FIFO swap during open.
103
- const access = params.syncTempFile ? fsSync.constants.O_RDWR : fsSync.constants.O_RDONLY;
104
- handle = await fs.open(params.tempPath, access | resolveReadOpenFlags());
197
+ if (handle) {
198
+ const opened = await inspectStage(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity);
199
+ await inspectPath(tempPath, opened);
105
200
  }
106
- catch (error) {
107
- if (error?.code === "ELOOP") {
108
- throw new FsSafeError("symlink", "symlink sibling temp not allowed", { cause: error });
201
+ else {
202
+ const before = await inspectPath(tempPath);
203
+ try {
204
+ // No create/truncate flags; O_NONBLOCK also bounds a FIFO swap during open.
205
+ const access = syncTempFile ? fsSync.constants.O_RDWR : fsSync.constants.O_RDONLY;
206
+ handle = await fs.open(tempPath, access | resolveReadOpenFlags());
207
+ }
208
+ catch (error) {
209
+ if (error?.code === "ELOOP") {
210
+ throw new FsSafeError("symlink", "symlink sibling temp not allowed", { cause: error });
211
+ }
212
+ throw error;
109
213
  }
110
- throw error;
214
+ const opened = await inspectStage(() => fsSync.fstatSync(handle.fd, { bigint: true }), before);
215
+ await inspectPath(tempPath, opened);
216
+ identity = opened;
217
+ unregister = registerTempPathForExit(tempPath, { identity, singleLinkFile: true });
111
218
  }
112
- const opened = await inspectStage(() => fsSync.fstatSync(handle.fd, { bigint: true }), before);
113
- await inspectPath(params.tempPath, opened);
114
219
  await assertParent();
115
- identity = opened;
116
- unregister = registerTempPathForExit(params.tempPath, { identity, singleLinkFile: true });
117
- const filePath = path.resolve(params.resolveFinalPath(result));
220
+ if (cleanupWorkspace) {
221
+ const cleanup = cleanupWorkspace;
222
+ cleanupWorkspace = undefined;
223
+ await cleanup();
224
+ }
225
+ const rawFilePath = Reflect.apply(resolveFinalPath, params, [result]);
226
+ assertNoWindowsPathAlias(rawFilePath, "filesystem", "final path uses a Windows filesystem namespace alias");
227
+ const filePath = path.resolve(rawFilePath);
228
+ assertNoWindowsPathAlias(filePath, "filesystem", "final path uses a Windows filesystem namespace alias");
118
229
  if (path.dirname(filePath) !== parent) {
119
230
  throw new Error("Final path must be in the sibling temp directory.");
120
231
  }
121
- if (filePath === params.tempPath) {
232
+ if (filePath === tempPath) {
122
233
  throw new FsSafeError("invalid-path", "final path must differ from the sibling temp path");
123
234
  }
124
235
  await serializePathWrite(filePath, async () => {
125
- await assertCurrent(params.tempPath);
126
- if (params.mode !== undefined) {
236
+ await assertCurrent(tempPath);
237
+ if (mode !== undefined) {
127
238
  try {
128
- await handle.chmod(params.mode);
239
+ await handle.chmod(mode);
129
240
  }
130
241
  catch (error) {
131
- if (!params.ignoreModeError)
242
+ if (!ignoreModeError)
132
243
  throw error;
133
244
  }
134
245
  }
135
- if (params.syncTempFile) {
136
- await assertCurrent(params.tempPath);
246
+ if (syncTempFile) {
247
+ await assertCurrent(tempPath);
137
248
  await syncFileBestEffort(handle);
138
249
  }
139
- await assertCurrent(params.tempPath);
140
- await fs.rename(params.tempPath, filePath);
250
+ await assertCurrent(tempPath);
251
+ await fs.rename(tempPath, filePath);
141
252
  // A later verification failure never authorizes rollback of the final name.
142
253
  renamed = true;
143
254
  unregister();
144
255
  await assertCurrent(filePath);
145
- if (params.syncParentDir)
256
+ if (syncParentDir)
146
257
  await syncDirectoryBestEffort(parent);
147
258
  await assertCurrent(filePath);
148
259
  });
@@ -153,13 +264,22 @@ export async function writeCallbackSibling(params) {
153
264
  throw error;
154
265
  }
155
266
  finally {
267
+ const settlementErrors = [];
268
+ if (cleanupWorkspace) {
269
+ try {
270
+ await cleanupWorkspace();
271
+ }
272
+ catch (error) {
273
+ settlementErrors.push(error);
274
+ }
275
+ }
156
276
  try {
157
277
  if (!renamed && identity) {
158
278
  try {
159
279
  await assertParent();
160
280
  await inspectStage(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity);
161
- await inspectPath(params.tempPath, identity);
162
- await fs.unlink(params.tempPath);
281
+ await inspectPath(tempPath, identity);
282
+ await fs.unlink(tempPath);
163
283
  unregister?.();
164
284
  }
165
285
  catch (error) {
@@ -175,9 +295,13 @@ export async function writeCallbackSibling(params) {
175
295
  await handle?.close();
176
296
  }
177
297
  catch (error) {
178
- if (failure)
179
- throw new AggregateError([failure.error, error], "sibling publication and close failed");
180
- throw error;
298
+ settlementErrors.push(error);
299
+ }
300
+ if (settlementErrors.length > 0) {
301
+ if (failure) {
302
+ throw new AggregateError([failure.error, ...settlementErrors], "sibling publication and settlement failed");
303
+ }
304
+ throw aggregateErrors(settlementErrors, "sibling publication settlement failed");
181
305
  }
182
306
  }
183
307
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sibling-temp.d.ts","sourceRoot":"","sources":["../src/sibling-temp.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5C,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uGAAuG;IACvG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AASF,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAqBxC;AAwBD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ChB"}
1
+ {"version":3,"file":"sibling-temp.d.ts","sourceRoot":"","sources":["../src/sibling-temp.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5C,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uGAAuG;IACvG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AASF,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CA0BxC;AAwBD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDhB"}
@@ -9,17 +9,22 @@ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
9
9
  import { root } from "./root.js";
10
10
  import { assertSafePathPrefix } from "./safe-path-segment.js";
11
11
  import { resolveSecureTempRoot } from "./secure-temp-dir.js";
12
- import { assertCallbackTempPathDeviceSafe, writeCallbackSibling } from "./sibling-staged-file.js";
12
+ import { resolveCallbackTempPath, writeCallbackSibling } from "./sibling-staged-file.js";
13
13
  import { tempFile } from "./temp-target.js";
14
14
  import { getFsSafeTestHooks } from "./test-hooks.js";
15
- function buildTempPath(dir, tempPrefix) {
15
+ import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
16
+ function buildTempName(tempPrefix) {
16
17
  const safePrefix = assertSafePathPrefix(tempPrefix ?? ".fs-safe-stream", {
17
18
  label: "sibling temp prefix",
18
19
  });
19
- return path.join(dir, `${safePrefix}.${process.pid}.${randomUUID()}.tmp`);
20
+ return `${safePrefix}.${process.pid}.${randomUUID()}.tmp`;
20
21
  }
21
22
  export async function writeSiblingTempFile(options) {
22
- const dir = path.resolve(options.dir);
23
+ const dirInput = options.dir;
24
+ assertNoWindowsPathAlias(dirInput, "filesystem", "sibling temp directory uses a Windows filesystem namespace alias");
25
+ const dir = resolvePathPreservingWindowsRoot(dirInput);
26
+ assertNoWindowsPathAlias(dir, "filesystem", "sibling temp directory uses a Windows filesystem namespace alias");
27
+ const producerIsolation = options.producerIsolation;
23
28
  await fs.mkdir(recursiveMkdirPath(dir), { recursive: true, mode: options.dirMode ?? 0o700 });
24
29
  if (options.chmodDir !== false) {
25
30
  await applyDirectoryMode({
@@ -30,9 +35,10 @@ export async function writeSiblingTempFile(options) {
30
35
  });
31
36
  }
32
37
  return await writeCallbackSibling({
33
- tempPath: buildTempPath(dir, options.tempPrefix),
38
+ tempDir: dir,
39
+ tempName: buildTempName(options.tempPrefix),
34
40
  write: options.writeTemp,
35
- producerIsolation: options.producerIsolation,
41
+ producerIsolation,
36
42
  resolveFinalPath: options.resolveFinalPath,
37
43
  mode: options.mode,
38
44
  ignoreModeError: true,
@@ -40,7 +46,7 @@ export async function writeSiblingTempFile(options) {
40
46
  syncParentDir: options.syncParentDir === true,
41
47
  });
42
48
  }
43
- function buildSiblingTempPath(params) {
49
+ function buildSiblingTempName(params) {
44
50
  const id = crypto.randomUUID();
45
51
  const safePrefix = assertSafePathPrefix(params.tempPrefix, {
46
52
  label: "sibling temp prefix",
@@ -52,17 +58,23 @@ function buildSiblingTempPath(params) {
52
58
  fileName: sanitizeUntrustedFileName(path.basename(params.targetPath), params.fallbackFileName),
53
59
  suffix,
54
60
  });
55
- return path.join(path.dirname(params.targetPath), `${prefix}${safeTail}${suffix}`);
61
+ return `${prefix}${safeTail}${suffix}`;
56
62
  }
57
63
  export async function writeViaSiblingTempPath(params) {
64
+ const rootDirInput = params.rootDir;
65
+ const targetPathInput = params.targetPath;
66
+ assertNoWindowsPathAlias(rootDirInput, "filesystem", "sibling temp root uses a Windows filesystem namespace alias");
67
+ assertNoWindowsPathAlias(targetPathInput, "filesystem", "sibling temp target uses a Windows filesystem namespace alias");
58
68
  let rootDir;
59
69
  try {
60
- rootDir = realpathSync.native(path.resolve(params.rootDir));
70
+ rootDir = realpathSync.native(resolvePathPreservingWindowsRoot(rootDirInput));
61
71
  }
62
72
  catch {
63
- rootDir = path.resolve(params.rootDir);
73
+ rootDir = resolvePathPreservingWindowsRoot(rootDirInput);
64
74
  }
65
- const requestedTargetPath = path.resolve(params.targetPath);
75
+ assertNoWindowsPathAlias(rootDir, "filesystem", "sibling temp root uses a Windows filesystem namespace alias");
76
+ const requestedTargetPath = path.resolve(targetPathInput);
77
+ assertNoWindowsPathAlias(requestedTargetPath, "filesystem", "sibling temp target uses a Windows filesystem namespace alias");
66
78
  let targetPath;
67
79
  try {
68
80
  const realDir = realpathSync.native(path.dirname(requestedTargetPath));
@@ -71,6 +83,7 @@ export async function writeViaSiblingTempPath(params) {
71
83
  catch {
72
84
  targetPath = requestedTargetPath;
73
85
  }
86
+ assertNoWindowsPathAlias(targetPath, "filesystem", "sibling temp target uses a Windows filesystem namespace alias");
74
87
  const relativeTargetPath = path.relative(rootDir, targetPath);
75
88
  if (!relativeTargetPath ||
76
89
  relativeTargetPath === ".." ||
@@ -87,13 +100,14 @@ export async function writeViaSiblingTempPath(params) {
87
100
  }),
88
101
  prefix: "fs-safe-output",
89
102
  });
103
+ assertNoWindowsPathAlias(workspace.dir, "filesystem", "sibling temp workspace uses a Windows filesystem namespace alias");
90
104
  try {
91
- const tempPath = buildSiblingTempPath({
105
+ const tempName = buildSiblingTempName({
92
106
  targetPath: path.join(workspace.dir, path.basename(targetPath)),
93
107
  fallbackFileName: params.fallbackFileName ?? "output.bin",
94
108
  tempPrefix: params.tempPrefix ?? ".fs-safe-output-",
95
109
  });
96
- assertCallbackTempPathDeviceSafe(tempPath);
110
+ const tempPath = resolveCallbackTempPath(workspace.dir, tempName);
97
111
  await getFsSafeTestHooks()?.beforeSiblingTempWrite?.(tempPath);
98
112
  await params.writeTemp(tempPath);
99
113
  await assertAsyncDirectoryGuard(rootGuard);
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock-acquire.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-acquire.ts"],"names":[],"mappings":"AAUA,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAW3C,OAAO,EAWL,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAI5F,KAAK,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC;AAEvF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CAClC,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACnC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,2BAA2B,IAAI,IAAI,CAAC;IACpC,iBAAiB,CAAC,oBAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;IAC1F,eAAe,CACb,oBAAoB,EAAE,MAAM,EAC5B,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB,CAAC;AAqBF,wBAAsB,kBAAkB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,EAC5C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,iBAAiB,CAAC,CA6T5B"}
1
+ {"version":3,"file":"sidecar-lock-acquire.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-acquire.ts"],"names":[],"mappings":"AAUA,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAW3C,OAAO,EAWL,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAK5F,KAAK,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC;AAEvF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CAClC,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACnC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,2BAA2B,IAAI,IAAI,CAAC;IACpC,iBAAiB,CAAC,oBAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;IAC1F,eAAe,CACb,oBAAoB,EAAE,MAAM,EAC5B,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB,CAAC;AAoCF,wBAAsB,kBAAkB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,EAC5C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,iBAAiB,CAAC,CA2U5B"}