@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 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,21 @@
1
1
  import fsSync from "node:fs";
2
2
  import { formatIcaclsResetCommand, inspectWindowsPermissions, } from "./permissions-windows.js";
3
+ import { hasWindowsPathAlias, pathForWindowsFilesystem, } from "./windows-path-alias.js";
3
4
  export { createIcaclsResetCommand, formatIcaclsResetCommand, formatWindowsAclSummary, inspectWindowsAcl, parseIcaclsOutput, resolveWindowsUserPrincipal, summarizeWindowsAcl, } from "./permissions-windows.js";
4
- export async function safeStat(targetPath) {
5
+ function failedSafeStat(error) {
6
+ return {
7
+ ok: false,
8
+ isSymlink: false,
9
+ isDir: false,
10
+ mode: null,
11
+ uid: null,
12
+ gid: null,
13
+ error,
14
+ };
15
+ }
16
+ async function safeStatAdmitted(targetPath) {
5
17
  try {
6
- const lst = fsSync.lstatSync(targetPath);
18
+ const lst = fsSync.lstatSync(pathForWindowsFilesystem(targetPath));
7
19
  return {
8
20
  ok: true,
9
21
  isSymlink: lst.isSymbolicLink(),
@@ -14,19 +26,36 @@ export async function safeStat(targetPath) {
14
26
  };
15
27
  }
16
28
  catch (err) {
29
+ return failedSafeStat(String(err));
30
+ }
31
+ }
32
+ export async function safeStat(targetPath) {
33
+ if (hasWindowsPathAlias(targetPath, "filesystem")) {
34
+ return failedSafeStat("Path uses a Windows filesystem namespace alias");
35
+ }
36
+ return await safeStatAdmitted(targetPath);
37
+ }
38
+ export async function inspectPathPermissions(targetPath, opts) {
39
+ const admissionPlatform = process.platform === "win32"
40
+ ? process.platform
41
+ : (opts?.platform ?? process.platform);
42
+ if (hasWindowsPathAlias(targetPath, "filesystem", admissionPlatform)) {
43
+ const st = failedSafeStat("Path uses a Windows filesystem namespace alias");
17
44
  return {
18
45
  ok: false,
19
46
  isSymlink: false,
20
47
  isDir: false,
21
48
  mode: null,
22
- uid: null,
23
- gid: null,
24
- error: String(err),
49
+ bits: null,
50
+ source: "unknown",
51
+ worldWritable: false,
52
+ groupWritable: false,
53
+ worldReadable: false,
54
+ groupReadable: false,
55
+ error: st.error,
25
56
  };
26
57
  }
27
- }
28
- export async function inspectPathPermissions(targetPath, opts) {
29
- const st = await safeStat(targetPath);
58
+ const st = await safeStatAdmitted(targetPath);
30
59
  if (!st.ok) {
31
60
  return {
32
61
  ok: false,
@@ -0,0 +1,25 @@
1
+ import { type DenyMutationPolicy } from "./deny-mutations.js";
2
+ import { type RootBoundaryIdentity } from "./root-boundary.js";
3
+ import type { PinnedWriteMutationAdmission } from "./pinned-write.js";
4
+ import type { MutationSymlinkPolicy } from "./root-symlink-policy.js";
5
+ export type PinnedMutationPolicySnapshot = Readonly<{
6
+ denyMutations?: DenyMutationPolicy;
7
+ mutationSymlinks?: MutationSymlinkPolicy;
8
+ }>;
9
+ export declare function snapshotPinnedMutationPolicy(denyMutations: DenyMutationPolicy | undefined, mutationSymlinks: MutationSymlinkPolicy | undefined): PinnedMutationPolicySnapshot | undefined;
10
+ export declare function preparePinnedWriteMutationAdmission(params: {
11
+ rootReal: string;
12
+ rootWithSep: string;
13
+ rootIdentity?: RootBoundaryIdentity;
14
+ resolvedTargetPath: string;
15
+ defaultRelativeParentPath: string;
16
+ originalPath?: string;
17
+ policy: PinnedMutationPolicySnapshot | undefined;
18
+ resolveCurrent(): Promise<{
19
+ resolved: string;
20
+ }>;
21
+ }): Promise<{
22
+ relativeParentPath: string;
23
+ mutationAdmission?: PinnedWriteMutationAdmission;
24
+ }>;
25
+ //# sourceMappingURL=pinned-mutation-admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-mutation-admission.d.ts","sourceRoot":"","sources":["../src/pinned-mutation-admission.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,KAAK,EAKV,4BAA4B,EAC7B,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAgCtE,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;CAC1C,CAAC,CAAC;AAEH,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,kBAAkB,GAAG,SAAS,EAC7C,gBAAgB,EAAE,qBAAqB,GAAG,SAAS,GAClD,4BAA4B,GAAG,SAAS,CAO1C;AAuJD,wBAAsB,mCAAmC,CAAC,MAAM,EAAE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,4BAA4B,GAAG,SAAS,CAAC;IACjD,cAAc,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD,GAAG,OAAO,CAAC;IACV,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,4BAA4B,CAAC;CAClD,CAAC,CAsQD"}
@@ -0,0 +1,425 @@
1
+ import path from "node:path";
2
+ import { assertMutationNotDenied } from "./deny-mutations.js";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { isPathInside } from "./path.js";
5
+ import { admitPathInsideRoot } from "./root-boundary.js";
6
+ import { ordinarySharedAbsoluteInsideRoot, simpleSharedRoute, } from "./pinned-mutation-shared-route.js";
7
+ import { resolvePathViaExistingAncestor } from "./root-path-existing.js";
8
+ import { outsideWorkspaceError } from "./root-errors.js";
9
+ import { getFsSafeNativeConfig } from "./native-config.js";
10
+ import { advanceMutationObservation, mutationObservationCurrent, observeMutationPath, mutationDirectoryObservationCurrent, mutationObservationUsesDirectory, missingMutationSegments, nextMissingMutationPath, } from "./pinned-mutation-observation.js";
11
+ export function snapshotPinnedMutationPolicy(denyMutations, mutationSymlinks) {
12
+ if (denyMutations === undefined && mutationSymlinks === undefined)
13
+ return undefined;
14
+ const denySnapshot = denyMutations === undefined ? undefined : Object.freeze({
15
+ paths: denyMutations.paths === undefined ? undefined : Object.freeze([...denyMutations.paths]),
16
+ prefixes: denyMutations.prefixes === undefined ? undefined : Object.freeze([...denyMutations.prefixes]),
17
+ });
18
+ return Object.freeze({ denyMutations: denySnapshot, mutationSymlinks });
19
+ }
20
+ async function canonicalMutationEntryPath(targetPath) {
21
+ const canonicalParent = await resolvePathViaExistingAncestor(path.dirname(targetPath));
22
+ return path.join(canonicalParent, path.basename(targetPath));
23
+ }
24
+ function sameAbsolutePath(left, right) {
25
+ return path.relative(path.resolve(left), path.resolve(right)) === "";
26
+ }
27
+ function simplePinnedRoute(rootReal, originalPath) {
28
+ // These routes can depend on raw traversal, platform aliases, home expansion,
29
+ // or live native canonicalization. They retain full admission at every step.
30
+ if (!originalPath || process.platform === "win32" || process.versions.bun ||
31
+ originalPath.startsWith("~") || originalPath.includes("\\") || originalPath.includes("\0"))
32
+ return undefined;
33
+ const segments = originalPath.split("/");
34
+ if (segments.some((segment, index) => segment === "." || segment === ".." ||
35
+ (segment === "" && index !== 0)))
36
+ return undefined;
37
+ const target = path.resolve(rootReal, originalPath);
38
+ return isPathInside(rootReal, target) && target !== rootReal ? target : undefined;
39
+ }
40
+ function nonzeroObservationIdentity(observation) {
41
+ return observation.entry.dev !== 0n && observation.entry.ino !== 0n &&
42
+ observation.identity.dev !== 0n && observation.identity.ino !== 0n;
43
+ }
44
+ function captureEpoch(rootReal, target, policy, session) {
45
+ const observations = new Map();
46
+ const observe = (pathname) => {
47
+ if (!observations.has(pathname)) {
48
+ const observation = observeMutationPath(pathname);
49
+ if (!observation)
50
+ throw new Error("incomplete mutation observation");
51
+ observations.set(pathname, observation);
52
+ }
53
+ return observations.get(pathname);
54
+ };
55
+ try {
56
+ const root = observe(rootReal);
57
+ const route = observe(target);
58
+ // A canonical spelling alone cannot certify a route containing aliases.
59
+ if (root.canonicalPath !== rootReal || route.canonicalPath !== target ||
60
+ route.ancestor !== route.canonicalAncestor || route.entry.dev !== route.identity.dev ||
61
+ route.entry.ino !== route.identity.ino)
62
+ return undefined;
63
+ const comparables = (entries) => Object.freeze((entries ?? []).map((entry) => {
64
+ if (!entry || !path.isAbsolute(entry) || entry.includes("\0"))
65
+ throw new Error("invalid policy observation");
66
+ const resolved = path.resolve(entry);
67
+ if (session.kind === "shared" && (!session.rootIdentity || entry !== resolved ||
68
+ !ordinarySharedAbsoluteInsideRoot(rootReal, resolved, session.rootIdentity))) {
69
+ throw new Error("ineligible shared policy observation");
70
+ }
71
+ const observation = observe(resolved);
72
+ if (session.kind === "shared" && (observation.canonicalPath !== resolved ||
73
+ observation.ancestor !== observation.canonicalAncestor ||
74
+ !nonzeroObservationIdentity(observation))) {
75
+ throw new Error("incomplete shared policy observation");
76
+ }
77
+ return Object.freeze([resolved, observation.canonicalPath]);
78
+ }));
79
+ const paths = comparables(policy.denyMutations?.paths);
80
+ const prefixes = comparables(policy.denyMutations?.prefixes);
81
+ if (session.kind === "shared" && (!session.rootIdentity ||
82
+ root.entry.dev !== session.rootIdentity.dev || root.entry.ino !== session.rootIdentity.ino ||
83
+ root.identity.dev !== session.rootIdentity.dev || root.identity.ino !== session.rootIdentity.ino ||
84
+ !nonzeroObservationIdentity(root) || !nonzeroObservationIdentity(route)))
85
+ return undefined;
86
+ return Object.freeze({
87
+ mode: getFsSafeNativeConfig().mode, target: route, paths, prefixes,
88
+ observations: Object.freeze([...observations.values()]), session,
89
+ });
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ function epochCurrent(epoch) {
96
+ return !process.versions.bun && epoch.mode === epoch.session.nativeMode &&
97
+ getFsSafeNativeConfig().mode === epoch.mode &&
98
+ epoch.observations.every(mutationObservationCurrent);
99
+ }
100
+ function assertCachedNotDenied(target, epoch) {
101
+ if (epoch.paths.some((paths) => paths.some((denied) => isPathInside(denied, target) && isPathInside(target, denied))) ||
102
+ epoch.prefixes.some((paths) => paths.some((denied) => isPathInside(denied, target)))) {
103
+ throw new FsSafeError("denied-path", "path is denied by denyMutations policy");
104
+ }
105
+ }
106
+ function reusableRequest(request, epoch) {
107
+ return request.targetPath === epoch.target.path &&
108
+ ((request.phase === "parent" && request.mutationPath === request.targetPath) ||
109
+ (request.phase === "parent-create" && missingMutationSegments(epoch.target) > 1 &&
110
+ request.mutationPath === nextMissingMutationPath(epoch.target)));
111
+ }
112
+ function reusableSharedCreateRequest(request, epoch) {
113
+ return request.phase === "parent-create" && request.targetPath === epoch.target.path &&
114
+ request.mutationPath === nextMissingMutationPath(epoch.target);
115
+ }
116
+ function reusableSharedRequest(request, epoch) {
117
+ return request.phase === "parent"
118
+ ? request.targetPath === epoch.target.path && request.mutationPath === request.targetPath
119
+ : reusableSharedCreateRequest(request, epoch);
120
+ }
121
+ function nonzeroDirectoryObservation(observation) {
122
+ return observation.identity.dev !== 0n && observation.identity.ino !== 0n;
123
+ }
124
+ function advanceAuthorizedDirectory(admittedEpoch, admitted, receipt) {
125
+ if (!admittedEpoch || !admitted || receipt.admission !== admitted ||
126
+ admitted.epoch !== admittedEpoch || admitted.session !== admittedEpoch.session ||
127
+ admitted.childPath !== receipt.child.path ||
128
+ (admittedEpoch.session.kind === "shared" &&
129
+ (!nonzeroDirectoryObservation(receipt.parent) ||
130
+ !nonzeroDirectoryObservation(receipt.child))))
131
+ return undefined;
132
+ // The walker calls this only after its live parent fence, exact-parent deny,
133
+ // authority callback, successful mkdir, and exact child checks.
134
+ const observations = admittedEpoch.observations.map((observation) => advanceMutationObservation(observation, receipt.parent, receipt.child));
135
+ if (observations.some((observation) => !observation))
136
+ return undefined;
137
+ const complete = observations;
138
+ const target = complete.find((observation) => observation.path === admittedEpoch.target.path);
139
+ if (!target || target.missingOffset !== admittedEpoch.target.missingOffset + 1)
140
+ return undefined;
141
+ const next = Object.freeze({
142
+ ...admittedEpoch,
143
+ target,
144
+ observations: Object.freeze(complete),
145
+ });
146
+ if (!mutationDirectoryObservationCurrent(receipt.parent) ||
147
+ !mutationDirectoryObservationCurrent(receipt.child) ||
148
+ !mutationObservationUsesDirectory(target, receipt.child) ||
149
+ !epochCurrent(next))
150
+ return undefined;
151
+ assertCachedNotDenied(target.path, next);
152
+ assertCachedNotDenied(receipt.child.path, next);
153
+ return next;
154
+ }
155
+ export async function preparePinnedWriteMutationAdmission(params) {
156
+ if (!params.policy) {
157
+ return { relativeParentPath: params.defaultRelativeParentPath };
158
+ }
159
+ const policy = params.policy;
160
+ const route = simplePinnedRoute(params.rootReal, params.originalPath);
161
+ const sharedRoute = simpleSharedRoute({
162
+ rootReal: params.rootReal,
163
+ rootIdentity: params.rootIdentity,
164
+ originalPath: params.originalPath,
165
+ selectedTarget: params.resolvedTargetPath,
166
+ policy,
167
+ });
168
+ let pinnedSession;
169
+ let epoch;
170
+ let pending;
171
+ const observedCanonicalParent = await resolvePathViaExistingAncestor(path.dirname(params.resolvedTargetPath));
172
+ const admittedCanonicalParent = admitPathInsideRoot({
173
+ rootPath: params.rootReal,
174
+ candidatePath: observedCanonicalParent,
175
+ rootIdentity: params.rootIdentity,
176
+ });
177
+ if (!admittedCanonicalParent)
178
+ throw outsideWorkspaceError();
179
+ const relativeCanonicalParent = admittedCanonicalParent.relativePath;
180
+ const authorizeFully = async (request) => {
181
+ // Preserve the original route's symlink/deny error ordering, then apply
182
+ // the same snapshot to the object-bound destination selected below.
183
+ const current = await params.resolveCurrent();
184
+ await assertMutationNotDenied(request.targetPath, policy.denyMutations);
185
+ if (!sameAbsolutePath(request.targetPath, request.mutationPath)) {
186
+ await assertMutationNotDenied(request.mutationPath, policy.denyMutations);
187
+ }
188
+ const [currentTarget, admittedTarget] = await Promise.all([
189
+ canonicalMutationEntryPath(current.resolved),
190
+ canonicalMutationEntryPath(request.targetPath),
191
+ ]);
192
+ if (!sameAbsolutePath(currentTarget, admittedTarget)) {
193
+ throw new FsSafeError("path-mismatch", "write target changed during mutation policy admission");
194
+ }
195
+ return current;
196
+ };
197
+ const beginSharedParentWalk = sharedRoute ? () => {
198
+ const session = Object.freeze({
199
+ kind: "shared",
200
+ route: sharedRoute.route,
201
+ selectedTarget: params.resolvedTargetPath,
202
+ nativeMode: getFsSafeNativeConfig().mode,
203
+ rootIdentity: sharedRoute.rootIdentity,
204
+ authorizationToken: Object.freeze({}),
205
+ });
206
+ let disposed = false;
207
+ let disabled = false;
208
+ let sharedEpoch;
209
+ let sharedPending;
210
+ const disable = () => {
211
+ disabled = true;
212
+ sharedEpoch = undefined;
213
+ sharedPending = undefined;
214
+ };
215
+ return Object.freeze({
216
+ retainedTargetPath: session.route,
217
+ tryAuthorizeAtParent(request, parent) {
218
+ if (disposed || disabled)
219
+ return undefined;
220
+ // The walk's first missing component has no receipt epoch yet. Treat
221
+ // that as an ordinary cache miss so authorize() can bracket the full
222
+ // ordered admission and seed this session.
223
+ if (!sharedEpoch && !sharedPending)
224
+ return undefined;
225
+ // A receipt must be consumed by the exact mkdir/advance pair before
226
+ // another parent-bound authorization can be attempted.
227
+ if (sharedPending) {
228
+ disable();
229
+ return undefined;
230
+ }
231
+ const reusable = sharedEpoch;
232
+ if (!reusable || reusable.session !== session ||
233
+ !reusableSharedCreateRequest(request, reusable) ||
234
+ !mutationObservationUsesDirectory(reusable.target, parent) ||
235
+ !epochCurrent(reusable)) {
236
+ disable();
237
+ return undefined;
238
+ }
239
+ assertCachedNotDenied(request.targetPath, reusable);
240
+ assertCachedNotDenied(request.mutationPath, reusable);
241
+ // This is the required end fence. A returned receipt remains usable
242
+ // only synchronously, while the exact parent identity and spelling
243
+ // still match the observation captured by this walk.
244
+ if (!nonzeroDirectoryObservation(parent) ||
245
+ !mutationDirectoryObservationCurrent(parent)) {
246
+ disable();
247
+ return undefined;
248
+ }
249
+ sharedPending = Object.freeze({
250
+ epoch: reusable,
251
+ childPath: request.mutationPath,
252
+ session,
253
+ });
254
+ return sharedPending;
255
+ },
256
+ async authorize(request) {
257
+ // A create receipt reaching a full before-use admission means mkdir
258
+ // did not yield owned, complete post-create evidence (including
259
+ // EEXIST). The rest of this walk stays on ordered admission.
260
+ if (sharedPending)
261
+ disable();
262
+ const reusable = sharedEpoch;
263
+ if (!disposed && !disabled && reusable?.session === session &&
264
+ reusableSharedRequest(request, reusable) && epochCurrent(reusable)) {
265
+ assertCachedNotDenied(request.targetPath, reusable);
266
+ assertCachedNotDenied(request.mutationPath, reusable);
267
+ return undefined;
268
+ }
269
+ if (reusable)
270
+ disable();
271
+ const captureRequested = !disposed && !disabled &&
272
+ request.targetPath === session.route;
273
+ const candidate = captureRequested
274
+ ? captureEpoch(params.rootReal, session.route, policy, session)
275
+ : undefined;
276
+ if (captureRequested &&
277
+ (!candidate || !reusableSharedRequest(request, candidate)))
278
+ disable();
279
+ const current = await authorizeFully(request);
280
+ if (disposed || disabled || !candidate) {
281
+ sharedEpoch = undefined;
282
+ return undefined;
283
+ }
284
+ if (current.resolved !== session.route ||
285
+ current.resolved !== session.selectedTarget || !epochCurrent(candidate)) {
286
+ disable();
287
+ return undefined;
288
+ }
289
+ // Observations bracket the ordered admission. Never attach a receipt
290
+ // to evidence collected only after the policy decisions it replaces.
291
+ sharedEpoch = candidate;
292
+ if (request.phase === "parent")
293
+ return undefined;
294
+ sharedPending = Object.freeze({
295
+ epoch: candidate,
296
+ childPath: request.mutationPath,
297
+ session,
298
+ });
299
+ return sharedPending;
300
+ },
301
+ advanceCreatedDirectory(receipt) {
302
+ const admitted = sharedPending;
303
+ const admittedEpoch = sharedEpoch;
304
+ sharedPending = undefined;
305
+ sharedEpoch = undefined;
306
+ if (disposed || disabled || admittedEpoch?.session !== session) {
307
+ disable();
308
+ return undefined;
309
+ }
310
+ const next = advanceAuthorizedDirectory(admittedEpoch, admitted, receipt);
311
+ if (!next) {
312
+ disable();
313
+ return undefined;
314
+ }
315
+ sharedEpoch = next;
316
+ return session.authorizationToken;
317
+ },
318
+ dispose() {
319
+ disposed = true;
320
+ disable();
321
+ },
322
+ });
323
+ } : undefined;
324
+ const mutationAdmission = Object.freeze({
325
+ rejectParentSymlinks: policy.mutationSymlinks === "reject",
326
+ beginParentWalk: route ? () => {
327
+ pinnedSession = Object.freeze({
328
+ kind: "pinned",
329
+ route,
330
+ selectedTarget: params.resolvedTargetPath,
331
+ nativeMode: getFsSafeNativeConfig().mode,
332
+ authorizationToken: Object.freeze({}),
333
+ });
334
+ epoch = undefined;
335
+ pending = undefined;
336
+ return route;
337
+ } : undefined,
338
+ beginSharedParentWalk,
339
+ tryAuthorizeAtParent(request, parent) {
340
+ pending = undefined;
341
+ const reusable = epoch;
342
+ if (!reusable || reusable.session !== pinnedSession ||
343
+ !reusableRequest(request, reusable) ||
344
+ !mutationObservationUsesDirectory(reusable.target, parent) ||
345
+ !epochCurrent(reusable)) {
346
+ epoch = undefined;
347
+ return undefined;
348
+ }
349
+ assertCachedNotDenied(request.targetPath, reusable);
350
+ assertCachedNotDenied(request.mutationPath, reusable);
351
+ // This is the required end fence. The caller can consume the returned
352
+ // token without awaiting only while the exact pathname, canonical
353
+ // spelling, descriptor identity, type, mode, and link count still match.
354
+ if (!mutationDirectoryObservationCurrent(parent)) {
355
+ epoch = undefined;
356
+ return undefined;
357
+ }
358
+ if (request.phase === "parent-create") {
359
+ pending = Object.freeze({
360
+ epoch: reusable,
361
+ childPath: request.mutationPath,
362
+ session: reusable.session,
363
+ });
364
+ return pending;
365
+ }
366
+ return reusable.session.authorizationToken;
367
+ },
368
+ async authorize(request) {
369
+ pending = undefined;
370
+ if (epoch && epoch.session === pinnedSession &&
371
+ reusableRequest(request, epoch) && epochCurrent(epoch)) {
372
+ assertCachedNotDenied(request.targetPath, epoch);
373
+ assertCachedNotDenied(request.mutationPath, epoch);
374
+ if (request.phase === "parent-create") {
375
+ pending = Object.freeze({
376
+ epoch,
377
+ childPath: request.mutationPath,
378
+ session: epoch.session,
379
+ });
380
+ return pending;
381
+ }
382
+ return undefined;
383
+ }
384
+ epoch = undefined;
385
+ const session = pinnedSession;
386
+ const candidate = session && route === request.targetPath
387
+ ? captureEpoch(params.rootReal, route, policy, session) : undefined;
388
+ const current = await authorizeFully(request);
389
+ // Observations bracket the ordered admission. Never attach a cache to
390
+ // evidence collected only after the policy decisions it would replace.
391
+ if (candidate && candidate.session === pinnedSession &&
392
+ current.resolved === route && epochCurrent(candidate)) {
393
+ epoch = candidate;
394
+ if (request.phase === "parent-create" && reusableRequest(request, candidate)) {
395
+ pending = Object.freeze({
396
+ epoch: candidate,
397
+ childPath: request.mutationPath,
398
+ session: candidate.session,
399
+ });
400
+ return pending;
401
+ }
402
+ }
403
+ return undefined;
404
+ },
405
+ advanceCreatedDirectory: route ? (receipt) => {
406
+ const admitted = pending;
407
+ pending = undefined;
408
+ const admittedEpoch = epoch;
409
+ // Completion is fail-closed: no partially checked candidate remains
410
+ // reusable if any provenance, observation, or freshness check fails.
411
+ epoch = undefined;
412
+ const next = advanceAuthorizedDirectory(admittedEpoch, admitted, receipt);
413
+ if (!next)
414
+ return undefined;
415
+ epoch = next;
416
+ return next.session.authorizationToken;
417
+ } : undefined,
418
+ });
419
+ return {
420
+ relativeParentPath: relativeCanonicalParent
421
+ ? relativeCanonicalParent.split(path.sep).join(path.posix.sep)
422
+ : "",
423
+ mutationAdmission,
424
+ };
425
+ }
@@ -0,0 +1,34 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ export type MutationIdentity = Readonly<{
3
+ dev: bigint;
4
+ ino: bigint;
5
+ mode: bigint;
6
+ nlink: bigint;
7
+ }>;
8
+ declare const checkedDirectoryObservation: unique symbol;
9
+ export type MutationDirectoryObservation = Readonly<{
10
+ path: string;
11
+ canonicalPath: string;
12
+ identity: MutationIdentity;
13
+ [checkedDirectoryObservation]: true;
14
+ }>;
15
+ export type MutationPathObservation = Readonly<{
16
+ path: string;
17
+ ancestor: string;
18
+ canonicalAncestor: string;
19
+ canonicalPath: string;
20
+ entry: MutationIdentity;
21
+ identity: MutationIdentity;
22
+ missingSegments: readonly string[];
23
+ missingOffset: number;
24
+ }>;
25
+ export declare function missingMutationSegments(observation: MutationPathObservation): number;
26
+ export declare function nextMissingMutationPath(observation: MutationPathObservation): string | undefined;
27
+ export declare function checkedMutationDirectory(pathname: string, canonicalPath: string, stat: BigIntStats): MutationDirectoryObservation;
28
+ export declare function mutationDirectoryObservationCurrent(observation: MutationDirectoryObservation): boolean;
29
+ export declare function mutationObservationUsesDirectory(observation: MutationPathObservation, directory: MutationDirectoryObservation): boolean;
30
+ export declare function observeMutationPath(pathname: string): MutationPathObservation | undefined;
31
+ export declare function mutationObservationCurrent(observation: MutationPathObservation): boolean;
32
+ export declare function advanceMutationObservation(observation: MutationPathObservation, parent: MutationDirectoryObservation, child: MutationDirectoryObservation): MutationPathObservation | undefined;
33
+ export {};
34
+ //# sourceMappingURL=pinned-mutation-observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-mutation-observation.d.ts","sourceRoot":"","sources":["../src/pinned-mutation-observation.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAM/C,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,QAAA,MAAM,2BAA2B,eAAmD,CAAC;AAErF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,CAAC,2BAA2B,CAAC,EAAE,IAAI,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AA8BH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,uBAAuB,GAAG,MAAM,CAEpF;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAGhG;AAKD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,WAAW,GAChB,4BAA4B,CAU9B;AAQD,wBAAgB,mCAAmC,CACjD,WAAW,EAAE,4BAA4B,GACxC,OAAO,CAUT;AAED,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,uBAAuB,EACpC,SAAS,EAAE,4BAA4B,GACtC,OAAO,CAMT;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CA0BzF;AAED,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAaxF;AAID,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,uBAAuB,EACpC,MAAM,EAAE,4BAA4B,EACpC,KAAK,EAAE,4BAA4B,GAClC,uBAAuB,GAAG,SAAS,CAiBrC"}