@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,196 @@
1
+ /**
2
+ * The rule for a tree whose links are KEPT: none of them may lead outside it.
3
+ *
4
+ * A copy that preserves links carries each link's text verbatim, so a link out of the tree
5
+ * arrives pointing at whatever stands at that path beside the COPY — the installing user's own
6
+ * files — and every reader of the copy that follows links reads them as the tree's content. So a
7
+ * tree that came from somewhere VAT does not own (a cloned repository, a directory an operator
8
+ * installs from) is checked here before anything reads it.
9
+ */
10
+ import { readdirSync, readlinkSync } from 'node:fs';
11
+ import { dirname } from 'node:path';
12
+ import { fsFaultOf } from '../errors/errno-table.js';
13
+ import { assertUnresolvableLink, withFsFaultSync } from '../errors/fs-fault.js';
14
+ import { VatError } from '../errors/vat-error.js';
15
+ import { requireFollowedStatSync } from '../link-following.js';
16
+ import { canonicalPath, isUnderRoot } from '../path-containment.js';
17
+ import { safePath, toForwardSlash } from '../path-core.js';
18
+ import { COPY_LINK_ESCAPES_SOURCE_CODE } from './followed-walk.js';
19
+ import { hostPlaceKind, walkHostChain } from './host-chain.js';
20
+ import { MAX_LINK_HOPS } from './link-chain.js';
21
+ import { linkTextRefusal } from './link-walk.js';
22
+ /**
23
+ * Where `candidate` stands relative to `root`, on real paths. The root itself counts as inside
24
+ * (`isUnderRoot` calls a root `outside` itself, which is right for a sink and wrong here).
25
+ *
26
+ * @param root - The tree
27
+ * @param candidate - A path in any spelling, existing or not
28
+ */
29
+ export function treeContainment(root, candidate) {
30
+ if (canonicalPath(candidate, root) === canonicalPath(root, root))
31
+ return 'inside';
32
+ return isUnderRoot(root, candidate);
33
+ }
34
+ /**
35
+ * Where `path` leads in an UNTRUSTED tree, read link by link by the one resolver — `lstat` and
36
+ * `readlink` alone — before anything resolves it: an absolute, drive-relative or network text on
37
+ * the way (under either flavor: the tree may be read on the other host), a climb above `root`,
38
+ * or a chain longer than the hop limit that is not a cycle, is refused; a cycle leads to
39
+ * nothing, as a dangling link does; nothing outside `root` is probed.
40
+ *
41
+ * @param root - The tree, spelled as `path` spells it (its own components are never probed)
42
+ * @param path - A path below it (a link, or a subpath the operator named)
43
+ * @param flavor - Whose kernel will resolve it: bound from the host at the edge
44
+ * @throws The raw errno of an `lstat` or `readlink` the OS refused, for the caller to classify
45
+ */
46
+ export function treeChain(root, path, flavor) {
47
+ return walkHostChain(path, { flavor, root: safePath.resolve(root), outside: 'refuse' });
48
+ }
49
+ /** The refusal of the link at `link`, named relative to the tree as the filesystem spells it. */
50
+ function linkRefusal(check, link, what) {
51
+ return new VatError(COPY_LINK_ESCAPES_SOURCE_CODE, `Refusing ${check.tree}: ${safePath.relative(canonicalPath(check.root, check.root), link)} is a symbolic link ${what}. ` +
52
+ `${check.consequence} — replace the link with the files, or point it inside with a relative target.`);
53
+ }
54
+ /** How a refused chain reads, after "is a symbolic link "; `text` is the link's own. */
55
+ function describedChainRefusal(check, text, refusal) {
56
+ switch (refusal.why) {
57
+ case 'network':
58
+ case 'absolute':
59
+ case 'drive-relative':
60
+ return `whose target leads through an absolute path (${refusal.text}), which names a place outside ${check.within}`;
61
+ case 'climbs-out':
62
+ return `to a path outside ${check.within} (${text})`;
63
+ case 'hop-limit':
64
+ return `whose target starts a chain of more than ${MAX_LINK_HOPS.toString()} symbolic links that is not a cycle, so where it ends cannot be shown`;
65
+ case 'unportable':
66
+ return `whose target names a component no Windows host can spell (${refusal.text}): a NUL, or a name ending in a dot or a space that Windows strips`;
67
+ }
68
+ }
69
+ /** How each refusal of a link's text reads, after "is a symbolic link ". */
70
+ function describedRefusal(check, text, refusal) {
71
+ switch (refusal.why) {
72
+ case 'absolute':
73
+ return `with an absolute target (${text}), which would still point there once ${check.within} is copied`;
74
+ case 'climbs-out':
75
+ return `to a path outside ${check.within} (${text})`;
76
+ case 'through-link':
77
+ return `whose target passes through another symbolic link (${refusal.at}), so where ${text} leads depends on where ${check.within} is copied`;
78
+ case 'dangling':
79
+ return `to nothing (${refusal.at} does not exist), so where it would lead once ${check.within} is copied cannot be shown`;
80
+ }
81
+ }
82
+ /**
83
+ * The last lock on a link of a tree that will be relocated: the KERNEL's resolution of it, at
84
+ * the source, must exist and be inside the tree. Asked only once the chain from the link has been
85
+ * read and shown local ({@link treeChain}). The walk of the text ({@link linkTextRefusal}) shows
86
+ * the text stays inside wherever the tree goes; this shows the walk's reading of the text is the
87
+ * kernel's. A probe that failed because the link resolves to nothing
88
+ * (`isUnresolvableLinkError`: it dangles, it is part of a cycle, Windows cannot follow its reparse
89
+ * data) is a refusal of the link on every host: nothing can be shown about where it leads. Every
90
+ * other failure — the OS declining (`EACCES`, `EPERM`), running short (`EMFILE`), held (`EBUSY`) or
91
+ * unable to look (`EIO`, `ETIMEDOUT`, Windows' ambiguous `UNKNOWN`) — says nothing about the link:
92
+ * a fault on the tree, thrown classified by the one write-lane catch body
93
+ * (`assertUnresolvableLink`, on the side the caller names in `check.side`, found as content).
94
+ *
95
+ * Exported apart from the walk so the lock can be shown to hold ALONE: with a correct walk, a
96
+ * link the kernel resolves outside never reaches it from a real tree.
97
+ *
98
+ * @param root - The tree, as its canonical path
99
+ * @param link - The link, spelled by its real directory
100
+ * @param text - The link's own text, for the refusal
101
+ * @returns Why the link is refused, after "is a symbolic link ", or `undefined` when the kernel agrees
102
+ * @throws FsFaultError on `check.side` for a probe that failed for any other reason; an error that carries no errno as it was
103
+ */
104
+ export function kernelRefusal(check, root, link, text) {
105
+ try {
106
+ // Followed to the end, as a reader of the link would: this fails where the kernel finds nothing.
107
+ requireFollowedStatSync(link, root);
108
+ }
109
+ catch (error) {
110
+ const fault = fsFaultOf(error);
111
+ if (fault === undefined)
112
+ throw error;
113
+ // The action names the link: a refusal on its chain names the place refused, which may be its target.
114
+ assertUnresolvableLink(error, { side: check.side, origin: 'content', action: `resolve the symbolic link ${link}`, path: link });
115
+ return `to nothing (${text} does not resolve: ${fault.errno}), so where it would lead once ${check.within} is copied cannot be shown`;
116
+ }
117
+ // It resolves, so its canonical path is its real one.
118
+ return treeContainment(root, link) === 'inside' ? undefined : `to a path outside ${check.within} (${text} resolves outside it)`;
119
+ }
120
+ /**
121
+ * Judge the link at `link` under `check.placement` — spelled by its REAL directory, so its text is
122
+ * read from where the link is, never from an alias of that directory, which reads a climbing text
123
+ * as staying inside.
124
+ *
125
+ * Either way the CHAIN is read first ({@link treeChain}) and the kernel last, so nothing the text
126
+ * names is resolved until every text on the way has been read. Relocated, the text is walked
127
+ * through no link before that ({@link linkTextRefusal}) and the kernel is asked after it
128
+ * ({@link kernelRefusal}); in place, the kernel's real path must be inside.
129
+ */
130
+ function judgeLink(check, root, link) {
131
+ const text = readlinkSync(link);
132
+ if (check.placement === 'relocated') {
133
+ const below = safePath.relative(root, dirname(link));
134
+ const textRefusal = linkTextRefusal(below === '' ? [] : toForwardSlash(below).split('/'), text, (place) => hostPlaceKind(safePath.join(root, ...place)), check.flavor);
135
+ if (textRefusal !== undefined)
136
+ return { refusal: describedRefusal(check, text, textRefusal) };
137
+ }
138
+ const chain = treeChain(root, link, check.flavor);
139
+ if (chain.kind === 'refused')
140
+ return { refusal: describedChainRefusal(check, text, chain.refusal) };
141
+ if (check.placement === 'relocated') {
142
+ // Every text on the way was read and is local: the kernel may now be asked, as the last lock.
143
+ const refusal = kernelRefusal(check, root, link, text);
144
+ if (refusal !== undefined)
145
+ return { refusal };
146
+ }
147
+ else if (chain.kind === 'nothing') {
148
+ // A dangle or a cycle, every text read: in place it reads nothing.
149
+ return { enter: undefined };
150
+ }
151
+ else if (treeContainment(root, link) === 'outside') {
152
+ return { refusal: `to a path outside ${check.within}` };
153
+ }
154
+ return { enter: chain.kind === 'resolved' && chain.final === 'directory' ? canonicalPath(link, root) : undefined };
155
+ }
156
+ /**
157
+ * Refuse a tree holding a symbolic link that may not be kept: one leading outside it, and — for a
158
+ * tree that will be relocated — one whose text cannot be shown to stay inside wherever the tree
159
+ * goes ({@link LinkContainmentCheck.placement}). Every directory is walked at its REAL path, once: a
160
+ * link that stays inside is followed to the directory it names, so a link into a sibling cannot
161
+ * smuggle out a second hop, a link cycle ends, and a directory first reached through an alias still
162
+ * has its own links judged where they live.
163
+ *
164
+ * @param check - The tree, where in it to look, and how the refusal names it
165
+ * @throws VatError `COPY_LINK_ESCAPES_SOURCE` — the tree's own link, so the input's refusal —
166
+ * naming the first link found leading out; FsFaultError on `check.side`, found as content, for
167
+ * every probe of the tree the OS refused (a listing, an `lstat`, a `readlink`, a relocated link's
168
+ * target)
169
+ */
170
+ export function refuseEscapingLinks(check) {
171
+ withFsFaultSync({ side: check.side, origin: 'content', action: `examine the links of ${check.tree}`, path: check.subtree ?? check.root }, () => {
172
+ const skipped = check.skip === undefined ? undefined : canonicalPath(check.skip, check.root);
173
+ const visited = new Set();
174
+ const root = canonicalPath(check.root, check.root);
175
+ const pending = [canonicalPath(check.subtree ?? check.root, check.root)];
176
+ for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
177
+ if (dir === skipped || visited.has(dir))
178
+ continue;
179
+ visited.add(dir);
180
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
181
+ const entryPath = safePath.join(dir, entry.name);
182
+ const next = entry.isSymbolicLink() ? enterOrThrow(check, root, entryPath) : entry.isDirectory() && entryPath;
183
+ if (typeof next === 'string')
184
+ pending.push(next);
185
+ }
186
+ }
187
+ });
188
+ }
189
+ /** The directory a kept link leads to, for the walk to enter; throws the link's refusal. */
190
+ function enterOrThrow(check, root, link) {
191
+ const verdict = judgeLink(check, root, link);
192
+ if ('refusal' in verdict)
193
+ throw linkRefusal(check, link, verdict.refusal);
194
+ return verdict.enter;
195
+ }
196
+ //# sourceMappingURL=link-containment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-containment.js","sourceRoot":"","sources":["../../src/tree-change/link-containment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where a symbolic link's text leads, decided by WALKING it — the rule for a link in a tree that
3
+ * will be RELOCATED (an install copies the tree, each link's text verbatim). Pure: the caller says
4
+ * what stands at each place.
5
+ *
6
+ * A lexical resolve cancels `x/..` without asking what `x` is, so `l -> up/../../../secret` beside
7
+ * `up -> ../..` reads as inside the tree while the kernel follows `up` first and lands three
8
+ * levels above it. And where such a link dangles at the source, its real path proves nothing: it
9
+ * resolves once the tree is copied somewhere those levels exist. So nothing is assumed:
10
+ *
11
+ * - the text is relative — an absolute one points at the same place wherever the copy lands;
12
+ * - walked one segment at a time from the link's own directory, the place never rises above the
13
+ * tree's root — not even to come back in by the tree's own name, which the copy may not keep;
14
+ * - no component the walk passes THROUGH is a link: a link may point at something, never through
15
+ * another link, whose own text decides where the rest of the walk goes;
16
+ * - the place it ends at exists. A link that dangles is refused, because nothing can be proved
17
+ * about where it lands after the copy.
18
+ *
19
+ * The walk is a MODEL of the kernel's resolution, and a model can be wrong (it once split a text
20
+ * on a backslash the POSIX kernel reads as part of a name). So its caller holds two more locks
21
+ * beside it — the one link-chain resolver (`link-chain.ts`), which reads the chain from the link to
22
+ * its end before anything follows it, and then the kernel's own resolution of the link at the
23
+ * source — and a link is kept only when all three agree.
24
+ */
25
+ import type { PathFlavor, PlaceKind } from './link-chain.js';
26
+ /** Why a link's text is refused, with the place (root-relative, `/`-joined) the walk stopped at. */
27
+ export type LinkTextRefusal = {
28
+ readonly why: 'absolute';
29
+ } | {
30
+ readonly why: 'climbs-out';
31
+ } | {
32
+ readonly why: 'through-link';
33
+ readonly at: string;
34
+ } | {
35
+ readonly why: 'dangling';
36
+ readonly at: string;
37
+ };
38
+ /**
39
+ * Walk a link's text from the directory the link is in.
40
+ *
41
+ * @param linkDir - The link's REAL directory, as segments below the tree's root (`[]`: the root itself)
42
+ * @param text - The link's text, verbatim
43
+ * @param kindAt - What stands at a place below the root, never following a link there
44
+ * @param flavor - Whose kernel resolves the link: its separators split the text
45
+ * @returns `undefined` when the text leads to something that exists inside the tree, by a walk
46
+ * that passes through no link; otherwise why it is refused
47
+ */
48
+ export declare function linkTextRefusal(linkDir: readonly string[], text: string, kindAt: (place: readonly string[]) => PlaceKind, flavor: PathFlavor): LinkTextRefusal | undefined;
49
+ //# sourceMappingURL=link-walk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-walk.d.ts","sourceRoot":"","sources":["../../src/tree-change/link-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE7D,oGAAoG;AACpG,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAgBtD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,eAAe,GAAG,SAAS,CAiB1K"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Where a symbolic link's text leads, decided by WALKING it — the rule for a link in a tree that
3
+ * will be RELOCATED (an install copies the tree, each link's text verbatim). Pure: the caller says
4
+ * what stands at each place.
5
+ *
6
+ * A lexical resolve cancels `x/..` without asking what `x` is, so `l -> up/../../../secret` beside
7
+ * `up -> ../..` reads as inside the tree while the kernel follows `up` first and lands three
8
+ * levels above it. And where such a link dangles at the source, its real path proves nothing: it
9
+ * resolves once the tree is copied somewhere those levels exist. So nothing is assumed:
10
+ *
11
+ * - the text is relative — an absolute one points at the same place wherever the copy lands;
12
+ * - walked one segment at a time from the link's own directory, the place never rises above the
13
+ * tree's root — not even to come back in by the tree's own name, which the copy may not keep;
14
+ * - no component the walk passes THROUGH is a link: a link may point at something, never through
15
+ * another link, whose own text decides where the rest of the walk goes;
16
+ * - the place it ends at exists. A link that dangles is refused, because nothing can be proved
17
+ * about where it lands after the copy.
18
+ *
19
+ * The walk is a MODEL of the kernel's resolution, and a model can be wrong (it once split a text
20
+ * on a backslash the POSIX kernel reads as part of a name). So its caller holds two more locks
21
+ * beside it — the one link-chain resolver (`link-chain.ts`), which reads the chain from the link to
22
+ * its end before anything follows it, and then the kernel's own resolution of the link at the
23
+ * source — and a link is kept only when all three agree.
24
+ */
25
+ /**
26
+ * What separates the components of a link's text is the answer of the kernel that resolves it,
27
+ * not VAT's. On POSIX only `/` does — a backslash is an ordinary character of a name, so `a\b` is
28
+ * one component, and reading it as two would count a `..` the kernel never takes.
29
+ */
30
+ const SEPARATOR = { posix: /\//, win32: /[/\\]/ };
31
+ /** A POSIX root, a UNC root or a drive prefix: absolute, or drive-relative, on some host. */
32
+ const ABSOLUTE = /^([/\\]|[A-Za-z]:)/;
33
+ /** The components of a link's text, as `flavor` splits it: `.` and empty ones dropped. */
34
+ function segmentsOf(text, flavor) {
35
+ return text.split(SEPARATOR[flavor]).filter((segment) => segment !== '' && segment !== '.');
36
+ }
37
+ /**
38
+ * Walk a link's text from the directory the link is in.
39
+ *
40
+ * @param linkDir - The link's REAL directory, as segments below the tree's root (`[]`: the root itself)
41
+ * @param text - The link's text, verbatim
42
+ * @param kindAt - What stands at a place below the root, never following a link there
43
+ * @param flavor - Whose kernel resolves the link: its separators split the text
44
+ * @returns `undefined` when the text leads to something that exists inside the tree, by a walk
45
+ * that passes through no link; otherwise why it is refused
46
+ */
47
+ export function linkTextRefusal(linkDir, text, kindAt, flavor) {
48
+ if (ABSOLUTE.test(text))
49
+ return { why: 'absolute' };
50
+ const segments = segmentsOf(text, flavor);
51
+ const place = [...linkDir];
52
+ for (const [index, segment] of segments.entries()) {
53
+ if (segment === '..') {
54
+ // Every place the walk stands at is a real directory of the tree, so its parent is `place` less one.
55
+ if (place.pop() === undefined)
56
+ return { why: 'climbs-out' };
57
+ continue;
58
+ }
59
+ place.push(segment);
60
+ const kind = kindAt(place);
61
+ const last = index === segments.length - 1;
62
+ if (kind === 'absent' || (!last && kind === 'file'))
63
+ return { why: 'dangling', at: place.join('/') };
64
+ if (!last && kind === 'link')
65
+ return { why: 'through-link', at: place.join('/') };
66
+ }
67
+ return undefined;
68
+ }
69
+ //# sourceMappingURL=link-walk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-walk.js","sourceRoot":"","sources":["../../src/tree-change/link-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAWH;;;;GAIG;AACH,MAAM,SAAS,GAAyC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACxF,6FAA6F;AAC7F,MAAM,QAAQ,GAAG,oBAAoB,CAAC;AAEtC,0FAA0F;AAC1F,SAAS,UAAU,CAAC,IAAY,EAAE,MAAkB;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,OAA0B,EAAE,IAAY,EAAE,MAA+C,EAAE,MAAkB;IAC3I,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QAClD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,qGAAqG;YACrG,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,SAAS;gBAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;YAC5D,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3C,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,KAAK,MAAM,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrG,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IACpF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,206 @@
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 { type FsSide } from '../errors/fs-fault.js';
42
+ import type { EntryContainment, EntrySameness } from './identity-compare.js';
43
+ import type { LinkPolicy } from './tree-walk.js';
44
+ /** A destination that must be absent or an empty directory already holds something. */
45
+ export declare const TREE_DEST_OCCUPIED_CODE = "TREE_DEST_OCCUPIED";
46
+ /** A destination VAT may replace only when it made it holds something its recogniser disowns. */
47
+ export declare const TREE_DEST_NOT_OWNED_CODE = "TREE_DEST_NOT_OWNED";
48
+ /**
49
+ * Two changes of one plan whose destinations are one entry or nest (other than a remove the other
50
+ * change takes with it): they cannot be one transaction — the outer park would carry the inner
51
+ * change's staging away. The calling verb built a plan it must not, so it is VAT's defect.
52
+ */
53
+ export declare const TREE_DESTS_OVERLAP_CODE = "TREE_DESTS_OVERLAP";
54
+ /** A copy's destination lies inside its source: the copy would copy into itself. */
55
+ export declare const TREE_SOURCE_HOLDS_DEST_CODE = "TREE_SOURCE_HOLDS_DEST";
56
+ /** A copy's source is its destination or lies inside it: the replace would delete what it copies. */
57
+ export declare const TREE_DEST_HOLDS_SOURCE_CODE = "TREE_DEST_HOLDS_SOURCE";
58
+ /** What a recogniser says about a destination that is there. */
59
+ export type OwnershipVerdict = {
60
+ readonly owned: true;
61
+ } | {
62
+ readonly owned: false;
63
+ readonly reason: string;
64
+ };
65
+ /** Who may lose whatever is at a destination. */
66
+ export type Ownership =
67
+ /** Only an absent destination or an empty directory; anything else is refused. */
68
+ {
69
+ readonly kind: 'must-be-free';
70
+ }
71
+ /** The user said `--force`: whatever is there may go. */
72
+ | {
73
+ readonly kind: 'force';
74
+ }
75
+ /** Something VAT made before, as `recognise` judges it; anything else is refused. */
76
+ | {
77
+ readonly kind: 'vat-made';
78
+ readonly recognise: (dest: string) => OwnershipVerdict;
79
+ }
80
+ /** State VAT owns outright (a cache, a marketplace under `~/.claude`, `dist/`). */
81
+ | {
82
+ readonly kind: 'vat-state';
83
+ };
84
+ /** How a replacement tree is made, in a staged place beside its destination. */
85
+ export type TreeFill =
86
+ /**
87
+ * `copy` copies `source` by the readable-tree walk. `side` is the side of the verb `source` is on — an input
88
+ * (`source`), VAT's staging (`environment`), or a copy already made into user state (`destination`) — and
89
+ * every read of it, by the proof and by the copy, is classified on it. Required: only the caller knows.
90
+ */
91
+ {
92
+ readonly from: 'copy';
93
+ readonly source: string;
94
+ readonly side: FsSide;
95
+ readonly links: LinkPolicy;
96
+ readonly filter?: (relative: string) => boolean;
97
+ }
98
+ /** `write` fills the empty staged directory; `reads` are the inputs it reads, held to the same holding check as a copy source. */
99
+ | {
100
+ readonly from: 'write';
101
+ readonly write: (staged: string) => Promise<void>;
102
+ readonly reads?: readonly string[];
103
+ }
104
+ /** The staged entry is a link to `target` (a `--dev` lane). */
105
+ | {
106
+ readonly from: 'link';
107
+ readonly target: string;
108
+ };
109
+ /**
110
+ * A `replace-file`'s bytes: given, or made when the file is staged. Staging runs in the plan's order, so a
111
+ * function is called only after every earlier change of the plan has staged — a file made FROM a tree the
112
+ * same plan stages (an archive of a staged bundle) is one change of that plan, not a second transaction.
113
+ */
114
+ export type FileContents = string | Uint8Array | (() => string | Uint8Array);
115
+ /** One change a verb wants. `label` names it in the dry run and in every message. */
116
+ export type TreeChange = {
117
+ readonly op: 'replace';
118
+ readonly dest: string;
119
+ readonly ownership: Ownership;
120
+ readonly fill: TreeFill;
121
+ readonly label: string;
122
+ } | {
123
+ readonly op: 'replace-file';
124
+ readonly dest: string;
125
+ readonly ownership: Ownership;
126
+ readonly contents: FileContents;
127
+ readonly label: string;
128
+ } | {
129
+ readonly op: 'remove';
130
+ readonly dest: string;
131
+ readonly ownership: Ownership;
132
+ readonly keepIfSameAs?: () => readonly string[];
133
+ readonly label: string;
134
+ };
135
+ /** What is at a destination. */
136
+ export type EntryKind = 'absent' | 'directory' | 'file' | 'link' | 'special';
137
+ /** What will happen to a destination. */
138
+ export type PlannedAction = 'create' | 'replace' | 'remove' | 'keep' | 'subsumed';
139
+ /** One change, decided. */
140
+ export interface PlannedChange {
141
+ readonly change: TreeChange;
142
+ readonly existing: EntryKind;
143
+ readonly action: PlannedAction;
144
+ /** Why it is kept or subsumed. */
145
+ readonly reason?: string;
146
+ /**
147
+ * Set on a remove kept because the OS refused to examine an entry it may be (its `keepIfSameAs`):
148
+ * that entry. A caller that drops its own record of the kept entry names it, so the user can tell
149
+ * what stayed and why (ruling R7 d-I-1: never delete what may BE a kept sibling).
150
+ */
151
+ readonly unexaminedSibling?: string;
152
+ }
153
+ /** Every change, decided; `describe()` is the dry run, one line per change. */
154
+ export interface TreePlan {
155
+ readonly changes: readonly PlannedChange[];
156
+ describe(): readonly string[];
157
+ }
158
+ /** The filesystem facts the planner decides by: live in {@link planTreeChanges}, faked in its unit test. */
159
+ export interface PlanFacts {
160
+ /** What is at `dest`; throws when the OS refuses to examine it. */
161
+ readonly existing: (dest: string) => EntryKind;
162
+ readonly isEmptyDirectory: (dest: string) => boolean;
163
+ /** Whether the link at `dest` leads to a directory (false for a dangling link, or one to a file). */
164
+ readonly linksToDirectory: (dest: string) => boolean;
165
+ readonly sameEntry: (a: string, b: string) => EntrySameness;
166
+ readonly isInside: (child: string, ancestor: string) => EntryContainment;
167
+ /** Throws when the OS refuses to examine `dest`'s identity: a remove's own unexaminability is never a keep. */
168
+ readonly requireExaminable: (dest: string) => void;
169
+ /** Whether the OS refuses to examine `entry`'s identity (an `unknown` that is a refusal, not a fold that cannot tell). */
170
+ readonly refusesToExamine: (entry: string) => boolean;
171
+ }
172
+ /** Whether a planned change touches its destination (`keep` and `subsumed` do not). */
173
+ export declare const isActive: ({ action }: PlannedChange) => boolean;
174
+ /**
175
+ * The pure planner: every decision of {@link planTreeChanges} but the source
176
+ * proof, made from `facts`.
177
+ *
178
+ * @throws VatError coded `TREE_DESTS_OVERLAP` (a defect in the caller's plan), `TREE_DEST_OCCUPIED`,
179
+ * `TREE_DEST_NOT_OWNED`, `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; whatever `facts` throws
180
+ */
181
+ export declare function planFromFacts(changes: readonly TreeChange[], facts: PlanFacts): TreePlan;
182
+ /**
183
+ * What is at `dest` NOW, for the apply to hold against what the plan decided
184
+ * (`changedSincePlan`). The emptiness of a directory is read only where the plan took it as free.
185
+ *
186
+ * @throws FsFaultError side `destination` when the OS refuses to examine or list it
187
+ */
188
+ export declare function destinationNow(dest: string, planned: PlannedChange): {
189
+ kind: EntryKind;
190
+ emptyDirectory: boolean;
191
+ };
192
+ /**
193
+ * Decide every change against the filesystem as it is, with no side effect. Each
194
+ * destination is taken in its canonical spelling (`safePath.resolve`: absolute,
195
+ * `.`/`..` resolved, forward slashes); `describe()` prints that spelling.
196
+ * Hand the result to `applyTreePlan`; print `describe()` for a dry run — the two
197
+ * cannot disagree, because the apply does exactly what the plan says.
198
+ *
199
+ * @param changes - What the verb wants, in the order it wants it
200
+ * @throws VatError coded `TREE_DEST_OCCUPIED`, `TREE_DEST_NOT_OWNED`,
201
+ * `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; `FsFaultError` side
202
+ * `destination` for a destination the OS refuses to examine, on the fill's
203
+ * declared side for a copy source it refuses to read
204
+ */
205
+ export declare function planTreeChanges(changes: readonly TreeChange[]): Promise<TreePlan>;
206
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/tree-change/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAMH,OAAO,EAA+B,KAAK,MAAM,EAAmB,MAAM,uBAAuB,CAAC;AAMlG,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,uFAAuF;AACvF,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAC5D,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAC9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAC5D,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AACpE,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7G,iDAAiD;AACjD,MAAM,MAAM,SAAS;AACnB,kFAAkF;AAChF;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAAE;AACnC,yDAAyD;GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AAC5B,qFAAqF;GACnF;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,CAAA;CAAE;AACvF,mFAAmF;GACjF;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnC,gFAAgF;AAChF,MAAM,MAAM,QAAQ;AAClB;;;;GAIG;AACD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE;AACxJ,kIAAkI;GAChI;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE;AACnH,+DAA+D;GAC7D;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC;AAE7E,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjI;IAAE,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9I;IAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7J,gCAAgC;AAChC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7E,yCAAyC;AACzC,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAElF,2BAA2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,+EAA+E;AAC/E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,IAAI,SAAS,MAAM,EAAE,CAAC;CAC/B;AAED,4GAA4G;AAC5G,MAAM,WAAW,SAAS;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,qGAAqG;IACrG,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC;IAC5D,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,gBAAgB,CAAC;IACzE,+GAA+G;IAC/G,QAAQ,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,0HAA0H;IAC1H,QAAQ,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACvD;AA8DD,uFAAuF;AACvF,eAAO,MAAM,QAAQ,GAAI,YAAY,aAAa,KAAG,OAA6E,CAAC;AAsFnI;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,QAAQ,CAYxF;AA8BD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAIjH;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOvF"}