@vibe-agent-toolkit/utils 0.2.0-rc.11 → 0.2.0-rc.12

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 (322) hide show
  1. package/README.md +111 -9
  2. package/dist/archive/archive.d.ts +129 -0
  3. package/dist/archive/archive.d.ts.map +1 -0
  4. package/dist/archive/archive.js +223 -0
  5. package/dist/archive/archive.js.map +1 -0
  6. package/dist/archive/entries.d.ts +132 -0
  7. package/dist/archive/entries.d.ts.map +1 -0
  8. package/dist/archive/entries.js +223 -0
  9. package/dist/archive/entries.js.map +1 -0
  10. package/dist/archive/limits.d.ts +39 -0
  11. package/dist/archive/limits.d.ts.map +1 -0
  12. package/dist/archive/limits.js +59 -0
  13. package/dist/archive/limits.js.map +1 -0
  14. package/dist/archive/records.d.ts +14 -0
  15. package/dist/archive/records.d.ts.map +1 -0
  16. package/dist/archive/records.js +3 -0
  17. package/dist/archive/records.js.map +1 -0
  18. package/dist/archive/tar-reader.d.ts +18 -0
  19. package/dist/archive/tar-reader.d.ts.map +1 -0
  20. package/dist/archive/tar-reader.js +173 -0
  21. package/dist/archive/tar-reader.js.map +1 -0
  22. package/dist/archive/zip-reader.d.ts +76 -0
  23. package/dist/archive/zip-reader.d.ts.map +1 -0
  24. package/dist/archive/zip-reader.js +376 -0
  25. package/dist/archive/zip-reader.js.map +1 -0
  26. package/dist/archive.d.ts +14 -0
  27. package/dist/archive.d.ts.map +1 -0
  28. package/dist/archive.js +12 -0
  29. package/dist/archive.js.map +1 -0
  30. package/dist/asset-reference.d.ts +17 -1
  31. package/dist/asset-reference.d.ts.map +1 -1
  32. package/dist/asset-reference.js +31 -7
  33. package/dist/asset-reference.js.map +1 -1
  34. package/dist/dirent-kind.d.ts +13 -40
  35. package/dist/dirent-kind.d.ts.map +1 -1
  36. package/dist/dirent-kind.js +21 -69
  37. package/dist/dirent-kind.js.map +1 -1
  38. package/dist/entrypoint.d.ts +2 -2
  39. package/dist/entrypoint.d.ts.map +1 -1
  40. package/dist/entrypoint.js +4 -3
  41. package/dist/entrypoint.js.map +1 -1
  42. package/dist/env-flag.d.ts +2 -0
  43. package/dist/env-flag.d.ts.map +1 -1
  44. package/dist/env-flag.js +2 -0
  45. package/dist/env-flag.js.map +1 -1
  46. package/dist/errors/confirmed-absent.d.ts +41 -0
  47. package/dist/errors/confirmed-absent.d.ts.map +1 -0
  48. package/dist/errors/confirmed-absent.js +43 -0
  49. package/dist/errors/confirmed-absent.js.map +1 -0
  50. package/dist/errors/errno-table.d.ts +177 -0
  51. package/dist/errors/errno-table.d.ts.map +1 -0
  52. package/dist/errors/errno-table.js +343 -0
  53. package/dist/errors/errno-table.js.map +1 -0
  54. package/dist/errors/fs-boundary.d.ts +36 -0
  55. package/dist/errors/fs-boundary.d.ts.map +1 -0
  56. package/dist/errors/fs-boundary.js +121 -0
  57. package/dist/errors/fs-boundary.js.map +1 -0
  58. package/dist/errors/fs-fault.d.ts +176 -0
  59. package/dist/errors/fs-fault.d.ts.map +1 -0
  60. package/dist/errors/fs-fault.js +215 -0
  61. package/dist/errors/fs-fault.js.map +1 -0
  62. package/dist/errors/git-errno.d.ts +13 -0
  63. package/dist/errors/git-errno.d.ts.map +1 -0
  64. package/dist/errors/git-errno.js +45 -0
  65. package/dist/errors/git-errno.js.map +1 -0
  66. package/dist/errors/path-present.d.ts +72 -0
  67. package/dist/errors/path-present.d.ts.map +1 -0
  68. package/dist/errors/path-present.js +110 -0
  69. package/dist/errors/path-present.js.map +1 -0
  70. package/dist/errors/rag-index-empty.d.ts +16 -0
  71. package/dist/errors/rag-index-empty.d.ts.map +1 -0
  72. package/dist/errors/rag-index-empty.js +16 -0
  73. package/dist/errors/rag-index-empty.js.map +1 -0
  74. package/dist/errors/suppressed-faults.d.ts +34 -0
  75. package/dist/errors/suppressed-faults.d.ts.map +1 -0
  76. package/dist/errors/suppressed-faults.js +66 -0
  77. package/dist/errors/suppressed-faults.js.map +1 -0
  78. package/dist/file-crawler.d.ts +13 -18
  79. package/dist/file-crawler.d.ts.map +1 -1
  80. package/dist/file-crawler.js +140 -24
  81. package/dist/file-crawler.js.map +1 -1
  82. package/dist/fs-utils.d.ts +75 -50
  83. package/dist/fs-utils.d.ts.map +1 -1
  84. package/dist/fs-utils.js +148 -158
  85. package/dist/fs-utils.js.map +1 -1
  86. package/dist/fs.d.ts +4 -4
  87. package/dist/fs.d.ts.map +1 -1
  88. package/dist/fs.js +4 -4
  89. package/dist/fs.js.map +1 -1
  90. package/dist/git-snapshot.d.ts +25 -17
  91. package/dist/git-snapshot.d.ts.map +1 -1
  92. package/dist/git-snapshot.js +65 -11
  93. package/dist/git-snapshot.js.map +1 -1
  94. package/dist/git-tracker.d.ts +11 -3
  95. package/dist/git-tracker.d.ts.map +1 -1
  96. package/dist/git-tracker.js +29 -6
  97. package/dist/git-tracker.js.map +1 -1
  98. package/dist/git-utils.d.ts.map +1 -1
  99. package/dist/git-utils.js +7 -35
  100. package/dist/git-utils.js.map +1 -1
  101. package/dist/git.d.ts +1 -1
  102. package/dist/git.d.ts.map +1 -1
  103. package/dist/git.js +1 -1
  104. package/dist/git.js.map +1 -1
  105. package/dist/gitignore-checker.d.ts.map +1 -1
  106. package/dist/gitignore-checker.js +5 -4
  107. package/dist/gitignore-checker.js.map +1 -1
  108. package/dist/in-order.d.ts +93 -0
  109. package/dist/in-order.d.ts.map +1 -0
  110. package/dist/in-order.js +165 -0
  111. package/dist/in-order.js.map +1 -0
  112. package/dist/index.d.ts +20 -4
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +30 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/kill-process-tree.d.ts +43 -0
  117. package/dist/kill-process-tree.d.ts.map +1 -0
  118. package/dist/kill-process-tree.js +48 -0
  119. package/dist/kill-process-tree.js.map +1 -0
  120. package/dist/link-following.d.ts +115 -0
  121. package/dist/link-following.d.ts.map +1 -0
  122. package/dist/link-following.js +283 -0
  123. package/dist/link-following.js.map +1 -0
  124. package/dist/listing-refusal.d.ts +44 -0
  125. package/dist/listing-refusal.d.ts.map +1 -1
  126. package/dist/listing-refusal.js +79 -2
  127. package/dist/listing-refusal.js.map +1 -1
  128. package/dist/name-fold.d.ts +25 -0
  129. package/dist/name-fold.d.ts.map +1 -0
  130. package/dist/name-fold.js +40 -0
  131. package/dist/name-fold.js.map +1 -0
  132. package/dist/path-containment.d.ts +3 -1
  133. package/dist/path-containment.d.ts.map +1 -1
  134. package/dist/path-containment.js +13 -7
  135. package/dist/path-containment.js.map +1 -1
  136. package/dist/path-utils.d.ts +14 -17
  137. package/dist/path-utils.d.ts.map +1 -1
  138. package/dist/path-utils.js +37 -55
  139. package/dist/path-utils.js.map +1 -1
  140. package/dist/process.d.ts +2 -0
  141. package/dist/process.d.ts.map +1 -1
  142. package/dist/process.js +2 -0
  143. package/dist/process.js.map +1 -1
  144. package/dist/project-utils.d.ts.map +1 -1
  145. package/dist/project-utils.js +8 -9
  146. package/dist/project-utils.js.map +1 -1
  147. package/dist/safe-exec.d.ts +6 -2
  148. package/dist/safe-exec.d.ts.map +1 -1
  149. package/dist/safe-exec.js +1 -1
  150. package/dist/safe-exec.js.map +1 -1
  151. package/dist/skill-targets.d.ts.map +1 -1
  152. package/dist/skill-targets.js +3 -1
  153. package/dist/skill-targets.js.map +1 -1
  154. package/dist/skill-test/spawn-claude.d.ts +0 -15
  155. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  156. package/dist/skill-test/spawn-claude.js +11 -50
  157. package/dist/skill-test/spawn-claude.js.map +1 -1
  158. package/dist/special-file.d.ts +18 -0
  159. package/dist/special-file.d.ts.map +1 -0
  160. package/dist/special-file.js +21 -0
  161. package/dist/special-file.js.map +1 -0
  162. package/dist/terminating-signals.d.ts +129 -0
  163. package/dist/terminating-signals.d.ts.map +1 -0
  164. package/dist/terminating-signals.js +210 -0
  165. package/dist/terminating-signals.js.map +1 -0
  166. package/dist/test-helpers.d.ts +9 -5
  167. package/dist/test-helpers.d.ts.map +1 -1
  168. package/dist/test-helpers.js +18 -21
  169. package/dist/test-helpers.js.map +1 -1
  170. package/dist/test-scratch-guard.d.ts +34 -0
  171. package/dist/test-scratch-guard.d.ts.map +1 -0
  172. package/dist/test-scratch-guard.js +45 -0
  173. package/dist/test-scratch-guard.js.map +1 -0
  174. package/dist/testing/archive-fixtures.d.ts +147 -0
  175. package/dist/testing/archive-fixtures.d.ts.map +1 -0
  176. package/dist/testing/archive-fixtures.js +449 -0
  177. package/dist/testing/archive-fixtures.js.map +1 -0
  178. package/dist/testing/executables.d.ts +6 -0
  179. package/dist/testing/executables.d.ts.map +1 -1
  180. package/dist/testing/executables.js +20 -9
  181. package/dist/testing/executables.js.map +1 -1
  182. package/dist/testing/fault-fs-preload.d.ts +16 -0
  183. package/dist/testing/fault-fs-preload.d.ts.map +1 -0
  184. package/dist/testing/fault-fs-preload.js +22 -0
  185. package/dist/testing/fault-fs-preload.js.map +1 -0
  186. package/dist/testing/fault-fs.d.ts +115 -0
  187. package/dist/testing/fault-fs.d.ts.map +1 -0
  188. package/dist/testing/fault-fs.js +411 -0
  189. package/dist/testing/fault-fs.js.map +1 -0
  190. package/dist/testing/fault-spec.d.ts +83 -0
  191. package/dist/testing/fault-spec.d.ts.map +1 -0
  192. package/dist/testing/fault-spec.js +132 -0
  193. package/dist/testing/fault-spec.js.map +1 -0
  194. package/dist/testing/hostile-tree.d.ts.map +1 -1
  195. package/dist/testing/hostile-tree.js +3 -4
  196. package/dist/testing/hostile-tree.js.map +1 -1
  197. package/dist/testing/platform-gates.d.ts +8 -0
  198. package/dist/testing/platform-gates.d.ts.map +1 -1
  199. package/dist/testing/platform-gates.js +24 -0
  200. package/dist/testing/platform-gates.js.map +1 -1
  201. package/dist/testing/temp-dir.d.ts +31 -0
  202. package/dist/testing/temp-dir.d.ts.map +1 -1
  203. package/dist/testing/temp-dir.js +63 -0
  204. package/dist/testing/temp-dir.js.map +1 -1
  205. package/dist/testing/tier-timeout.d.ts +34 -0
  206. package/dist/testing/tier-timeout.d.ts.map +1 -0
  207. package/dist/testing/tier-timeout.js +45 -0
  208. package/dist/testing/tier-timeout.js.map +1 -0
  209. package/dist/testing/tree-snapshot.d.ts +71 -0
  210. package/dist/testing/tree-snapshot.d.ts.map +1 -0
  211. package/dist/testing/tree-snapshot.js +163 -0
  212. package/dist/testing/tree-snapshot.js.map +1 -0
  213. package/dist/testing.d.ts +6 -0
  214. package/dist/testing.d.ts.map +1 -1
  215. package/dist/testing.js +8 -1
  216. package/dist/testing.js.map +1 -1
  217. package/dist/text-file.d.ts +14 -1
  218. package/dist/text-file.d.ts.map +1 -1
  219. package/dist/text-file.js +41 -8
  220. package/dist/text-file.js.map +1 -1
  221. package/dist/timing-dump.d.ts.map +1 -1
  222. package/dist/timing-dump.js +2 -10
  223. package/dist/timing-dump.js.map +1 -1
  224. package/dist/tree-change/apply-decisions.d.ts +101 -0
  225. package/dist/tree-change/apply-decisions.d.ts.map +1 -0
  226. package/dist/tree-change/apply-decisions.js +139 -0
  227. package/dist/tree-change/apply-decisions.js.map +1 -0
  228. package/dist/tree-change/apply.d.ts +98 -0
  229. package/dist/tree-change/apply.d.ts.map +1 -0
  230. package/dist/tree-change/apply.js +364 -0
  231. package/dist/tree-change/apply.js.map +1 -0
  232. package/dist/tree-change/copy-decisions.d.ts +58 -0
  233. package/dist/tree-change/copy-decisions.d.ts.map +1 -0
  234. package/dist/tree-change/copy-decisions.js +66 -0
  235. package/dist/tree-change/copy-decisions.js.map +1 -0
  236. package/dist/tree-change/copy-tree.d.ts +125 -0
  237. package/dist/tree-change/copy-tree.d.ts.map +1 -0
  238. package/dist/tree-change/copy-tree.js +279 -0
  239. package/dist/tree-change/copy-tree.js.map +1 -0
  240. package/dist/tree-change/files.d.ts +167 -0
  241. package/dist/tree-change/files.d.ts.map +1 -0
  242. package/dist/tree-change/files.js +423 -0
  243. package/dist/tree-change/files.js.map +1 -0
  244. package/dist/tree-change/followed-walk.d.ts +54 -0
  245. package/dist/tree-change/followed-walk.d.ts.map +1 -0
  246. package/dist/tree-change/followed-walk.js +81 -0
  247. package/dist/tree-change/followed-walk.js.map +1 -0
  248. package/dist/tree-change/host-chain.d.ts +48 -0
  249. package/dist/tree-change/host-chain.d.ts.map +1 -0
  250. package/dist/tree-change/host-chain.js +85 -0
  251. package/dist/tree-change/host-chain.js.map +1 -0
  252. package/dist/tree-change/identity-compare.d.ts +61 -0
  253. package/dist/tree-change/identity-compare.d.ts.map +1 -0
  254. package/dist/tree-change/identity-compare.js +115 -0
  255. package/dist/tree-change/identity-compare.js.map +1 -0
  256. package/dist/tree-change/identity.d.ts +62 -0
  257. package/dist/tree-change/identity.d.ts.map +1 -0
  258. package/dist/tree-change/identity.js +152 -0
  259. package/dist/tree-change/identity.js.map +1 -0
  260. package/dist/tree-change/index.d.ts +24 -0
  261. package/dist/tree-change/index.d.ts.map +1 -0
  262. package/dist/tree-change/index.js +14 -0
  263. package/dist/tree-change/index.js.map +1 -0
  264. package/dist/tree-change/link-chain.d.ts +180 -0
  265. package/dist/tree-change/link-chain.d.ts.map +1 -0
  266. package/dist/tree-change/link-chain.js +377 -0
  267. package/dist/tree-change/link-chain.js.map +1 -0
  268. package/dist/tree-change/link-containment.d.ts +105 -0
  269. package/dist/tree-change/link-containment.d.ts.map +1 -0
  270. package/dist/tree-change/link-containment.js +196 -0
  271. package/dist/tree-change/link-containment.js.map +1 -0
  272. package/dist/tree-change/link-walk.d.ts +49 -0
  273. package/dist/tree-change/link-walk.d.ts.map +1 -0
  274. package/dist/tree-change/link-walk.js +69 -0
  275. package/dist/tree-change/link-walk.js.map +1 -0
  276. package/dist/tree-change/plan.d.ts +206 -0
  277. package/dist/tree-change/plan.d.ts.map +1 -0
  278. package/dist/tree-change/plan.js +320 -0
  279. package/dist/tree-change/plan.js.map +1 -0
  280. package/dist/tree-change/readable-tree.d.ts +44 -0
  281. package/dist/tree-change/readable-tree.d.ts.map +1 -0
  282. package/dist/tree-change/readable-tree.js +52 -0
  283. package/dist/tree-change/readable-tree.js.map +1 -0
  284. package/dist/tree-change/rollback-error.d.ts +29 -0
  285. package/dist/tree-change/rollback-error.d.ts.map +1 -0
  286. package/dist/tree-change/rollback-error.js +31 -0
  287. package/dist/tree-change/rollback-error.js.map +1 -0
  288. package/dist/tree-change/staging-names.d.ts +27 -0
  289. package/dist/tree-change/staging-names.d.ts.map +1 -0
  290. package/dist/tree-change/staging-names.js +40 -0
  291. package/dist/tree-change/staging-names.js.map +1 -0
  292. package/dist/tree-change/tree-walk.d.ts +81 -0
  293. package/dist/tree-change/tree-walk.d.ts.map +1 -0
  294. package/dist/tree-change/tree-walk.js +212 -0
  295. package/dist/tree-change/tree-walk.js.map +1 -0
  296. package/dist/win32-namespace.d.ts +24 -0
  297. package/dist/win32-namespace.d.ts.map +1 -0
  298. package/dist/win32-namespace.js +31 -0
  299. package/dist/win32-namespace.js.map +1 -0
  300. package/dist/yaml/surgical-yaml.d.ts +12 -3
  301. package/dist/yaml/surgical-yaml.d.ts.map +1 -1
  302. package/dist/yaml/surgical-yaml.js +16 -6
  303. package/dist/yaml/surgical-yaml.js.map +1 -1
  304. package/eslint/README.md +12 -4
  305. package/eslint/rules/fs-binding-scan.cjs +278 -0
  306. package/eslint/rules/no-adhoc-errno.cjs +364 -0
  307. package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
  308. package/eslint/rules/no-blind-catch.cjs +1 -1
  309. package/eslint/rules/no-destructive-fs.cjs +359 -0
  310. package/eslint/rules/no-existssync.cjs +70 -0
  311. package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
  312. package/eslint/rules/no-link-following-fs.cjs +68 -0
  313. package/eslint/rules/no-literal-process-exit.cjs +3 -16
  314. package/eslint/rules/no-literal-test-timeout.cjs +214 -0
  315. package/eslint/rules/no-raw-signal-listener.cjs +224 -0
  316. package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
  317. package/package.json +10 -2
  318. package/dist/errors/errno.d.ts +0 -51
  319. package/dist/errors/errno.d.ts.map +0 -1
  320. package/dist/errors/errno.js +0 -112
  321. package/dist/errors/errno.js.map +0 -1
  322. package/eslint/rules/no-fs-promises-cp.cjs +0 -43
@@ -0,0 +1,320 @@
1
+ /**
2
+ * The plan half of the tree-change primitive: every destructive verb describes
3
+ * what it wants as {@link TreeChange}s, and {@link planTreeChanges} decides, with
4
+ * no side effect, what will actually happen to each — before anything is written.
5
+ *
6
+ * The decisions, in order:
7
+ *
8
+ * 1. **What is there** at each destination (`lstat`; a destination the OS refuses
9
+ * to examine throws, classified `destination`; "absent" is believed only when
10
+ * the parent's listing agrees).
11
+ * 2. **Keep.** A `remove` with nothing there, or whose `keepIfSameAs` names an
12
+ * entry that is `same` **or `unknown`** to it, is kept, with the reason. Only
13
+ * the target's own unexaminability throws, never a sibling's. A `remove` that
14
+ * holds a kept entry (or cannot be proven not to) is kept too: a kept entry is
15
+ * kept whole, never taken away with a directory above it.
16
+ * 3. **Aliasing and containment, by identity.** A `remove` that is the same entry
17
+ * as a `replace` (`plugins/Old` and `plugins/old` on APFS or NTFS, a link and its
18
+ * target), or an earlier `remove`, or lies inside any other change's
19
+ * destination, is `subsumed`: that change parks it. `unknown` is never a merge
20
+ * and never a refusal — the two changes run as two, and a wrong guess fails the
21
+ * apply, which rolls back; refusing would refuse every change on a filesystem
22
+ * that reports `ino` 0. Any other two changes still active whose destinations
23
+ * are one entry or nest are refused ({@link TREE_DESTS_OVERLAP_CODE}): the
24
+ * calling verb's defect, never the user's input.
25
+ * 4. **Ownership** of whatever is there: `must-be-free` refuses anything but an
26
+ * empty directory ({@link TREE_DEST_OCCUPIED_CODE}); `vat-made` refuses what its
27
+ * `recognise` disowns ({@link TREE_DEST_NOT_OWNED_CODE}); `force` and
28
+ * `vat-state` take anything — except that a `replace-file` never takes a directory
29
+ * (or a link to one), under any ownership ({@link TREE_DEST_OCCUPIED_CODE}).
30
+ * 5. **Holding.** A `copy` source, or a `write` fill's declared `reads`, that is the
31
+ * destination or inside it ({@link TREE_DEST_HOLDS_SOURCE_CODE}: the replace
32
+ * would delete it — never asked of a destination that is a link, which holds
33
+ * nothing: only the link is parked), or that holds the destination
34
+ * ({@link TREE_SOURCE_HOLDS_DEST_CODE}: a copy into itself), is refused.
35
+ * 6. **Readable source.** Every `copy` source is proven readable (the one
36
+ * special-file and link policy, `proveTreeReadable`, on the fill's declared `side`).
37
+ *
38
+ * Utils names no refusal code: these are library codes, and the CLI's refusal map
39
+ * decides which refusal each is.
40
+ */
41
+ import { lstatSync, readdirSync } from 'node:fs';
42
+ import { requireConfirmedAbsent } from '../errors/confirmed-absent.js';
43
+ import { isPathAbsentError } from '../errors/errno-table.js';
44
+ import { classifyFsFault, everywhere, withFsFaultSync } from '../errors/fs-fault.js';
45
+ import { VatError } from '../errors/vat-error.js';
46
+ import { forEachInOrder } from '../in-order.js';
47
+ import { followedStatSync } from '../link-following.js';
48
+ import { relativeEscapesRoot, safePath } from '../path-core.js';
49
+ import { identityOracle } from './identity.js';
50
+ import { proveTreeReadable } from './readable-tree.js';
51
+ /** A destination that must be absent or an empty directory already holds something. */
52
+ export const TREE_DEST_OCCUPIED_CODE = 'TREE_DEST_OCCUPIED';
53
+ /** A destination VAT may replace only when it made it holds something its recogniser disowns. */
54
+ export const TREE_DEST_NOT_OWNED_CODE = 'TREE_DEST_NOT_OWNED';
55
+ /**
56
+ * Two changes of one plan whose destinations are one entry or nest (other than a remove the other
57
+ * change takes with it): they cannot be one transaction — the outer park would carry the inner
58
+ * change's staging away. The calling verb built a plan it must not, so it is VAT's defect.
59
+ */
60
+ export const TREE_DESTS_OVERLAP_CODE = 'TREE_DESTS_OVERLAP';
61
+ /** A copy's destination lies inside its source: the copy would copy into itself. */
62
+ export const TREE_SOURCE_HOLDS_DEST_CODE = 'TREE_SOURCE_HOLDS_DEST';
63
+ /** A copy's source is its destination or lies inside it: the replace would delete what it copies. */
64
+ export const TREE_DEST_HOLDS_SOURCE_CODE = 'TREE_DEST_HOLDS_SOURCE';
65
+ function refusal(code, message) {
66
+ return new VatError(code, message);
67
+ }
68
+ /** Why a remove is kept — and the sibling the OS refused to examine, when that is why — or `undefined` when it goes ahead. */
69
+ function keepReason(change, existing, facts) {
70
+ if (change.op !== 'remove')
71
+ return undefined;
72
+ if (existing === 'absent')
73
+ return { reason: 'nothing there' };
74
+ const others = change.keepIfSameAs?.() ?? [];
75
+ // The target's own refusal throws; only an OTHER entry's refusal is `unknown`, a keep.
76
+ if (others.length > 0)
77
+ facts.requireExaminable(change.dest);
78
+ for (const other of others) {
79
+ const verdict = facts.sameEntry(change.dest, other);
80
+ if (verdict === 'same')
81
+ return { reason: `${other} is the same entry` };
82
+ if (verdict === 'unknown') {
83
+ const reason = `could not tell whether ${other} is the same entry`;
84
+ return facts.refusesToExamine(other) ? { reason, unexaminedSibling: other } : { reason };
85
+ }
86
+ }
87
+ return undefined;
88
+ }
89
+ /** The kept entry `planned` (a remove) holds, or may hold: removing it would take that entry with it. */
90
+ function heldKeep(planned, all, facts) {
91
+ if (planned.action !== 'remove')
92
+ return undefined;
93
+ return all.find((other) => other !== planned && other.action === 'keep' && other.existing !== 'absent' && facts.isInside(other.change.dest, planned.change.dest) !== 'outside');
94
+ }
95
+ /**
96
+ * Keep every remove that holds — or cannot be proven not to hold — a kept entry: a
97
+ * kept entry is kept whole, never taken away with a directory above it. Repeated until
98
+ * nothing changes, so a remove holding one that was just kept is kept as well.
99
+ */
100
+ function keepingHolders(planned, facts) {
101
+ const next = planned.map((each) => {
102
+ const held = heldKeep(each, planned, facts);
103
+ return held === undefined ? each : { ...each, action: 'keep', reason: `holds ${held.change.dest}, which is kept` };
104
+ });
105
+ return next.some((each, index) => each !== planned[index]) ? keepingHolders(next, facts) : planned;
106
+ }
107
+ function initialAction(change, existing) {
108
+ if (change.op === 'remove')
109
+ return 'remove';
110
+ return existing === 'absent' ? 'create' : 'replace';
111
+ }
112
+ /** Whether `other` takes `change`'s destination with it: the same entry as a replace or an earlier remove, or an ancestor of it. */
113
+ function subsumes(change, index, other, otherIndex, facts) {
114
+ if (otherIndex === index || other.action === 'keep' || other.existing === 'absent')
115
+ return false;
116
+ const sameAllowed = other.change.op !== 'remove' || otherIndex < index;
117
+ if (sameAllowed && facts.sameEntry(change.change.dest, other.change.dest) === 'same')
118
+ return true;
119
+ return facts.isInside(change.change.dest, other.change.dest) === 'inside';
120
+ }
121
+ function withSubsumption(planned, index, all, facts) {
122
+ if (planned.action !== 'remove')
123
+ return planned;
124
+ const by = all.find((other, otherIndex) => subsumes(planned, index, other, otherIndex, facts));
125
+ return by === undefined ? planned : { ...planned, action: 'subsumed', reason: `parked by ${by.change.op} ${by.change.label}` };
126
+ }
127
+ /** Whether a planned change touches its destination (`keep` and `subsumed` do not). */
128
+ export const isActive = ({ action }) => action === 'create' || action === 'replace' || action === 'remove';
129
+ /**
130
+ * Whether `inner`'s destination is `outer`'s or lies under it. Judged by identity where `outer`
131
+ * exists; an absent destination has no identity, so there it is judged by the spelling.
132
+ */
133
+ function holds(outer, inner, facts) {
134
+ if (outer.existing === 'absent')
135
+ return !relativeEscapesRoot(safePath.relative(outer.change.dest, inner.change.dest));
136
+ return facts.sameEntry(inner.change.dest, outer.change.dest) === 'same' || facts.isInside(inner.change.dest, outer.change.dest) === 'inside';
137
+ }
138
+ /** Refuse two active changes over one tree: the plan is the calling verb's defect. `unknown` is never proof. */
139
+ function checkOverlap(planned, facts) {
140
+ const active = planned.filter((each) => isActive(each));
141
+ for (const [index, first] of active.entries()) {
142
+ const second = active.slice(index + 1).find((other) => holds(first, other, facts) || holds(other, first, facts));
143
+ if (second !== undefined) {
144
+ throw refusal(TREE_DESTS_OVERLAP_CODE, `${first.change.label} (${first.change.dest}) and ${second.change.label} (${second.change.dest}) change one tree; a plan may not overlap its own destinations`);
145
+ }
146
+ }
147
+ }
148
+ /**
149
+ * Refuse a FILE change whose destination is a directory, or a link to one — under every ownership,
150
+ * `force` included: `--force` means "overwrite the file", and a file landing on a directory's name
151
+ * would park the whole tree and delete it.
152
+ */
153
+ function checkFileOverDirectory({ change, existing }, facts) {
154
+ if (change.op !== 'replace-file')
155
+ return;
156
+ const { dest, label } = change;
157
+ if (existing === 'directory') {
158
+ throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} is a directory; a file is never written over a directory`);
159
+ }
160
+ if (existing === 'link' && facts.linksToDirectory(dest)) {
161
+ throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} is a link to a directory; a file is never written over a directory`);
162
+ }
163
+ }
164
+ /** Refuse a destination its ownership does not let this change take. */
165
+ function checkOwnership(planned, facts) {
166
+ const { change, existing, action } = planned;
167
+ if (existing === 'absent' || action === 'keep' || action === 'subsumed')
168
+ return;
169
+ checkFileOverDirectory(planned, facts);
170
+ const { ownership, dest, label } = change;
171
+ // An empty directory is free for a tree that lands in its place, never for a FILE: a file
172
+ // replacing a directory the user made (a folder they named as an output) is a surprise, not a fill.
173
+ const freeDirectory = change.op !== 'replace-file' && existing === 'directory' && facts.isEmptyDirectory(dest);
174
+ // The refusal says what this change would have done to the destination.
175
+ const verb = change.op === 'remove' ? 'remove' : 'replace';
176
+ if (ownership.kind === 'must-be-free' && !freeDirectory) {
177
+ throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} already holds something (a ${existing}); refusing to ${verb} it`);
178
+ }
179
+ if (ownership.kind === 'vat-made') {
180
+ const verdict = ownership.recognise(dest);
181
+ if (!verdict.owned)
182
+ throw refusal(TREE_DEST_NOT_OWNED_CODE, `${label}: refusing to ${verb} ${dest}: ${verdict.reason}`);
183
+ }
184
+ }
185
+ /** The inputs a replace reads, which its destination must neither be, hold, nor lie inside. */
186
+ function readsOf(change) {
187
+ if (change.op !== 'replace')
188
+ return [];
189
+ if (change.fill.from === 'copy')
190
+ return [change.fill.source];
191
+ return change.fill.from === 'write' ? change.fill.reads ?? [] : [];
192
+ }
193
+ function checkHolding(planned, facts) {
194
+ if (planned.action !== 'create' && planned.action !== 'replace')
195
+ return;
196
+ const { dest, label } = planned.change;
197
+ // A destination that is a LINK holds nothing: the replace parks the link itself, never what it points
198
+ // at — so a source the link leads to (a `--dev` link to the build being installed) is not lost with it.
199
+ const destHoldsEntries = planned.existing !== 'link';
200
+ for (const source of readsOf(planned.change)) {
201
+ if (destHoldsEntries && (facts.sameEntry(source, dest) === 'same' || facts.isInside(source, dest) === 'inside')) {
202
+ throw refusal(TREE_DEST_HOLDS_SOURCE_CODE, `${label}: ${source} is ${dest} or lies inside it, so replacing ${dest} would delete it`);
203
+ }
204
+ if (facts.isInside(dest, source) === 'inside') {
205
+ throw refusal(TREE_SOURCE_HOLDS_DEST_CODE, `${label}: ${dest} lies inside ${source}, which it is made from`);
206
+ }
207
+ }
208
+ }
209
+ function describeChange({ change, action, reason }) {
210
+ const line = `${action} ${change.label} ${change.dest}`;
211
+ return reason === undefined ? line : `${line} (${reason})`;
212
+ }
213
+ /**
214
+ * The pure planner: every decision of {@link planTreeChanges} but the source
215
+ * proof, made from `facts`.
216
+ *
217
+ * @throws VatError coded `TREE_DESTS_OVERLAP` (a defect in the caller's plan), `TREE_DEST_OCCUPIED`,
218
+ * `TREE_DEST_NOT_OWNED`, `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; whatever `facts` throws
219
+ */
220
+ export function planFromFacts(changes, facts) {
221
+ const drafted = changes.map((change) => {
222
+ const existing = facts.existing(change.dest);
223
+ const kept = keepReason(change, existing, facts);
224
+ return kept === undefined ? { change, existing, action: initialAction(change, existing) } : { change, existing, action: 'keep', ...kept };
225
+ });
226
+ const kept = keepingHolders(drafted, facts);
227
+ const planned = kept.map((draft, index) => withSubsumption(draft, index, kept, facts));
228
+ checkOverlap(planned, facts);
229
+ for (const each of planned)
230
+ checkOwnership(each, facts);
231
+ for (const each of planned)
232
+ checkHolding(each, facts);
233
+ return { changes: planned, describe: () => planned.map((each) => describeChange(each)) };
234
+ }
235
+ const EXAMINE_DEST = { side: 'destination', action: 'examine the destination' };
236
+ function kindOf(dest) {
237
+ let stats;
238
+ try {
239
+ stats = lstatSync(dest);
240
+ }
241
+ catch (error) {
242
+ if (!isPathAbsentError(error))
243
+ throw classifyFsFault(error, { ...EXAMINE_DEST, path: dest });
244
+ // Absent only when the parent's listing agrees: a refused probe of an entry that is there is a fault.
245
+ requireConfirmedAbsent(dest, error, { action: EXAMINE_DEST.action, path: dest }, { follows: false, sideOf: everywhere(EXAMINE_DEST.side) });
246
+ return 'absent';
247
+ }
248
+ if (stats.isSymbolicLink())
249
+ return 'link';
250
+ if (stats.isDirectory())
251
+ return 'directory';
252
+ return stats.isFile() ? 'file' : 'special';
253
+ }
254
+ /** Whether the link at `dest` leads to a directory: a dangling link leads nowhere; a target the OS refuses to examine is a fault. */
255
+ function linksToDirectory(dest) {
256
+ try {
257
+ // `dest` is the destination the verb names: the operator's own path, its own link the edge.
258
+ return followedStatSync(dest, dest)?.isDirectory() ?? false;
259
+ }
260
+ catch (error) {
261
+ if (isPathAbsentError(error))
262
+ return false;
263
+ throw classifyFsFault(error, { ...EXAMINE_DEST, path: dest });
264
+ }
265
+ }
266
+ /**
267
+ * What is at `dest` NOW, for the apply to hold against what the plan decided
268
+ * (`changedSincePlan`). The emptiness of a directory is read only where the plan took it as free.
269
+ *
270
+ * @throws FsFaultError side `destination` when the OS refuses to examine or list it
271
+ */
272
+ export function destinationNow(dest, planned) {
273
+ const kind = kindOf(dest);
274
+ const askEmpty = kind === 'directory' && planned.existing === 'directory' && planned.change.ownership.kind === 'must-be-free';
275
+ return { kind, emptyDirectory: askEmpty && withFsFaultSync({ side: 'destination', action: 'list the destination', path: dest }, () => readdirSync(dest).length === 0) };
276
+ }
277
+ /** The live facts of one plan: every identity question asks each entry once, so no two decisions see the filesystem differently. */
278
+ function liveFacts() {
279
+ const oracle = identityOracle();
280
+ return {
281
+ existing: kindOf,
282
+ isEmptyDirectory: (dest) => withFsFaultSync({ side: 'destination', action: 'list the destination', path: dest }, () => readdirSync(dest).length === 0),
283
+ linksToDirectory,
284
+ sameEntry: oracle.sameEntry,
285
+ isInside: oracle.isInside,
286
+ requireExaminable: (dest) => {
287
+ oracle.identities(dest);
288
+ },
289
+ refusesToExamine: (entry) => oracle.refuses(entry),
290
+ };
291
+ }
292
+ /** Prove a copy source readable, as the copy will read it. */
293
+ function proveSource({ change, action }) {
294
+ if (change.op !== 'replace' || change.fill.from !== 'copy' || (action !== 'create' && action !== 'replace'))
295
+ return Promise.resolve();
296
+ const { source, links, side, filter } = change.fill;
297
+ return proveTreeReadable(source, { links, side, ...(filter === undefined ? {} : { filter }) });
298
+ }
299
+ /**
300
+ * Decide every change against the filesystem as it is, with no side effect. Each
301
+ * destination is taken in its canonical spelling (`safePath.resolve`: absolute,
302
+ * `.`/`..` resolved, forward slashes); `describe()` prints that spelling.
303
+ * Hand the result to `applyTreePlan`; print `describe()` for a dry run — the two
304
+ * cannot disagree, because the apply does exactly what the plan says.
305
+ *
306
+ * @param changes - What the verb wants, in the order it wants it
307
+ * @throws VatError coded `TREE_DEST_OCCUPIED`, `TREE_DEST_NOT_OWNED`,
308
+ * `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; `FsFaultError` side
309
+ * `destination` for a destination the OS refuses to examine, on the fill's
310
+ * declared side for a copy source it refuses to read
311
+ */
312
+ export async function planTreeChanges(changes) {
313
+ // One canonical spelling of every destination (absolute, normalized, forward slashes), which the
314
+ // plan, the dry run and the apply — down to the parents a create makes and removes — all use.
315
+ const canonical = changes.map((change) => ({ ...change, dest: safePath.resolve(change.dest) }));
316
+ const plan = planFromFacts(canonical, liveFacts());
317
+ await forEachInOrder(plan.changes, proveSource);
318
+ return plan;
319
+ }
320
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/tree-change/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The readable-tree proof: before a verb writes anything for a tree it is about to
3
+ * read whole, it proves every entry readable, so an unreadable input is refused as
4
+ * the input it is rather than surfacing half-way through as an output failure.
5
+ */
6
+ import type { FsSide } from '../errors/fs-fault.js';
7
+ import { type TreeWalkOptions } from './tree-walk.js';
8
+ /** What {@link proveTreeReadable} is told: the walk's link policy and filter, and the side its faults are on. */
9
+ export interface ProveTreeReadableOptions extends TreeWalkOptions {
10
+ /**
11
+ * The side of the verb the tree is on: an input (`source`), VAT's staging
12
+ * (`environment`), or a copy already made into user state (`destination`).
13
+ */
14
+ readonly side: FsSide;
15
+ }
16
+ /**
17
+ * Prove the directory `root` readable: every directory listed, every regular file
18
+ * opened without blocking and judged by `fstat` on that handle, each entry held to
19
+ * the one special-file and link policy `copyTree` copies by (see `tree-walk.ts`).
20
+ * A root that is not a directory is refused by its listing (`ENOTDIR`), as
21
+ * `copyTree` refuses it. Each file is opened, never `access`ed: on Windows `access`
22
+ * does not consult ACLs.
23
+ *
24
+ * @param root - The directory the caller is about to read
25
+ * @param options - `links` and `filter` as the read will use them, and the `side` the tree is on
26
+ * @throws FsFaultError on `options.side` (origin `content`) naming the first entry
27
+ * the OS refused, in listing order — a named pipe, socket or device as class
28
+ * `wrong-type`; `CopyLinkEscapesSourceError` / `DirectoryWalkRevisitedError` for
29
+ * a link out of, or back into, a `follow-contained` tree
30
+ */
31
+ export declare function proveTreeReadable(root: string, options: ProveTreeReadableOptions): Promise<void>;
32
+ /**
33
+ * Read a regular file whole, through a handle opened without blocking and judged
34
+ * by `fstat` on that handle: a named pipe, socket or device is refused before a
35
+ * byte is read, never waited on.
36
+ *
37
+ * @param path - The file to read
38
+ * @returns Its bytes
39
+ * @throws an `EFTYPE` errno for a special file, `EISDIR` for a directory, and
40
+ * whatever `open` / `read` throw otherwise — raw, for the caller to classify on
41
+ * the side it knows the file is on
42
+ */
43
+ export declare function readRegularFile(path: string): Promise<Buffer>;
44
+ //# sourceMappingURL=readable-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readable-tree.d.ts","sourceRoot":"","sources":["../../src/tree-change/readable-tree.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EAAoB,KAAK,eAAe,EAAY,MAAM,gBAAgB,CAAC;AAElF,iHAAiH;AACjH,MAAM,WAAW,wBAAyB,SAAQ,eAAe;IAC/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtG;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnE"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The readable-tree proof: before a verb writes anything for a tree it is about to
3
+ * read whole, it proves every entry readable, so an unreadable input is refused as
4
+ * the input it is rather than surfacing half-way through as an output failure.
5
+ */
6
+ import { openForReading } from '../text-file.js';
7
+ import { walkTree } from './tree-walk.js';
8
+ /** Proving reads nothing past the open: the open, the `fstat` and the listing are the proof. */
9
+ const PROVE = {
10
+ directory: () => Promise.resolve(),
11
+ file: () => Promise.resolve(),
12
+ link: () => Promise.resolve(),
13
+ };
14
+ /**
15
+ * Prove the directory `root` readable: every directory listed, every regular file
16
+ * opened without blocking and judged by `fstat` on that handle, each entry held to
17
+ * the one special-file and link policy `copyTree` copies by (see `tree-walk.ts`).
18
+ * A root that is not a directory is refused by its listing (`ENOTDIR`), as
19
+ * `copyTree` refuses it. Each file is opened, never `access`ed: on Windows `access`
20
+ * does not consult ACLs.
21
+ *
22
+ * @param root - The directory the caller is about to read
23
+ * @param options - `links` and `filter` as the read will use them, and the `side` the tree is on
24
+ * @throws FsFaultError on `options.side` (origin `content`) naming the first entry
25
+ * the OS refused, in listing order — a named pipe, socket or device as class
26
+ * `wrong-type`; `CopyLinkEscapesSourceError` / `DirectoryWalkRevisitedError` for
27
+ * a link out of, or back into, a `follow-contained` tree
28
+ */
29
+ export async function proveTreeReadable(root, options) {
30
+ await walkTree(root, options, options.side, PROVE);
31
+ }
32
+ /**
33
+ * Read a regular file whole, through a handle opened without blocking and judged
34
+ * by `fstat` on that handle: a named pipe, socket or device is refused before a
35
+ * byte is read, never waited on.
36
+ *
37
+ * @param path - The file to read
38
+ * @returns Its bytes
39
+ * @throws an `EFTYPE` errno for a special file, `EISDIR` for a directory, and
40
+ * whatever `open` / `read` throw otherwise — raw, for the caller to classify on
41
+ * the side it knows the file is on
42
+ */
43
+ export async function readRegularFile(path) {
44
+ const handle = await openForReading(path);
45
+ try {
46
+ return await handle.readFile();
47
+ }
48
+ finally {
49
+ await handle.close();
50
+ }
51
+ }
52
+ //# sourceMappingURL=readable-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readable-tree.js","sourceRoot":"","sources":["../../src/tree-change/readable-tree.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAA0C,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAWlF,gGAAgG;AAChG,MAAM,KAAK,GAAgB;IACzB,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAClC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC7B,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAiC;IACrF,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The one shape of a change that failed and could not be fully undone. Pure: it only words
3
+ * what the rollback found.
4
+ */
5
+ import { VatError } from '../errors/vat-error.js';
6
+ /** A failed change that could not be fully undone: a previous entry not back at its destination, or a new one not off it. */
7
+ export declare const TREE_ROLLBACK_INCOMPLETE_CODE = "TREE_ROLLBACK_INCOMPLETE";
8
+ /** One destination a rollback could not restore: where its previous entry is, if it had one, and why. */
9
+ export interface TreeRollbackStranded {
10
+ readonly dest: string;
11
+ readonly parked: string | undefined;
12
+ readonly why: string;
13
+ }
14
+ /**
15
+ * A change failed, and the rollback could not undo all of it: each previous entry
16
+ * not back at its destination is under its parked name, never deleted; a create's
17
+ * new entry — or a rewritten file's new content — may still be at its destination.
18
+ * `cause` is the failure that started the rollback. The one shape of
19
+ * {@link TREE_ROLLBACK_INCOMPLETE_CODE}: a caller that undoes writes of its own (a
20
+ * registry edit run as `afterSwap`) throws it too, its entries with no `parked`.
21
+ */
22
+ export declare class TreeRollbackIncompleteError extends VatError {
23
+ /** Where each previous entry that is not at its destination now is. */
24
+ readonly parked: readonly string[];
25
+ /** Every destination the rollback could not restore. */
26
+ readonly stranded: readonly TreeRollbackStranded[];
27
+ constructor(original: unknown, stranded: readonly TreeRollbackStranded[]);
28
+ }
29
+ //# sourceMappingURL=rollback-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback-error.d.ts","sourceRoot":"","sources":["../../src/tree-change/rollback-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,6HAA6H;AAC7H,eAAO,MAAM,6BAA6B,6BAA6B,CAAC;AAExE,yGAAyG;AACzG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;GAOG;AACH,qBAAa,2BAA4B,SAAQ,QAAQ;IACvD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,CAAC;gBAEvC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,oBAAoB,EAAE;CAMzE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The one shape of a change that failed and could not be fully undone. Pure: it only words
3
+ * what the rollback found.
4
+ */
5
+ import { VatError } from '../errors/vat-error.js';
6
+ /** A failed change that could not be fully undone: a previous entry not back at its destination, or a new one not off it. */
7
+ export const TREE_ROLLBACK_INCOMPLETE_CODE = 'TREE_ROLLBACK_INCOMPLETE';
8
+ function strandedSentence({ dest, parked, why }) {
9
+ return parked === undefined ? `the new content at ${dest} could not be undone (${why})` : `the previous content of ${dest} is at ${parked} (${why})`;
10
+ }
11
+ /**
12
+ * A change failed, and the rollback could not undo all of it: each previous entry
13
+ * not back at its destination is under its parked name, never deleted; a create's
14
+ * new entry — or a rewritten file's new content — may still be at its destination.
15
+ * `cause` is the failure that started the rollback. The one shape of
16
+ * {@link TREE_ROLLBACK_INCOMPLETE_CODE}: a caller that undoes writes of its own (a
17
+ * registry edit run as `afterSwap`) throws it too, its entries with no `parked`.
18
+ */
19
+ export class TreeRollbackIncompleteError extends VatError {
20
+ /** Where each previous entry that is not at its destination now is. */
21
+ parked;
22
+ /** Every destination the rollback could not restore. */
23
+ stranded;
24
+ constructor(original, stranded) {
25
+ const where = stranded.map((s) => strandedSentence(s)).join('; ');
26
+ super(TREE_ROLLBACK_INCOMPLETE_CODE, `${original instanceof Error ? original.message : String(original)}; and the change could not be undone: ${where}`, { cause: original });
27
+ this.parked = stranded.flatMap((s) => (s.parked === undefined ? [] : [s.parked]));
28
+ this.stranded = stranded;
29
+ }
30
+ }
31
+ //# sourceMappingURL=rollback-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollback-error.js","sourceRoot":"","sources":["../../src/tree-change/rollback-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,6HAA6H;AAC7H,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AASxE,SAAS,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAwB;IACnE,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,IAAI,yBAAyB,GAAG,GAAG,CAAC,CAAC,CAAC,2BAA2B,IAAI,UAAU,MAAM,KAAK,GAAG,GAAG,CAAC;AACvJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,2BAA4B,SAAQ,QAAQ;IACvD,uEAAuE;IAC9D,MAAM,CAAoB;IACnC,wDAAwD;IAC/C,QAAQ,CAAkC;IAEnD,YAAY,QAAiB,EAAE,QAAyC;QACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,KAAK,CAAC,6BAA6B,EAAE,GAAG,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,yCAAyC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9K,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The names the tree-change primitive gives what it stages, parks and discards beside a
3
+ * destination, and the two questions a listing asks of a name. Pure: no filesystem call.
4
+ */
5
+ /** What a previous entry is renamed to while its replacement goes in: `<staged>.previous`. */
6
+ export declare const PARKED_SUFFIX = ".previous";
7
+ /**
8
+ * Whether a directory entry is the primitive's own: a staged tree, a parked
9
+ * previous one, or a discarded one, left beside a destination by a crash or by a
10
+ * removal the OS refused (`.<base>.vat-staged-<random>[.previous]`). A listing of
11
+ * the directory a change sits in must never read one as a sibling it holds.
12
+ *
13
+ * @param name - A directory entry's name
14
+ */
15
+ export declare function isTreeChangeResidue(name: string): boolean;
16
+ /**
17
+ * Whether a directory entry is a PARKED previous entry: the one residue that is safe to sweep, since a
18
+ * staged entry may be another process's change still in flight.
19
+ *
20
+ * @param name - A directory entry's name
21
+ */
22
+ export declare function isParkedTreeEntry(name: string): boolean;
23
+ /** The `mkdtemp` prefix of a staged tree beside `dest`. */
24
+ export declare function stagingPrefix(dest: string): string;
25
+ /** A fresh staging name beside `dest`, for an entry `mkdtemp` does not make: a file, a link, or a `write` fill's directory (made with the caller's mode). */
26
+ export declare function stagingName(dest: string): string;
27
+ //# sourceMappingURL=staging-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staging-names.d.ts","sourceRoot":"","sources":["../../src/tree-change/staging-names.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,8FAA8F;AAC9F,eAAO,MAAM,aAAa,cAAc,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,6JAA6J;AAC7J,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The names the tree-change primitive gives what it stages, parks and discards beside a
3
+ * destination, and the two questions a listing asks of a name. Pure: no filesystem call.
4
+ */
5
+ import { randomBytes } from 'node:crypto';
6
+ import path from 'node:path';
7
+ import { safePath } from '../path-core.js';
8
+ /** Every staged, parked or discarded entry is named `.<base>.vat-staged-<random>[suffix]` beside the destination. */
9
+ const STAGED_INFIX = '.vat-staged-';
10
+ /** What a previous entry is renamed to while its replacement goes in: `<staged>.previous`. */
11
+ export const PARKED_SUFFIX = '.previous';
12
+ /**
13
+ * Whether a directory entry is the primitive's own: a staged tree, a parked
14
+ * previous one, or a discarded one, left beside a destination by a crash or by a
15
+ * removal the OS refused (`.<base>.vat-staged-<random>[.previous]`). A listing of
16
+ * the directory a change sits in must never read one as a sibling it holds.
17
+ *
18
+ * @param name - A directory entry's name
19
+ */
20
+ export function isTreeChangeResidue(name) {
21
+ return name.startsWith('.') && name.includes(STAGED_INFIX);
22
+ }
23
+ /**
24
+ * Whether a directory entry is a PARKED previous entry: the one residue that is safe to sweep, since a
25
+ * staged entry may be another process's change still in flight.
26
+ *
27
+ * @param name - A directory entry's name
28
+ */
29
+ export function isParkedTreeEntry(name) {
30
+ return isTreeChangeResidue(name) && name.endsWith(PARKED_SUFFIX);
31
+ }
32
+ /** The `mkdtemp` prefix of a staged tree beside `dest`. */
33
+ export function stagingPrefix(dest) {
34
+ return safePath.join(path.dirname(dest), `.${path.basename(dest)}${STAGED_INFIX}`);
35
+ }
36
+ /** A fresh staging name beside `dest`, for an entry `mkdtemp` does not make: a file, a link, or a `write` fill's directory (made with the caller's mode). */
37
+ export function stagingName(dest) {
38
+ return `${stagingPrefix(dest)}${randomBytes(4).toString('hex')}`;
39
+ }
40
+ //# sourceMappingURL=staging-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staging-names.js","sourceRoot":"","sources":["../../src/tree-change/staging-names.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,qHAAqH;AACrH,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AACnE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,6JAA6J;AAC7J,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AACnE,CAAC"}