@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
@@ -1,25 +1,28 @@
1
1
  import fs, {} from "node:fs";
2
2
  import path from "node:path";
3
- import { normalizeMaxBytes } from "./byte-budget.js";
4
- import { readBoundedAsync } from "./bounded-read.js";
5
3
  import { syncDirectory } from "./directory-durability.js";
6
4
  import { syncQueueDirectoryCreation } from "./json-durable-queue-directory.js";
7
- import { getErrorCode, acknowledgeDurableQueueEntry, claimDurableQueueEntry, completeDeliveredQueueEntry, moveDurableQueueEntryToFailed, } from "./json-durable-queue-ownership.js";
5
+ import { queueValidationRoot, queueValidationRoots, resolveQueueFilesystemPath, } from "./json-durable-queue-paths.js";
6
+ import { getErrorCode, acknowledgeDurableQueueEntry, claimDurableQueueEntry, completeDeliveredQueueEntry, migrateDurableQueueEntry, moveDurableQueueEntryToFailed, validateDurableQueueEntryPaths, } from "./json-durable-queue-ownership.js";
7
+ import { withJsonDurableQueueEntry } from "./json-durable-queue-read.js";
8
8
  import { stringifyJsonDocument } from "./json-stringify.js";
9
- import { resolveReadOpenFlags } from "./read-open-flags.js";
10
9
  import { realpathSync } from "./realpath.js";
11
10
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
12
11
  import { replaceFileAtomicWithDirectorySync } from "./replace-file.js";
13
12
  import { assertSafePathSegment } from "./safe-path-segment.js";
14
- import { inspectFileIdentity } from "./strict-file-identity.js";
15
- export const DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES = 16 * 1024 * 1024;
13
+ import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
14
+ import { assertNoWindowsPathAlias, hasWindowsPathAlias, } from "./windows-path-alias.js";
15
+ export { DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES } from "./json-durable-queue-read.js";
16
16
  function assertSafeQueueEntryId(id) {
17
17
  assertSafePathSegment(id, { label: "queue entry id" });
18
18
  }
19
19
  export async function unlinkBestEffort(filePath) {
20
+ if (hasWindowsPathAlias(filePath, "filesystem"))
21
+ return;
20
22
  await fs.promises.unlink(filePath).catch(() => undefined);
21
23
  }
22
24
  export async function jsonDurableQueueEntryExists(filePath) {
25
+ assertNoWindowsPathAlias(filePath);
23
26
  try {
24
27
  const stat = fs.lstatSync(filePath);
25
28
  return stat.isFile();
@@ -46,6 +49,7 @@ async function unlinkStaleTmpBestEffort(filePath, now, maxAgeMs) {
46
49
  }
47
50
  export function resolveJsonDurableQueueEntryPaths(queueDir, id) {
48
51
  assertSafeQueueEntryId(id);
52
+ assertNoWindowsPathAlias(queueDir);
49
53
  return {
50
54
  jsonPath: path.join(queueDir, `${id}.json`),
51
55
  deliveredPath: path.join(queueDir, `${id}.delivered`),
@@ -53,9 +57,13 @@ export function resolveJsonDurableQueueEntryPaths(queueDir, id) {
53
57
  };
54
58
  }
55
59
  export async function ensureJsonDurableQueueDirs(params) {
56
- const roots = await queueValidationRoots(params.queueDir, params.failedDir);
57
- await ensureJsonDurableQueueDir(params.queueDir, roots.queueRoot);
58
- await ensureJsonDurableQueueDir(params.failedDir, roots.failedRoot);
60
+ const queueDir = params.queueDir;
61
+ const failedDir = params.failedDir;
62
+ assertNoWindowsPathAlias(queueDir);
63
+ assertNoWindowsPathAlias(failedDir);
64
+ const roots = await queueValidationRoots(queueDir, failedDir);
65
+ await ensureJsonDurableQueueDir(queueDir, roots.queueRoot);
66
+ await ensureJsonDurableQueueDir(failedDir, roots.failedRoot);
59
67
  }
60
68
  async function ensureJsonDurableQueueDir(dir, validationRoot) {
61
69
  const root = validationRoot
@@ -73,41 +81,6 @@ async function assertJsonDurableQueueDir(dir, validationRoot) {
73
81
  : queueValidationRoot(dir);
74
82
  await assertNoSymlinkDirectorySegments(root, dir, false);
75
83
  }
76
- function commonPathAncestor(paths) {
77
- const resolved = paths.map((entry) => path.resolve(entry));
78
- const root = path.parse(resolved[0] ?? process.cwd()).root;
79
- const parts = resolved.map((entry) => path.relative(root, entry).split(path.sep));
80
- const common = [];
81
- for (let index = 0; parts.every((part) => index < part.length); index++) {
82
- const segment = parts[0]?.[index];
83
- if (!segment || !parts.every((part) => part[index] === segment))
84
- break;
85
- common.push(segment);
86
- }
87
- return path.join(root, ...common);
88
- }
89
- function samePathRoot(paths) {
90
- const roots = paths.map((entry) => path.parse(path.resolve(entry)).root);
91
- const first = process.platform === "win32" ? roots[0]?.toLowerCase() : roots[0];
92
- return roots.every((root) => (process.platform === "win32" ? root.toLowerCase() : root) === first);
93
- }
94
- async function queueValidationRoots(queueDir, failedDir) {
95
- if (samePathRoot([queueDir, failedDir])) {
96
- const common = commonPathAncestor([queueDir, failedDir]);
97
- const root = queueValidationRoot(common);
98
- return { failedRoot: root, queueRoot: root };
99
- }
100
- return {
101
- failedRoot: queueValidationRoot(failedDir),
102
- queueRoot: queueValidationRoot(queueDir),
103
- };
104
- }
105
- function queueValidationRoot(dir) {
106
- return {
107
- path: path.parse(path.resolve(dir)).root,
108
- allowSymlinkBase: process.platform === "darwin",
109
- };
110
- }
111
84
  async function isDarwinSystemAlias(dir, stat) {
112
85
  if (process.platform !== "darwin" || !stat.isSymbolicLink()) {
113
86
  return false;
@@ -124,8 +97,8 @@ async function isDarwinSystemAlias(dir, stat) {
124
97
  }
125
98
  }
126
99
  async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissing) {
127
- let base = path.resolve(validationRoot.path);
128
- let target = path.resolve(dir);
100
+ let base = resolveQueueFilesystemPath(validationRoot.path);
101
+ let target = resolveQueueFilesystemPath(dir);
129
102
  let current = base;
130
103
  let baseStat = fs.lstatSync(base);
131
104
  if (baseStat.isSymbolicLink() && validationRoot.allowSymlinkBase) {
@@ -213,88 +186,51 @@ async function chmodQueueDirectory(dir) {
213
186
  }
214
187
  }
215
188
  export async function writeJsonDurableQueueEntry(params) {
189
+ await writeQueueEntry(params);
190
+ }
191
+ async function writeQueueEntry(params, beforeRename) {
192
+ const filePath = admitStandalonePublicationPath(params.filePath);
193
+ const entry = params.entry;
194
+ const tempPrefix = params.tempPrefix;
216
195
  await replaceFileAtomicWithDirectorySync({
217
- filePath: params.filePath,
218
- content: stringifyJsonDocument(params.entry, null, 2),
196
+ filePath,
197
+ content: stringifyJsonDocument(entry, null, 2),
219
198
  mode: 0o600,
220
- tempPrefix: params.tempPrefix,
199
+ tempPrefix,
221
200
  syncTempFile: true,
201
+ beforeRename,
222
202
  }, syncDirectory);
223
203
  }
224
- async function inspectQueueEntry(inspect, maxBytes, expected) {
225
- let inspectionFailed = false;
226
- try {
227
- return await inspectFileIdentity(async () => {
228
- try {
229
- const stat = inspect();
230
- if (stat.isSymbolicLink() || !stat.isFile()) {
231
- throw new Error("queue entry is not a regular file");
232
- }
233
- if (stat.nlink > 1n)
234
- throw new Error("queue entry hardlinks are not allowed");
235
- if (stat.size > maxBytes)
236
- throw new Error(`queue entry exceeds ${maxBytes} bytes`);
237
- return stat;
238
- }
239
- catch (error) {
240
- inspectionFailed = true;
241
- throw error;
242
- }
243
- }, expected);
244
- }
245
- catch (error) {
246
- // Translate only the identity helper's rejection, never inspection errors.
247
- if (inspectionFailed)
248
- throw error;
249
- throw new Error("queue entry changed during read", { cause: error });
250
- }
251
- }
252
- async function readBoundedUtf8File(params) {
253
- const inspectPath = () => fs.lstatSync(params.filePath, { bigint: true });
254
- const initialStat = await inspectQueueEntry(inspectPath, params.maxBytes);
255
- const handle = await fs.promises.open(params.filePath, resolveReadOpenFlags());
256
- try {
257
- const openedStat = await inspectQueueEntry(() => fs.fstatSync(handle.fd, { bigint: true }), params.maxBytes, initialStat);
258
- await inspectQueueEntry(inspectPath, params.maxBytes, openedStat);
259
- const bytes = await readBoundedAsync(params.maxBytes, async (buffer, length) => (await handle.read(buffer, 0, length, null)).bytesRead, {
260
- initialSize: openedStat.size >= 0n && Number.isSafeInteger(Number(openedStat.size))
261
- ? Number(openedStat.size) : undefined,
262
- createLimitError: () => new Error(`queue entry exceeds ${params.maxBytes} bytes`),
263
- });
264
- return bytes.toString("utf8");
265
- }
266
- finally {
267
- await handle.close();
268
- }
269
- }
270
204
  export async function readJsonDurableQueueEntry(filePath, options = {}) {
271
- return JSON.parse(await readBoundedUtf8File({
272
- filePath,
273
- maxBytes: normalizeMaxBytes(options.maxBytes, {
274
- defaultValue: DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES,
275
- }),
276
- }));
205
+ return await withJsonDurableQueueEntry(filePath, options, async (entry) => entry);
206
+ }
207
+ async function migrateClaimedQueueEntry(params) {
208
+ await migrateDurableQueueEntry(params.paths, params.identity, params.releaseReadPin, async (filePath, beforePublish) => {
209
+ await writeQueueEntry({ filePath, entry: params.entry, tempPrefix: params.tempPrefix }, beforePublish);
210
+ });
277
211
  }
278
212
  export async function ackJsonDurableQueueEntry(paths) {
279
213
  await acknowledgeDurableQueueEntry(paths);
280
214
  }
281
215
  export async function loadJsonDurableQueueEntry(params) {
282
216
  try {
283
- const claimedPath = await claimDurableQueueEntry(params.paths);
217
+ const pathsInput = params.paths;
218
+ const paths = validateDurableQueueEntryPaths(pathsInput);
219
+ const claimedPath = await claimDurableQueueEntry(paths);
284
220
  if (!claimedPath)
285
221
  return null;
286
- const raw = await readJsonDurableQueueEntry(claimedPath, {
222
+ return await withJsonDurableQueueEntry(claimedPath, {
287
223
  maxBytes: params.maxBytes,
224
+ }, async (raw, identity, releaseReadPin) => {
225
+ const read = params.read;
226
+ const result = read ? await Reflect.apply(read, params, [raw, paths.jsonPath]) : { entry: raw };
227
+ if (result.migrated) {
228
+ await migrateClaimedQueueEntry({
229
+ paths, identity, releaseReadPin, entry: result.entry, tempPrefix: params.tempPrefix,
230
+ });
231
+ }
232
+ return result.entry;
288
233
  });
289
- const result = params.read ? await params.read(raw, params.paths.jsonPath) : { entry: raw };
290
- if (result.migrated) {
291
- await writeJsonDurableQueueEntry({
292
- filePath: claimedPath,
293
- entry: result.entry,
294
- tempPrefix: params.tempPrefix,
295
- });
296
- }
297
- return result.entry;
298
234
  }
299
235
  catch (error) {
300
236
  if (getErrorCode(error) === "ENOENT") {
@@ -304,9 +240,11 @@ export async function loadJsonDurableQueueEntry(params) {
304
240
  }
305
241
  }
306
242
  export async function loadPendingJsonDurableQueueEntries(options) {
243
+ const queueDir = options.queueDir;
244
+ assertNoWindowsPathAlias(queueDir);
307
245
  let files;
308
246
  try {
309
- files = await fs.promises.readdir(options.queueDir);
247
+ files = await fs.promises.readdir(queueDir);
310
248
  }
311
249
  catch (error) {
312
250
  if (getErrorCode(error) === "ENOENT") {
@@ -324,10 +262,11 @@ export async function loadPendingJsonDurableQueueEntries(options) {
324
262
  catch {
325
263
  continue;
326
264
  }
327
- await completeDeliveredQueueEntry(resolveJsonDurableQueueEntryPaths(options.queueDir, id));
265
+ const paths = validateDurableQueueEntryPaths(resolveJsonDurableQueueEntryPaths(queueDir, id));
266
+ await completeDeliveredQueueEntry(paths);
328
267
  }
329
268
  else if (options.cleanupTmpMaxAgeMs !== undefined && file.endsWith(".tmp")) {
330
- await unlinkStaleTmpBestEffort(path.join(options.queueDir, file), now, options.cleanupTmpMaxAgeMs);
269
+ await unlinkStaleTmpBestEffort(path.join(queueDir, file), now, options.cleanupTmpMaxAgeMs);
331
270
  }
332
271
  }
333
272
  const ids = [];
@@ -354,36 +293,46 @@ export async function loadPendingJsonDurableQueueEntries(options) {
354
293
  }
355
294
  const entries = [];
356
295
  for (const id of ids) {
357
- const paths = resolveJsonDurableQueueEntryPaths(options.queueDir, id);
296
+ const paths = validateDurableQueueEntryPaths(resolveJsonDurableQueueEntryPaths(queueDir, id));
358
297
  const claimedPath = await claimDurableQueueEntry(paths, { skipUnowned: true });
359
298
  if (!claimedPath)
360
299
  continue;
361
- let result;
300
+ let migrationStarted = false;
362
301
  try {
363
- const raw = await readJsonDurableQueueEntry(claimedPath, { maxBytes: options.maxBytes });
364
- result = options.read ? await options.read(raw, paths.jsonPath) : { entry: raw };
302
+ const entry = await withJsonDurableQueueEntry(claimedPath, { maxBytes: options.maxBytes }, async (raw, identity, releaseReadPin) => {
303
+ const read = options.read;
304
+ const result = read ? await Reflect.apply(read, options, [raw, paths.jsonPath]) : { entry: raw };
305
+ if (result.migrated) {
306
+ migrationStarted = true;
307
+ await migrateClaimedQueueEntry({
308
+ paths, identity, releaseReadPin, entry: result.entry, tempPrefix: options.tempPrefix,
309
+ });
310
+ }
311
+ return result.entry;
312
+ });
313
+ entries.push(entry);
365
314
  }
366
- catch {
315
+ catch (error) {
316
+ if (migrationStarted)
317
+ throw error;
367
318
  continue;
368
319
  }
369
- if (result.migrated) {
370
- await writeJsonDurableQueueEntry({
371
- filePath: claimedPath,
372
- entry: result.entry,
373
- tempPrefix: options.tempPrefix,
374
- });
375
- }
376
- entries.push(result.entry);
377
320
  }
378
321
  return entries;
379
322
  }
380
323
  export async function moveJsonDurableQueueEntryToFailed(params) {
381
- assertSafeQueueEntryId(params.id);
382
- const roots = await queueValidationRoots(params.queueDir, params.failedDir);
383
- await assertJsonDurableQueueDir(params.queueDir, roots.queueRoot);
384
- await ensureJsonDurableQueueDir(params.failedDir, roots.failedRoot);
324
+ const id = params.id;
325
+ const queueDir = params.queueDir;
326
+ const failedDir = params.failedDir;
327
+ assertSafeQueueEntryId(id);
328
+ assertNoWindowsPathAlias(queueDir);
329
+ assertNoWindowsPathAlias(failedDir);
330
+ const roots = await queueValidationRoots(queueDir, failedDir);
331
+ await assertJsonDurableQueueDir(queueDir, roots.queueRoot);
332
+ await ensureJsonDurableQueueDir(failedDir, roots.failedRoot);
333
+ const paths = validateDurableQueueEntryPaths(resolveJsonDurableQueueEntryPaths(queueDir, id));
385
334
  await moveDurableQueueEntryToFailed({
386
- paths: resolveJsonDurableQueueEntryPaths(params.queueDir, params.id),
387
- failedPath: path.join(params.failedDir, `${params.id}.json`),
335
+ paths,
336
+ failedPath: path.join(failedDir, `${id}.json`),
388
337
  });
389
338
  }
@@ -1 +1 @@
1
- {"version":3,"file":"json-store.d.ts","sourceRoot":"","sources":["../src/json-store.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,oBAAoB,GAAG;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC;AAEtE,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAQvE"}
1
+ {"version":3,"file":"json-store.d.ts","sourceRoot":"","sources":["../src/json-store.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAIlC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,oBAAoB,GAAG;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC;AAEtE,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAUvE"}
@@ -1,8 +1,12 @@
1
1
  import path from "node:path";
2
2
  import { fileStore } from "./file-store.js";
3
3
  import {} from "./json-document-store.js";
4
+ import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
5
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
4
6
  export function jsonStore(options) {
5
- const filePath = path.resolve(options.filePath);
7
+ const filePathInput = admitStandalonePublicationPath(options.filePath);
8
+ const filePath = path.resolve(filePathInput);
9
+ assertNoWindowsPathAlias(filePath);
6
10
  return fileStore({
7
11
  rootDir: path.dirname(filePath),
8
12
  private: true,
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
package/dist/json.js CHANGED
@@ -9,8 +9,10 @@ import { readRegularFile, readRegularFileSync, statRegularFile } from "./regular
9
9
  import { openRootFileSync } from "./root-file.js";
10
10
  import { resolveReadOpenFlags } from "./read-open-flags.js";
11
11
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
12
+ import { admitStandalonePublicationPath } from "./standalone-publication-path.js";
12
13
  import { writeTextAtomic } from "./text-atomic.js";
13
14
  import { sleep } from "./timing.js";
15
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
14
16
  const READ_RETRY_MAX_ATTEMPTS = 5;
15
17
  const READ_RETRY_BASE_DELAY_MS = 50;
16
18
  function isRetryableReadError(err, options) {
@@ -167,16 +169,17 @@ export function tryReadJsonSync(pathname, options = {}) {
167
169
  }
168
170
  }
169
171
  export function writeJsonSync(pathname, data) {
172
+ const filePath = admitStandalonePublicationPath(pathname);
170
173
  // Keep literal parent segments so staging follows the same symlinks as the target.
171
- const tmpPath = path.format({ ...path.parse(pathname), base: `.fs-safe-${randomUUID()}.tmp` });
174
+ const tmpPath = path.format({ ...path.parse(filePath), base: `.fs-safe-${randomUUID()}.tmp` });
172
175
  const payload = `${stringifyJsonDocument(data, null, 2)}\n`;
173
- fsSync.mkdirSync(recursiveMkdirPath(path.dirname(pathname)), { recursive: true, mode: JSON_DIR_MODE });
176
+ fsSync.mkdirSync(recursiveMkdirPath(path.dirname(filePath)), { recursive: true, mode: JSON_DIR_MODE });
174
177
  try {
175
178
  const tempIdentity = writeTempJsonFile(tmpPath, payload);
176
179
  trySetSecureMode(tmpPath, tempIdentity);
177
- renameJsonFileWithFallback(tmpPath, pathname);
178
- trySetSecureMode(pathname, tempIdentity);
179
- trySyncDirectory(pathname);
180
+ renameJsonFileWithFallback(tmpPath, filePath);
181
+ trySetSecureMode(filePath, tempIdentity);
182
+ trySyncDirectory(filePath);
180
183
  }
181
184
  finally {
182
185
  try {
@@ -207,29 +210,59 @@ function resolveInvalidMessage(invalidMessage, relativePath) {
207
210
  return invalidMessage ?? `${relativePath} has an unexpected shape`;
208
211
  }
209
212
  export function readRootStructuredFileSync(options) {
210
- const absolutePath = path.resolve(options.rootDir, options.relativePath);
213
+ return readRootStructuredFileSyncInternal(options, options);
214
+ }
215
+ function readRootStructuredFileSyncInternal(options, parser) {
216
+ let absolutePath;
217
+ let relativePath;
218
+ let rootDir;
219
+ let rootRealPath;
220
+ try {
221
+ rootDir = options.rootDir;
222
+ relativePath = options.relativePath;
223
+ rootRealPath = options.rootRealPath;
224
+ assertNoWindowsPathAlias(rootDir);
225
+ assertNoWindowsPathAlias(relativePath, "relative");
226
+ if (rootRealPath !== undefined) {
227
+ assertNoWindowsPathAlias(rootRealPath);
228
+ }
229
+ absolutePath = path.resolve(rootDir, relativePath);
230
+ assertNoWindowsPathAlias(absolutePath);
231
+ }
232
+ catch (error) {
233
+ return {
234
+ ok: false,
235
+ reason: "open",
236
+ failure: { ok: false, reason: "validation", error },
237
+ };
238
+ }
239
+ const boundaryLabel = options.boundaryLabel;
240
+ const rejectHardlinks = options.rejectHardlinks;
241
+ const maxBytes = options.maxBytes;
211
242
  const opened = openRootFileSync({
212
243
  absolutePath,
213
- rootPath: options.rootDir,
214
- ...(options.rootRealPath !== undefined ? { rootRealPath: options.rootRealPath } : {}),
215
- boundaryLabel: options.boundaryLabel,
216
- rejectHardlinks: options.rejectHardlinks,
217
- maxBytes: options.maxBytes,
244
+ rootPath: rootDir,
245
+ rootRealPath,
246
+ boundaryLabel,
247
+ rejectHardlinks,
248
+ maxBytes,
218
249
  allowedType: "file",
219
250
  });
220
251
  if (!opened.ok) {
221
252
  return { ok: false, reason: "open", failure: opened };
222
253
  }
223
254
  try {
224
- const raw = options.maxBytes === undefined
255
+ const raw = maxBytes === undefined
225
256
  ? fsSync.readFileSync(opened.fd, "utf8")
226
- : readFileDescriptorBoundedSync(opened.fd, options.maxBytes).toString("utf8");
227
- const parsed = options.parse(raw);
228
- if (options.validate && !options.validate(parsed)) {
257
+ : readFileDescriptorBoundedSync(opened.fd, maxBytes).toString("utf8");
258
+ const parse = parser.parse;
259
+ const parsed = Reflect.apply(parse, parser, [raw]);
260
+ const validate = parser.validate;
261
+ if (validate && !Reflect.apply(validate, parser, [parsed])) {
229
262
  return {
230
263
  ok: false,
231
264
  reason: "invalid",
232
- error: resolveInvalidMessage(options.invalidMessage, options.relativePath),
265
+ error: resolveInvalidMessage(parser.invalidMessage, relativePath),
233
266
  };
234
267
  }
235
268
  return {
@@ -244,7 +277,7 @@ export function readRootStructuredFileSync(options) {
244
277
  return {
245
278
  ok: false,
246
279
  reason: "parse",
247
- error: `failed to parse ${options.relativePath}: ${String(error)}`,
280
+ error: `failed to parse ${relativePath}: ${String(error)}`,
248
281
  };
249
282
  }
250
283
  finally {
@@ -252,14 +285,12 @@ export function readRootStructuredFileSync(options) {
252
285
  }
253
286
  }
254
287
  export function readRootJsonSync(options) {
255
- return readRootStructuredFileSync({
256
- ...options,
288
+ return readRootStructuredFileSyncInternal(options, {
257
289
  parse: (raw) => JSON.parse(raw),
258
290
  });
259
291
  }
260
292
  export function readRootJsonObjectSync(options) {
261
- return readRootStructuredFileSync({
262
- ...options,
293
+ return readRootStructuredFileSyncInternal(options, {
263
294
  parse: (raw) => JSON.parse(raw),
264
295
  validate: isRecord,
265
296
  invalidMessage: (relativePath) => `${relativePath} must contain a JSON object`,
@@ -334,8 +365,9 @@ export function readJsonSync(filePath, options = {}) {
334
365
  }
335
366
  }
336
367
  export async function writeJson(filePath, value, options) {
368
+ const admittedPath = admitStandalonePublicationPath(filePath);
337
369
  const text = stringifyJsonDocument(value, null, 2);
338
- await writeTextAtomic(filePath, text, {
370
+ await writeTextAtomic(admittedPath, text, {
339
371
  mode: options?.mode,
340
372
  dirMode: options?.dirMode,
341
373
  trailingNewline: options?.trailingNewline,
@@ -1 +1 @@
1
- {"version":3,"file":"local-file-access.d.ts","sourceRoot":"","sources":["../src/local-file-access.ts"],"names":[],"mappings":"AAOA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhD;AAOD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAeT;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAET;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI,CAIjF;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,CAqBR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB3E"}
1
+ {"version":3,"file":"local-file-access.d.ts","sourceRoot":"","sources":["../src/local-file-access.ts"],"names":[],"mappings":"AAQA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhD;AAOD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAeT;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAET;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI,CAIjF;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,CAwBR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgB3E"}
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { fileURLToPath, URL } from "node:url";
3
3
  import { normalizeLowercaseStringOrEmpty } from "./string-coerce.js";
4
+ import { hasWindowsPathAlias } from "./windows-path-alias.js";
4
5
  const ENCODED_FILE_URL_SEPARATOR_RE = /%(?:2f|5c)/i;
5
6
  const FILE_URL_PREFIX_RE = /^file:\/\//i;
6
7
  export function isFileUrl(input) {
@@ -54,6 +55,9 @@ export function safeFileURLToPath(fileUrl, platform = process.platform) {
54
55
  throw new Error(`file:// URLs cannot encode path separators: ${fileUrl}`);
55
56
  }
56
57
  const filePath = fileURLToPath(parsed, { windows: platform === "win32" });
58
+ if (hasWindowsPathAlias(filePath, "filesystem", platform)) {
59
+ throw new Error(`Local file URL cannot use Windows filesystem namespace aliases: ${filePath}`);
60
+ }
57
61
  if (isWindowsNetworkPath(filePath, platform)) {
58
62
  throw new Error(`Local file URL cannot use Windows network paths: ${filePath}`);
59
63
  }
@@ -0,0 +1,17 @@
1
+ import type { BigIntStats, Stats } from "node:fs";
2
+ import type { FileHandle } from "node:fs/promises";
3
+ import type { HardlinkPolicy } from "./root-options.js";
4
+ import type { SymlinkPolicy } from "./root-symlink-policy.js";
5
+ type OwnedLocalFile = {
6
+ handle: FileHandle;
7
+ stat: Stats;
8
+ identity: BigIntStats;
9
+ preOpenStat: BigIntStats | undefined;
10
+ };
11
+ export declare function openLocalFileDescriptor(filePath: string, options?: {
12
+ hardlinks?: HardlinkPolicy;
13
+ symlinks?: SymlinkPolicy;
14
+ readWrite?: true;
15
+ }): Promise<OwnedLocalFile>;
16
+ export {};
17
+ //# sourceMappingURL=local-file-descriptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-file-descriptor.d.ts","sourceRoot":"","sources":["../src/local-file-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAYnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAO9D,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;CACtC,CAAC;AAIF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB,GACA,OAAO,CAAC,cAAc,CAAC,CA0EzB"}
@@ -0,0 +1,84 @@
1
+ import fsSync from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { recordFileOpenFailure, recordPreOpenFileChange } from "./opened-file-failure.js";
5
+ import { assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isSymlinkOpenError, } from "./path.js";
6
+ import { resolveReadOpenFlags } from "./read-open-flags.js";
7
+ import { fileNotFoundError, hardlinkedPathNotAllowedError } from "./root-errors.js";
8
+ import { inspectFileIdentity } from "./strict-file-identity.js";
9
+ import { getFsSafeTestHooks } from "./test-hooks.js";
10
+ const OPEN_READ_FLAGS = resolveReadOpenFlags();
11
+ const OPEN_READ_FOLLOW_FLAGS = resolveReadOpenFlags({ followSymlinks: true });
12
+ // Ownership transfers only after descriptor verification. Each admission caller
13
+ // must close on rejection and construct an OpenResult only after admission.
14
+ export async function openLocalFileDescriptor(filePath, options) {
15
+ assertNoUnsafeDeviceReadPath(filePath);
16
+ const fsSafeTestHooks = getFsSafeTestHooks();
17
+ let preOpenStat;
18
+ let observedBeforeOpen = false;
19
+ // Reject directories before opening so we never surface EISDIR to callers (e.g. tool
20
+ // results that get sent to messaging channels). See openclaw/openclaw#31186.
21
+ try {
22
+ preOpenStat = await inspectFileIdentity(() => {
23
+ const stat = fsSync.lstatSync(filePath, { bigint: true });
24
+ observedBeforeOpen = true;
25
+ if (stat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
26
+ throw new FsSafeError("symlink", "symlink not allowed");
27
+ }
28
+ if (!stat.isFile() && !stat.isSymbolicLink()) {
29
+ throw new FsSafeError("not-file", "not a file");
30
+ }
31
+ return stat;
32
+ });
33
+ }
34
+ catch (err) {
35
+ if (err instanceof FsSafeError || observedBeforeOpen) {
36
+ throw err;
37
+ }
38
+ // Only an initial lookup failure falls through; a failed re-inspection aborts.
39
+ }
40
+ if (preOpenStat) {
41
+ await fsSafeTestHooks?.afterPreOpenLstat?.(filePath);
42
+ }
43
+ const openFlags = (options?.symlinks === "follow-within-root"
44
+ ? OPEN_READ_FOLLOW_FLAGS
45
+ : OPEN_READ_FLAGS) | (options?.readWrite ? fsSync.constants.O_RDWR : 0);
46
+ await fsSafeTestHooks?.beforeOpen?.(filePath, openFlags);
47
+ let handle;
48
+ try {
49
+ handle = await fs.open(filePath, openFlags).catch((error) => recordFileOpenFailure(isNotFoundPathError(error) ? fileNotFoundError() : error, filePath));
50
+ }
51
+ catch (err) {
52
+ if (isSymlinkOpenError(err)) {
53
+ throw new FsSafeError("symlink", "symlink open blocked", { cause: err });
54
+ }
55
+ // Defensive: if open still throws EISDIR (e.g. race), sanitize so it never leaks.
56
+ if (hasNodeErrorCode(err, "EISDIR")) {
57
+ throw new FsSafeError("not-file", "not a file");
58
+ }
59
+ throw err;
60
+ }
61
+ try {
62
+ await fsSafeTestHooks?.afterOpen?.(filePath, handle);
63
+ const stat = fsSync.fstatSync(handle.fd);
64
+ if (!stat.isFile()) {
65
+ throw new FsSafeError("not-file", "not a file");
66
+ }
67
+ // Keep numeric Stats for the public receipt, never for identity verification.
68
+ let openedIdentity;
69
+ const identity = await inspectFileIdentity(() => (openedIdentity = fsSync.fstatSync(handle.fd, { bigint: true })), preOpenStat && !preOpenStat.isSymbolicLink() ? preOpenStat : undefined).catch(async (error) => {
70
+ if ([0, 1].includes(stat.nlink)) {
71
+ await recordPreOpenFileChange(error, handle, filePath, preOpenStat, openedIdentity);
72
+ }
73
+ throw error;
74
+ });
75
+ if (options?.hardlinks === "reject" && stat.nlink > 1) {
76
+ throw hardlinkedPathNotAllowedError();
77
+ }
78
+ return { handle, stat, identity, preOpenStat };
79
+ }
80
+ catch (err) {
81
+ await handle.close().catch(() => { });
82
+ throw err;
83
+ }
84
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"local-roots.d.ts","sourceRoot":"","sources":["../src/local-roots.ts"],"names":[],"mappings":"AAQA,OAAO,EAAQ,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3F,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG,sBAAsB,GAAG;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,sBAAsB,GAAG;IACnE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAsDF,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,oBAAoB,GAAG,IAAI,CAqC7B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAiCtC"}
1
+ {"version":3,"file":"local-roots.d.ts","sourceRoot":"","sources":["../src/local-roots.ts"],"names":[],"mappings":"AAQA,OAAO,EAAQ,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAQ3F,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG,sBAAsB,GAAG;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,sBAAsB,GAAG;IACnE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAiEF,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC,GAC5C,oBAAoB,GAAG,IAAI,CA2C7B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuCtC"}