@openclaw/fs-safe 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +12 -7
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +32 -11
  5. package/dist/advanced.d.ts +2 -0
  6. package/dist/advanced.d.ts.map +1 -1
  7. package/dist/advanced.js +2 -0
  8. package/dist/archive-input.d.ts.map +1 -1
  9. package/dist/archive-input.js +6 -2
  10. package/dist/archive-merge.d.ts.map +1 -1
  11. package/dist/archive-merge.js +15 -2
  12. package/dist/archive-native.d.ts.map +1 -1
  13. package/dist/archive-native.js +7 -19
  14. package/dist/archive-plan.js +1 -1
  15. package/dist/archive-read.d.ts.map +1 -1
  16. package/dist/archive-read.js +18 -13
  17. package/dist/archive-staging.d.ts.map +1 -1
  18. package/dist/archive-staging.js +43 -10
  19. package/dist/archive-tar-inspect.d.ts.map +1 -1
  20. package/dist/archive-tar-inspect.js +4 -1
  21. package/dist/archive-zip-admission.d.ts +1 -1
  22. package/dist/archive-zip-admission.d.ts.map +1 -1
  23. package/dist/archive-zip-admission.js +2 -2
  24. package/dist/archive-zip-directory.d.ts +3 -0
  25. package/dist/archive-zip-directory.d.ts.map +1 -1
  26. package/dist/archive-zip-directory.js +13 -2
  27. package/dist/archive-zip-loader.d.ts +3 -2
  28. package/dist/archive-zip-loader.d.ts.map +1 -1
  29. package/dist/archive-zip-loader.js +30 -4
  30. package/dist/archive-zip-manifest.d.ts +5 -0
  31. package/dist/archive-zip-manifest.d.ts.map +1 -0
  32. package/dist/archive-zip-manifest.js +22 -0
  33. package/dist/archive-zip-names.d.ts +6 -1
  34. package/dist/archive-zip-names.d.ts.map +1 -1
  35. package/dist/archive-zip-names.js +35 -14
  36. package/dist/archive-zip-preflight.d.ts.map +1 -1
  37. package/dist/archive-zip-preflight.js +3 -2
  38. package/dist/archive.d.ts.map +1 -1
  39. package/dist/archive.js +9 -4
  40. package/dist/darwin-acl.d.ts +4 -0
  41. package/dist/darwin-acl.d.ts.map +1 -0
  42. package/dist/darwin-acl.js +24 -0
  43. package/dist/deny-mutations.d.ts.map +1 -1
  44. package/dist/deny-mutations.js +8 -2
  45. package/dist/directory-durability.d.ts.map +1 -1
  46. package/dist/directory-durability.js +67 -23
  47. package/dist/directory-entry-path.d.ts +3 -0
  48. package/dist/directory-entry-path.d.ts.map +1 -0
  49. package/dist/directory-entry-path.js +21 -0
  50. package/dist/directory-guard.d.ts +17 -1
  51. package/dist/directory-guard.d.ts.map +1 -1
  52. package/dist/directory-guard.js +134 -48
  53. package/dist/directory-mode-node.d.ts +12 -0
  54. package/dist/directory-mode-node.d.ts.map +1 -1
  55. package/dist/directory-mode-node.js +102 -4
  56. package/dist/effective-uid.d.ts +2 -0
  57. package/dist/effective-uid.d.ts.map +1 -0
  58. package/dist/effective-uid.js +25 -0
  59. package/dist/file-handle-transfer.d.ts.map +1 -1
  60. package/dist/file-handle-transfer.js +98 -27
  61. package/dist/file-hash.d.ts.map +1 -1
  62. package/dist/file-hash.js +3 -0
  63. package/dist/file-lock-sync.d.ts.map +1 -1
  64. package/dist/file-lock-sync.js +36 -6
  65. package/dist/file-lock.d.ts.map +1 -1
  66. package/dist/file-lock.js +29 -9
  67. package/dist/file-observation.d.ts +1 -1
  68. package/dist/file-observation.d.ts.map +1 -1
  69. package/dist/file-store-boundary.d.ts +6 -2
  70. package/dist/file-store-boundary.d.ts.map +1 -1
  71. package/dist/file-store-boundary.js +20 -65
  72. package/dist/file-store-copy-source.d.ts +5 -0
  73. package/dist/file-store-copy-source.d.ts.map +1 -0
  74. package/dist/file-store-copy-source.js +31 -0
  75. package/dist/file-store-path.d.ts.map +1 -1
  76. package/dist/file-store-path.js +4 -1
  77. package/dist/file-store-sync-directory.d.ts +16 -0
  78. package/dist/file-store-sync-directory.d.ts.map +1 -0
  79. package/dist/file-store-sync-directory.js +349 -0
  80. package/dist/file-store.d.ts.map +1 -1
  81. package/dist/file-store.js +11 -28
  82. package/dist/filename.d.ts.map +1 -1
  83. package/dist/filename.js +65 -20
  84. package/dist/fs.d.ts.map +1 -1
  85. package/dist/fs.js +3 -2
  86. package/dist/guarded-mkdir.d.ts +8 -0
  87. package/dist/guarded-mkdir.d.ts.map +1 -1
  88. package/dist/guarded-mkdir.js +176 -27
  89. package/dist/guest.d.ts.map +1 -1
  90. package/dist/guest.js +4 -1
  91. package/dist/home-dir.d.ts.map +1 -1
  92. package/dist/home-dir.js +73 -10
  93. package/dist/install-path.d.ts.map +1 -1
  94. package/dist/install-path.js +54 -19
  95. package/dist/json-durable-queue-ownership.d.ts +6 -0
  96. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  97. package/dist/json-durable-queue-ownership.js +89 -42
  98. package/dist/json-durable-queue-paths.d.ts +11 -0
  99. package/dist/json-durable-queue-paths.d.ts.map +1 -0
  100. package/dist/json-durable-queue-paths.js +42 -0
  101. package/dist/json-durable-queue-read.d.ts.map +1 -1
  102. package/dist/json-durable-queue-read.js +2 -0
  103. package/dist/json-durable-queue.d.ts.map +1 -1
  104. package/dist/json-durable-queue.js +50 -58
  105. package/dist/json-store.d.ts.map +1 -1
  106. package/dist/json-store.js +5 -1
  107. package/dist/json.d.ts.map +1 -1
  108. package/dist/json.js +54 -22
  109. package/dist/local-file-access.d.ts.map +1 -1
  110. package/dist/local-file-access.js +4 -0
  111. package/dist/local-file-descriptor.d.ts +17 -0
  112. package/dist/local-file-descriptor.d.ts.map +1 -0
  113. package/dist/local-file-descriptor.js +84 -0
  114. package/dist/local-roots.d.ts.map +1 -1
  115. package/dist/local-roots.js +35 -7
  116. package/dist/move-path-cleanup.d.ts +2 -0
  117. package/dist/move-path-cleanup.d.ts.map +1 -1
  118. package/dist/move-path-cleanup.js +44 -18
  119. package/dist/move-path.d.ts.map +1 -1
  120. package/dist/move-path.js +31 -6
  121. package/dist/native-binding.d.ts +14 -0
  122. package/dist/native-binding.d.ts.map +1 -1
  123. package/dist/native-directory-observation.d.ts +17 -0
  124. package/dist/native-directory-observation.d.ts.map +1 -0
  125. package/dist/native-directory-observation.js +37 -0
  126. package/dist/native-parent-admission.d.ts +31 -0
  127. package/dist/native-parent-admission.d.ts.map +1 -0
  128. package/dist/native-parent-admission.js +124 -0
  129. package/dist/native-pinned-write-windows.d.ts +0 -1
  130. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  131. package/dist/native-pinned-write-windows.js +0 -3
  132. package/dist/native-pinned-write.d.ts.map +1 -1
  133. package/dist/native-pinned-write.js +309 -40
  134. package/dist/output.d.ts.map +1 -1
  135. package/dist/output.js +15 -5
  136. package/dist/overwrite-file-handle.d.ts.map +1 -1
  137. package/dist/overwrite-file-handle.js +5 -1
  138. package/dist/owner-dacl.d.ts.map +1 -1
  139. package/dist/owner-dacl.js +2 -0
  140. package/dist/path-policy.d.ts.map +1 -1
  141. package/dist/path-policy.js +7 -2
  142. package/dist/path-prefix.d.ts +7 -0
  143. package/dist/path-prefix.d.ts.map +1 -0
  144. package/dist/path-prefix.js +82 -0
  145. package/dist/path-scope-lexical.d.ts.map +1 -1
  146. package/dist/path-scope-lexical.js +18 -8
  147. package/dist/path-segment-route.d.ts +7 -0
  148. package/dist/path-segment-route.d.ts.map +1 -0
  149. package/dist/path-segment-route.js +24 -0
  150. package/dist/path-suffix-aliases.d.ts +10 -0
  151. package/dist/path-suffix-aliases.d.ts.map +1 -0
  152. package/dist/path-suffix-aliases.js +386 -0
  153. package/dist/path.d.ts.map +1 -1
  154. package/dist/path.js +16 -5
  155. package/dist/permissions-windows.d.ts.map +1 -1
  156. package/dist/permissions-windows.js +14 -3
  157. package/dist/permissions.d.ts.map +1 -1
  158. package/dist/permissions.js +37 -8
  159. package/dist/pinned-mutation-admission.d.ts +25 -0
  160. package/dist/pinned-mutation-admission.d.ts.map +1 -0
  161. package/dist/pinned-mutation-admission.js +425 -0
  162. package/dist/pinned-mutation-observation.d.ts +34 -0
  163. package/dist/pinned-mutation-observation.d.ts.map +1 -0
  164. package/dist/pinned-mutation-observation.js +142 -0
  165. package/dist/pinned-mutation-shared-route.d.ts +24 -0
  166. package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
  167. package/dist/pinned-mutation-shared-route.js +70 -0
  168. package/dist/pinned-open.d.ts +6 -0
  169. package/dist/pinned-open.d.ts.map +1 -1
  170. package/dist/pinned-open.js +28 -10
  171. package/dist/pinned-write-types.d.ts +75 -0
  172. package/dist/pinned-write-types.d.ts.map +1 -0
  173. package/dist/pinned-write-types.js +1 -0
  174. package/dist/pinned-write.d.ts +7 -33
  175. package/dist/pinned-write.d.ts.map +1 -1
  176. package/dist/pinned-write.js +151 -17
  177. package/dist/private-directory.d.ts.map +1 -1
  178. package/dist/private-directory.js +2 -0
  179. package/dist/private-producer-handoff.d.ts +16 -0
  180. package/dist/private-producer-handoff.d.ts.map +1 -0
  181. package/dist/private-producer-handoff.js +272 -0
  182. package/dist/private-temp-workspace.d.ts +2 -39
  183. package/dist/private-temp-workspace.d.ts.map +1 -1
  184. package/dist/private-temp-workspace.js +183 -77
  185. package/dist/publish-file.d.ts.map +1 -1
  186. package/dist/publish-file.js +33 -29
  187. package/dist/regular-file.d.ts.map +1 -1
  188. package/dist/regular-file.js +56 -45
  189. package/dist/replace-directory.d.ts.map +1 -1
  190. package/dist/replace-directory.js +12 -5
  191. package/dist/replace-file-temp-owner.d.ts +1 -0
  192. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  193. package/dist/replace-file-temp-owner.js +12 -1
  194. package/dist/replace-file.d.ts.map +1 -1
  195. package/dist/replace-file.js +24 -24
  196. package/dist/root-boundary.d.ts +4 -0
  197. package/dist/root-boundary.d.ts.map +1 -1
  198. package/dist/root-boundary.js +6 -1
  199. package/dist/root-context.d.ts +12 -1
  200. package/dist/root-context.d.ts.map +1 -1
  201. package/dist/root-context.js +57 -11
  202. package/dist/root-directory-creation.d.ts +13 -0
  203. package/dist/root-directory-creation.d.ts.map +1 -0
  204. package/dist/root-directory-creation.js +212 -0
  205. package/dist/root-directory-list.d.ts +16 -4
  206. package/dist/root-directory-list.d.ts.map +1 -1
  207. package/dist/root-directory-list.js +180 -39
  208. package/dist/root-directory.d.ts +23 -0
  209. package/dist/root-directory.d.ts.map +1 -0
  210. package/dist/root-directory.js +141 -0
  211. package/dist/root-errors.d.ts.map +1 -1
  212. package/dist/root-errors.js +12 -1
  213. package/dist/root-file-final-admission.d.ts +21 -0
  214. package/dist/root-file-final-admission.d.ts.map +1 -0
  215. package/dist/root-file-final-admission.js +83 -0
  216. package/dist/root-file.d.ts.map +1 -1
  217. package/dist/root-file.js +103 -24
  218. package/dist/root-impl.d.ts +1 -0
  219. package/dist/root-impl.d.ts.map +1 -1
  220. package/dist/root-impl.js +289 -317
  221. package/dist/root-move-noreplace.d.ts +14 -0
  222. package/dist/root-move-noreplace.d.ts.map +1 -0
  223. package/dist/root-move-noreplace.js +202 -0
  224. package/dist/root-observed-path.d.ts +9 -0
  225. package/dist/root-observed-path.d.ts.map +1 -0
  226. package/dist/root-observed-path.js +95 -0
  227. package/dist/root-path-errors.d.ts +12 -0
  228. package/dist/root-path-errors.d.ts.map +1 -0
  229. package/dist/root-path-errors.js +13 -0
  230. package/dist/root-path-existing.d.ts.map +1 -1
  231. package/dist/root-path-existing.js +53 -20
  232. package/dist/root-path-observation.d.ts +63 -0
  233. package/dist/root-path-observation.d.ts.map +1 -0
  234. package/dist/root-path-observation.js +180 -0
  235. package/dist/root-path-stat.d.ts +5 -0
  236. package/dist/root-path-stat.d.ts.map +1 -0
  237. package/dist/root-path-stat.js +101 -0
  238. package/dist/root-path-symlink.d.ts.map +1 -1
  239. package/dist/root-path-symlink.js +23 -4
  240. package/dist/root-path.d.ts +11 -0
  241. package/dist/root-path.d.ts.map +1 -1
  242. package/dist/root-path.js +215 -53
  243. package/dist/root-paths-lexical.d.ts +9 -0
  244. package/dist/root-paths-lexical.d.ts.map +1 -0
  245. package/dist/root-paths-lexical.js +22 -0
  246. package/dist/root-paths.d.ts +3 -25
  247. package/dist/root-paths.d.ts.map +1 -1
  248. package/dist/root-paths.js +77 -154
  249. package/dist/root-read-admission.d.ts +26 -0
  250. package/dist/root-read-admission.d.ts.map +1 -0
  251. package/dist/root-read-admission.js +94 -0
  252. package/dist/root-remove-identity.d.ts +15 -0
  253. package/dist/root-remove-identity.d.ts.map +1 -0
  254. package/dist/root-remove-identity.js +89 -0
  255. package/dist/root-remove-receipt.d.ts +17 -0
  256. package/dist/root-remove-receipt.d.ts.map +1 -0
  257. package/dist/root-remove-receipt.js +37 -0
  258. package/dist/root-remove.d.ts +2 -1
  259. package/dist/root-remove.d.ts.map +1 -1
  260. package/dist/root-remove.js +172 -10
  261. package/dist/root-write-admission.d.ts +68 -0
  262. package/dist/root-write-admission.d.ts.map +1 -0
  263. package/dist/root-write-admission.js +322 -0
  264. package/dist/root-write-compatibility.d.ts +13 -0
  265. package/dist/root-write-compatibility.d.ts.map +1 -0
  266. package/dist/root-write-compatibility.js +74 -0
  267. package/dist/root-write-complete-parent.d.ts +42 -0
  268. package/dist/root-write-complete-parent.d.ts.map +1 -0
  269. package/dist/root-write-complete-parent.js +195 -0
  270. package/dist/root-write-publication.d.ts +24 -0
  271. package/dist/root-write-publication.d.ts.map +1 -0
  272. package/dist/root-write-publication.js +85 -0
  273. package/dist/root-write-verification.d.ts.map +1 -1
  274. package/dist/root-write-verification.js +6 -4
  275. package/dist/secret-file.d.ts.map +1 -1
  276. package/dist/secret-file.js +62 -24
  277. package/dist/secret-read-async.d.ts.map +1 -1
  278. package/dist/secret-read-async.js +18 -13
  279. package/dist/secret-read-policy.d.ts.map +1 -1
  280. package/dist/secret-read-policy.js +5 -1
  281. package/dist/secure-file.d.ts.map +1 -1
  282. package/dist/secure-file.js +92 -7
  283. package/dist/secure-temp-dir.d.ts +6 -3
  284. package/dist/secure-temp-dir.d.ts.map +1 -1
  285. package/dist/secure-temp-dir.js +121 -101
  286. package/dist/secure-temp-repair.d.ts +35 -0
  287. package/dist/secure-temp-repair.d.ts.map +1 -0
  288. package/dist/secure-temp-repair.js +106 -0
  289. package/dist/sibling-staged-file.d.ts +3 -2
  290. package/dist/sibling-staged-file.d.ts.map +1 -1
  291. package/dist/sibling-staged-file.js +179 -55
  292. package/dist/sibling-temp.d.ts.map +1 -1
  293. package/dist/sibling-temp.js +27 -13
  294. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  295. package/dist/sidecar-lock-acquire.js +44 -14
  296. package/dist/sidecar-lock-root.d.ts.map +1 -1
  297. package/dist/sidecar-lock-root.js +4 -2
  298. package/dist/staged-directory.d.ts +14 -5
  299. package/dist/staged-directory.d.ts.map +1 -1
  300. package/dist/staged-directory.js +54 -3
  301. package/dist/standalone-publication-path.d.ts +2 -0
  302. package/dist/standalone-publication-path.d.ts.map +1 -0
  303. package/dist/standalone-publication-path.js +6 -0
  304. package/dist/stat-observation.d.ts +11 -0
  305. package/dist/stat-observation.d.ts.map +1 -0
  306. package/dist/stat-observation.js +64 -0
  307. package/dist/strict-file-identity.d.ts.map +1 -1
  308. package/dist/strict-file-identity.js +39 -2
  309. package/dist/symlink-parents.d.ts.map +1 -1
  310. package/dist/symlink-parents.js +7 -2
  311. package/dist/temp-target.d.ts +2 -0
  312. package/dist/temp-target.d.ts.map +1 -1
  313. package/dist/temp-target.js +130 -3
  314. package/dist/temp-workspace-admission.d.ts +22 -0
  315. package/dist/temp-workspace-admission.d.ts.map +1 -0
  316. package/dist/temp-workspace-admission.js +374 -0
  317. package/dist/temp-workspace-child-admission.d.ts +13 -0
  318. package/dist/temp-workspace-child-admission.d.ts.map +1 -0
  319. package/dist/temp-workspace-child-admission.js +95 -0
  320. package/dist/temp-workspace-descriptor.d.ts +48 -0
  321. package/dist/temp-workspace-descriptor.d.ts.map +1 -0
  322. package/dist/temp-workspace-descriptor.js +363 -0
  323. package/dist/temp-workspace-identity.d.ts +15 -0
  324. package/dist/temp-workspace-identity.d.ts.map +1 -0
  325. package/dist/temp-workspace-identity.js +41 -0
  326. package/dist/temp-workspace-owner.d.ts +7 -6
  327. package/dist/temp-workspace-owner.d.ts.map +1 -1
  328. package/dist/temp-workspace-owner.js +121 -61
  329. package/dist/temp-workspace-permissions.d.ts +4 -0
  330. package/dist/temp-workspace-permissions.d.ts.map +1 -0
  331. package/dist/temp-workspace-permissions.js +32 -0
  332. package/dist/temp-workspace-types.d.ts +40 -0
  333. package/dist/temp-workspace-types.d.ts.map +1 -0
  334. package/dist/temp-workspace-types.js +1 -0
  335. package/dist/temp.d.ts +1 -1
  336. package/dist/temp.d.ts.map +1 -1
  337. package/dist/temp.js +1 -1
  338. package/dist/test-hooks.d.ts +7 -0
  339. package/dist/test-hooks.d.ts.map +1 -1
  340. package/dist/text-atomic.d.ts.map +1 -1
  341. package/dist/text-atomic.js +3 -1
  342. package/dist/trash.d.ts.map +1 -1
  343. package/dist/trash.js +54 -8
  344. package/dist/walk.d.ts.map +1 -1
  345. package/dist/walk.js +9 -6
  346. package/dist/windows-owner.d.ts.map +1 -1
  347. package/dist/windows-owner.js +5 -0
  348. package/dist/windows-path-alias.d.ts +39 -0
  349. package/dist/windows-path-alias.d.ts.map +1 -0
  350. package/dist/windows-path-alias.js +153 -0
  351. package/docs/advanced.md +25 -0
  352. package/docs/archive.md +23 -2
  353. package/docs/atomic.md +27 -3
  354. package/docs/copy.md +3 -0
  355. package/docs/durability.md +14 -0
  356. package/docs/errors.md +14 -6
  357. package/docs/file-store.md +24 -3
  358. package/docs/filename.md +14 -7
  359. package/docs/install-path.md +2 -2
  360. package/docs/install.md +8 -7
  361. package/docs/json-store.md +5 -1
  362. package/docs/json.md +11 -0
  363. package/docs/mutation-policy-proof.md +65 -0
  364. package/docs/native-helper.md +26 -9
  365. package/docs/native.md +32 -8
  366. package/docs/output.md +33 -11
  367. package/docs/path-prefix.md +64 -0
  368. package/docs/path-suffix-aliases.md +159 -0
  369. package/docs/path.md +11 -0
  370. package/docs/private-file-store.md +14 -0
  371. package/docs/public-api.md +9 -0
  372. package/docs/quickstart.md +1 -1
  373. package/docs/reading.md +8 -1
  374. package/docs/root.md +18 -3
  375. package/docs/secret-file.md +3 -0
  376. package/docs/secure-file.md +6 -2
  377. package/docs/security-model.md +75 -8
  378. package/docs/sidecar-lock.md +28 -1
  379. package/docs/store.md +5 -1
  380. package/docs/temp.md +260 -36
  381. package/docs/test-hooks.md +14 -0
  382. package/docs/writing.md +38 -8
  383. package/package.json +10 -10
@@ -3,6 +3,7 @@ import fsSync from "node:fs";
3
3
  import path from "node:path";
4
4
  import { isPathInside } from "./path.js";
5
5
  import { realpathSync } from "./realpath.js";
6
+ import { hasWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
6
7
  export function safeDirName(input) {
7
8
  const trimmed = input.trim();
8
9
  if (!trimmed) {
@@ -40,10 +41,24 @@ export function safePathSegmentHashedV2(input) {
40
41
  return `id-v2-${hash}`;
41
42
  }
42
43
  export function resolveSafeInstallDir(params) {
44
+ const baseDir = params.baseDir;
45
+ if (hasWindowsPathAlias(baseDir, "filesystem")) {
46
+ return { ok: false, error: params.invalidNameMessage };
47
+ }
43
48
  const encodedName = (params.nameEncoder ?? safeDirName)(params.id);
44
- const targetDir = path.join(params.baseDir, encodedName);
45
- const resolvedBase = path.resolve(params.baseDir);
49
+ if (hasWindowsPathAlias(encodedName, "relative")) {
50
+ return { ok: false, error: params.invalidNameMessage };
51
+ }
52
+ const targetDir = path.join(baseDir, encodedName);
53
+ if (hasWindowsPathAlias(targetDir, "filesystem")) {
54
+ return { ok: false, error: params.invalidNameMessage };
55
+ }
56
+ const resolvedBase = resolvePathPreservingWindowsRoot(baseDir);
46
57
  const resolvedTarget = path.resolve(targetDir);
58
+ if (hasWindowsPathAlias(resolvedBase, "filesystem") ||
59
+ hasWindowsPathAlias(resolvedTarget, "filesystem")) {
60
+ return { ok: false, error: params.invalidNameMessage };
61
+ }
47
62
  const relative = path.relative(resolvedBase, resolvedTarget);
48
63
  if (!relative ||
49
64
  relative === ".." ||
@@ -54,40 +69,58 @@ export function resolveSafeInstallDir(params) {
54
69
  return { ok: true, path: resolvedTarget };
55
70
  }
56
71
  export async function assertCanonicalPathWithinBase(params) {
57
- const baseDir = path.resolve(params.baseDir);
58
- const candidatePath = path.resolve(params.candidatePath);
72
+ const baseDirInput = params.baseDir;
73
+ const candidatePathInput = params.candidatePath;
74
+ const boundaryLabel = params.boundaryLabel;
75
+ const invalidPath = () => new Error(`Invalid path: must stay within ${boundaryLabel}`);
76
+ const assertAdmittedPath = (value) => {
77
+ if (hasWindowsPathAlias(value, "filesystem"))
78
+ throw invalidPath();
79
+ };
80
+ assertAdmittedPath(baseDirInput);
81
+ assertAdmittedPath(candidatePathInput);
82
+ const baseDir = resolvePathPreservingWindowsRoot(baseDirInput);
83
+ const candidatePath = resolvePathPreservingWindowsRoot(candidatePathInput);
84
+ assertAdmittedPath(baseDir);
85
+ assertAdmittedPath(candidatePath);
59
86
  if (!isPathInside(baseDir, candidatePath)) {
60
- throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
87
+ throw invalidPath();
61
88
  }
62
- const baseLstat = fsSync.lstatSync(baseDir);
89
+ const baseOperationPath = pathForWindowsFilesystem(baseDir);
90
+ const baseLstat = fsSync.lstatSync(baseOperationPath);
63
91
  if (baseLstat.isSymbolicLink()) {
64
- const baseStat = fsSync.statSync(baseDir);
92
+ const baseStat = fsSync.statSync(baseOperationPath);
65
93
  if (!baseStat.isDirectory()) {
66
- throw new Error(`Invalid ${params.boundaryLabel}: base directory must resolve to a directory`);
94
+ throw new Error(`Invalid ${boundaryLabel}: base directory must resolve to a directory`);
67
95
  }
68
96
  }
69
97
  else if (!baseLstat.isDirectory()) {
70
- throw new Error(`Invalid ${params.boundaryLabel}: base directory must be a directory`);
98
+ throw new Error(`Invalid ${boundaryLabel}: base directory must be a directory`);
71
99
  }
72
- const baseRealPath = realpathSync.native(baseDir);
100
+ const baseRealPath = realpathSync.native(baseOperationPath);
101
+ assertAdmittedPath(baseRealPath);
73
102
  const validateDirectory = async (dirPath) => {
74
- const resolvedDirPath = path.resolve(dirPath);
75
- const dirLstat = fsSync.lstatSync(dirPath);
103
+ assertAdmittedPath(dirPath);
104
+ const resolvedDirPath = resolvePathPreservingWindowsRoot(dirPath);
105
+ assertAdmittedPath(resolvedDirPath);
106
+ const operationPath = pathForWindowsFilesystem(dirPath);
107
+ const dirLstat = fsSync.lstatSync(operationPath);
76
108
  if (dirLstat.isSymbolicLink()) {
77
109
  if (resolvedDirPath !== baseDir) {
78
- throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
110
+ throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
79
111
  }
80
- const dirStat = fsSync.statSync(dirPath);
112
+ const dirStat = fsSync.statSync(operationPath);
81
113
  if (!dirStat.isDirectory()) {
82
- throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
114
+ throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
83
115
  }
84
116
  }
85
117
  else if (!dirLstat.isDirectory()) {
86
- throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
118
+ throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
87
119
  }
88
- const dirRealPath = realpathSync.native(dirPath);
120
+ const dirRealPath = realpathSync.native(operationPath);
121
+ assertAdmittedPath(dirRealPath);
89
122
  if (!isPathInside(baseRealPath, dirRealPath)) {
90
- throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
123
+ throw invalidPath();
91
124
  }
92
125
  };
93
126
  try {
@@ -100,5 +133,7 @@ export async function assertCanonicalPathWithinBase(params) {
100
133
  throw err;
101
134
  }
102
135
  }
103
- await validateDirectory(path.dirname(candidatePath));
136
+ const candidateParent = path.dirname(candidatePath);
137
+ assertAdmittedPath(candidateParent);
138
+ await validateDirectory(candidateParent);
104
139
  }
@@ -4,6 +4,12 @@ export type DurableQueueEntryPathsLike = {
4
4
  deliveredPath: string;
5
5
  processingPath?: string;
6
6
  };
7
+ export type ValidatedDurableQueueEntryPaths = Readonly<{
8
+ jsonPath: string;
9
+ deliveredPath: string;
10
+ processingPath: string;
11
+ }>;
12
+ export declare function validateDurableQueueEntryPaths(paths: DurableQueueEntryPathsLike): ValidatedDurableQueueEntryPaths;
7
13
  export declare function durableQueueProcessingPath(paths: DurableQueueEntryPathsLike): string;
8
14
  export declare function claimDurableQueueEntry(paths: DurableQueueEntryPathsLike, options?: {
9
15
  skipUnowned?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAEA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AASnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAKpF;AAwED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,0BAA0B,EACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,KAAK,EAAE,0BAA0B,EACjC,QAAQ,EAAE,WAAW,EACrB,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACnC,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxE,OAAO,CAAC,CAAC,CAAC,CAkCZ;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDhB;AAKD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI1D"}
1
+ {"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAEA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAUnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC;AAUH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,0BAA0B,GAChC,+BAA+B,CAkBjC;AAiBD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAIpF;AA0ED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,0BAA0B,EACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,KAAK,EAAE,0BAA0B,EACjC,QAAQ,EAAE,WAAW,EACrB,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACnC,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxE,OAAO,CAAC,CAAC,CAAC,CAmCZ;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,OAAO,CAAC,CAclB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDhB;AAKD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI1D"}
@@ -8,12 +8,48 @@ import { FsSafeError } from "./errors.js";
8
8
  import { sameFileIdentityForCleanup, sha256Hex } from "./file-identity.js";
9
9
  import { inspectFileIdentity } from "./strict-file-identity.js";
10
10
  import { serializePathWrite } from "./write-queue.js";
11
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
12
+ const validatedDurableQueueEntryPaths = new WeakSet();
13
+ function assertQueuePathString(value, label) {
14
+ if (typeof value !== "string") {
15
+ throw new FsSafeError("invalid-path", `${label} must be a string`);
16
+ }
17
+ }
18
+ export function validateDurableQueueEntryPaths(paths) {
19
+ const jsonPath = paths.jsonPath;
20
+ const deliveredPath = paths.deliveredPath;
21
+ const requestedProcessingPath = paths.processingPath;
22
+ assertQueuePathString(jsonPath, "queue JSON path");
23
+ assertQueuePathString(deliveredPath, "queue delivered path");
24
+ if (requestedProcessingPath !== undefined) {
25
+ assertQueuePathString(requestedProcessingPath, "queue processing path");
26
+ }
27
+ assertNoWindowsPathAlias(jsonPath);
28
+ assertNoWindowsPathAlias(deliveredPath);
29
+ if (requestedProcessingPath !== undefined) {
30
+ assertNoWindowsPathAlias(requestedProcessingPath);
31
+ }
32
+ const processingPath = requestedProcessingPath || processingPathFromJsonPath(jsonPath);
33
+ const validated = Object.freeze({ deliveredPath, jsonPath, processingPath });
34
+ validatedDurableQueueEntryPaths.add(validated);
35
+ return validated;
36
+ }
37
+ function ownDurableQueueEntryPaths(paths) {
38
+ if (validatedDurableQueueEntryPaths.has(paths)) {
39
+ return paths;
40
+ }
41
+ return validateDurableQueueEntryPaths(paths);
42
+ }
43
+ function processingPathFromJsonPath(jsonPath) {
44
+ return jsonPath.endsWith(".json")
45
+ ? `${jsonPath.slice(0, -".json".length)}.processing`
46
+ : `${jsonPath}.processing`;
47
+ }
11
48
  export function durableQueueProcessingPath(paths) {
12
- if (paths.processingPath)
13
- return paths.processingPath;
14
- return paths.jsonPath.endsWith(".json")
15
- ? `${paths.jsonPath.slice(0, -".json".length)}.processing`
16
- : `${paths.jsonPath}.processing`;
49
+ const processingPath = paths.processingPath;
50
+ if (processingPath)
51
+ return processingPath;
52
+ return processingPathFromJsonPath(paths.jsonPath);
17
53
  }
18
54
  async function regularQueueFileIdentity(filePath) {
19
55
  const identity = await lstatOrNull(filePath);
@@ -25,10 +61,12 @@ async function regularQueueFileIdentity(filePath) {
25
61
  return identity;
26
62
  }
27
63
  async function withQueueEntryLock(paths, run) {
28
- return await serializePathWrite(paths.jsonPath, async () => await withQueueTransferLock(path.resolve(paths.jsonPath), run));
64
+ const resolvedJsonPath = path.resolve(paths.jsonPath);
65
+ assertNoWindowsPathAlias(resolvedJsonPath);
66
+ return await serializePathWrite(paths.jsonPath, async () => await withQueueTransferLock(resolvedJsonPath, run));
29
67
  }
30
68
  async function claimDurableQueueEntryUnlocked(paths, options = {}) {
31
- const processingPath = durableQueueProcessingPath(paths);
69
+ const processingPath = paths.processingPath;
32
70
  await recoverDurableQueueRetirement({ jsonPath: paths.jsonPath, processingPath });
33
71
  const existingProcessing = await regularQueueFileIdentity(processingPath);
34
72
  if (existingProcessing) {
@@ -80,14 +118,16 @@ async function claimDurableQueueEntryUnlocked(paths, options = {}) {
80
118
  return processingPath;
81
119
  }
82
120
  export async function claimDurableQueueEntry(paths, options = {}) {
83
- if (!(await lstatOrNull(path.dirname(paths.jsonPath))))
121
+ const validatedPaths = ownDurableQueueEntryPaths(paths);
122
+ if (!(await lstatOrNull(path.dirname(validatedPaths.jsonPath))))
84
123
  return null;
85
- return await withQueueEntryLock(paths, async () => await claimDurableQueueEntryUnlocked(paths, options));
124
+ return await withQueueEntryLock(validatedPaths, async () => await claimDurableQueueEntryUnlocked(validatedPaths, options));
86
125
  }
87
126
  // The caller retains its read pin until this owner releases it or migration settles.
88
127
  export async function migrateDurableQueueEntry(paths, expected, releaseReadPin, run) {
89
- return await withQueueEntryLock(paths, async () => {
90
- const processingPath = durableQueueProcessingPath(paths);
128
+ const validatedPaths = ownDurableQueueEntryPaths(paths);
129
+ return await withQueueEntryLock(validatedPaths, async () => {
130
+ const processingPath = validatedPaths.processingPath;
91
131
  const assertCurrent = async () => {
92
132
  try {
93
133
  await inspectFileIdentity(() => {
@@ -118,83 +158,90 @@ export async function migrateDurableQueueEntry(paths, expected, releaseReadPin,
118
158
  });
119
159
  }
120
160
  export async function completeDeliveredQueueEntry(paths) {
121
- if (!(await lstatOrNull(path.dirname(paths.deliveredPath))))
161
+ const validatedPaths = ownDurableQueueEntryPaths(paths);
162
+ if (!(await lstatOrNull(path.dirname(validatedPaths.deliveredPath))))
122
163
  return false;
123
- return await withQueueEntryLock(paths, async () => {
124
- if (!(await regularQueueFileIdentity(paths.deliveredPath)))
164
+ return await withQueueEntryLock(validatedPaths, async () => {
165
+ if (!(await regularQueueFileIdentity(validatedPaths.deliveredPath)))
125
166
  return false;
126
- if (await regularQueueFileIdentity(durableQueueProcessingPath(paths))) {
127
- await fs.unlink(paths.deliveredPath);
128
- await syncDirectory(path.dirname(paths.deliveredPath));
167
+ if (await regularQueueFileIdentity(validatedPaths.processingPath)) {
168
+ await fs.unlink(validatedPaths.deliveredPath);
169
+ await syncDirectory(path.dirname(validatedPaths.deliveredPath));
129
170
  return false;
130
171
  }
131
- await fs.unlink(paths.deliveredPath);
132
- await syncDirectory(path.dirname(paths.deliveredPath));
172
+ await fs.unlink(validatedPaths.deliveredPath);
173
+ await syncDirectory(path.dirname(validatedPaths.deliveredPath));
133
174
  return true;
134
175
  });
135
176
  }
136
177
  export async function acknowledgeDurableQueueEntry(paths) {
137
- if (!(await lstatOrNull(path.dirname(paths.jsonPath))))
178
+ const validatedPaths = ownDurableQueueEntryPaths(paths);
179
+ if (!(await lstatOrNull(path.dirname(validatedPaths.jsonPath))))
138
180
  return;
139
- await withQueueEntryLock(paths, async () => {
140
- const processingPath = durableQueueProcessingPath(paths);
181
+ await withQueueEntryLock(validatedPaths, async () => {
182
+ const processingPath = validatedPaths.processingPath;
141
183
  const processing = await regularQueueFileIdentity(processingPath);
142
- const delivered = await regularQueueFileIdentity(paths.deliveredPath);
184
+ const delivered = await regularQueueFileIdentity(validatedPaths.deliveredPath);
143
185
  if (!processing) {
144
186
  if (delivered) {
145
- await fs.unlink(paths.deliveredPath);
187
+ await fs.unlink(validatedPaths.deliveredPath);
146
188
  }
147
- await syncDirectory(path.dirname(paths.deliveredPath));
148
- if (await regularQueueFileIdentity(paths.jsonPath)) {
189
+ await syncDirectory(path.dirname(validatedPaths.deliveredPath));
190
+ if (await regularQueueFileIdentity(validatedPaths.jsonPath)) {
149
191
  throw new FsSafeError("path-mismatch", "queue acknowledgement requires a processing claim");
150
192
  }
151
193
  return;
152
194
  }
153
195
  if (delivered)
154
- await fs.unlink(paths.deliveredPath);
155
- await fs.rename(processingPath, paths.deliveredPath);
156
- await syncDirectory(path.dirname(paths.deliveredPath));
157
- await fs.unlink(paths.deliveredPath);
158
- await syncDirectory(path.dirname(paths.deliveredPath));
196
+ await fs.unlink(validatedPaths.deliveredPath);
197
+ await fs.rename(processingPath, validatedPaths.deliveredPath);
198
+ await syncDirectory(path.dirname(validatedPaths.deliveredPath));
199
+ await fs.unlink(validatedPaths.deliveredPath);
200
+ await syncDirectory(path.dirname(validatedPaths.deliveredPath));
159
201
  });
160
202
  }
161
203
  export async function moveDurableQueueEntryToFailed(params) {
162
- await withQueueEntryLock(params.paths, async () => {
163
- const processingPath = durableQueueProcessingPath(params.paths);
204
+ const pathsInput = params.paths;
205
+ const paths = ownDurableQueueEntryPaths(pathsInput);
206
+ const failedPath = params.failedPath;
207
+ assertQueuePathString(failedPath, "failed queue path");
208
+ assertNoWindowsPathAlias(failedPath);
209
+ await withQueueEntryLock(paths, async () => {
210
+ const processingPath = paths.processingPath;
164
211
  const processing = await regularQueueFileIdentity(processingPath);
165
- const existingFailed = await regularQueueFileIdentity(params.failedPath);
212
+ const existingFailed = await regularQueueFileIdentity(failedPath);
166
213
  if (!processing && existingFailed) {
167
- await syncDirectory(path.dirname(params.failedPath));
214
+ await syncDirectory(path.dirname(failedPath));
168
215
  await syncDirectory(path.dirname(processingPath));
169
- const recoveredFailed = await regularQueueFileIdentity(params.failedPath);
216
+ const recoveredFailed = await regularQueueFileIdentity(failedPath);
170
217
  if (!recoveredFailed ||
171
218
  !sameFileIdentityForCleanup(existingFailed, recoveredFailed)) {
172
219
  throw new FsSafeError("path-mismatch", "failed queue destination changed during recovery");
173
220
  }
174
- if (await regularQueueFileIdentity(params.paths.jsonPath)) {
221
+ if (await regularQueueFileIdentity(paths.jsonPath)) {
175
222
  throw new FsSafeError("already-exists", "failed queue destination already exists");
176
223
  }
177
224
  return;
178
225
  }
179
226
  const sourcePath = processing
180
227
  ? processingPath
181
- : await claimDurableQueueEntryUnlocked(params.paths);
228
+ : await claimDurableQueueEntryUnlocked(paths);
182
229
  if (!sourcePath) {
183
230
  throw Object.assign(new Error("queue entry does not exist"), { code: "ENOENT" });
184
231
  }
185
232
  const source = await regularQueueFileIdentity(sourcePath);
186
- const failed = await regularQueueFileIdentity(params.failedPath);
233
+ const failed = await regularQueueFileIdentity(failedPath);
187
234
  if (failed) {
188
235
  if (source && sameFileIdentityForCleanup(source, failed)) {
189
- await syncDirectory(path.dirname(params.failedPath));
236
+ await syncDirectory(path.dirname(failedPath));
190
237
  await fs.unlink(sourcePath);
191
238
  await syncDirectory(path.dirname(sourcePath));
192
239
  return;
193
240
  }
194
241
  throw new FsSafeError("already-exists", "failed queue destination already exists");
195
242
  }
196
- await fs.link(sourcePath, params.failedPath);
197
- await syncDirectory(path.dirname(params.failedPath));
243
+ await fs.link(sourcePath, failedPath);
244
+ await syncDirectory(path.dirname(failedPath));
198
245
  await fs.unlink(sourcePath);
199
246
  await syncDirectory(path.dirname(sourcePath));
200
247
  });
@@ -0,0 +1,11 @@
1
+ export type QueueValidationRoot = {
2
+ path: string;
3
+ allowSymlinkBase: boolean;
4
+ };
5
+ export declare function resolveQueueFilesystemPath(value: string): string;
6
+ export declare function queueValidationRoots(queueDir: string, failedDir: string): Promise<{
7
+ queueRoot: QueueValidationRoot;
8
+ failedRoot: QueueValidationRoot;
9
+ }>;
10
+ export declare function queueValidationRoot(dir: string): QueueValidationRoot;
11
+ //# sourceMappingURL=json-durable-queue-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-durable-queue-paths.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-paths.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhE;AAqBD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,SAAS,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAU9E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAKpE"}
@@ -0,0 +1,42 @@
1
+ import path from "node:path";
2
+ import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
3
+ export function resolveQueueFilesystemPath(value) {
4
+ const resolved = resolvePathPreservingWindowsRoot(value);
5
+ assertNoWindowsPathAlias(resolved);
6
+ return resolved;
7
+ }
8
+ function commonPathAncestor(paths) {
9
+ const resolved = paths.map(resolveQueueFilesystemPath);
10
+ const root = path.parse(resolved[0] ?? process.cwd()).root;
11
+ const parts = resolved.map((entry) => path.relative(root, entry).split(path.sep));
12
+ const common = [];
13
+ for (let index = 0; parts.every((part) => index < part.length); index++) {
14
+ const segment = parts[0]?.[index];
15
+ if (!segment || !parts.every((part) => part[index] === segment))
16
+ break;
17
+ common.push(segment);
18
+ }
19
+ return path.join(root, ...common);
20
+ }
21
+ function samePathRoot(paths) {
22
+ const roots = paths.map((entry) => path.parse(resolveQueueFilesystemPath(entry)).root);
23
+ const first = process.platform === "win32" ? roots[0]?.toLowerCase() : roots[0];
24
+ return roots.every((root) => (process.platform === "win32" ? root.toLowerCase() : root) === first);
25
+ }
26
+ export async function queueValidationRoots(queueDir, failedDir) {
27
+ if (samePathRoot([queueDir, failedDir])) {
28
+ const common = commonPathAncestor([queueDir, failedDir]);
29
+ const root = queueValidationRoot(common);
30
+ return { failedRoot: root, queueRoot: root };
31
+ }
32
+ return {
33
+ failedRoot: queueValidationRoot(failedDir),
34
+ queueRoot: queueValidationRoot(queueDir),
35
+ };
36
+ }
37
+ export function queueValidationRoot(dir) {
38
+ return {
39
+ path: path.parse(resolveQueueFilesystemPath(dir)).root,
40
+ allowSymlinkBase: process.platform === "darwin",
41
+ };
42
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"json-durable-queue-read.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-read.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAM/C,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AA8B3E,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC,CA8BZ"}
1
+ {"version":3,"file":"json-durable-queue-read.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-read.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAO/C,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AA8B3E,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC,CA+BZ"}
@@ -3,6 +3,7 @@ import { normalizeMaxBytes } from "./byte-budget.js";
3
3
  import { readBoundedAsync } from "./bounded-read.js";
4
4
  import { resolveReadOpenFlags } from "./read-open-flags.js";
5
5
  import { inspectFileIdentity } from "./strict-file-identity.js";
6
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
6
7
  export const DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES = 16 * 1024 * 1024;
7
8
  async function inspectQueueEntry(inspect, maxBytes, expected) {
8
9
  let inspectionFailed = false;
@@ -36,6 +37,7 @@ export async function withJsonDurableQueueEntry(filePath, options, run) {
36
37
  const maxBytes = normalizeMaxBytes(options.maxBytes, {
37
38
  defaultValue: DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES,
38
39
  });
40
+ assertNoWindowsPathAlias(filePath);
39
41
  const inspectPath = () => fs.lstatSync(filePath, { bigint: true });
40
42
  const initialStat = await inspectQueueEntry(inspectPath, maxBytes);
41
43
  const handle = await fs.promises.open(filePath, resolveReadOpenFlags());
@@ -1 +1 @@
1
- {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,0CAA0C,EAAE,MAAM,8BAA8B,CAAC;AAE1F,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAWF,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAO5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAiLD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhB;AAgBD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAgBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAqBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CAqEd;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
1
+ {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,0CAA0C,EAAE,MAAM,8BAA8B,CAAC;AAE1F,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAQ5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAwID,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhB;AAmBD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAgBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CA2Ed;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB"}