@openclaw/fs-safe 0.11.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 (416) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +15 -8
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +32 -11
  5. package/dist/advanced.d.ts +3 -1
  6. package/dist/advanced.d.ts.map +1 -1
  7. package/dist/advanced.js +3 -1
  8. package/dist/archive-entry.d.ts.map +1 -1
  9. package/dist/archive-entry.js +4 -3
  10. package/dist/archive-gzip-tail.d.ts.map +1 -1
  11. package/dist/archive-gzip-tail.js +13 -9
  12. package/dist/archive-input.d.ts.map +1 -1
  13. package/dist/archive-input.js +6 -2
  14. package/dist/archive-merge.d.ts.map +1 -1
  15. package/dist/archive-merge.js +15 -2
  16. package/dist/archive-native.d.ts.map +1 -1
  17. package/dist/archive-native.js +7 -19
  18. package/dist/archive-plan.js +1 -1
  19. package/dist/archive-read.d.ts.map +1 -1
  20. package/dist/archive-read.js +55 -46
  21. package/dist/archive-staging.d.ts.map +1 -1
  22. package/dist/archive-staging.js +43 -10
  23. package/dist/archive-tar-inspect.d.ts.map +1 -1
  24. package/dist/archive-tar-inspect.js +4 -1
  25. package/dist/archive-zip-admission.d.ts +1 -1
  26. package/dist/archive-zip-admission.d.ts.map +1 -1
  27. package/dist/archive-zip-admission.js +2 -2
  28. package/dist/archive-zip-directory.d.ts +3 -0
  29. package/dist/archive-zip-directory.d.ts.map +1 -1
  30. package/dist/archive-zip-directory.js +13 -2
  31. package/dist/archive-zip-loader.d.ts +3 -2
  32. package/dist/archive-zip-loader.d.ts.map +1 -1
  33. package/dist/archive-zip-loader.js +30 -4
  34. package/dist/archive-zip-manifest.d.ts +5 -0
  35. package/dist/archive-zip-manifest.d.ts.map +1 -0
  36. package/dist/archive-zip-manifest.js +22 -0
  37. package/dist/archive-zip-names.d.ts +6 -1
  38. package/dist/archive-zip-names.d.ts.map +1 -1
  39. package/dist/archive-zip-names.js +50 -17
  40. package/dist/archive-zip-preflight.d.ts.map +1 -1
  41. package/dist/archive-zip-preflight.js +3 -2
  42. package/dist/archive.d.ts.map +1 -1
  43. package/dist/archive.js +9 -4
  44. package/dist/bounded-read.js +2 -2
  45. package/dist/darwin-acl.d.ts +4 -0
  46. package/dist/darwin-acl.d.ts.map +1 -0
  47. package/dist/darwin-acl.js +24 -0
  48. package/dist/deny-mutations.d.ts.map +1 -1
  49. package/dist/deny-mutations.js +8 -2
  50. package/dist/device-path.d.ts.map +1 -1
  51. package/dist/device-path.js +5 -3
  52. package/dist/directory-durability.d.ts.map +1 -1
  53. package/dist/directory-durability.js +67 -23
  54. package/dist/directory-entry-path.d.ts +3 -0
  55. package/dist/directory-entry-path.d.ts.map +1 -0
  56. package/dist/directory-entry-path.js +21 -0
  57. package/dist/directory-guard.d.ts +17 -1
  58. package/dist/directory-guard.d.ts.map +1 -1
  59. package/dist/directory-guard.js +134 -48
  60. package/dist/directory-mode-node.d.ts +12 -0
  61. package/dist/directory-mode-node.d.ts.map +1 -1
  62. package/dist/directory-mode-node.js +102 -4
  63. package/dist/effective-uid.d.ts +2 -0
  64. package/dist/effective-uid.d.ts.map +1 -0
  65. package/dist/effective-uid.js +25 -0
  66. package/dist/file-handle-transfer.d.ts.map +1 -1
  67. package/dist/file-handle-transfer.js +98 -27
  68. package/dist/file-hash.d.ts.map +1 -1
  69. package/dist/file-hash.js +12 -2
  70. package/dist/file-lock-sync.d.ts.map +1 -1
  71. package/dist/file-lock-sync.js +36 -6
  72. package/dist/file-lock.d.ts.map +1 -1
  73. package/dist/file-lock.js +29 -9
  74. package/dist/file-observation.d.ts +1 -1
  75. package/dist/file-observation.d.ts.map +1 -1
  76. package/dist/file-store-boundary.d.ts +6 -2
  77. package/dist/file-store-boundary.d.ts.map +1 -1
  78. package/dist/file-store-boundary.js +20 -65
  79. package/dist/file-store-copy-source.d.ts +5 -0
  80. package/dist/file-store-copy-source.d.ts.map +1 -0
  81. package/dist/file-store-copy-source.js +31 -0
  82. package/dist/file-store-path.d.ts.map +1 -1
  83. package/dist/file-store-path.js +4 -1
  84. package/dist/file-store-prune.d.ts.map +1 -1
  85. package/dist/file-store-prune.js +9 -1
  86. package/dist/file-store-sync-directory.d.ts +16 -0
  87. package/dist/file-store-sync-directory.d.ts.map +1 -0
  88. package/dist/file-store-sync-directory.js +349 -0
  89. package/dist/file-store.d.ts.map +1 -1
  90. package/dist/file-store.js +11 -28
  91. package/dist/filename.d.ts +1 -0
  92. package/dist/filename.d.ts.map +1 -1
  93. package/dist/filename.js +85 -23
  94. package/dist/fs.d.ts.map +1 -1
  95. package/dist/fs.js +3 -2
  96. package/dist/guarded-mkdir.d.ts +10 -0
  97. package/dist/guarded-mkdir.d.ts.map +1 -1
  98. package/dist/guarded-mkdir.js +217 -33
  99. package/dist/guest.d.ts.map +1 -1
  100. package/dist/guest.js +16 -5
  101. package/dist/home-dir.d.ts.map +1 -1
  102. package/dist/home-dir.js +73 -10
  103. package/dist/install-path.d.ts +6 -0
  104. package/dist/install-path.d.ts.map +1 -1
  105. package/dist/install-path.js +67 -19
  106. package/dist/json-durable-queue-ownership.d.ts +8 -0
  107. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  108. package/dist/json-durable-queue-ownership.js +134 -43
  109. package/dist/json-durable-queue-paths.d.ts +11 -0
  110. package/dist/json-durable-queue-paths.d.ts.map +1 -0
  111. package/dist/json-durable-queue-paths.js +42 -0
  112. package/dist/json-durable-queue-read.d.ts +6 -0
  113. package/dist/json-durable-queue-read.d.ts.map +1 -0
  114. package/dist/json-durable-queue-read.js +61 -0
  115. package/dist/json-durable-queue.d.ts +1 -1
  116. package/dist/json-durable-queue.d.ts.map +1 -1
  117. package/dist/json-durable-queue.js +83 -134
  118. package/dist/json-store.d.ts.map +1 -1
  119. package/dist/json-store.js +5 -1
  120. package/dist/json.d.ts.map +1 -1
  121. package/dist/json.js +54 -22
  122. package/dist/local-file-access.d.ts.map +1 -1
  123. package/dist/local-file-access.js +4 -0
  124. package/dist/local-file-descriptor.d.ts +17 -0
  125. package/dist/local-file-descriptor.d.ts.map +1 -0
  126. package/dist/local-file-descriptor.js +84 -0
  127. package/dist/local-roots.d.ts.map +1 -1
  128. package/dist/local-roots.js +35 -7
  129. package/dist/move-path-cleanup.d.ts +2 -0
  130. package/dist/move-path-cleanup.d.ts.map +1 -1
  131. package/dist/move-path-cleanup.js +44 -18
  132. package/dist/move-path.d.ts.map +1 -1
  133. package/dist/move-path.js +31 -6
  134. package/dist/native-binding.d.ts +21 -0
  135. package/dist/native-binding.d.ts.map +1 -1
  136. package/dist/native-directory-observation.d.ts +17 -0
  137. package/dist/native-directory-observation.d.ts.map +1 -0
  138. package/dist/native-directory-observation.js +37 -0
  139. package/dist/native-parent-admission.d.ts +31 -0
  140. package/dist/native-parent-admission.d.ts.map +1 -0
  141. package/dist/native-parent-admission.js +124 -0
  142. package/dist/native-pinned-write-windows.d.ts +0 -1
  143. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  144. package/dist/native-pinned-write-windows.js +0 -3
  145. package/dist/native-pinned-write.d.ts.map +1 -1
  146. package/dist/native-pinned-write.js +309 -40
  147. package/dist/output.d.ts.map +1 -1
  148. package/dist/output.js +15 -5
  149. package/dist/overwrite-file-handle.d.ts.map +1 -1
  150. package/dist/overwrite-file-handle.js +5 -1
  151. package/dist/owner-dacl.d.ts.map +1 -1
  152. package/dist/owner-dacl.js +2 -0
  153. package/dist/path-policy.d.ts.map +1 -1
  154. package/dist/path-policy.js +7 -2
  155. package/dist/path-prefix.d.ts +7 -0
  156. package/dist/path-prefix.d.ts.map +1 -0
  157. package/dist/path-prefix.js +82 -0
  158. package/dist/path-scope-lexical.d.ts +14 -0
  159. package/dist/path-scope-lexical.d.ts.map +1 -0
  160. package/dist/path-scope-lexical.js +37 -0
  161. package/dist/path-segment-route.d.ts +7 -0
  162. package/dist/path-segment-route.d.ts.map +1 -0
  163. package/dist/path-segment-route.js +24 -0
  164. package/dist/path-suffix-aliases.d.ts +10 -0
  165. package/dist/path-suffix-aliases.d.ts.map +1 -0
  166. package/dist/path-suffix-aliases.js +386 -0
  167. package/dist/path.d.ts.map +1 -1
  168. package/dist/path.js +35 -6
  169. package/dist/permissions-windows.d.ts.map +1 -1
  170. package/dist/permissions-windows.js +14 -3
  171. package/dist/permissions.d.ts.map +1 -1
  172. package/dist/permissions.js +37 -8
  173. package/dist/pinned-mutation-admission.d.ts +25 -0
  174. package/dist/pinned-mutation-admission.d.ts.map +1 -0
  175. package/dist/pinned-mutation-admission.js +425 -0
  176. package/dist/pinned-mutation-observation.d.ts +34 -0
  177. package/dist/pinned-mutation-observation.d.ts.map +1 -0
  178. package/dist/pinned-mutation-observation.js +142 -0
  179. package/dist/pinned-mutation-shared-route.d.ts +24 -0
  180. package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
  181. package/dist/pinned-mutation-shared-route.js +70 -0
  182. package/dist/pinned-open.d.ts +6 -0
  183. package/dist/pinned-open.d.ts.map +1 -1
  184. package/dist/pinned-open.js +28 -10
  185. package/dist/pinned-write-types.d.ts +75 -0
  186. package/dist/pinned-write-types.d.ts.map +1 -0
  187. package/dist/pinned-write-types.js +1 -0
  188. package/dist/pinned-write.d.ts +7 -33
  189. package/dist/pinned-write.d.ts.map +1 -1
  190. package/dist/pinned-write.js +152 -17
  191. package/dist/private-directory.d.ts.map +1 -1
  192. package/dist/private-directory.js +2 -0
  193. package/dist/private-producer-handoff.d.ts +16 -0
  194. package/dist/private-producer-handoff.d.ts.map +1 -0
  195. package/dist/private-producer-handoff.js +272 -0
  196. package/dist/private-temp-workspace.d.ts +2 -39
  197. package/dist/private-temp-workspace.d.ts.map +1 -1
  198. package/dist/private-temp-workspace.js +183 -77
  199. package/dist/publish-file.d.ts.map +1 -1
  200. package/dist/publish-file.js +33 -29
  201. package/dist/regular-file.d.ts.map +1 -1
  202. package/dist/regular-file.js +56 -45
  203. package/dist/replace-directory.d.ts.map +1 -1
  204. package/dist/replace-directory.js +12 -5
  205. package/dist/replace-file-temp-owner.d.ts +1 -0
  206. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  207. package/dist/replace-file-temp-owner.js +12 -1
  208. package/dist/replace-file.d.ts.map +1 -1
  209. package/dist/replace-file.js +24 -24
  210. package/dist/root-boundary.d.ts +29 -0
  211. package/dist/root-boundary.d.ts.map +1 -0
  212. package/dist/root-boundary.js +182 -0
  213. package/dist/root-context.d.ts +12 -1
  214. package/dist/root-context.d.ts.map +1 -1
  215. package/dist/root-context.js +89 -16
  216. package/dist/root-directory-creation.d.ts +13 -0
  217. package/dist/root-directory-creation.d.ts.map +1 -0
  218. package/dist/root-directory-creation.js +212 -0
  219. package/dist/root-directory-list.d.ts +16 -4
  220. package/dist/root-directory-list.d.ts.map +1 -1
  221. package/dist/root-directory-list.js +182 -33
  222. package/dist/root-directory.d.ts +23 -0
  223. package/dist/root-directory.d.ts.map +1 -0
  224. package/dist/root-directory.js +141 -0
  225. package/dist/root-errors.d.ts.map +1 -1
  226. package/dist/root-errors.js +12 -1
  227. package/dist/root-file-final-admission.d.ts +21 -0
  228. package/dist/root-file-final-admission.d.ts.map +1 -0
  229. package/dist/root-file-final-admission.js +83 -0
  230. package/dist/root-file.d.ts.map +1 -1
  231. package/dist/root-file.js +103 -24
  232. package/dist/root-impl.d.ts +1 -0
  233. package/dist/root-impl.d.ts.map +1 -1
  234. package/dist/root-impl.js +316 -325
  235. package/dist/root-move-noreplace.d.ts +14 -0
  236. package/dist/root-move-noreplace.d.ts.map +1 -0
  237. package/dist/root-move-noreplace.js +202 -0
  238. package/dist/root-move-preflight.d.ts +8 -0
  239. package/dist/root-move-preflight.d.ts.map +1 -0
  240. package/dist/root-move-preflight.js +16 -0
  241. package/dist/root-observed-path.d.ts +9 -0
  242. package/dist/root-observed-path.d.ts.map +1 -0
  243. package/dist/root-observed-path.js +95 -0
  244. package/dist/root-path-errors.d.ts +12 -0
  245. package/dist/root-path-errors.d.ts.map +1 -0
  246. package/dist/root-path-errors.js +13 -0
  247. package/dist/root-path-existing.d.ts +2 -0
  248. package/dist/root-path-existing.d.ts.map +1 -1
  249. package/dist/root-path-existing.js +63 -22
  250. package/dist/root-path-observation.d.ts +63 -0
  251. package/dist/root-path-observation.d.ts.map +1 -0
  252. package/dist/root-path-observation.js +180 -0
  253. package/dist/root-path-stat.d.ts +5 -0
  254. package/dist/root-path-stat.d.ts.map +1 -0
  255. package/dist/root-path-stat.js +101 -0
  256. package/dist/root-path-symlink.d.ts.map +1 -1
  257. package/dist/root-path-symlink.js +23 -4
  258. package/dist/root-path.d.ts +13 -0
  259. package/dist/root-path.d.ts.map +1 -1
  260. package/dist/root-path.js +263 -73
  261. package/dist/root-paths-lexical.d.ts +9 -0
  262. package/dist/root-paths-lexical.d.ts.map +1 -0
  263. package/dist/root-paths-lexical.js +22 -0
  264. package/dist/root-paths.d.ts +3 -29
  265. package/dist/root-paths.d.ts.map +1 -1
  266. package/dist/root-paths.js +89 -178
  267. package/dist/root-read-admission.d.ts +26 -0
  268. package/dist/root-read-admission.d.ts.map +1 -0
  269. package/dist/root-read-admission.js +94 -0
  270. package/dist/root-remove-identity.d.ts +15 -0
  271. package/dist/root-remove-identity.d.ts.map +1 -0
  272. package/dist/root-remove-identity.js +89 -0
  273. package/dist/root-remove-receipt.d.ts +17 -0
  274. package/dist/root-remove-receipt.d.ts.map +1 -0
  275. package/dist/root-remove-receipt.js +37 -0
  276. package/dist/root-remove.d.ts +2 -1
  277. package/dist/root-remove.d.ts.map +1 -1
  278. package/dist/root-remove.js +172 -10
  279. package/dist/root-walk.d.ts.map +1 -1
  280. package/dist/root-walk.js +2 -1
  281. package/dist/root-write-admission.d.ts +68 -0
  282. package/dist/root-write-admission.d.ts.map +1 -0
  283. package/dist/root-write-admission.js +322 -0
  284. package/dist/root-write-compatibility.d.ts +13 -0
  285. package/dist/root-write-compatibility.d.ts.map +1 -0
  286. package/dist/root-write-compatibility.js +74 -0
  287. package/dist/root-write-complete-parent.d.ts +42 -0
  288. package/dist/root-write-complete-parent.d.ts.map +1 -0
  289. package/dist/root-write-complete-parent.js +195 -0
  290. package/dist/root-write-mode.d.ts +2 -0
  291. package/dist/root-write-mode.d.ts.map +1 -1
  292. package/dist/root-write-mode.js +20 -7
  293. package/dist/root-write-publication.d.ts +24 -0
  294. package/dist/root-write-publication.d.ts.map +1 -0
  295. package/dist/root-write-publication.js +85 -0
  296. package/dist/root-write-verification.d.ts.map +1 -1
  297. package/dist/root-write-verification.js +18 -7
  298. package/dist/safe-path-segment.d.ts.map +1 -1
  299. package/dist/safe-path-segment.js +3 -1
  300. package/dist/secret-file.d.ts.map +1 -1
  301. package/dist/secret-file.js +62 -24
  302. package/dist/secret-read-async.d.ts.map +1 -1
  303. package/dist/secret-read-async.js +18 -13
  304. package/dist/secret-read-policy.d.ts.map +1 -1
  305. package/dist/secret-read-policy.js +5 -1
  306. package/dist/secure-file-windows.d.ts +10 -0
  307. package/dist/secure-file-windows.d.ts.map +1 -0
  308. package/dist/secure-file-windows.js +186 -0
  309. package/dist/secure-file.d.ts.map +1 -1
  310. package/dist/secure-file.js +98 -10
  311. package/dist/secure-temp-dir.d.ts +6 -3
  312. package/dist/secure-temp-dir.d.ts.map +1 -1
  313. package/dist/secure-temp-dir.js +121 -101
  314. package/dist/secure-temp-repair.d.ts +35 -0
  315. package/dist/secure-temp-repair.d.ts.map +1 -0
  316. package/dist/secure-temp-repair.js +106 -0
  317. package/dist/sibling-staged-file.d.ts +3 -1
  318. package/dist/sibling-staged-file.d.ts.map +1 -1
  319. package/dist/sibling-staged-file.js +181 -50
  320. package/dist/sibling-temp.d.ts.map +1 -1
  321. package/dist/sibling-temp.js +27 -12
  322. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  323. package/dist/sidecar-lock-acquire.js +45 -15
  324. package/dist/sidecar-lock-root.d.ts.map +1 -1
  325. package/dist/sidecar-lock-root.js +4 -2
  326. package/dist/sidecar-lock.d.ts.map +1 -1
  327. package/dist/sidecar-lock.js +2 -3
  328. package/dist/staged-directory.d.ts +14 -5
  329. package/dist/staged-directory.d.ts.map +1 -1
  330. package/dist/staged-directory.js +54 -3
  331. package/dist/standalone-publication-path.d.ts +2 -0
  332. package/dist/standalone-publication-path.d.ts.map +1 -0
  333. package/dist/standalone-publication-path.js +6 -0
  334. package/dist/stat-observation.d.ts +11 -0
  335. package/dist/stat-observation.d.ts.map +1 -0
  336. package/dist/stat-observation.js +64 -0
  337. package/dist/strict-file-identity.d.ts.map +1 -1
  338. package/dist/strict-file-identity.js +39 -2
  339. package/dist/symlink-parents.d.ts.map +1 -1
  340. package/dist/symlink-parents.js +7 -2
  341. package/dist/temp-target.d.ts +2 -0
  342. package/dist/temp-target.d.ts.map +1 -1
  343. package/dist/temp-target.js +133 -4
  344. package/dist/temp-workspace-admission.d.ts +22 -0
  345. package/dist/temp-workspace-admission.d.ts.map +1 -0
  346. package/dist/temp-workspace-admission.js +374 -0
  347. package/dist/temp-workspace-child-admission.d.ts +13 -0
  348. package/dist/temp-workspace-child-admission.d.ts.map +1 -0
  349. package/dist/temp-workspace-child-admission.js +95 -0
  350. package/dist/temp-workspace-descriptor.d.ts +48 -0
  351. package/dist/temp-workspace-descriptor.d.ts.map +1 -0
  352. package/dist/temp-workspace-descriptor.js +363 -0
  353. package/dist/temp-workspace-identity.d.ts +15 -0
  354. package/dist/temp-workspace-identity.d.ts.map +1 -0
  355. package/dist/temp-workspace-identity.js +41 -0
  356. package/dist/temp-workspace-owner.d.ts +7 -6
  357. package/dist/temp-workspace-owner.d.ts.map +1 -1
  358. package/dist/temp-workspace-owner.js +121 -61
  359. package/dist/temp-workspace-permissions.d.ts +4 -0
  360. package/dist/temp-workspace-permissions.d.ts.map +1 -0
  361. package/dist/temp-workspace-permissions.js +32 -0
  362. package/dist/temp-workspace-types.d.ts +40 -0
  363. package/dist/temp-workspace-types.d.ts.map +1 -0
  364. package/dist/temp-workspace-types.js +1 -0
  365. package/dist/temp.d.ts +1 -1
  366. package/dist/temp.d.ts.map +1 -1
  367. package/dist/temp.js +1 -1
  368. package/dist/test-hooks.d.ts +7 -0
  369. package/dist/test-hooks.d.ts.map +1 -1
  370. package/dist/text-atomic.d.ts.map +1 -1
  371. package/dist/text-atomic.js +3 -1
  372. package/dist/trash.d.ts.map +1 -1
  373. package/dist/trash.js +54 -8
  374. package/dist/walk.d.ts.map +1 -1
  375. package/dist/walk.js +20 -16
  376. package/dist/windows-owner.d.ts.map +1 -1
  377. package/dist/windows-owner.js +5 -0
  378. package/dist/windows-path-alias.d.ts +39 -0
  379. package/dist/windows-path-alias.d.ts.map +1 -0
  380. package/dist/windows-path-alias.js +153 -0
  381. package/docs/advanced.md +28 -1
  382. package/docs/archive.md +29 -2
  383. package/docs/atomic.md +27 -3
  384. package/docs/contributing.md +4 -1
  385. package/docs/copy.md +4 -1
  386. package/docs/durability.md +18 -2
  387. package/docs/errors.md +14 -6
  388. package/docs/file-store.md +30 -3
  389. package/docs/filename.md +21 -7
  390. package/docs/guest.md +5 -0
  391. package/docs/install-path.md +61 -15
  392. package/docs/install.md +12 -8
  393. package/docs/json-store.md +5 -1
  394. package/docs/json.md +11 -0
  395. package/docs/mutation-policy-proof.md +65 -0
  396. package/docs/native-helper.md +28 -9
  397. package/docs/native.md +46 -10
  398. package/docs/output.md +33 -11
  399. package/docs/path-prefix.md +64 -0
  400. package/docs/path-suffix-aliases.md +159 -0
  401. package/docs/path.md +12 -1
  402. package/docs/permissions.md +26 -1
  403. package/docs/private-file-store.md +14 -0
  404. package/docs/public-api.md +14 -0
  405. package/docs/quickstart.md +1 -1
  406. package/docs/reading.md +8 -1
  407. package/docs/root.md +21 -4
  408. package/docs/secret-file.md +3 -0
  409. package/docs/secure-file.md +21 -16
  410. package/docs/security-model.md +76 -9
  411. package/docs/sidecar-lock.md +28 -1
  412. package/docs/store.md +27 -2
  413. package/docs/temp.md +262 -34
  414. package/docs/test-hooks.md +14 -0
  415. package/docs/writing.md +38 -8
  416. package/package.json +10 -10
@@ -0,0 +1,14 @@
1
+ import { type DenyMutationPolicy } from "./deny-mutations.js";
2
+ import { type RootContext } from "./root-context.js";
3
+ import { type MutationSymlinkPolicy } from "./root-symlink-policy.js";
4
+ export declare function movePathNoReplaceNative(root: RootContext, params: {
5
+ assertBeforeMutation?: () => void;
6
+ denyMutations?: DenyMutationPolicy;
7
+ mutationSymlinks?: MutationSymlinkPolicy;
8
+ }, paths: {
9
+ sourcePath: string;
10
+ sourceParentPath: string;
11
+ targetPath: string;
12
+ targetParentPath: string;
13
+ }): Promise<void>;
14
+ //# sourceMappingURL=root-move-noreplace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-move-noreplace.d.ts","sourceRoot":"","sources":["../src/root-move-noreplace.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AASvF,OAAO,EAA4D,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAU/G,OAAO,EAA8B,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAqDlG,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE;IACN,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;CAC1C,EACD,KAAK,EAAE;IACL,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,IAAI,CAAC,CA0If"}
@@ -0,0 +1,202 @@
1
+ import fsSync, {} from "node:fs";
2
+ import path from "node:path";
3
+ import { assertAsyncDirectoryGuard, assertSyncDirectoryGuard } from "./directory-guard.js";
4
+ import { FsSafeError } from "./errors.js";
5
+ import { assertMutationNotDenied } from "./deny-mutations.js";
6
+ import { closeNativeParentAdmission, openNativeParentAdmission, openNativeRootAdmission, } from "./native-parent-admission.js";
7
+ import { getNativeBinding } from "./native.js";
8
+ import { isNotFoundPathError } from "./path.js";
9
+ import { assertRootIdentityCurrent, assertRootIdentityCurrentSync } from "./root-context.js";
10
+ import { resolveRootPathSync } from "./root-path.js";
11
+ import { admitPathInsideRoot } from "./root-boundary.js";
12
+ import { fileNotFoundError, hardlinkedPathNotAllowedError, isAlreadyExistsError, normalizePinnedPathError, outsideWorkspaceError, } from "./root-errors.js";
13
+ import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
14
+ import { createSuppressedError } from "./suppressed-error.js";
15
+ import { getFsSafeTestHooks } from "./test-hooks.js";
16
+ function nativeParentRelativePath(rootReal, parentPath) {
17
+ const relative = path.relative(rootReal, parentPath);
18
+ if (relative === "" || relative === ".")
19
+ return "";
20
+ const firstSegment = relative.split(path.sep)[0];
21
+ if (firstSegment === ".." ||
22
+ path.isAbsolute(relative)) {
23
+ throw outsideWorkspaceError();
24
+ }
25
+ return relative.split(path.sep).join(path.posix.sep);
26
+ }
27
+ function admitMovePath(root, parent, basename) {
28
+ const admitted = admitPathInsideRoot({
29
+ rootPath: root.rootReal,
30
+ candidatePath: path.join(parent.guard.realPath, basename),
31
+ rootIdentity: root.rootIdentity,
32
+ });
33
+ if (!admitted)
34
+ throw outsideWorkspaceError();
35
+ return admitted.path;
36
+ }
37
+ function nativePrimitiveUnavailable(error) {
38
+ const code = error?.code;
39
+ return code === "ENOSYS" || code === "ENOTSUP" || code === "EOPNOTSUPP";
40
+ }
41
+ function normalizeMoveError(error) {
42
+ if (isNotFoundPathError(error)) {
43
+ return fileNotFoundError(error instanceof Error ? error : undefined);
44
+ }
45
+ if (nativePrimitiveUnavailable(error)) {
46
+ return new FsSafeError("helper-unavailable", "native no-replace move is unavailable", {
47
+ cause: error instanceof Error ? error : undefined,
48
+ });
49
+ }
50
+ return error;
51
+ }
52
+ function normalizeRenameNoReplaceError(error) {
53
+ if (error?.code === "EINVAL") {
54
+ return new FsSafeError("helper-unavailable", "native no-replace move is unavailable on this filesystem", {
55
+ cause: error instanceof Error ? error : undefined,
56
+ });
57
+ }
58
+ return normalizeMoveError(error);
59
+ }
60
+ export async function movePathNoReplaceNative(root, params, paths) {
61
+ try {
62
+ fsSync.lstatSync(paths.targetPath);
63
+ throw new FsSafeError("already-exists", "destination exists");
64
+ }
65
+ catch (error) {
66
+ if (error instanceof FsSafeError)
67
+ throw error;
68
+ if (!isNotFoundPathError(error))
69
+ throw error;
70
+ // Advisory fast rejection only. renameNoReplace owns the collision decision.
71
+ }
72
+ const binding = getNativeBinding();
73
+ if (!binding || typeof binding.renameNoReplace !== "function") {
74
+ throw new FsSafeError("helper-unavailable", "native no-replace move is unavailable");
75
+ }
76
+ const rootAdmission = await openNativeRootAdmission(binding, {
77
+ rootPath: root.rootReal,
78
+ rootIdentity: root.rootIdentity,
79
+ operation: "native move",
80
+ });
81
+ let sourceParent;
82
+ let targetParent;
83
+ let failed = false;
84
+ let operationError;
85
+ try {
86
+ const sourceParentRelativePath = nativeParentRelativePath(root.rootReal, paths.sourceParentPath);
87
+ sourceParent = await openNativeParentAdmission(binding, rootAdmission, sourceParentRelativePath);
88
+ const targetParentRelativePath = nativeParentRelativePath(root.rootReal, paths.targetParentPath);
89
+ targetParent = targetParentRelativePath === sourceParentRelativePath
90
+ ? sourceParent
91
+ : await openNativeParentAdmission(binding, rootAdmission, targetParentRelativePath);
92
+ const parentAdmissions = targetParent === sourceParent
93
+ ? [sourceParent]
94
+ : [sourceParent, targetParent];
95
+ await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("move", paths.targetPath);
96
+ const admittedSourcePath = admitMovePath(root, sourceParent, path.basename(paths.sourcePath));
97
+ const admittedTargetPath = admitMovePath(root, targetParent, path.basename(paths.targetPath));
98
+ if (params.denyMutations) {
99
+ // Native admission can follow a newly introduced contained parent alias.
100
+ // Authorize the selected names, then recheck every retained directory.
101
+ await assertMutationNotDenied(admittedSourcePath, params.denyMutations, { protectAncestors: true });
102
+ await assertMutationNotDenied(admittedTargetPath, params.denyMutations, { protectAncestors: true });
103
+ }
104
+ await assertRootIdentityCurrent(root);
105
+ for (const admission of parentAdmissions)
106
+ assertSyncDirectoryGuard(admission.guard);
107
+ let sourceStat;
108
+ try {
109
+ sourceStat = fsSync.lstatSync(admittedSourcePath);
110
+ }
111
+ catch (error) {
112
+ throw normalizeMoveError(error);
113
+ }
114
+ if (sourceStat.isSymbolicLink()) {
115
+ throw new FsSafeError("symlink", "symlink not allowed");
116
+ }
117
+ if (sourceStat.isFile() && sourceStat.nlink > 1) {
118
+ throw hardlinkedPathNotAllowedError();
119
+ }
120
+ if (sourceStat.isDirectory()) {
121
+ throw new FsSafeError("invalid-path", "directory moves require overwrite: true");
122
+ }
123
+ assertFinalSymlinkRejected(admittedTargetPath, params.mutationSymlinks !== undefined);
124
+ params.assertBeforeMutation?.();
125
+ if (params.assertBeforeMutation || params.mutationSymlinks === "reject") {
126
+ // Do not carry directory freshness across the live authority callback.
127
+ assertRootIdentityCurrentSync(root);
128
+ if (params.mutationSymlinks === "reject") {
129
+ // Validate both full pre-native routes; selected canonical names have
130
+ // already erased any parent symlink followed during admission.
131
+ for (const absolutePath of [paths.sourcePath, paths.targetPath]) {
132
+ resolveRootPathSync({
133
+ absolutePath,
134
+ rootPath: root.rootReal,
135
+ rootCanonicalPath: root.rootReal,
136
+ rootIdentity: root.rootIdentity,
137
+ boundaryLabel: "root",
138
+ rejectSymlinks: true,
139
+ rejectFinalSymlink: true,
140
+ });
141
+ }
142
+ }
143
+ for (const admission of parentAdmissions)
144
+ assertSyncDirectoryGuard(admission.guard);
145
+ }
146
+ try {
147
+ binding.renameNoReplace(sourceParent.fd, path.basename(paths.sourcePath), targetParent.fd, path.basename(paths.targetPath));
148
+ }
149
+ catch (error) {
150
+ if (isAlreadyExistsError(error) || error?.code === "ENOTEMPTY") {
151
+ throw new FsSafeError("already-exists", "destination exists", {
152
+ cause: error instanceof Error ? error : undefined,
153
+ });
154
+ }
155
+ throw normalizeRenameNoReplaceError(error);
156
+ }
157
+ try {
158
+ for (const admission of parentAdmissions)
159
+ await assertAsyncDirectoryGuard(admission.guard);
160
+ }
161
+ catch (error) {
162
+ throw normalizePinnedPathError(error);
163
+ }
164
+ }
165
+ catch (error) {
166
+ failed = true;
167
+ operationError = normalizeMoveError(error);
168
+ }
169
+ const closeErrors = [];
170
+ const closedFds = new Set();
171
+ for (const admission of [targetParent, sourceParent]) {
172
+ if (!admission || closedFds.has(admission.fd))
173
+ continue;
174
+ closedFds.add(admission.fd);
175
+ try {
176
+ closeNativeParentAdmission(admission);
177
+ }
178
+ catch (error) {
179
+ closeErrors.push(error);
180
+ }
181
+ }
182
+ try {
183
+ await rootAdmission.root.close();
184
+ }
185
+ catch (error) {
186
+ closeErrors.push(error);
187
+ }
188
+ if (failed) {
189
+ if (closeErrors.length > 0) {
190
+ const closeError = closeErrors.length === 1
191
+ ? closeErrors[0]
192
+ : new AggregateError(closeErrors, "native move descriptor closes failed");
193
+ throw createSuppressedError(closeError, operationError, "native move and descriptor close failed");
194
+ }
195
+ throw operationError;
196
+ }
197
+ if (closeErrors.length === 1)
198
+ throw closeErrors[0];
199
+ if (closeErrors.length > 1) {
200
+ throw new AggregateError(closeErrors, "native move descriptor closes failed");
201
+ }
202
+ }
@@ -0,0 +1,8 @@
1
+ import { type DenyMutationPolicy } from "./deny-mutations.js";
2
+ import { type RootContext } from "./root-context.js";
3
+ export declare function assertMoveMutationAllowed(root: RootContext, params: {
4
+ fromRelative: string;
5
+ toRelative: string;
6
+ denyMutations?: DenyMutationPolicy;
7
+ }): Promise<void>;
8
+ //# sourceMappingURL=root-move-preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-move-preflight.d.ts","sourceRoot":"","sources":["../src/root-move-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAExE,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE;IACN,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC,GACA,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -0,0 +1,16 @@
1
+ import { assertMutationNotDenied, } from "./deny-mutations.js";
2
+ import { resolvePathInRoot } from "./root-context.js";
3
+ export async function assertMoveMutationAllowed(root, params) {
4
+ // Keep this preflight separate from the pinned resolutions in movePathFallback:
5
+ // mutation denials must take precedence over source alias or identity failures.
6
+ const source = await resolvePathInRoot(root, params.fromRelative, {
7
+ aliasErrorCode: "path-alias",
8
+ allowFinalSymlink: true,
9
+ });
10
+ await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
11
+ const target = await resolvePathInRoot(root, params.toRelative, {
12
+ aliasErrorCode: "path-alias",
13
+ allowFinalSymlink: true,
14
+ });
15
+ await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
16
+ }
@@ -0,0 +1,9 @@
1
+ import { type RootPathObservationKind, type RootPathObservationReceipt } from "./root-path.js";
2
+ import { type RootContext } from "./root-context.js";
3
+ export type PinnedObservedPath = {
4
+ rootReal: string;
5
+ resolved: string;
6
+ receipt?: RootPathObservationReceipt;
7
+ };
8
+ export declare function resolvePinnedObservedPathInRoot(root: RootContext, relativePath: string, kind: RootPathObservationKind): Promise<PinnedObservedPath | undefined>;
9
+ //# sourceMappingURL=root-observed-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-observed-path.d.ts","sourceRoot":"","sources":["../src/root-observed-path.ts"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAChC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAI3B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,0BAA0B,CAAC;CACtC,CAAC;AAEF,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAgFzC"}
@@ -0,0 +1,95 @@
1
+ import path from "node:path";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { getNativeDirectoryObservationBackend } from "./native-directory-observation.js";
4
+ import { isNotFoundPathError } from "./path.js";
5
+ import { PATH_ALIAS_POLICIES } from "./path-policy.js";
6
+ import { admitPathInsideRoot } from "./root-boundary.js";
7
+ import { assertRootPathObservationReceiptCurrent } from "./root-directory-list.js";
8
+ import { RootPathObservationError, resolveRootPathWithObservation, } from "./root-path.js";
9
+ import { createRootObservationGuard, ensureTrailingSep, expandRelativePathWithHome, } from "./root-context.js";
10
+ import { fileNotFoundError, outsideWorkspaceError } from "./root-errors.js";
11
+ import { getFsSafeTestHooks } from "./test-hooks.js";
12
+ export async function resolvePinnedObservedPathInRoot(root, relativePath, kind) {
13
+ const rootGuard = await createRootObservationGuard(root);
14
+ if (!rootGuard)
15
+ return undefined;
16
+ // Race hooks exercise the established JavaScript observation points. Do not
17
+ // silently move those points into the native helper in test configurations.
18
+ const directoryObserver = getNativeDirectoryObservationBackend(getFsSafeTestHooks() === undefined);
19
+ const rootReal = root.rootReal;
20
+ let observed;
21
+ try {
22
+ const expandedPath = await expandRelativePathWithHome(relativePath);
23
+ const observationPath = process.platform === "win32"
24
+ ? expandedPath.replaceAll("/", path.sep)
25
+ : expandedPath;
26
+ observed = await resolveRootPathWithObservation({
27
+ absolutePath: path.isAbsolute(observationPath)
28
+ ? observationPath
29
+ : observationPath === ""
30
+ ? rootReal
31
+ : `${ensureTrailingSep(rootReal)}${observationPath}`,
32
+ rootPath: rootReal,
33
+ rootCanonicalPath: rootReal,
34
+ rootIdentity: root.rootIdentity,
35
+ boundaryLabel: "root",
36
+ policy: PATH_ALIAS_POLICIES.strict,
37
+ }, { kind, rootGuard, directoryObserver });
38
+ }
39
+ catch (error) {
40
+ if (error instanceof RootPathObservationError) {
41
+ if (error.parentReceipt) {
42
+ assertRootPathObservationReceiptCurrent(root, error.parentReceipt);
43
+ if (error.traversalFailure && !isNotFoundPathError(error.error)) {
44
+ if (error.error instanceof FsSafeError && error.error.code === "symlink")
45
+ throw error.error;
46
+ throw new FsSafeError("path-alias", "path alias escape blocked", { cause: error.error });
47
+ }
48
+ }
49
+ if (isNotFoundPathError(error.error)) {
50
+ if (kind === "stat") {
51
+ throw fileNotFoundError(error.error instanceof Error ? error.error : undefined);
52
+ }
53
+ throw new FsSafeError("not-found", "directory not found", {
54
+ cause: error.error instanceof Error ? error.error : undefined,
55
+ });
56
+ }
57
+ throw error.error;
58
+ }
59
+ if (error instanceof FsSafeError && error.code === "symlink")
60
+ throw error;
61
+ throw new FsSafeError("path-alias", "path alias escape blocked", { cause: error });
62
+ }
63
+ const resolved = observed.resolved;
64
+ if (observed.receipt) {
65
+ // A receipt is emitted only for the straight traversal whose lexical and
66
+ // canonical cursors stayed identical and inside the checked boundary.
67
+ return {
68
+ rootReal: resolved.rootCanonicalPath,
69
+ resolved: resolved.canonicalPath,
70
+ receipt: observed.receipt,
71
+ };
72
+ }
73
+ const relativeResolved = path.relative(resolved.rootCanonicalPath, resolved.canonicalPath);
74
+ if (relativeResolved === "" || relativeResolved === ".") {
75
+ return {
76
+ rootReal: resolved.rootCanonicalPath,
77
+ resolved: resolved.canonicalPath,
78
+ };
79
+ }
80
+ const firstSegment = relativeResolved.split(path.sep)[0];
81
+ if (firstSegment === ".." || path.isAbsolute(relativeResolved)) {
82
+ throw outsideWorkspaceError();
83
+ }
84
+ const admittedCanonicalPath = admitPathInsideRoot({
85
+ rootPath: resolved.rootCanonicalPath,
86
+ candidatePath: resolved.canonicalPath,
87
+ rootIdentity: root.rootIdentity,
88
+ });
89
+ if (!admittedCanonicalPath)
90
+ throw outsideWorkspaceError();
91
+ return {
92
+ rootReal: resolved.rootCanonicalPath,
93
+ resolved: admittedCanonicalPath.path,
94
+ };
95
+ }
@@ -0,0 +1,12 @@
1
+ export declare function sanitizeRootPathError(error: unknown): unknown;
2
+ export declare function pathEscapeError(params: {
3
+ boundaryLabel: string;
4
+ rootPath: string;
5
+ absolutePath: string;
6
+ }): Error;
7
+ export declare function symlinkEscapeError(params: {
8
+ boundaryLabel: string;
9
+ rootCanonicalPath: string;
10
+ symlinkPath: string;
11
+ }): Error;
12
+ //# sourceMappingURL=root-path-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-path-errors.d.ts","sourceRoot":"","sources":["../src/root-path-errors.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK7D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,KAAK,CAIR;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,KAAK,CAIR"}
@@ -0,0 +1,13 @@
1
+ import { formatErrorDetail, shortPath } from "./error-detail.js";
2
+ export function sanitizeRootPathError(error) {
3
+ if (error instanceof Error) {
4
+ error.message = formatErrorDetail(error.message);
5
+ }
6
+ return error;
7
+ }
8
+ export function pathEscapeError(params) {
9
+ return new Error(`Path escapes ${params.boundaryLabel} (${shortPath(params.rootPath)}): ${shortPath(params.absolutePath)}`);
10
+ }
11
+ export function symlinkEscapeError(params) {
12
+ return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
13
+ }
@@ -1,7 +1,9 @@
1
+ import { type RootBoundaryIdentity } from "./root-boundary.js";
1
2
  export declare function absolutePathWithRawSegments(candidate: string): string;
2
3
  export declare function rawPathRelativeToCanonicalRoot(candidate: string, rootCanonicalPath: string, options?: {
3
4
  rejectSymlinks?: boolean;
4
5
  rejectFinalSymlink?: boolean;
6
+ rootIdentity?: RootBoundaryIdentity;
5
7
  }): string | undefined;
6
8
  export declare function resolvePathViaExistingAncestor(targetPath: string): Promise<string>;
7
9
  export declare function resolvePathViaExistingAncestorSync(targetPath: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAOA,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAO,GACvE,MAAM,GAAG,SAAS,CAuCpB;AAkBD,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
1
+ {"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAWA,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAarE;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,oBAAoB,CAAC;CAChC,GACL,MAAM,GAAG,SAAS,CAqDpB;AAkBD,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkCxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAqC7E"}
@@ -4,14 +4,28 @@ import { FsSafeError } from "./errors.js";
4
4
  import { formatErrorDetail } from "./error-detail.js";
5
5
  import { isNotFoundPathError, isPathInside } from "./path.js";
6
6
  import { realpathSync } from "./realpath.js";
7
+ import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
8
+ import { admitPathInsideRoot } from "./root-boundary.js";
7
9
  export function absolutePathWithRawSegments(candidate) {
8
- if (path.isAbsolute(candidate))
9
- return candidate;
10
- const drive = path.parse(candidate).root;
10
+ if (path.sep !== "\\") {
11
+ if (path.isAbsolute(candidate))
12
+ return candidate;
13
+ const base = process.cwd();
14
+ return `${base}${base.endsWith(path.sep) ? "" : path.sep}${candidate}`;
15
+ }
16
+ const raw = path.sep === "\\" ? candidate.replaceAll("/", "\\") : candidate;
17
+ const absolute = path.isAbsolute(raw);
18
+ if (absolute && raw[0] !== "\\")
19
+ return raw;
20
+ const drive = path.parse(raw).root;
21
+ if (absolute && drive !== "\\")
22
+ return raw;
11
23
  const base = drive ? path.resolve(drive) : process.cwd();
12
- return `${base}${path.sep}${candidate.slice(drive.length)}`;
24
+ return `${base}${base.endsWith(path.sep) ? "" : path.sep}${raw.slice(drive.length)}`;
13
25
  }
14
26
  export function rawPathRelativeToCanonicalRoot(candidate, rootCanonicalPath, options = {}) {
27
+ assertNoWindowsPathAlias(candidate);
28
+ assertNoWindowsPathAlias(rootCanonicalPath);
15
29
  const absolute = absolutePathWithRawSegments(candidate);
16
30
  const raw = process.platform === "win32" ? absolute.replaceAll("/", path.sep) : absolute;
17
31
  const filesystemRoot = path.parse(raw).root;
@@ -19,12 +33,14 @@ export function rawPathRelativeToCanonicalRoot(candidate, rootCanonicalPath, opt
19
33
  const finalComponentIndex = segments.findLastIndex(segment => segment !== "" && segment !== ".");
20
34
  let prefix = filesystemRoot;
21
35
  let traversedSymlink = false;
36
+ const identityCache = process.platform === "win32" ? new Map() : undefined;
22
37
  for (let index = 0; index < segments.length; index += 1) {
23
38
  prefix += `${prefix.endsWith(path.sep) ? "" : path.sep}${segments[index]}`;
24
39
  let canonical;
25
40
  let isSymlink = false;
26
41
  try {
27
- const stat = fs.lstatSync(prefix);
42
+ const operationPath = pathForWindowsFilesystem(prefix);
43
+ const stat = fs.lstatSync(operationPath);
28
44
  isSymlink = stat.isSymbolicLink();
29
45
  // Check the original leaf before canonicalization can erase an entry alias.
30
46
  if (isSymlink && options.rejectFinalSymlink && index === finalComponentIndex) {
@@ -33,8 +49,10 @@ export function rawPathRelativeToCanonicalRoot(candidate, rootCanonicalPath, opt
33
49
  if (!isSymlink && !stat.isDirectory() && index < segments.length - 1)
34
50
  return undefined;
35
51
  traversedSymlink ||= isSymlink;
36
- canonical = realpathSync.native(prefix);
37
- if (isSymlink && index < segments.length - 1 && !fs.statSync(canonical).isDirectory())
52
+ canonical = realpathSync.native(operationPath);
53
+ assertNoWindowsPathAlias(canonical);
54
+ if (isSymlink && index < segments.length - 1 &&
55
+ !fs.statSync(pathForWindowsFilesystem(canonical)).isDirectory())
38
56
  return undefined;
39
57
  if (isSymlink && !isPathInside(rootCanonicalPath, canonical) && !isPathInside(canonical, rootCanonicalPath)) {
40
58
  throw new FsSafeError("outside-workspace", `symlink prefix resolves outside the root ancestry: ${formatErrorDetail(candidate)}`);
@@ -47,12 +65,18 @@ export function rawPathRelativeToCanonicalRoot(candidate, rootCanonicalPath, opt
47
65
  return undefined;
48
66
  continue;
49
67
  }
50
- if (!isPathInside(rootCanonicalPath, canonical))
68
+ const admitted = admitPathInsideRoot({
69
+ rootPath: rootCanonicalPath,
70
+ candidatePath: canonical,
71
+ rootIdentity: options.rootIdentity,
72
+ identityCache,
73
+ });
74
+ if (!admitted)
51
75
  continue;
52
76
  if (options.rejectSymlinks && traversedSymlink) {
53
77
  throw new FsSafeError("symlink", "symlink path component not allowed");
54
78
  }
55
- return [path.relative(rootCanonicalPath, canonical), ...segments.slice(index + 1)]
79
+ return [admitted.relativePath, ...segments.slice(index + 1)]
56
80
  .filter(Boolean).join(path.sep);
57
81
  }
58
82
  return undefined;
@@ -62,7 +86,7 @@ function isFilesystemRoot(candidate) {
62
86
  }
63
87
  async function pathExists(targetPath) {
64
88
  try {
65
- fs.lstatSync(targetPath);
89
+ fs.lstatSync(pathForWindowsFilesystem(targetPath));
66
90
  return true;
67
91
  }
68
92
  catch (error) {
@@ -73,7 +97,9 @@ async function pathExists(targetPath) {
73
97
  }
74
98
  }
75
99
  export async function resolvePathViaExistingAncestor(targetPath) {
76
- const normalized = path.resolve(targetPath);
100
+ assertNoWindowsPathAlias(targetPath);
101
+ const normalized = resolvePathPreservingWindowsRoot(targetPath);
102
+ assertNoWindowsPathAlias(normalized);
77
103
  let cursor = normalized;
78
104
  const missingSuffix = [];
79
105
  while (!isFilesystemRoot(cursor) && !(await pathExists(cursor))) {
@@ -87,21 +113,30 @@ export async function resolvePathViaExistingAncestor(targetPath) {
87
113
  if (!(await pathExists(cursor))) {
88
114
  return normalized;
89
115
  }
116
+ let rawResolvedAncestor;
90
117
  try {
91
- const resolvedAncestor = path.resolve(realpathSync.native(cursor));
92
- return missingSuffix.length === 0
93
- ? resolvedAncestor
94
- : path.resolve(resolvedAncestor, ...missingSuffix);
118
+ rawResolvedAncestor = realpathSync.native(pathForWindowsFilesystem(cursor));
95
119
  }
96
120
  catch {
97
121
  return normalized;
98
122
  }
123
+ assertNoWindowsPathAlias(rawResolvedAncestor);
124
+ const resolvedAncestor = resolvePathPreservingWindowsRoot(rawResolvedAncestor);
125
+ assertNoWindowsPathAlias(resolvedAncestor);
126
+ const resolved = missingSuffix.length === 0
127
+ ? resolvedAncestor
128
+ : path.resolve(resolvedAncestor, ...missingSuffix);
129
+ assertNoWindowsPathAlias(resolved);
130
+ return resolved;
99
131
  }
100
132
  export function resolvePathViaExistingAncestorSync(targetPath) {
101
- const normalized = path.resolve(targetPath);
133
+ assertNoWindowsPathAlias(targetPath);
134
+ const normalized = resolvePathPreservingWindowsRoot(targetPath);
135
+ assertNoWindowsPathAlias(normalized);
102
136
  let cursor = normalized;
103
137
  const missingSuffix = [];
104
- while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
138
+ while (!isFilesystemRoot(cursor) &&
139
+ !fs.existsSync(pathForWindowsFilesystem(cursor))) {
105
140
  missingSuffix.unshift(path.basename(cursor));
106
141
  const parent = path.dirname(cursor);
107
142
  if (parent === cursor) {
@@ -109,16 +144,22 @@ export function resolvePathViaExistingAncestorSync(targetPath) {
109
144
  }
110
145
  cursor = parent;
111
146
  }
112
- if (!fs.existsSync(cursor)) {
147
+ if (!fs.existsSync(pathForWindowsFilesystem(cursor))) {
113
148
  return normalized;
114
149
  }
150
+ let rawResolvedAncestor;
115
151
  try {
116
- const resolvedAncestor = path.resolve(realpathSync(cursor));
117
- return missingSuffix.length === 0
118
- ? resolvedAncestor
119
- : path.resolve(resolvedAncestor, ...missingSuffix);
152
+ rawResolvedAncestor = realpathSync(pathForWindowsFilesystem(cursor));
120
153
  }
121
154
  catch {
122
155
  return normalized;
123
156
  }
157
+ assertNoWindowsPathAlias(rawResolvedAncestor);
158
+ const resolvedAncestor = resolvePathPreservingWindowsRoot(rawResolvedAncestor);
159
+ assertNoWindowsPathAlias(resolvedAncestor);
160
+ const resolved = missingSuffix.length === 0
161
+ ? resolvedAncestor
162
+ : path.resolve(resolvedAncestor, ...missingSuffix);
163
+ assertNoWindowsPathAlias(resolved);
164
+ return resolved;
124
165
  }
@@ -0,0 +1,63 @@
1
+ import fs, { type BigIntStats } from "node:fs";
2
+ import { type DirectoryObservationGuard } from "./directory-guard.js";
3
+ import { type NativeDirectoryObservationBackend, type NativeDirectoryObservationGuard } from "./native-directory-observation.js";
4
+ import { type RootBoundaryIdentity } from "./root-boundary.js";
5
+ import { type StatObservationReceipt } from "./stat-observation.js";
6
+ export type RootPathObservationKind = "stat" | "directory";
7
+ export type RootPathDirectoryObservationGuard = DirectoryObservationGuard | NativeDirectoryObservationGuard;
8
+ export type RootPathTargetObservation = StatObservationReceipt | NativeDirectoryObservationGuard;
9
+ /** An exact receipt owned by one stat/list operation. Never cache it. */
10
+ export type RootPathObservationReceipt = {
11
+ kind: RootPathObservationKind;
12
+ rootGuard: DirectoryObservationGuard;
13
+ directoryGuard: RootPathDirectoryObservationGuard;
14
+ directoryObserver?: NativeDirectoryObservationBackend;
15
+ targetPath: string;
16
+ target: RootPathTargetObservation;
17
+ };
18
+ /** A failed initial target lookup must not discard its already admitted parent. */
19
+ export type RootPathParentObservationReceipt = Omit<RootPathObservationReceipt, "kind" | "target"> & {
20
+ kind: "stat-parent";
21
+ };
22
+ export type RootPathObservationRequest = {
23
+ kind: RootPathObservationKind;
24
+ rootGuard: DirectoryObservationGuard;
25
+ directoryObserver?: NativeDirectoryObservationBackend;
26
+ };
27
+ export type RootPathTraversalObservation = {
28
+ enabled: boolean;
29
+ request: RootPathObservationRequest;
30
+ targetIndex: number;
31
+ directoryIndex: number;
32
+ directoryGuard?: RootPathDirectoryObservationGuard;
33
+ directoryObserver?: NativeDirectoryObservationBackend;
34
+ targetPath?: string;
35
+ target?: RootPathTargetObservation;
36
+ };
37
+ export type RootPathObservedTraversalEntry = StatObservationReceipt | {
38
+ stat: fs.Stats | BigIntStats;
39
+ identity?: undefined;
40
+ } | NativeDirectoryObservationGuard;
41
+ export declare class RootPathObservationError extends Error {
42
+ readonly error: unknown;
43
+ readonly parentReceipt?: RootPathParentObservationReceipt | undefined;
44
+ readonly traversalFailure: boolean;
45
+ constructor(error: unknown, parentReceipt?: RootPathParentObservationReceipt | undefined, traversalFailure?: boolean);
46
+ }
47
+ export type RootPathTraversalContext = {
48
+ observationEligible: boolean;
49
+ rootPath: string;
50
+ rootCanonicalPath: string;
51
+ resolveParams: {
52
+ rootIdentity?: RootBoundaryIdentity;
53
+ };
54
+ state: {
55
+ finalComponentIndex: number;
56
+ relativePath: string;
57
+ segments: string[];
58
+ };
59
+ };
60
+ export declare function createRootPathTraversalObservation(context: RootPathTraversalContext, request?: RootPathObservationRequest): RootPathTraversalObservation | undefined;
61
+ export declare function inspectRootPathTraversalEntry(pathname: string, directorySlot: boolean, observation: RootPathTraversalObservation): RootPathObservedTraversalEntry;
62
+ export declare function captureRootPathObservedDirectory(observation: RootPathTraversalObservation, observed: RootPathObservedTraversalEntry, publicPath: string, canonicalPath: string, rootCanonicalPath: string, rootIdentity?: RootBoundaryIdentity): boolean;
63
+ //# sourceMappingURL=root-path-observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-path-observation.d.ts","sourceRoot":"","sources":["../src/root-path-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAIL,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,EACrC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA8B,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEhG,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,MAAM,iCAAiC,GACzC,yBAAyB,GACzB,+BAA+B,CAAC;AAEpC,MAAM,MAAM,yBAAyB,GACjC,sBAAsB,GACtB,+BAA+B,CAAC;AAEpC,yEAAyE;AACzE,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,yBAAyB,CAAC;IACrC,cAAc,EAAE,iCAAiC,CAAC;IAClD,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,yBAAyB,CAAC;CACnC,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,gCAAgC,GAAG,IAAI,CAAC,0BAA0B,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG;IACnG,IAAI,EAAE,aAAa,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,yBAAyB,CAAC;IACrC,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,0BAA0B,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,iCAAiC,CAAC;IACnD,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACtC,sBAAsB,GACtB;IAAE,IAAI,EAAE,EAAE,CAAC,KAAK,GAAG,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GACtD,+BAA+B,CAAC;AAIpC,qBAAa,wBAAyB,SAAQ,KAAK;IAE/C,QAAQ,CAAC,KAAK,EAAE,OAAO;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,gCAAgC;IACzD,QAAQ,CAAC,gBAAgB;IAH3B,YACW,KAAK,EAAE,OAAO,EACd,aAAa,CAAC,EAAE,gCAAgC,YAAA,EAChD,gBAAgB,UAAQ,EAIlC;CACF;AAoBD,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE;QAAE,YAAY,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE;QACL,mBAAmB,EAAE,MAAM,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,wBAAwB,EACjC,OAAO,CAAC,EAAE,0BAA0B,GACnC,4BAA4B,GAAG,SAAS,CAkF1C;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,OAAO,EACtB,WAAW,EAAE,4BAA4B,GACxC,8BAA8B,CAyBhC;AAED,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,4BAA4B,EACzC,QAAQ,EAAE,8BAA8B,EACxC,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,YAAY,CAAC,EAAE,oBAAoB,GAClC,OAAO,CAgCT"}