@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
package/dist/root-path.js CHANGED
@@ -1,12 +1,18 @@
1
- import fs from "node:fs";
1
+ import fs, {} from "node:fs";
2
2
  import path from "node:path";
3
- import { formatErrorDetail, shortPath } from "./error-detail.js";
3
+ import { shortPath } from "./error-detail.js";
4
+ import { pathEscapeError, sanitizeRootPathError, symlinkEscapeError } from "./root-path-errors.js";
4
5
  import { FsSafeError } from "./errors.js";
5
- import { assertNoNulPathInput, isNotFoundPathError, isPathInside, isPathRelativeEscape, } from "./path.js";
6
+ import { isNativeDirectoryObservationGuard } from "./native-directory-observation.js";
7
+ import { assertNoNulPathInput, isNotFoundPathError, isPathRelativeEscape, } from "./path.js";
6
8
  import { absolutePathWithRawSegments, rawPathRelativeToCanonicalRoot, resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
9
+ import { admitPathInsideRoot, } from "./root-boundary.js";
10
+ import { RootPathObservationError, captureRootPathObservedDirectory, createRootPathTraversalObservation, inspectRootPathTraversalEntry, } from "./root-path-observation.js";
7
11
  import { resolveSymlinkHopPath, resolveSymlinkHopPathSync } from "./root-path-symlink.js";
8
12
  import { assertNoDriveRelativePathSegments } from "./safe-path-segment.js";
13
+ import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathFromBasePreservingWindowsRoot, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
9
14
  export { resolvePathViaExistingAncestorSync } from "./root-path-existing.js";
15
+ export { RootPathObservationError, } from "./root-path-observation.js";
10
16
  export const ROOT_PATH_ALIAS_POLICIES = {
11
17
  strict: Object.freeze({
12
18
  allowFinalSymlinkForUnlink: false,
@@ -25,15 +31,47 @@ export async function resolveRootPath(params) {
25
31
  throw sanitizeRootPathError(error);
26
32
  }
27
33
  }
28
- async function resolveRootPathInternal(params) {
29
- assertValidRootPathInputs(params);
30
- params = { ...params, absolutePath: absolutePathWithRawSegments(params.absolutePath) };
31
- const rootPath = path.resolve(params.rootPath);
32
- const absolutePath = path.resolve(params.absolutePath);
33
- const rootCanonicalPath = params.rootCanonicalPath
34
- ? path.resolve(params.rootCanonicalPath)
34
+ export async function resolveRootPathWithCanonicalRootObservation(params, observeRoot) {
35
+ try {
36
+ return await resolveRootPathInternal(params, undefined, undefined, observeRoot);
37
+ }
38
+ catch (error) {
39
+ throw sanitizeRootPathError(error);
40
+ }
41
+ }
42
+ async function resolveRootPathInternal(params, observationRequest, observationOutput, observeRoot, removalReceipts) {
43
+ const input = captureValidRootPathInputs(params);
44
+ const rawAbsolutePath = absolutePathWithRawSegments(input.absolutePath);
45
+ const rootPath = resolvePathPreservingWindowsRoot(input.rootPath);
46
+ const absolutePath = resolvePathPreservingWindowsRoot(rawAbsolutePath);
47
+ const rootCanonicalPath = input.rootCanonicalPath
48
+ ? resolvePathPreservingWindowsRoot(input.rootCanonicalPath)
35
49
  : await resolvePathViaExistingAncestor(rootPath);
36
- return resolveRootPathLexicalAsync(prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath));
50
+ assertNoWindowsPathAlias(rootPath);
51
+ assertNoWindowsPathAlias(absolutePath);
52
+ assertNoWindowsPathAlias(rootCanonicalPath);
53
+ observeRoot?.(rootCanonicalPath);
54
+ return resolveRootPathLexicalAsync(prepareRootTraversal(input, rootPath, rootCanonicalPath, absolutePath, rawAbsolutePath), observationRequest, observationOutput, removalReceipts);
55
+ }
56
+ export async function resolveRootPathWithObservation(params, request) {
57
+ const output = {};
58
+ try {
59
+ const resolved = await resolveRootPathInternal(params, request, output);
60
+ return output.receipt ? { resolved, receipt: output.receipt } : { resolved };
61
+ }
62
+ catch (error) {
63
+ throw sanitizeRootPathError(error);
64
+ }
65
+ }
66
+ // Removal receipts are separate from metadata observations and canonical-root
67
+ // callbacks; the public resolver's options and result remain unchanged.
68
+ export async function resolveRootPathForRemoval(params, receipts) {
69
+ try {
70
+ return await resolveRootPathInternal(params, undefined, undefined, undefined, receipts);
71
+ }
72
+ catch (error) {
73
+ throw sanitizeRootPathError(error);
74
+ }
37
75
  }
38
76
  export function resolveRootPathSync(params) {
39
77
  try {
@@ -43,46 +81,90 @@ export function resolveRootPathSync(params) {
43
81
  throw sanitizeRootPathError(error);
44
82
  }
45
83
  }
46
- function resolveRootPathSyncInternal(params) {
47
- assertValidRootPathInputs(params);
48
- params = { ...params, absolutePath: absolutePathWithRawSegments(params.absolutePath) };
49
- const rootPath = path.resolve(params.rootPath);
50
- const absolutePath = path.resolve(params.absolutePath);
51
- const rootCanonicalPath = params.rootCanonicalPath
52
- ? path.resolve(params.rootCanonicalPath)
84
+ export function resolveRootPathSyncWithCanonicalRootObservation(params, observeRoot) {
85
+ try {
86
+ return resolveRootPathSyncInternal(params, observeRoot);
87
+ }
88
+ catch (error) {
89
+ throw sanitizeRootPathError(error);
90
+ }
91
+ }
92
+ function resolveRootPathSyncInternal(params, observeRoot) {
93
+ const input = captureValidRootPathInputs(params);
94
+ const rawAbsolutePath = absolutePathWithRawSegments(input.absolutePath);
95
+ const rootPath = resolvePathPreservingWindowsRoot(input.rootPath);
96
+ const absolutePath = resolvePathPreservingWindowsRoot(rawAbsolutePath);
97
+ const rootCanonicalPath = input.rootCanonicalPath
98
+ ? resolvePathPreservingWindowsRoot(input.rootCanonicalPath)
53
99
  : resolvePathViaExistingAncestorSync(rootPath);
54
- return resolveRootPathLexicalSync(prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath));
100
+ assertNoWindowsPathAlias(rootPath);
101
+ assertNoWindowsPathAlias(absolutePath);
102
+ assertNoWindowsPathAlias(rootCanonicalPath);
103
+ observeRoot?.(rootCanonicalPath);
104
+ return resolveRootPathLexicalSync(prepareRootTraversal(input, rootPath, rootCanonicalPath, absolutePath, rawAbsolutePath));
55
105
  }
56
- function prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath) {
57
- let raw = params.absolutePath;
58
- if (rawPathRelativeToRoot(rootPath, raw) === undefined) {
59
- const relative = rawPathRelativeToRoot(rootCanonicalPath, raw)
106
+ function prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath, rawAbsolutePath) {
107
+ let raw = rawAbsolutePath;
108
+ let trustedAbsolutePath = false;
109
+ const direct = admitRawPathInsideRoot(rootPath, raw, params.rootIdentity);
110
+ if (direct) {
111
+ raw = direct.path;
112
+ trustedAbsolutePath = direct.admission === "identity";
113
+ }
114
+ else {
115
+ const canonical = admitRawPathInsideRoot(rootCanonicalPath, raw, params.rootIdentity);
116
+ const relative = canonical?.relativePath
60
117
  ?? rawPathRelativeToCanonicalRoot(raw, rootCanonicalPath, params);
61
118
  if (relative === undefined) {
62
119
  throw pathEscapeError({ rootPath, absolutePath: raw, boundaryLabel: params.boundaryLabel });
63
120
  }
64
- raw = `${rootPath}${path.sep}${relative}`;
121
+ trustedAbsolutePath = canonical?.admission === "identity";
122
+ raw = relative === "" ? rootPath : `${rootPath}${path.sep}${relative}`;
65
123
  }
66
124
  return {
67
- params: raw === params.absolutePath ? params : { ...params, absolutePath: raw },
68
- rootPath, rootCanonicalPath, absolutePath,
125
+ params,
126
+ rawAbsolutePath: raw,
127
+ rootPath,
128
+ rootCanonicalPath,
129
+ // Preserve the caller-spelling receipt for ordinary aliases. Only a
130
+ // Windows case-fold admission must return the identity-gated Root spelling,
131
+ // because downstream I/O must not reuse the ambiguous caller prefix.
132
+ absolutePath: trustedAbsolutePath ? resolvePathPreservingWindowsRoot(raw) : absolutePath,
133
+ observationEligible: raw === rawAbsolutePath && !trustedAbsolutePath,
69
134
  };
70
135
  }
71
- function sanitizeRootPathError(error) {
72
- if (error instanceof Error) {
73
- error.message = formatErrorDetail(error.message);
74
- }
75
- return error;
76
- }
77
- function assertValidRootPathInputs(params) {
78
- assertNoNulPathInput(params.rootPath, "root path contains a NUL byte");
79
- assertNoNulPathInput(params.absolutePath, "absolute path contains a NUL byte");
80
- assertNoEmbeddedDriveRelativeSegment(params.rootPath, "root path");
81
- assertNoEmbeddedDriveRelativeSegment(params.absolutePath, "absolute path");
82
- if (params.rootCanonicalPath !== undefined) {
83
- assertNoNulPathInput(params.rootCanonicalPath, "canonical root path contains a NUL byte");
84
- assertNoEmbeddedDriveRelativeSegment(params.rootCanonicalPath, "canonical root path");
136
+ function captureValidRootPathInputs(params) {
137
+ const rootPath = params.rootPath;
138
+ assertNoNulPathInput(rootPath, "root path contains a NUL byte");
139
+ const absolutePath = params.absolutePath;
140
+ assertNoNulPathInput(absolutePath, "absolute path contains a NUL byte");
141
+ assertNoWindowsPathAlias(rootPath, "filesystem", "root path uses a Windows filesystem namespace alias");
142
+ assertNoWindowsPathAlias(absolutePath, "filesystem", "absolute path uses a Windows filesystem namespace alias");
143
+ assertNoEmbeddedDriveRelativeSegment(rootPath, "root path");
144
+ assertNoEmbeddedDriveRelativeSegment(absolutePath, "absolute path");
145
+ const rootCanonicalPath = params.rootCanonicalPath;
146
+ if (rootCanonicalPath !== undefined) {
147
+ assertNoNulPathInput(rootCanonicalPath, "canonical root path contains a NUL byte");
148
+ assertNoWindowsPathAlias(rootCanonicalPath, "filesystem", "canonical root path uses a Windows filesystem namespace alias");
149
+ assertNoEmbeddedDriveRelativeSegment(rootCanonicalPath, "canonical root path");
85
150
  }
151
+ // Keep traversal policy with the admitted paths across ancestor and symlink
152
+ // resolution. Caller-owned flags and getters must not change a running walk.
153
+ const policy = params.policy;
154
+ const rootIdentity = params.rootIdentity;
155
+ return {
156
+ rootPath,
157
+ absolutePath,
158
+ rootCanonicalPath,
159
+ rootIdentity: rootIdentity == null ? undefined : { dev: rootIdentity.dev, ino: rootIdentity.ino },
160
+ boundaryLabel: params.boundaryLabel,
161
+ policy: policy == null ? undefined : {
162
+ allowFinalSymlinkForUnlink: policy.allowFinalSymlinkForUnlink,
163
+ },
164
+ rejectSymlinks: params.rejectSymlinks,
165
+ rejectFinalSymlink: params.rejectFinalSymlink,
166
+ rejectUnresolvedSymlinks: params.rejectUnresolvedSymlinks,
167
+ };
86
168
  }
87
169
  function assertNoEmbeddedDriveRelativeSegment(filePath, label) {
88
170
  if (process.platform !== "win32") {
@@ -92,13 +174,15 @@ function assertNoEmbeddedDriveRelativeSegment(filePath, label) {
92
174
  assertNoDriveRelativePathSegments(filePath.slice(root.length).replaceAll("\\", "/"), label);
93
175
  }
94
176
  function createLexicalTraversalState(params) {
95
- const rawAbsolutePath = params.params.absolutePath;
96
- const relative = rawPathRelativeToRoot(params.rootPath, rawAbsolutePath);
177
+ const rawAbsolutePath = params.rawAbsolutePath;
178
+ const relative = admitRawPathInsideRoot(params.rootPath, rawAbsolutePath, params.params.rootIdentity)?.relativePath;
97
179
  if (relative === undefined)
98
180
  throw new Error("Path traversal must begin at the root");
99
181
  const segments = splitTraversalSegments(relative);
100
182
  return {
101
183
  segments,
184
+ relativePath: relative,
185
+ reuseLexicalCanonical: false,
102
186
  finalComponentIndex: segments.findLastIndex((segment) => segment !== "."),
103
187
  allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
104
188
  canonicalCursor: params.rootCanonicalPath,
@@ -114,8 +198,16 @@ function createLexicalTraversalContext(params) {
114
198
  rootPath: params.rootPath,
115
199
  rootCanonicalPath: params.rootCanonicalPath,
116
200
  absolutePath: params.absolutePath,
201
+ observationEligible: params.observationEligible,
117
202
  };
118
203
  }
204
+ function createLexicalTraversalObservation(context, request) {
205
+ const observation = createRootPathTraversalObservation(context, request);
206
+ if (observation && observation.targetIndex >= 0) {
207
+ context.state.reuseLexicalCanonical = true;
208
+ }
209
+ return observation;
210
+ }
119
211
  function splitTraversalSegments(value) {
120
212
  const segments = value
121
213
  .split(process.platform === "win32" ? /[\\/]+/ : /\/+/)
@@ -124,38 +216,53 @@ function splitTraversalSegments(value) {
124
216
  segments.push(".");
125
217
  return segments;
126
218
  }
127
- function rawPathRelativeToRoot(rootPath, candidatePath) {
219
+ function admitRawPathInsideRoot(rootPath, candidatePath, rootIdentity) {
128
220
  if (!path.isAbsolute(candidatePath)) {
129
221
  return undefined;
130
222
  }
131
- const root = path.resolve(rootPath);
223
+ // Every caller supplies the already-resolved root selected by
224
+ // resolveRootPathInternal; resolving it again is redundant on each traversal.
225
+ const root = rootPath;
132
226
  const candidate = process.platform === "win32"
133
227
  ? candidatePath.replaceAll("/", path.sep)
134
228
  : candidatePath;
229
+ if (process.platform === "win32") {
230
+ return admitPathInsideRoot({ rootPath: root, candidatePath: candidate, rootIdentity });
231
+ }
135
232
  if (candidate === root) {
136
- return "";
233
+ return { admission: "exact", path: candidate, relativePath: "" };
137
234
  }
138
235
  const rootWithSep = root.endsWith(path.sep) ? root : `${root}${path.sep}`;
139
236
  const candidatePrefix = candidate.slice(0, rootWithSep.length);
140
- const prefixMatches = process.platform === "win32"
141
- ? candidatePrefix.toLowerCase() === rootWithSep.toLowerCase()
142
- : candidatePrefix === rootWithSep;
143
- return prefixMatches ? candidate.slice(rootWithSep.length) : undefined;
237
+ return candidatePrefix === rootWithSep
238
+ ? {
239
+ admission: "exact",
240
+ path: candidate,
241
+ relativePath: candidate.slice(rootWithSep.length),
242
+ }
243
+ : undefined;
144
244
  }
145
245
  function assertLexicalCursorInsideBoundary(context, candidatePath) {
146
- assertInsideBoundary({
246
+ return assertInsideBoundary({
147
247
  boundaryLabel: context.resolveParams.boundaryLabel,
148
248
  rootCanonicalPath: context.rootCanonicalPath,
249
+ rootIdentity: context.resolveParams.rootIdentity,
149
250
  candidatePath,
150
251
  absolutePath: context.absolutePath,
151
252
  });
152
253
  }
153
254
  function advanceCanonicalCursorForSegment(context, segment) {
154
- context.state.canonicalCursor = path.resolve(context.state.canonicalCursor, segment);
155
- assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
255
+ const candidatePath = context.state.reuseLexicalCanonical
256
+ ? context.state.lexicalCursor
257
+ : resolvePathFromBasePreservingWindowsRoot(context.state.canonicalCursor, segment);
258
+ context.state.canonicalCursor = assertLexicalCursorInsideBoundary(context, candidatePath);
259
+ }
260
+ function disableLexicalTraversalObservation(context, observation) {
261
+ observation.enabled = false;
262
+ context.state.reuseLexicalCanonical = false;
156
263
  }
157
264
  function finalizeLexicalResolution(context, kind) {
158
- assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
265
+ context.state.canonicalCursor = assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
159
266
  return buildResolvedRootPath({
160
267
  absolutePath: context.absolutePath,
161
268
  canonicalPath: context.state.canonicalCursor,
@@ -181,18 +288,22 @@ function lexicalStatDisposition(params) {
181
288
  return params.allowFinalSymlink && params.isLast ? "break" : "resolve-link";
182
289
  }
183
290
  function applyResolvedSymlinkHop(context, linkCanonical) {
184
- if (!isPathInside(context.rootCanonicalPath, linkCanonical)) {
291
+ let admitted;
292
+ try {
293
+ admitted = assertLexicalCursorInsideBoundary(context, linkCanonical);
294
+ }
295
+ catch {
185
296
  throw symlinkEscapeError({
186
297
  boundaryLabel: context.resolveParams.boundaryLabel,
187
298
  rootCanonicalPath: context.rootCanonicalPath,
188
299
  symlinkPath: context.state.lexicalCursor,
189
300
  });
190
301
  }
191
- context.state.canonicalCursor = linkCanonical;
192
- context.state.lexicalCursor = linkCanonical;
302
+ context.state.canonicalCursor = admitted;
303
+ context.state.lexicalCursor = admitted;
193
304
  }
194
305
  function applyParentTraversalStep(context) {
195
- context.state.lexicalCursor = path.resolve(context.state.lexicalCursor, "..");
306
+ context.state.lexicalCursor = resolvePathFromBasePreservingWindowsRoot(context.state.lexicalCursor, "..");
196
307
  advanceCanonicalCursorForSegment(context, "..");
197
308
  if (context.state.missingDepth > 0)
198
309
  context.state.missingDepth -= 1;
@@ -205,12 +316,13 @@ function assertDirectoryBeforeMoreSegments(stat, pathname, isLast) {
205
316
  function assertResolvedLinkDirectory(pathname, isLast) {
206
317
  if (isLast)
207
318
  return;
208
- const stat = fs.statSync(pathname);
319
+ const stat = fs.statSync(pathForWindowsFilesystem(pathname));
209
320
  assertDirectoryBeforeMoreSegments(stat, pathname, isLast);
210
321
  }
211
- async function resolveRootPathLexicalAsync(params) {
322
+ async function resolveRootPathLexicalAsync(params, observationRequest, observationOutput, removalReceipts) {
212
323
  const context = createLexicalTraversalContext(params);
213
324
  const { state } = context;
325
+ const observation = createLexicalTraversalObservation(context, observationRequest);
214
326
  for (let idx = 0; idx < state.segments.length; idx += 1) {
215
327
  const segment = state.segments[idx] ?? "";
216
328
  const isLast = idx === state.segments.length - 1;
@@ -227,14 +339,56 @@ async function resolveRootPathLexicalAsync(params) {
227
339
  continue;
228
340
  }
229
341
  let stat;
342
+ let observed;
230
343
  try {
231
- stat = fs.lstatSync(state.lexicalCursor);
344
+ if (removalReceipts && !isLast) {
345
+ const directoryStat = fs.lstatSync(pathForWindowsFilesystem(state.lexicalCursor), { bigint: true });
346
+ if (directoryStat.isDirectory() && !directoryStat.isSymbolicLink()) {
347
+ removalReceipts.observeDirectory(state.lexicalCursor, directoryStat);
348
+ }
349
+ stat = directoryStat;
350
+ }
351
+ else {
352
+ const directorySlot = observation?.enabled === true && idx === observation.directoryIndex;
353
+ const targetSlot = observation?.enabled === true && idx === observation.targetIndex;
354
+ const observeExactly = directorySlot || targetSlot;
355
+ if (observeExactly) {
356
+ observed = inspectRootPathTraversalEntry(state.lexicalCursor, directorySlot, observation);
357
+ }
358
+ stat = isNativeDirectoryObservationGuard(observed)
359
+ ? undefined
360
+ : observed?.stat ?? fs.lstatSync(state.lexicalCursor);
361
+ }
232
362
  }
233
363
  catch (error) {
364
+ if (observation?.enabled && observation.request.kind === "stat" &&
365
+ idx === observation.targetIndex && observation.directoryGuard) {
366
+ const parentReceipt = {
367
+ kind: "stat-parent",
368
+ rootGuard: observation.request.rootGuard,
369
+ directoryGuard: observation.directoryGuard,
370
+ directoryObserver: observation.directoryObserver,
371
+ targetPath: state.lexicalCursor,
372
+ };
373
+ // Stop here: the generic missing-path fallback would re-admit a parent.
374
+ // Preserve observation-only versus ordinary traversal error precedence.
375
+ throw new RootPathObservationError(error instanceof RootPathObservationError ? error.error : sanitizeRootPathError(error), parentReceipt, !(error instanceof RootPathObservationError));
376
+ }
234
377
  if (handleLexicalLstatFailure(context, error, segment))
235
378
  continue;
236
379
  throw error;
237
380
  }
381
+ if (isNativeDirectoryObservationGuard(observed)) {
382
+ advanceCanonicalCursorForSegment(context, segment);
383
+ captureRootPathObservedDirectory(observation, observed, state.lexicalCursor, state.canonicalCursor, context.rootCanonicalPath, context.resolveParams.rootIdentity);
384
+ if (idx === observation.targetIndex) {
385
+ observation.targetPath = state.canonicalCursor;
386
+ observation.target = observed;
387
+ }
388
+ continue;
389
+ }
390
+ if (!stat)
391
+ throw new Error("directory observation did not return metadata");
238
392
  const isSymbolicLink = stat.isSymbolicLink();
239
393
  if (!isSymbolicLink)
240
394
  assertDirectoryBeforeMoreSegments(stat, state.lexicalCursor, isLast);
@@ -248,10 +402,24 @@ async function resolveRootPathLexicalAsync(params) {
248
402
  if (disposition !== "resolve-link") {
249
403
  state.preserveFinalSymlink = disposition === "break";
250
404
  advanceCanonicalCursorForSegment(context, segment);
405
+ if (observation?.enabled && isSymbolicLink) {
406
+ disableLexicalTraversalObservation(context, observation);
407
+ }
408
+ if (observation?.enabled && idx === observation.directoryIndex) {
409
+ const captured = observed?.identity && captureRootPathObservedDirectory(observation, observed, state.canonicalCursor, state.canonicalCursor, context.rootCanonicalPath, context.resolveParams.rootIdentity);
410
+ if (!captured)
411
+ disableLexicalTraversalObservation(context, observation);
412
+ }
413
+ if (observation?.enabled && idx === observation.targetIndex) {
414
+ observation.targetPath = state.canonicalCursor;
415
+ observation.target = observed?.identity ? observed : undefined;
416
+ }
251
417
  if (state.preserveFinalSymlink)
252
418
  break;
253
419
  continue;
254
420
  }
421
+ if (observation?.enabled)
422
+ disableLexicalTraversalObservation(context, observation);
255
423
  const linkCanonical = await resolveSymlinkHopPath(state.lexicalCursor, {
256
424
  rejectUnresolved: context.resolveParams.rejectUnresolvedSymlinks,
257
425
  });
@@ -261,7 +429,26 @@ async function resolveRootPathLexicalAsync(params) {
261
429
  }
262
430
  assertResolvedLinkDirectory(linkCanonical, isLast);
263
431
  }
264
- const kind = await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
432
+ const completeObservation = observation?.enabled === true && observation.directoryGuard !== undefined &&
433
+ observation.targetPath === state.canonicalCursor && observation.target !== undefined;
434
+ const kind = completeObservation
435
+ ? {
436
+ exists: true,
437
+ kind: isNativeDirectoryObservationGuard(observation.target)
438
+ ? "directory"
439
+ : toResolvedKind(observation.target.stat),
440
+ }
441
+ : await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
442
+ if (completeObservation && observationOutput) {
443
+ observationOutput.receipt = {
444
+ kind: observation.request.kind,
445
+ rootGuard: observation.request.rootGuard,
446
+ directoryGuard: observation.directoryGuard,
447
+ directoryObserver: observation.directoryObserver,
448
+ targetPath: observation.targetPath,
449
+ target: observation.target,
450
+ };
451
+ }
265
452
  return finalizeLexicalResolution(context, kind);
266
453
  }
267
454
  function resolveRootPathLexicalSync(params) {
@@ -333,9 +520,10 @@ function buildResolvedRootPath(params) {
333
520
  }
334
521
  async function getPathKind(absolutePath, preserveFinalSymlink) {
335
522
  try {
523
+ const operationPath = pathForWindowsFilesystem(absolutePath);
336
524
  const stat = preserveFinalSymlink
337
- ? fs.lstatSync(absolutePath)
338
- : fs.statSync(absolutePath);
525
+ ? fs.lstatSync(operationPath)
526
+ : fs.statSync(operationPath);
339
527
  return { exists: true, kind: toResolvedKind(stat) };
340
528
  }
341
529
  catch (error) {
@@ -347,7 +535,10 @@ async function getPathKind(absolutePath, preserveFinalSymlink) {
347
535
  }
348
536
  function getPathKindSync(absolutePath, preserveFinalSymlink) {
349
537
  try {
350
- const stat = preserveFinalSymlink ? fs.lstatSync(absolutePath) : fs.statSync(absolutePath);
538
+ const operationPath = pathForWindowsFilesystem(absolutePath);
539
+ const stat = preserveFinalSymlink
540
+ ? fs.lstatSync(operationPath)
541
+ : fs.statSync(operationPath);
351
542
  return { exists: true, kind: toResolvedKind(stat) };
352
543
  }
353
544
  catch (error) {
@@ -370,7 +561,7 @@ function toResolvedKind(stat) {
370
561
  return "other";
371
562
  }
372
563
  function relativeInsideRoot(rootPath, targetPath) {
373
- const relative = path.relative(path.resolve(rootPath), path.resolve(targetPath));
564
+ const relative = path.relative(resolvePathPreservingWindowsRoot(rootPath), resolvePathPreservingWindowsRoot(targetPath));
374
565
  if (!relative || relative === ".") {
375
566
  return "";
376
567
  }
@@ -380,14 +571,13 @@ function relativeInsideRoot(rootPath, targetPath) {
380
571
  return relative;
381
572
  }
382
573
  function assertInsideBoundary(params) {
383
- if (isPathInside(params.rootCanonicalPath, params.candidatePath)) {
384
- return;
574
+ const admitted = admitPathInsideRoot({
575
+ rootPath: params.rootCanonicalPath,
576
+ candidatePath: params.candidatePath,
577
+ rootIdentity: params.rootIdentity,
578
+ });
579
+ if (admitted) {
580
+ return admitted.path;
385
581
  }
386
582
  throw new Error(`Path resolves outside ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.absolutePath)}`);
387
583
  }
388
- function pathEscapeError(params) {
389
- return new Error(`Path escapes ${params.boundaryLabel} (${shortPath(params.rootPath)}): ${shortPath(params.absolutePath)}`);
390
- }
391
- function symlinkEscapeError(params) {
392
- return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
393
- }
@@ -0,0 +1,9 @@
1
+ import { type ResolvePathWithinRootParams } from "./path-scope-lexical.js";
2
+ export declare function resolvePathWithinRoot(params: ResolvePathWithinRootParams): {
3
+ ok: true;
4
+ path: string;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ };
9
+ //# sourceMappingURL=root-paths-lexical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-paths-lexical.d.ts","sourceRoot":"","sources":["../src/root-paths-lexical.ts"],"names":[],"mappings":"AACA,OAAO,EAAmC,KAAK,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAU5G,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,2BAA2B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgB3D"}
@@ -0,0 +1,22 @@
1
+ import path from "node:path";
2
+ import { resolvePathWithinNormalizedRoot } from "./path-scope-lexical.js";
3
+ import { hasWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
4
+ function invalidPath(scopeLabel) {
5
+ return { ok: false, error: `Invalid path: must stay within ${scopeLabel}` };
6
+ }
7
+ export function resolvePathWithinRoot(params) {
8
+ const rootDir = params.rootDir;
9
+ if (typeof rootDir !== "string")
10
+ path.resolve(rootDir);
11
+ const requestedPath = params.requestedPath;
12
+ const scopeLabel = params.scopeLabel;
13
+ const defaultFileName = params.defaultFileName;
14
+ if (hasWindowsPathAlias(rootDir, "filesystem") ||
15
+ hasWindowsPathAlias(requestedPath, "filesystem") ||
16
+ (defaultFileName !== undefined &&
17
+ hasWindowsPathAlias(defaultFileName, "filesystem"))) {
18
+ return invalidPath(scopeLabel);
19
+ }
20
+ const root = resolvePathPreservingWindowsRoot(rootDir);
21
+ return resolvePathWithinNormalizedRoot({ rootDir, requestedPath, scopeLabel, defaultFileName }, root);
22
+ }
@@ -1,16 +1,10 @@
1
- import { FsSafeError } from "./errors.js";
1
+ import { type DirectoryResult } from "./root-directory.js";
2
+ export { resolvePathWithinRoot } from "./root-paths-lexical.js";
3
+ export { ensureDirectoryWithinRoot } from "./root-directory.js";
2
4
  type InvalidPathResult = {
3
5
  ok: false;
4
6
  error: string;
5
7
  };
6
- type DirectoryResult = {
7
- ok: true;
8
- path: string;
9
- } | {
10
- ok: false;
11
- error: string;
12
- diagnostic?: FsSafeError;
13
- };
14
8
  type ResolvePathsWithinRootParams = {
15
9
  rootDir: string;
16
10
  requestedPaths: string[];
@@ -50,18 +44,6 @@ export type PathScope = {
50
44
  mode?: number;
51
45
  }): Promise<DirectoryResult>;
52
46
  };
53
- export declare function resolvePathWithinRoot(params: {
54
- rootDir: string;
55
- requestedPath: string;
56
- scopeLabel: string;
57
- defaultFileName?: string;
58
- }): {
59
- ok: true;
60
- path: string;
61
- } | {
62
- ok: false;
63
- error: string;
64
- };
65
47
  export declare function resolveWritablePathWithinRoot(params: {
66
48
  rootDir: string;
67
49
  requestedPath: string;
@@ -74,16 +56,8 @@ export declare function resolveWritablePathWithinRoot(params: {
74
56
  ok: false;
75
57
  error: string;
76
58
  }>;
77
- export declare function ensureDirectoryWithinRoot(params: {
78
- rootDir: string;
79
- requestedPath: string;
80
- scopeLabel: string;
81
- defaultDirName?: string;
82
- mode?: number;
83
- }): Promise<DirectoryResult>;
84
59
  export declare function resolvePathsWithinRoot(params: ResolvePathsWithinRootParams): ResolvePathsWithinRootResult;
85
60
  export declare function resolveExistingPathsWithinRoot(params: ResolvePathsWithinRootParams): Promise<ResolvePathsWithinRootResult>;
86
61
  export declare function resolveStrictExistingPathsWithinRoot(params: ResolvePathsWithinRootParams): Promise<ResolvePathsWithinRootResult>;
87
62
  export declare function pathScope(rootDir: string, options: PathScopeOptions): PathScope;
88
- export {};
89
63
  //# sourceMappingURL=root-paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,KAAK,iBAAiB,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACtD,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,KAAK,4BAA4B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,iBAAiB,CAAC;AACtF,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CACL,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,uBAAuB,GAChC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,4BAA4B,CAAC;IACnE,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC1E,KAAK,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IACvE,QAAQ,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,SAAS,CACP,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,uBAAuB,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GACpD,OAAO,CAAC,eAAe,CAAC,CAAC;CAC7B,CAAC;AAoEF,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA0CrE;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,4BAA4B,GACnC,4BAA4B,CA2B9B;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,4BAA4B,CAAC,CAEvC;AAED,wBAAsB,oCAAoC,CACxD,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,4BAA4B,CAAC,CAEvC;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAyC/E"}