@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
@@ -0,0 +1,142 @@
1
+ import fs, {} from "node:fs";
2
+ import path from "node:path";
3
+ import { isNotFoundPathError } from "./path.js";
4
+ import { realpathSync } from "./realpath.js";
5
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
+ const checkedDirectoryObservation = Symbol("checked mutation directory observation");
7
+ function identity(stat) {
8
+ return Object.freeze({ dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink });
9
+ }
10
+ function sameIdentity(left, right, directory = false) {
11
+ return left.dev === right.dev && left.ino === right.ino && left.mode === right.mode &&
12
+ // mkdir changes a parent's link count. File link counts remain live evidence.
13
+ (directory || left.nlink === right.nlink);
14
+ }
15
+ function directoryMode(mode) {
16
+ return (mode & 61440n) === 16384n;
17
+ }
18
+ function absent(pathname) {
19
+ try {
20
+ fs.lstatSync(pathname);
21
+ return false;
22
+ }
23
+ catch (error) {
24
+ // ENOTDIR is incomplete evidence, not a missing child of a directory.
25
+ return isNotFoundPathError(error) && error.code === "ENOENT";
26
+ }
27
+ }
28
+ export function missingMutationSegments(observation) {
29
+ return observation.missingSegments.length - observation.missingOffset;
30
+ }
31
+ export function nextMissingMutationPath(observation) {
32
+ const segment = observation.missingSegments[observation.missingOffset];
33
+ return segment === undefined ? undefined : path.join(observation.ancestor, segment);
34
+ }
35
+ // Callers may construct this only from their just-completed exact pathname and
36
+ // canonical fences. The private brand prevents unverified structural objects
37
+ // from advancing an admission epoch.
38
+ export function checkedMutationDirectory(pathname, canonicalPath, stat) {
39
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
40
+ throw new TypeError("mutation directory evidence must describe a real directory");
41
+ }
42
+ return Object.freeze({
43
+ path: pathname,
44
+ canonicalPath,
45
+ identity: identity(stat),
46
+ [checkedDirectoryObservation]: true,
47
+ });
48
+ }
49
+ function checkedDirectory(observation) {
50
+ return observation[checkedDirectoryObservation] === true &&
51
+ path.isAbsolute(observation.path) && path.isAbsolute(observation.canonicalPath) &&
52
+ directoryMode(observation.identity.mode);
53
+ }
54
+ export function mutationDirectoryObservationCurrent(observation) {
55
+ if (!checkedDirectory(observation))
56
+ return false;
57
+ try {
58
+ const stat = inspectFileIdentitySync(() => fs.lstatSync(observation.path, { bigint: true }));
59
+ return stat.isDirectory() && !stat.isSymbolicLink() &&
60
+ sameIdentity(observation.identity, stat) &&
61
+ realpathSync.native(observation.path) === observation.canonicalPath;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ export function mutationObservationUsesDirectory(observation, directory) {
68
+ return checkedDirectory(directory) &&
69
+ observation.ancestor === directory.path &&
70
+ observation.canonicalAncestor === directory.canonicalPath &&
71
+ sameIdentity(observation.entry, directory.identity) &&
72
+ sameIdentity(observation.identity, directory.identity);
73
+ }
74
+ // Optional evidence only: failures select full admission, never a new error order.
75
+ export function observeMutationPath(pathname) {
76
+ try {
77
+ let ancestor = pathname;
78
+ const reversedMissing = [];
79
+ while (absent(ancestor)) {
80
+ const parent = path.dirname(ancestor);
81
+ if (parent === ancestor)
82
+ return undefined;
83
+ reversedMissing.push(path.basename(ancestor));
84
+ ancestor = parent;
85
+ }
86
+ reversedMissing.reverse();
87
+ const missingSegments = Object.freeze(reversedMissing);
88
+ const entry = inspectFileIdentitySync(() => fs.lstatSync(ancestor, { bigint: true }));
89
+ const canonicalAncestor = realpathSync.native(ancestor);
90
+ const stat = canonicalAncestor === ancestor ? entry :
91
+ inspectFileIdentitySync(() => fs.lstatSync(canonicalAncestor, { bigint: true }));
92
+ if (stat.isSymbolicLink() || (missingSegments.length > 0 && !stat.isDirectory()))
93
+ return undefined;
94
+ const observation = Object.freeze({
95
+ path: pathname, ancestor, canonicalAncestor,
96
+ canonicalPath: path.join(canonicalAncestor, ...missingSegments),
97
+ entry: identity(entry), identity: identity(stat), missingSegments, missingOffset: 0,
98
+ });
99
+ return mutationObservationCurrent(observation) ? observation : undefined;
100
+ }
101
+ catch {
102
+ return undefined;
103
+ }
104
+ }
105
+ export function mutationObservationCurrent(observation) {
106
+ try {
107
+ const entry = inspectFileIdentitySync(() => fs.lstatSync(observation.ancestor, { bigint: true }));
108
+ if (!sameIdentity(observation.entry, entry, entry.isDirectory()) ||
109
+ realpathSync.native(observation.ancestor) !== observation.canonicalAncestor)
110
+ return false;
111
+ const stat = observation.canonicalAncestor === observation.ancestor ? entry :
112
+ inspectFileIdentitySync(() => fs.lstatSync(observation.canonicalAncestor, { bigint: true }));
113
+ return sameIdentity(observation.identity, stat, stat.isDirectory()) &&
114
+ (missingMutationSegments(observation) === 0 ||
115
+ (stat.isDirectory() && absent(nextMissingMutationPath(observation))));
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ }
121
+ // This is deliberately a pure tentative update. The immediately following
122
+ // parent authorization validates the complete epoch once before reusing it.
123
+ export function advanceMutationObservation(observation, parent, child) {
124
+ const nextMissingPath = nextMissingMutationPath(observation);
125
+ if (nextMissingPath === undefined || nextMissingPath !== child.path)
126
+ return observation;
127
+ if (!checkedDirectory(parent) || !checkedDirectory(child) ||
128
+ observation.ancestor !== parent.path || observation.canonicalAncestor !== parent.canonicalPath ||
129
+ !sameIdentity(observation.entry, parent.identity, true) ||
130
+ !sameIdentity(observation.identity, parent.identity, true) ||
131
+ parent.path !== parent.canonicalPath || child.path !== child.canonicalPath ||
132
+ path.dirname(child.path) !== parent.path)
133
+ return undefined;
134
+ return Object.freeze({
135
+ ...observation,
136
+ ancestor: child.path,
137
+ canonicalAncestor: child.canonicalPath,
138
+ entry: child.identity,
139
+ identity: child.identity,
140
+ missingOffset: observation.missingOffset + 1,
141
+ });
142
+ }
@@ -0,0 +1,24 @@
1
+ import type { DenyMutationPolicy } from "./deny-mutations.js";
2
+ import type { RootBoundaryIdentity } from "./root-boundary.js";
3
+ import type { MutationSymlinkPolicy } from "./root-symlink-policy.js";
4
+ export type ExactRootIdentity = Readonly<{
5
+ dev: bigint;
6
+ ino: bigint;
7
+ }>;
8
+ type SharedMutationPolicy = Readonly<{
9
+ denyMutations?: DenyMutationPolicy;
10
+ mutationSymlinks?: MutationSymlinkPolicy;
11
+ }>;
12
+ export declare function ordinarySharedAbsoluteInsideRoot(rootReal: string, candidatePath: string, rootIdentity: ExactRootIdentity): boolean;
13
+ export declare function simpleSharedRoute(params: {
14
+ rootReal: string;
15
+ rootIdentity?: RootBoundaryIdentity;
16
+ originalPath?: string;
17
+ selectedTarget: string;
18
+ policy: SharedMutationPolicy;
19
+ }): {
20
+ route: string;
21
+ rootIdentity: ExactRootIdentity;
22
+ } | undefined;
23
+ export {};
24
+ //# sourceMappingURL=pinned-mutation-shared-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-mutation-shared-route.d.ts","sourceRoot":"","sources":["../src/pinned-mutation-shared-route.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEvE,KAAK,oBAAoB,GAAG,QAAQ,CAAC;IACnC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;CAC1C,CAAC,CAAC;AA+BH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,iBAAiB,GAC9B,OAAO,CAeT;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,oBAAoB,CAAC;CAC9B,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,iBAAiB,CAAA;CAAE,GAAG,SAAS,CAqBjE"}
@@ -0,0 +1,70 @@
1
+ import path from "node:path";
2
+ import { WINDOWS_RESERVED_DEVICE_NAMES } from "./device-path.js";
3
+ import { admitPathInsideRoot } from "./root-boundary.js";
4
+ import { isSafePathSegment } from "./safe-path-segment.js";
5
+ function windowsReservedDeviceSegment(segment) {
6
+ const extension = segment.indexOf(".");
7
+ const stem = segment.slice(0, extension < 0 ? segment.length : extension).toUpperCase();
8
+ return WINDOWS_RESERVED_DEVICE_NAMES.has(stem);
9
+ }
10
+ function ordinaryWindowsSegments(relativePath) {
11
+ if (relativePath === "" || relativePath.includes("/") || relativePath.includes("\0"))
12
+ return false;
13
+ const segments = relativePath.split("\\");
14
+ return segments.every((segment) => isSafePathSegment(segment, { allowDotPrefix: true }) &&
15
+ !segment.endsWith(".") && !segment.endsWith(" ") &&
16
+ !windowsReservedDeviceSegment(segment));
17
+ }
18
+ function ordinaryPosixSegments(relativePath) {
19
+ return relativePath !== "" && !relativePath.includes("\\") &&
20
+ relativePath.split("/").every((segment) => isSafePathSegment(segment, { allowDotPrefix: true }));
21
+ }
22
+ function exactNonzeroRootIdentity(identity) {
23
+ return typeof identity?.dev === "bigint" && typeof identity.ino === "bigint" &&
24
+ identity.dev !== 0n && identity.ino !== 0n
25
+ ? Object.freeze({ dev: identity.dev, ino: identity.ino })
26
+ : undefined;
27
+ }
28
+ export function ordinarySharedAbsoluteInsideRoot(rootReal, candidatePath, rootIdentity) {
29
+ if (!path.isAbsolute(candidatePath) || candidatePath.includes("\0") ||
30
+ path.resolve(candidatePath) !== candidatePath ||
31
+ (process.platform === "win32"
32
+ ? candidatePath.startsWith("\\\\") || candidatePath.includes("/")
33
+ : candidatePath.includes("\\")))
34
+ return false;
35
+ const admitted = admitPathInsideRoot({
36
+ rootPath: rootReal,
37
+ candidatePath,
38
+ rootIdentity,
39
+ });
40
+ return admitted?.admission === "exact" && admitted.path === candidatePath &&
41
+ (admitted.relativePath === "" || (process.platform === "win32"
42
+ ? ordinaryWindowsSegments(admitted.relativePath)
43
+ : ordinaryPosixSegments(admitted.relativePath)));
44
+ }
45
+ export function simpleSharedRoute(params) {
46
+ if (process.versions.bun ||
47
+ params.policy.mutationSymlinks !== "reject" || !params.originalPath ||
48
+ params.originalPath.startsWith("~") || path.isAbsolute(params.originalPath) ||
49
+ !(process.platform === "win32"
50
+ ? ordinaryWindowsSegments(params.originalPath) &&
51
+ !params.rootReal.startsWith("\\\\") && !params.rootReal.includes("/")
52
+ : ordinaryPosixSegments(params.originalPath) && !params.rootReal.includes("\\")) ||
53
+ path.resolve(params.rootReal) !== params.rootReal)
54
+ return undefined;
55
+ const rootIdentity = exactNonzeroRootIdentity(params.rootIdentity);
56
+ if (!rootIdentity)
57
+ return undefined;
58
+ const route = path.resolve(params.rootReal, params.originalPath);
59
+ if (route !== params.selectedTarget ||
60
+ !ordinarySharedAbsoluteInsideRoot(params.rootReal, route, rootIdentity))
61
+ return undefined;
62
+ const policyEntries = [
63
+ ...(params.policy.denyMutations?.paths ?? []),
64
+ ...(params.policy.denyMutations?.prefixes ?? []),
65
+ ];
66
+ if (policyEntries.some((entry) => entry !== path.resolve(entry) ||
67
+ !ordinarySharedAbsoluteInsideRoot(params.rootReal, entry, rootIdentity)))
68
+ return undefined;
69
+ return { route, rootIdentity };
70
+ }
@@ -11,6 +11,11 @@ export type PinnedOpenSyncResult = {
11
11
  error?: unknown;
12
12
  };
13
13
  export type PinnedOpenSyncAllowedType = "file" | "directory";
14
+ export type PinnedOpenSyncFinalAdmission = (params: {
15
+ path: string;
16
+ preRealpathIdentity: fs.BigIntStats;
17
+ descriptorIdentity: fs.BigIntStats;
18
+ }) => string;
14
19
  export type PinnedOpenSyncFs = Pick<typeof fs, "constants" | "lstatSync" | "realpathSync" | "openSync" | "fstatSync" | "closeSync">;
15
20
  export declare function openPinnedFileSync(params: {
16
21
  filePath: string;
@@ -20,5 +25,6 @@ export declare function openPinnedFileSync(params: {
20
25
  maxBytes?: number;
21
26
  allowedType?: PinnedOpenSyncAllowedType;
22
27
  ioFs?: PinnedOpenSyncFs;
28
+ finalAdmission?: PinnedOpenSyncFinalAdmission;
23
29
  }): PinnedOpenSyncResult;
24
30
  //# sourceMappingURL=pinned-open.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pinned-open.d.ts","sourceRoot":"","sources":["../src/pinned-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,2BAA2B,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,OAAO,EAAE,EACT,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CACpF,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,GAAG,oBAAoB,CAqDvB"}
1
+ {"version":3,"file":"pinned-open.d.ts","sourceRoot":"","sources":["../src/pinned-open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAQzB,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,2BAA2B,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE7D,MAAM,MAAM,4BAA4B,GAAG,CAAC,MAAM,EAAE;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,EAAE,CAAC,WAAW,CAAC;IACpC,kBAAkB,EAAE,EAAE,CAAC,WAAW,CAAC;CACpC,KAAK,MAAM,CAAC;AAEb,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,OAAO,EAAE,EACT,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CACpF,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,cAAc,CAAC,EAAE,4BAA4B,CAAC;CAC/C,GAAG,oBAAoB,CAuEvB"}
@@ -3,49 +3,67 @@ import { isUnsafeDeviceReadPath } from "./device-path.js";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
5
5
  import { resolveReadOpenFlags } from "./read-open-flags.js";
6
+ import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
6
7
  import { realpathSync } from "./realpath.js";
7
8
  function isExpectedPathError(error) {
8
9
  const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
9
10
  return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
10
11
  }
11
12
  export function openPinnedFileSync(params) {
13
+ const filePath = params.filePath;
14
+ const resolvedPath = params.resolvedPath;
12
15
  const ioFs = params.ioFs ?? fs;
13
16
  const allowedType = params.allowedType ?? "file";
17
+ const rejectPathSymlink = params.rejectPathSymlink === true;
18
+ const statPolicy = {
19
+ rejectHardlinks: params.rejectHardlinks,
20
+ maxBytes: params.maxBytes,
21
+ };
14
22
  const openReadFlags = resolveReadOpenFlags({ constants: ioFs.constants });
15
23
  let fd = null;
16
24
  try {
17
- if (isUnsafeDeviceReadPath(params.filePath)) {
25
+ assertNoWindowsPathAlias(filePath, "filesystem", "file path uses a Windows filesystem namespace alias");
26
+ if (resolvedPath !== undefined) {
27
+ assertNoWindowsPathAlias(resolvedPath, "filesystem", "resolved path uses a Windows filesystem namespace alias");
28
+ }
29
+ if (isUnsafeDeviceReadPath(filePath)) {
18
30
  return { ok: false, reason: "validation" };
19
31
  }
20
- if (params.rejectPathSymlink) {
21
- const candidateStat = ioFs.lstatSync(params.filePath);
32
+ if (rejectPathSymlink) {
33
+ const candidateStat = ioFs.lstatSync(filePath);
22
34
  if (candidateStat.isSymbolicLink()) {
23
35
  return { ok: false, reason: "validation" };
24
36
  }
25
37
  }
26
- const realPath = params.resolvedPath ??
27
- (ioFs === fs ? realpathSync(params.filePath) : ioFs.realpathSync(params.filePath));
38
+ const realPath = resolvedPath ??
39
+ (ioFs === fs ? realpathSync(filePath) : ioFs.realpathSync(filePath));
40
+ assertNoWindowsPathAlias(realPath, "filesystem", "resolved path uses a Windows filesystem namespace alias");
28
41
  if (isUnsafeDeviceReadPath(realPath)) {
29
42
  return { ok: false, reason: "validation" };
30
43
  }
31
44
  const preOpenStat = inspectFileIdentitySync(() => {
32
45
  const stat = ioFs.lstatSync(realPath, { bigint: true });
33
- assertAllowedStat(stat, allowedType, params);
46
+ assertAllowedStat(stat, allowedType, statPolicy);
34
47
  return stat;
35
48
  });
36
49
  fd = ioFs.openSync(realPath, openReadFlags);
37
50
  const openedStat = ioFs.fstatSync(fd);
38
51
  const identity = inspectFileIdentitySync(() => {
39
52
  const stat = ioFs.fstatSync(fd, { bigint: true });
40
- assertAllowedStat(stat, allowedType, params);
53
+ assertAllowedStat(stat, allowedType, statPolicy);
41
54
  return stat;
42
55
  }, preOpenStat);
43
- inspectFileIdentitySync(() => {
56
+ const pathIdentity = inspectFileIdentitySync(() => {
44
57
  const stat = ioFs.lstatSync(realPath, { bigint: true });
45
- assertAllowedStat(stat, allowedType, params);
58
+ assertAllowedStat(stat, allowedType, statPolicy);
46
59
  return stat;
47
60
  }, identity);
48
- const opened = { ok: true, path: realPath, fd, stat: openedStat };
61
+ const admittedPath = params.finalAdmission?.({
62
+ path: realPath,
63
+ preRealpathIdentity: pathIdentity,
64
+ descriptorIdentity: identity,
65
+ }) ?? realPath;
66
+ const opened = { ok: true, path: admittedPath, fd, stat: openedStat };
49
67
  fd = null;
50
68
  return opened;
51
69
  }
@@ -0,0 +1,75 @@
1
+ import type { CopyFileInput } from "./copy-file-input.js";
2
+ import type { AnyAsyncDirectoryGuard } from "./directory-guard.js";
3
+ import type { FileIdentityStat } from "./file-identity.js";
4
+ import type { MutationDirectoryObservation } from "./pinned-mutation-observation.js";
5
+ export type PinnedWriteInput = {
6
+ kind: "buffer";
7
+ data: string | Buffer;
8
+ encoding?: BufferEncoding;
9
+ } | {
10
+ kind: "stream";
11
+ stream: AsyncIterable<Uint8Array | string>;
12
+ stageBeforePublish?: boolean;
13
+ } | CopyFileInput;
14
+ export type RenameIdentityPolicy = "strict" | "verify-content-with-lock";
15
+ export type PublishedWriteIdentity = Readonly<{
16
+ dev: bigint;
17
+ ino: bigint;
18
+ }>;
19
+ export type PinnedMutationAdmissionReceipt = Readonly<object>;
20
+ export type PinnedMutationAuthorizationToken = Readonly<object>;
21
+ export type PinnedCreatedDirectoryReceipt = Readonly<{
22
+ admission: PinnedMutationAdmissionReceipt;
23
+ parent: MutationDirectoryObservation;
24
+ child: MutationDirectoryObservation;
25
+ }>;
26
+ export type PinnedMutationParentWalkSession = Readonly<{
27
+ retainedTargetPath: string;
28
+ tryAuthorizeAtParent(request: Readonly<{
29
+ targetPath: string;
30
+ mutationPath: string;
31
+ phase: "parent" | "parent-create";
32
+ }>, parent: MutationDirectoryObservation): PinnedMutationAuthorizationToken | undefined;
33
+ authorize(request: Readonly<{
34
+ targetPath: string;
35
+ mutationPath: string;
36
+ phase: "parent" | "parent-create";
37
+ }>): Promise<PinnedMutationAdmissionReceipt | undefined>;
38
+ advanceCreatedDirectory(receipt: PinnedCreatedDirectoryReceipt): PinnedMutationAuthorizationToken | undefined;
39
+ dispose(): void;
40
+ }>;
41
+ export type PinnedWriteMutationAdmission = Readonly<{
42
+ rejectParentSymlinks: boolean;
43
+ beginParentWalk?(): string | undefined;
44
+ beginSharedParentWalk?(): PinnedMutationParentWalkSession | undefined;
45
+ tryAuthorizeAtParent?(request: Readonly<{
46
+ targetPath: string;
47
+ mutationPath: string;
48
+ phase: "parent" | "parent-create";
49
+ }>, parent: MutationDirectoryObservation): PinnedMutationAuthorizationToken | undefined;
50
+ authorize(request: Readonly<{
51
+ targetPath: string;
52
+ mutationPath: string;
53
+ phase: "parent" | "parent-create";
54
+ }>): Promise<PinnedMutationAdmissionReceipt | undefined>;
55
+ advanceCreatedDirectory?(receipt: PinnedCreatedDirectoryReceipt): PinnedMutationAuthorizationToken | undefined;
56
+ }>;
57
+ export type PinnedWriteParams = {
58
+ rootPath: string;
59
+ relativeParentPath: string;
60
+ basename: string;
61
+ mkdir: boolean;
62
+ mode: number;
63
+ sync?: boolean;
64
+ overwrite?: boolean;
65
+ assertBeforeMutation?: () => void;
66
+ rejectFinalSymlink?: boolean;
67
+ maxBytes?: number;
68
+ input: PinnedWriteInput;
69
+ rootIdentity?: FileIdentityStat;
70
+ mutationAdmission?: PinnedWriteMutationAdmission;
71
+ onRenameIdentityMismatch?: "verify-content";
72
+ onPublished?: (identity: PublishedWriteIdentity) => void;
73
+ verifyPublished?: (fd: number, identity: PublishedWriteIdentity, parentGuard: AnyAsyncDirectoryGuard) => Promise<void>;
74
+ };
75
+ //# sourceMappingURL=pinned-write-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-write-types.d.ts","sourceRoot":"","sources":["../src/pinned-write-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,40 +1,14 @@
1
- import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
2
1
  import type { FileIdentityStat } from "./file-identity.js";
3
- import { type CopyFileInput } from "./copy-file-input.js";
4
- export type PinnedWriteInput = {
5
- kind: "buffer";
6
- data: string | Buffer;
7
- encoding?: BufferEncoding;
8
- } | {
9
- kind: "stream";
10
- stream: AsyncIterable<Uint8Array | string>;
11
- stageBeforePublish?: boolean;
12
- } | CopyFileInput;
13
- export type RenameIdentityPolicy = "strict" | "verify-content-with-lock";
14
- export type PublishedWriteIdentity = Readonly<{
15
- dev: bigint;
16
- ino: bigint;
17
- }>;
18
- export type PinnedWriteParams = {
19
- rootPath: string;
20
- relativeParentPath: string;
21
- basename: string;
22
- mkdir: boolean;
23
- mode: number;
24
- sync?: boolean;
25
- overwrite?: boolean;
26
- assertBeforeMutation?: () => void;
27
- rejectFinalSymlink?: boolean;
28
- maxBytes?: number;
29
- input: PinnedWriteInput;
30
- rootIdentity?: FileIdentityStat;
31
- onRenameIdentityMismatch?: "verify-content";
32
- onPublished?: (identity: PublishedWriteIdentity) => void;
33
- verifyPublished?: (fd: number, identity: PublishedWriteIdentity, parentGuard: AnyAsyncDirectoryGuard) => Promise<void>;
34
- };
2
+ import type { PinnedWriteParams, RenameIdentityPolicy } from "./pinned-write-types.js";
3
+ export type { PinnedWriteInput, RenameIdentityPolicy, PublishedWriteIdentity, PinnedMutationAdmissionReceipt, PinnedMutationAuthorizationToken, PinnedCreatedDirectoryReceipt, PinnedMutationParentWalkSession, PinnedWriteMutationAdmission, PinnedWriteParams, } from "./pinned-write-types.js";
35
4
  export declare function runPinnedWriteHelper(params: PinnedWriteParams): Promise<FileIdentityStat>;
36
5
  export declare function runPinnedWriteWithRenamePolicy(params: PinnedWriteParams & {
37
6
  targetPath: string;
38
7
  renameIdentity?: RenameIdentityPolicy;
39
8
  }): Promise<FileIdentityStat>;
9
+ export declare function withPinnedWriteRenameIdentityLock<T>(params: {
10
+ rootPath: string;
11
+ targetPath: string;
12
+ relativeTargetPath: string;
13
+ }, run: () => Promise<T>): Promise<T>;
40
14
  //# sourceMappingURL=pinned-write.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAOA,OAAO,EAA4F,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAG7J,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAa3D,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,sBAAsB,CAAC;AAG7E,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5F,aAAa,CAAC;AA4ClB,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,0BAA0B,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,CAAC;IACxB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,wBAAwB,CAAC,EAAE,gBAAgB,CAAC;IAC5C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAEzD,eAAe,CAAC,EAAE,CAChB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,sBAAsB,EAChC,WAAW,EAAE,sBAAsB,KAChC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgB/F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,iBAAiB,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,GACA,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
1
+ {"version":3,"file":"pinned-write.d.ts","sourceRoot":"","sources":["../src/pinned-write.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAiB3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACvF,YAAY,EACV,gBAAgB,EAAE,oBAAoB,EAAE,sBAAsB,EAC9D,8BAA8B,EAAE,gCAAgC,EAChE,6BAA6B,EAAE,+BAA+B,EAC9D,4BAA4B,EAAE,iBAAiB,GAChD,MAAM,yBAAyB,CAAC;AAgFjC,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsD/F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,iBAAiB,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC,GACA,OAAO,CAAC,gBAAgB,CAAC,CAkB3B;AAED,wBAAsB,iCAAiC,CAAC,CAAC,EACvD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,EAC5E,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACpB,OAAO,CAAC,CAAC,CAAC,CAiBZ"}