@vibe-agent-toolkit/utils 0.2.0-rc.10 → 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 (330) hide show
  1. package/README.md +112 -10
  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 +13 -5
  43. package/dist/env-flag.d.ts.map +1 -1
  44. package/dist/env-flag.js +13 -5
  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 +5 -4
  87. package/dist/fs.d.ts.map +1 -1
  88. package/dist/fs.js +5 -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 +17 -0
  133. package/dist/path-containment.d.ts.map +1 -1
  134. package/dist/path-containment.js +28 -5
  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-content.d.ts +29 -0
  218. package/dist/text-content.d.ts.map +1 -1
  219. package/dist/text-content.js +64 -0
  220. package/dist/text-content.js.map +1 -1
  221. package/dist/text-file.d.ts +58 -6
  222. package/dist/text-file.d.ts.map +1 -1
  223. package/dist/text-file.js +111 -11
  224. package/dist/text-file.js.map +1 -1
  225. package/dist/text.d.ts +1 -1
  226. package/dist/text.d.ts.map +1 -1
  227. package/dist/text.js +1 -1
  228. package/dist/text.js.map +1 -1
  229. package/dist/timing-dump.d.ts.map +1 -1
  230. package/dist/timing-dump.js +2 -10
  231. package/dist/timing-dump.js.map +1 -1
  232. package/dist/tree-change/apply-decisions.d.ts +101 -0
  233. package/dist/tree-change/apply-decisions.d.ts.map +1 -0
  234. package/dist/tree-change/apply-decisions.js +139 -0
  235. package/dist/tree-change/apply-decisions.js.map +1 -0
  236. package/dist/tree-change/apply.d.ts +98 -0
  237. package/dist/tree-change/apply.d.ts.map +1 -0
  238. package/dist/tree-change/apply.js +364 -0
  239. package/dist/tree-change/apply.js.map +1 -0
  240. package/dist/tree-change/copy-decisions.d.ts +58 -0
  241. package/dist/tree-change/copy-decisions.d.ts.map +1 -0
  242. package/dist/tree-change/copy-decisions.js +66 -0
  243. package/dist/tree-change/copy-decisions.js.map +1 -0
  244. package/dist/tree-change/copy-tree.d.ts +125 -0
  245. package/dist/tree-change/copy-tree.d.ts.map +1 -0
  246. package/dist/tree-change/copy-tree.js +279 -0
  247. package/dist/tree-change/copy-tree.js.map +1 -0
  248. package/dist/tree-change/files.d.ts +167 -0
  249. package/dist/tree-change/files.d.ts.map +1 -0
  250. package/dist/tree-change/files.js +423 -0
  251. package/dist/tree-change/files.js.map +1 -0
  252. package/dist/tree-change/followed-walk.d.ts +54 -0
  253. package/dist/tree-change/followed-walk.d.ts.map +1 -0
  254. package/dist/tree-change/followed-walk.js +81 -0
  255. package/dist/tree-change/followed-walk.js.map +1 -0
  256. package/dist/tree-change/host-chain.d.ts +48 -0
  257. package/dist/tree-change/host-chain.d.ts.map +1 -0
  258. package/dist/tree-change/host-chain.js +85 -0
  259. package/dist/tree-change/host-chain.js.map +1 -0
  260. package/dist/tree-change/identity-compare.d.ts +61 -0
  261. package/dist/tree-change/identity-compare.d.ts.map +1 -0
  262. package/dist/tree-change/identity-compare.js +115 -0
  263. package/dist/tree-change/identity-compare.js.map +1 -0
  264. package/dist/tree-change/identity.d.ts +62 -0
  265. package/dist/tree-change/identity.d.ts.map +1 -0
  266. package/dist/tree-change/identity.js +152 -0
  267. package/dist/tree-change/identity.js.map +1 -0
  268. package/dist/tree-change/index.d.ts +24 -0
  269. package/dist/tree-change/index.d.ts.map +1 -0
  270. package/dist/tree-change/index.js +14 -0
  271. package/dist/tree-change/index.js.map +1 -0
  272. package/dist/tree-change/link-chain.d.ts +180 -0
  273. package/dist/tree-change/link-chain.d.ts.map +1 -0
  274. package/dist/tree-change/link-chain.js +377 -0
  275. package/dist/tree-change/link-chain.js.map +1 -0
  276. package/dist/tree-change/link-containment.d.ts +105 -0
  277. package/dist/tree-change/link-containment.d.ts.map +1 -0
  278. package/dist/tree-change/link-containment.js +196 -0
  279. package/dist/tree-change/link-containment.js.map +1 -0
  280. package/dist/tree-change/link-walk.d.ts +49 -0
  281. package/dist/tree-change/link-walk.d.ts.map +1 -0
  282. package/dist/tree-change/link-walk.js +69 -0
  283. package/dist/tree-change/link-walk.js.map +1 -0
  284. package/dist/tree-change/plan.d.ts +206 -0
  285. package/dist/tree-change/plan.d.ts.map +1 -0
  286. package/dist/tree-change/plan.js +320 -0
  287. package/dist/tree-change/plan.js.map +1 -0
  288. package/dist/tree-change/readable-tree.d.ts +44 -0
  289. package/dist/tree-change/readable-tree.d.ts.map +1 -0
  290. package/dist/tree-change/readable-tree.js +52 -0
  291. package/dist/tree-change/readable-tree.js.map +1 -0
  292. package/dist/tree-change/rollback-error.d.ts +29 -0
  293. package/dist/tree-change/rollback-error.d.ts.map +1 -0
  294. package/dist/tree-change/rollback-error.js +31 -0
  295. package/dist/tree-change/rollback-error.js.map +1 -0
  296. package/dist/tree-change/staging-names.d.ts +27 -0
  297. package/dist/tree-change/staging-names.d.ts.map +1 -0
  298. package/dist/tree-change/staging-names.js +40 -0
  299. package/dist/tree-change/staging-names.js.map +1 -0
  300. package/dist/tree-change/tree-walk.d.ts +81 -0
  301. package/dist/tree-change/tree-walk.d.ts.map +1 -0
  302. package/dist/tree-change/tree-walk.js +212 -0
  303. package/dist/tree-change/tree-walk.js.map +1 -0
  304. package/dist/win32-namespace.d.ts +24 -0
  305. package/dist/win32-namespace.d.ts.map +1 -0
  306. package/dist/win32-namespace.js +31 -0
  307. package/dist/win32-namespace.js.map +1 -0
  308. package/dist/yaml/surgical-yaml.d.ts +12 -3
  309. package/dist/yaml/surgical-yaml.d.ts.map +1 -1
  310. package/dist/yaml/surgical-yaml.js +16 -6
  311. package/dist/yaml/surgical-yaml.js.map +1 -1
  312. package/eslint/README.md +12 -4
  313. package/eslint/rules/fs-binding-scan.cjs +278 -0
  314. package/eslint/rules/no-adhoc-errno.cjs +364 -0
  315. package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
  316. package/eslint/rules/no-blind-catch.cjs +1 -1
  317. package/eslint/rules/no-destructive-fs.cjs +359 -0
  318. package/eslint/rules/no-existssync.cjs +70 -0
  319. package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
  320. package/eslint/rules/no-link-following-fs.cjs +68 -0
  321. package/eslint/rules/no-literal-process-exit.cjs +96 -8
  322. package/eslint/rules/no-literal-test-timeout.cjs +214 -0
  323. package/eslint/rules/no-raw-signal-listener.cjs +224 -0
  324. package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
  325. package/package.json +10 -2
  326. package/dist/errors/errno.d.ts +0 -51
  327. package/dist/errors/errno.d.ts.map +0 -1
  328. package/dist/errors/errno.js +0 -112
  329. package/dist/errors/errno.js.map +0 -1
  330. package/eslint/rules/no-fs-promises-cp.cjs +0 -43
@@ -0,0 +1,180 @@
1
+ /**
2
+ * The ONE resolver of a symbolic-link chain: where a path leads, walked hop by hop from what
3
+ * `lstat` and `readlink` say alone, so nothing is handed to the kernel until the whole chain has
4
+ * been read as text and shown to be local.
5
+ *
6
+ * That order is the point. A Windows kernel asked to resolve a path through a link to
7
+ * `\\host\share` opens an SMB session and offers the operator's NTLM credentials to that host
8
+ * before any answer comes back — so a `realpath` or a `stat` that follows links is a SECOND lock,
9
+ * asked only once this walk has proved the chain local, never the first.
10
+ *
11
+ * Pure: the caller supplies the path {@link PathFlavor} and the probe, so a test runs Windows
12
+ * semantics on any host. The flavor decides two things the kernel decides:
13
+ *
14
+ * - what separates a link text's components, and what makes it absolute or a network path
15
+ * (`\\host\share`, `\\?\UNC\…`, `\\.\…`, `//host/share` under win32; on POSIX a backslash is a
16
+ * character of a name and `//x` is the local root);
17
+ * - what `..` means. POSIX resolves a link before the `..` after it, physically (the parent of
18
+ * where the link LED). Windows joins a relative link text onto the link's directory and collapses
19
+ * every `..` lexically before reparsing — `x\..\y` is `y` whether `x` exists, is a file or a link —
20
+ * so under win32 each splice is collapsed by string first, and every component it leaves is examined.
21
+ *
22
+ * Running out of hops ({@link MAX_LINK_HOPS}) is a refusal, never an answer: a chain the walk did
23
+ * not finish reading is a chain the kernel would finish for it. A CYCLE is different, and is told
24
+ * apart exactly: the walk meets the same link with the same text left to read, so every text in
25
+ * the cycle has been read and is local, and the kernel can only answer `ELOOP` — it resolves to
26
+ * nothing, as a dangling link does, in every lane.
27
+ *
28
+ * An untrusted tree ({@link ChainRule.outside} `refuse`) may be read on either host — a clone made
29
+ * here is installed there — so its every link text is also held to the OTHER flavor's rules
30
+ * ({@link untrustedTextRefusal}): a text that is absolute, drive-relative or a network path on
31
+ * either kernel is refused on both.
32
+ */
33
+ /** What stands at a place, a link there NOT followed. */
34
+ export type PlaceKind = 'directory' | 'file' | 'link' | 'absent';
35
+ /** Whose path rules a link's text is read by. Bound from `process.platform` at the edge ({@link HOST_PATH_FLAVOR}). */
36
+ export type PathFlavor = 'posix' | 'win32';
37
+ /** The flavor of the host VAT runs on — the edge where a pure caller's flavor is bound. */
38
+ export declare const HOST_PATH_FLAVOR: PathFlavor;
39
+ /**
40
+ * How many links one resolution follows before it refuses. Windows' limit — 63 reparse points in
41
+ * one path — is the larger of the two kernels' (Linux's `MAXSYMLINKS` is 40), so no chain a kernel
42
+ * would still follow is ever cut short and answered.
43
+ */
44
+ export declare const MAX_LINK_HOPS = 63;
45
+ /** Why a chain is refused before the kernel is asked anything, with the link whose text decided it. */
46
+ export type ChainRefusal =
47
+ /** A network or device path, at any hop: never handed to the kernel, in any lane. */
48
+ {
49
+ readonly why: 'network';
50
+ readonly link: string;
51
+ readonly text: string;
52
+ }
53
+ /** An absolute local text, in a tree whose links may not leave it ({@link ChainRule.outside} `refuse`). */
54
+ | {
55
+ readonly why: 'absolute';
56
+ readonly link: string;
57
+ readonly text: string;
58
+ }
59
+ /** A drive-relative text (`C:x`): it names a place relative to a per-drive working directory no walk can know. */
60
+ | {
61
+ readonly why: 'drive-relative';
62
+ readonly link: string;
63
+ readonly text: string;
64
+ }
65
+ /** A text holding a NUL, or a component ending in a dot or a space, in a tree whose links may not leave it: not the text a kernel resolves. */
66
+ | {
67
+ readonly why: 'unportable';
68
+ readonly link: string;
69
+ readonly text: string;
70
+ }
71
+ /** A step above the tree's root, in a tree whose links may not leave it. */
72
+ | {
73
+ readonly why: 'climbs-out';
74
+ readonly link: string;
75
+ readonly text: string;
76
+ }
77
+ /** More than {@link MAX_LINK_HOPS} links that are not a cycle: a chain longer than either kernel follows. */
78
+ | {
79
+ readonly why: 'hop-limit';
80
+ readonly link: string;
81
+ };
82
+ /** Where a chain leads, as far as text can tell. */
83
+ export type LinkChain =
84
+ /** A local path, every link on the way read: the kernel may now be asked about it. */
85
+ {
86
+ readonly kind: 'resolved';
87
+ readonly path: string;
88
+ readonly final: 'directory' | 'file';
89
+ }
90
+ /**
91
+ * It leads to nothing: a component is absent or a file stands where a directory must
92
+ * (`absent`), or the chain comes back to a link it already read with the same text left
93
+ * (`cycle` — what the kernel answers `ELOOP` for).
94
+ */
95
+ | {
96
+ readonly kind: 'nothing';
97
+ readonly at: string;
98
+ readonly cause: 'absent' | 'cycle';
99
+ }
100
+ /** It may not be resolved at all. */
101
+ | {
102
+ readonly kind: 'refused';
103
+ readonly refusal: ChainRefusal;
104
+ };
105
+ /** The tree a chain is resolved in, and what it may do outside it. */
106
+ export interface ChainRule {
107
+ readonly flavor: PathFlavor;
108
+ /** The tree's root, canonical (its own components are real directories). */
109
+ readonly root: string;
110
+ /**
111
+ * - `refuse`: an untrusted tree (a clone, an archive, a tree an install copies) — an absolute or
112
+ * drive-relative text, or any step above the root, is refused, and nothing outside is probed.
113
+ * - `follow`: the user's own tree — an absolute local text is followed and walked by the same
114
+ * rules, so a later hop's network text is still caught; where the chain ends is the caller's call.
115
+ */
116
+ readonly outside: 'refuse' | 'follow';
117
+ }
118
+ /** What the walk may ask the filesystem: never anything that follows a link. */
119
+ export interface ChainProbe {
120
+ /** What stands at `path`, a link there NOT followed (`lstat`). */
121
+ readonly kindAt: (path: string) => PlaceKind;
122
+ /** The text of the link at `path` (`readlink`). */
123
+ readonly textAt: (path: string) => string;
124
+ }
125
+ declare const CLIMBS_OUT = "climbs-out";
126
+ /**
127
+ * The root a NAMED path hangs from — `/`, a drive's `C:/`, a share's `//host/share/` — for a walk
128
+ * of the user's own path, which may lead anywhere local.
129
+ *
130
+ * @param path - Absolute, forward- or back-slashed
131
+ * @param flavor - Whose rules
132
+ * @throws TypeError for a path that is not absolute under `flavor`
133
+ */
134
+ export declare function namedPathRoot(path: string, flavor: PathFlavor): string;
135
+ /**
136
+ * Why a link text in an UNTRUSTED tree is refused by its text alone, under EITHER flavor — the rule
137
+ * a chain in a `refuse` tree is held to at every hop, and the one a caller that has only the text
138
+ * (a git clone reading link blobs before checkout) asks directly.
139
+ *
140
+ * A text is also `unportable` when it holds a NUL (the OS cuts the text short there, so the walk
141
+ * would judge bytes no kernel sees) or a component ending in a dot or a space (Win32 strips those,
142
+ * so `.. ` would climb where the walk read a name) — the win32 rule held on every host.
143
+ *
144
+ * @param text - A link's text, verbatim
145
+ * @returns why either kernel would not read it as a relative text inside the tree; `undefined` for one it would
146
+ */
147
+ export declare function untrustedTextRefusal(text: string): UntrustedTextRefusal | undefined;
148
+ /** Why an untrusted link's text alone is refused ({@link untrustedTextRefusal}). */
149
+ type UntrustedTextRefusal = 'network' | 'absolute' | 'drive-relative' | 'unportable';
150
+ /**
151
+ * Why a committed link — `linkPath` (its `/`-separated place in the tree) whose text is `text` — is
152
+ * refused by its text alone, before it exists on disk: {@link untrustedTextRefusal}, or `climbs-out`
153
+ * when, joined onto the link's directory and split by EITHER flavor's separators, its `..`s rise above
154
+ * the tree's root — the same collapse the walk applies under win32.
155
+ *
156
+ * @returns The refusal, or `undefined` for a text that stays inside by its text alone
157
+ */
158
+ export declare function untrustedLinkRefusal(linkPath: string, text: string): UntrustedTextRefusal | typeof CLIMBS_OUT | undefined;
159
+ /**
160
+ * A name folded as Windows compares names: each character upcased on its own (an ordinal, simple
161
+ * case fold — never `toLowerCase`, never a mapping that changes a character's length, like `ß` → `SS`).
162
+ */
163
+ export declare function win32FoldedName(name: string): string;
164
+ /**
165
+ * Whether `path` is `root` or below it, both forward-slashed — case-blind under win32 by the
166
+ * ordinal fold Windows compares names with ({@link win32FoldedName}).
167
+ */
168
+ export declare function isWithinUnder(path: string, root: string, flavor: PathFlavor): boolean;
169
+ /**
170
+ * Where `start` leads, every link on the way read by its text before anything follows it.
171
+ *
172
+ * @param start - An absolute path, in `rule.flavor`'s spelling (forward or back slashes)
173
+ * @param rule - The flavor, the tree, and what the chain may do outside it
174
+ * @param probe - `lstat` and `readlink`, and nothing that follows a link
175
+ * @returns The local path the kernel may now be asked about, nothing, or the refusal
176
+ * @throws whatever `probe` throws: a probe the OS refused is the caller's to classify
177
+ */
178
+ export declare function walkLinkChain(start: string, rule: ChainRule, probe: ChainProbe): LinkChain;
179
+ export {};
180
+ //# sourceMappingURL=link-chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-chain.d.ts","sourceRoot":"","sources":["../../src/tree-change/link-chain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,377 @@
1
+ /**
2
+ * The ONE resolver of a symbolic-link chain: where a path leads, walked hop by hop from what
3
+ * `lstat` and `readlink` say alone, so nothing is handed to the kernel until the whole chain has
4
+ * been read as text and shown to be local.
5
+ *
6
+ * That order is the point. A Windows kernel asked to resolve a path through a link to
7
+ * `\\host\share` opens an SMB session and offers the operator's NTLM credentials to that host
8
+ * before any answer comes back — so a `realpath` or a `stat` that follows links is a SECOND lock,
9
+ * asked only once this walk has proved the chain local, never the first.
10
+ *
11
+ * Pure: the caller supplies the path {@link PathFlavor} and the probe, so a test runs Windows
12
+ * semantics on any host. The flavor decides two things the kernel decides:
13
+ *
14
+ * - what separates a link text's components, and what makes it absolute or a network path
15
+ * (`\\host\share`, `\\?\UNC\…`, `\\.\…`, `//host/share` under win32; on POSIX a backslash is a
16
+ * character of a name and `//x` is the local root);
17
+ * - what `..` means. POSIX resolves a link before the `..` after it, physically (the parent of
18
+ * where the link LED). Windows joins a relative link text onto the link's directory and collapses
19
+ * every `..` lexically before reparsing — `x\..\y` is `y` whether `x` exists, is a file or a link —
20
+ * so under win32 each splice is collapsed by string first, and every component it leaves is examined.
21
+ *
22
+ * Running out of hops ({@link MAX_LINK_HOPS}) is a refusal, never an answer: a chain the walk did
23
+ * not finish reading is a chain the kernel would finish for it. A CYCLE is different, and is told
24
+ * apart exactly: the walk meets the same link with the same text left to read, so every text in
25
+ * the cycle has been read and is local, and the kernel can only answer `ELOOP` — it resolves to
26
+ * nothing, as a dangling link does, in every lane.
27
+ *
28
+ * An untrusted tree ({@link ChainRule.outside} `refuse`) may be read on either host — a clone made
29
+ * here is installed there — so its every link text is also held to the OTHER flavor's rules
30
+ * ({@link untrustedTextRefusal}): a text that is absolute, drive-relative or a network path on
31
+ * either kernel is refused on both.
32
+ */
33
+ import { posix } from 'node:path';
34
+ import { toForwardSlashAnyPlatform } from '../path-core.js';
35
+ /** The flavor of the host VAT runs on — the edge where a pure caller's flavor is bound. */
36
+ export const HOST_PATH_FLAVOR = process.platform === 'win32' ? 'win32' : 'posix';
37
+ /**
38
+ * How many links one resolution follows before it refuses. Windows' limit — 63 reparse points in
39
+ * one path — is the larger of the two kernels' (Linux's `MAXSYMLINKS` is 40), so no chain a kernel
40
+ * would still follow is ever cut short and answered.
41
+ */
42
+ export const MAX_LINK_HOPS = 63;
43
+ /** `X:\…` / `X:/…`, slashes already forward: the one absolute form of a link text a Windows kernel resolves locally. */
44
+ const WIN32_DRIVE_ABSOLUTE = /^([A-Za-z]:)\//;
45
+ /** `\\?\C:\…` / `\\.\C:\…`, slashes already forward: a local drive through the Win32 namespace — a NAMED path only. */
46
+ const WIN32_NAMESPACED_DRIVE = /^\/\/[?.]\/([A-Za-z]:)(?:\/|$)/;
47
+ /** `\\?\Volume{GUID}\…`, slashes already forward: a local volume by its GUID — a NAMED path only. */
48
+ const WIN32_VOLUME = /^\/\/\?\/(Volume\{[0-9A-Fa-f-]+\})(?:\/|$)/;
49
+ /** `\\host\share\…` / `\\?\UNC\host\share\…`, slashes already forward: the share a NAMED path (never a link's text) hangs from. */
50
+ const WIN32_SHARE_ROOT = /^\/\/(?:\?\/UNC\/)?([^/?.][^/]*\/[^/]+)(?:\/|$)/i;
51
+ const WIN32_DRIVE_ROOT = /^([A-Za-z]:)(?:\/|$)/;
52
+ const WIN32_DRIVE_RELATIVE = /^[A-Za-z]:/;
53
+ /** The non-empty, non-`.` components of `text`, spelled with POSIX separators (a win32 text is converted first). */
54
+ function partsOf(text) {
55
+ return text.split(posix.sep).filter((part) => part !== '' && part !== '.');
56
+ }
57
+ const DRIVE_RELATIVE = 'drive-relative';
58
+ const CLIMBS_OUT = 'climbs-out';
59
+ /**
60
+ * How a link's TEXT reads under `flavor` — an ALLOWLIST under win32, never a list of network
61
+ * spellings: a text is local only when it is relative (no leading separator, no drive) or
62
+ * drive-absolute (`X:\…`). Every other form — UNC, `\\?\…`, `\\.\…`, the NT object paths libuv's
63
+ * `readlink` hands back verbatim (`\??\GLOBALROOT\Device\Mup\…`, `\??\Volume{…}\…`, `\Device\…`,
64
+ * `\DosDevices\…`, `\GLOBAL??\…`), a root-relative `\x`, and anything not recognised — is a
65
+ * network or device path, which the walk never resolves and the kernel is never asked about.
66
+ *
67
+ * @param text - A link's text, verbatim
68
+ * @param flavor - Whose rules
69
+ */
70
+ function textForm(text, flavor) {
71
+ if (flavor === 'posix') {
72
+ return text.startsWith('/') ? { form: 'rooted', rooted: { prefix: '/', parts: partsOf(text) } } : { form: 'relative', parts: partsOf(text) };
73
+ }
74
+ const slashed = toForwardSlashAnyPlatform(text);
75
+ const drive = WIN32_DRIVE_ABSOLUTE.exec(slashed);
76
+ if (drive?.[1] !== undefined)
77
+ return { form: 'rooted', rooted: { prefix: `${drive[1].toUpperCase()}/`, parts: partsOf(slashed.slice(drive[0].length)) } };
78
+ if (WIN32_DRIVE_RELATIVE.test(slashed))
79
+ return { form: DRIVE_RELATIVE };
80
+ if (slashed.startsWith('/'))
81
+ return { form: 'network' };
82
+ return { form: 'relative', parts: partsOf(slashed) };
83
+ }
84
+ /**
85
+ * How a NAMED path (the start or the root — what the operator or VAT spelled, never a link's text)
86
+ * reads. Under win32 it may hang from a drive, a namespaced drive, a volume or a share: a tree on
87
+ * `\\host\share` is the operator's choice, while a link's text naming any of them is never
88
+ * followed ({@link textForm}). A rooted-but-driveless `\x` hangs from `drive`.
89
+ */
90
+ function anchoredForm(path, flavor, drive) {
91
+ if (flavor === 'posix')
92
+ return textForm(path, flavor);
93
+ const slashed = toForwardSlashAnyPlatform(path);
94
+ const rooted = (prefix, rest) => ({ form: 'rooted', rooted: { prefix, parts: partsOf(rest) } });
95
+ const anchors = [
96
+ [WIN32_DRIVE_ROOT, (name) => `${name.toUpperCase()}/`],
97
+ [WIN32_NAMESPACED_DRIVE, (name) => `${name.toUpperCase()}/`],
98
+ [WIN32_VOLUME, (name) => `//?/${name}/`],
99
+ [WIN32_SHARE_ROOT, (name) => `//${name}/`],
100
+ ];
101
+ for (const [pattern, prefixOf] of anchors) {
102
+ const match = pattern.exec(slashed);
103
+ if (match?.[1] !== undefined)
104
+ return rooted(prefixOf(match[1]), slashed.slice(match[0].length));
105
+ }
106
+ if (slashed.startsWith('//'))
107
+ return { form: 'network' };
108
+ return slashed.startsWith('/') ? rooted(drive, slashed) : textForm(path, flavor);
109
+ }
110
+ /**
111
+ * The root a NAMED path hangs from — `/`, a drive's `C:/`, a share's `//host/share/` — for a walk
112
+ * of the user's own path, which may lead anywhere local.
113
+ *
114
+ * @param path - Absolute, forward- or back-slashed
115
+ * @param flavor - Whose rules
116
+ * @throws TypeError for a path that is not absolute under `flavor`
117
+ */
118
+ export function namedPathRoot(path, flavor) {
119
+ const form = anchoredForm(path, flavor, '/');
120
+ if (form.form !== 'rooted')
121
+ throw new TypeError(`namedPathRoot: not an absolute local path: ${path}`);
122
+ return form.rooted.prefix;
123
+ }
124
+ const FLAVORS = ['posix', 'win32'];
125
+ /**
126
+ * Why a link text in an UNTRUSTED tree is refused by its text alone, under EITHER flavor — the rule
127
+ * a chain in a `refuse` tree is held to at every hop, and the one a caller that has only the text
128
+ * (a git clone reading link blobs before checkout) asks directly.
129
+ *
130
+ * A text is also `unportable` when it holds a NUL (the OS cuts the text short there, so the walk
131
+ * would judge bytes no kernel sees) or a component ending in a dot or a space (Win32 strips those,
132
+ * so `.. ` would climb where the walk read a name) — the win32 rule held on every host.
133
+ *
134
+ * @param text - A link's text, verbatim
135
+ * @returns why either kernel would not read it as a relative text inside the tree; `undefined` for one it would
136
+ */
137
+ export function untrustedTextRefusal(text) {
138
+ if (isUnportableText(text))
139
+ return 'unportable';
140
+ const [posixForm, win32Form] = FLAVORS.map((flavor) => textForm(text, flavor).form);
141
+ // `/etc/x` is a POSIX absolute path (and a device path to Windows); `\\host\share`, `\??\…` and
142
+ // `//host/share` name a network or device path to Windows whatever POSIX reads them as.
143
+ if (win32Form === 'network')
144
+ return posixForm === 'rooted' && !toForwardSlashAnyPlatform(text).startsWith('//') ? 'absolute' : 'network';
145
+ if (win32Form === DRIVE_RELATIVE)
146
+ return DRIVE_RELATIVE;
147
+ if (posixForm === 'rooted' || win32Form === 'rooted')
148
+ return 'absolute';
149
+ return undefined;
150
+ }
151
+ /** A component that is a name ending in `.` or a space (never `.` or `..` themselves): Win32 strips the ending. */
152
+ const STRIPPED_ENDING = /(?:^|[/\\])(?!\.\.?(?:[/\\]|$))[^/\\]*[. ](?:[/\\]|$)/;
153
+ /** Whether `text` holds a NUL, or a component Windows would rename by stripping its ending. */
154
+ function isUnportableText(text) {
155
+ return text.includes(String.fromCodePoint(0)) || STRIPPED_ENDING.test(text);
156
+ }
157
+ /**
158
+ * Collapse `..` by string — what Windows does to a name before it reparses it — saying whether a
159
+ * `..` met nothing left to pop: the name climbed above the place it started from.
160
+ */
161
+ function collapse(parts) {
162
+ const out = [];
163
+ let climbed = false;
164
+ for (const part of parts) {
165
+ if (part !== '..')
166
+ out.push(part);
167
+ else if (out.pop() === undefined)
168
+ climbed = true;
169
+ }
170
+ return { parts: out, climbed };
171
+ }
172
+ /** {@link collapse}, never above the root. */
173
+ function collapsed(parts) {
174
+ return collapse(parts).parts;
175
+ }
176
+ /**
177
+ * Why a committed link — `linkPath` (its `/`-separated place in the tree) whose text is `text` — is
178
+ * refused by its text alone, before it exists on disk: {@link untrustedTextRefusal}, or `climbs-out`
179
+ * when, joined onto the link's directory and split by EITHER flavor's separators, its `..`s rise above
180
+ * the tree's root — the same collapse the walk applies under win32.
181
+ *
182
+ * @returns The refusal, or `undefined` for a text that stays inside by its text alone
183
+ */
184
+ export function untrustedLinkRefusal(linkPath, text) {
185
+ const why = untrustedTextRefusal(text);
186
+ if (why !== undefined)
187
+ return why;
188
+ const splitters = [(value) => value, toForwardSlashAnyPlatform];
189
+ const climbs = splitters.some((spell) => collapse([...partsOf(posix.dirname(spell(linkPath))), ...partsOf(spell(text))]).climbed);
190
+ return climbs ? CLIMBS_OUT : undefined;
191
+ }
192
+ /**
193
+ * A name folded as Windows compares names: each character upcased on its own (an ordinal, simple
194
+ * case fold — never `toLowerCase`, never a mapping that changes a character's length, like `ß` → `SS`).
195
+ */
196
+ export function win32FoldedName(name) {
197
+ let folded = '';
198
+ for (const character of name) {
199
+ const upper = character.toUpperCase();
200
+ folded += upper.length === character.length ? upper : character;
201
+ }
202
+ return folded;
203
+ }
204
+ /** `prefix` + `parts`, forward-slashed. */
205
+ function joined(prefix, parts) {
206
+ return prefix + parts.join('/');
207
+ }
208
+ /**
209
+ * Whether `path` is `root` or below it, both forward-slashed — case-blind under win32 by the
210
+ * ordinal fold Windows compares names with ({@link win32FoldedName}).
211
+ */
212
+ export function isWithinUnder(path, root, flavor) {
213
+ const [p, r] = flavor === 'win32' ? [win32FoldedName(path), win32FoldedName(root)] : [path, root];
214
+ const base = r.endsWith('/') ? r : `${r}/`;
215
+ return p === r || p.startsWith(base);
216
+ }
217
+ /**
218
+ * Splice a link's text into the walk in place of the link (already popped from `done`), as
219
+ * `flavor`'s kernel would; or the refusal of a text it never resolves.
220
+ */
221
+ function splice(walk, link, text, rule) {
222
+ walk.link = link;
223
+ walk.text = text;
224
+ const form = textForm(text, rule.flavor);
225
+ if (form.form === 'network')
226
+ return { why: 'network', link, text };
227
+ if (form.form === 'drive-relative')
228
+ return { why: DRIVE_RELATIVE, link, text };
229
+ if (rule.outside === 'refuse') {
230
+ const why = untrustedTextRefusal(text);
231
+ if (why !== undefined)
232
+ return { why, link, text };
233
+ }
234
+ if (form.form === 'rooted') {
235
+ if (rule.flavor === 'win32')
236
+ return rebase(walk, form.rooted.prefix, collapsed([...form.rooted.parts, ...walk.pending]), rule);
237
+ walk.prefix = form.rooted.prefix;
238
+ walk.pending = [...form.rooted.parts, ...walk.pending];
239
+ walk.done = [];
240
+ return undefined;
241
+ }
242
+ if (rule.flavor === 'win32') {
243
+ // Joined onto the link's directory and collapsed by string BEFORE anything is looked at, then
244
+ // re-walked: a `..` never asks what the component before it is.
245
+ const whole = [...walk.done, ...form.parts, ...walk.pending];
246
+ // The collapse forgets a climb that comes back in by the root's own name; a tree that may not be left sees it on the way.
247
+ if (rule.outside === 'refuse' && risesAbove(whole, walk.base.parts.length))
248
+ return { why: CLIMBS_OUT, link, text };
249
+ return rebase(walk, walk.prefix, collapsed(whole), rule);
250
+ }
251
+ walk.pending = [...form.parts, ...walk.pending];
252
+ return undefined;
253
+ }
254
+ /** Whether collapsing `parts` by string ever leaves fewer than `floor` components: the name passes above the root. */
255
+ function risesAbove(parts, floor) {
256
+ let depth = 0;
257
+ for (const part of parts) {
258
+ if (part === '..')
259
+ depth -= 1;
260
+ else if (part !== '.' && part !== '')
261
+ depth += 1;
262
+ if (depth < floor && part === '..')
263
+ return true;
264
+ }
265
+ return false;
266
+ }
267
+ /**
268
+ * Re-walk the whole name `prefix` + `parts` (a win32 splice, already collapsed): from the tree's
269
+ * root when the name is below it — the root's own components are real directories — else from the
270
+ * name's own root, which only a tree whose links may leave it allows.
271
+ */
272
+ function rebase(walk, prefix, parts, rule) {
273
+ const { base } = walk;
274
+ if (isWithinUnder(joined(prefix, parts), joined(base.prefix, base.parts), rule.flavor)) {
275
+ walk.prefix = base.prefix;
276
+ walk.done = [...base.parts];
277
+ walk.pending = parts.slice(base.parts.length);
278
+ return undefined;
279
+ }
280
+ if (rule.outside === 'refuse')
281
+ return { why: CLIMBS_OUT, link: walk.link, text: walk.text };
282
+ walk.prefix = prefix;
283
+ walk.done = [];
284
+ walk.pending = parts;
285
+ return undefined;
286
+ }
287
+ /** The walk's starting position for `start`: from the root when `start` is below it, else from its own root. */
288
+ function startOf(start, rule, rootParts) {
289
+ const form = anchoredForm(start, rule.flavor, rootParts.prefix);
290
+ if (form.form === 'network')
291
+ return { why: 'network', link: start, text: start };
292
+ if (form.form !== 'rooted')
293
+ return { why: DRIVE_RELATIVE, link: start, text: start };
294
+ const parts = rule.flavor === 'win32' ? collapsed(form.rooted.parts) : [...form.rooted.parts];
295
+ const path = joined(form.rooted.prefix, parts);
296
+ const root = joined(rootParts.prefix, rootParts.parts);
297
+ if (isWithinUnder(path, root, rule.flavor) && (rule.flavor === 'win32' || !parts.includes('..'))) {
298
+ return { base: rootParts, prefix: rootParts.prefix, done: [...rootParts.parts], pending: parts.slice(rootParts.parts.length), link: start, text: start };
299
+ }
300
+ if (rule.outside === 'refuse')
301
+ return { why: CLIMBS_OUT, link: start, text: start };
302
+ return { base: rootParts, prefix: form.rooted.prefix, done: [], pending: parts, link: start, text: start };
303
+ }
304
+ /**
305
+ * Where `start` leads, every link on the way read by its text before anything follows it.
306
+ *
307
+ * @param start - An absolute path, in `rule.flavor`'s spelling (forward or back slashes)
308
+ * @param rule - The flavor, the tree, and what the chain may do outside it
309
+ * @param probe - `lstat` and `readlink`, and nothing that follows a link
310
+ * @returns The local path the kernel may now be asked about, nothing, or the refusal
311
+ * @throws whatever `probe` throws: a probe the OS refused is the caller's to classify
312
+ */
313
+ export function walkLinkChain(start, rule, probe) {
314
+ const rootForm = anchoredForm(rule.root, rule.flavor, '/');
315
+ if (rootForm.form !== 'rooted')
316
+ throw new TypeError(`walkLinkChain: the root must be a local absolute path, not ${rule.root}`);
317
+ const root = joined(rootForm.rooted.prefix, rootForm.rooted.parts);
318
+ const begun = startOf(start, rule, rootForm.rooted);
319
+ if ('why' in begun)
320
+ return { kind: 'refused', refusal: begun };
321
+ const walk = begun;
322
+ let final = 'directory';
323
+ // Each link met, with the text still to read after it: meeting one again is a cycle, exactly.
324
+ const followed = { met: new Set(), hops: 0 };
325
+ for (let part = walk.pending.shift(); part !== undefined; part = walk.pending.shift()) {
326
+ const step = stepTo(walk, part, rule, root, probe);
327
+ if (step.kind !== 'step')
328
+ return step;
329
+ if (step.stands === 'link') {
330
+ const ended = followLink(walk, rule, probe, followed);
331
+ if (ended !== undefined)
332
+ return ended;
333
+ }
334
+ else {
335
+ final = step.stands;
336
+ }
337
+ }
338
+ return { kind: 'resolved', path: joined(walk.prefix, walk.done), final };
339
+ }
340
+ /**
341
+ * Take one component: the kind that now stands at the walk's place (a `..` is a directory — the
342
+ * parent of a real directory), or the answer that ends the walk.
343
+ */
344
+ function stepTo(walk, part, rule, root, probe) {
345
+ if (part === '..')
346
+ walk.done.pop();
347
+ else
348
+ walk.done.push(part);
349
+ const here = joined(walk.prefix, walk.done);
350
+ if (rule.outside === 'refuse' && !isWithinUnder(here, root, rule.flavor))
351
+ return { kind: 'refused', refusal: { why: CLIMBS_OUT, link: walk.link, text: walk.text } };
352
+ if (part === '..')
353
+ return { kind: 'step', stands: 'directory' };
354
+ const kind = probe.kindAt(here);
355
+ if (kind === 'absent' || (kind === 'file' && walk.pending.length > 0))
356
+ return { kind: 'nothing', at: here, cause: 'absent' };
357
+ return { kind: 'step', stands: kind };
358
+ }
359
+ /**
360
+ * Follow the link at the walk's place by its text: a cycle when this link was already met with the
361
+ * same text left to read, the hop limit's refusal past {@link MAX_LINK_HOPS} links, the text's own
362
+ * refusal, or `undefined` to walk on. The hop count bounds the walk on its own, cycle or not.
363
+ */
364
+ function followLink(walk, rule, probe, followed) {
365
+ const here = joined(walk.prefix, walk.done);
366
+ const state = `${here}\0${walk.pending.join('/')}`;
367
+ followed.hops += 1;
368
+ if (followed.met.has(state))
369
+ return { kind: 'nothing', at: here, cause: 'cycle' };
370
+ followed.met.add(state);
371
+ if (followed.hops > MAX_LINK_HOPS)
372
+ return { kind: 'refused', refusal: { why: 'hop-limit', link: here } };
373
+ walk.done.pop();
374
+ const refusal = splice(walk, here, probe.textAt(here), rule);
375
+ return refusal === undefined ? undefined : { kind: 'refused', refusal };
376
+ }
377
+ //# sourceMappingURL=link-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-chain.js","sourceRoot":"","sources":["../../src/tree-change/link-chain.ts"],"names":[],"mappings":"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"}