@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,152 @@
1
+ /**
2
+ * Entry identity: are two names ONE entry on disk?
3
+ *
4
+ * Two changes to a tree can alias without sharing a spelling — `plugins/Old` and
5
+ * `plugins/old` on APFS or NTFS, a link and its target, two links to one target.
6
+ * Comparing names cannot see it, and acting on a wrong "different" deletes what the
7
+ * user just installed. So identity is asked of the filesystem:
8
+ *
9
+ * - `dev` and `ino` both nonzero → the id `${dev}:${ino}`, from a `bigint` `lstat` of
10
+ * the entry, plus a `stat` of a link's target. A link answers to BOTH: removing it
11
+ * removes only the link, but it aliases its target.
12
+ * - Otherwise (a filesystem that reports `ino` 0) → a folded real path: the real
13
+ * parent, a `/`, the base name, NFC then lower-cased. The fold can only err toward
14
+ * "same", which keeps what it is asked to remove.
15
+ *
16
+ * An entry the OS refuses to examine is `unknown`, never `different`: the caller
17
+ * keeps. {@link entryIdentities} throws the refusal as an `FsFaultError` on the side
18
+ * the CALLER names, so whatever surfaces it reports a classified fault, not a raw errno.
19
+ */
20
+ import { lstatSync } from 'node:fs';
21
+ import path from 'node:path';
22
+ import { requireConfirmedAbsent } from '../errors/confirmed-absent.js';
23
+ import { isPathAbsentError } from '../errors/errno-table.js';
24
+ import { classifyFsFault, everywhere, isFsFaultError } from '../errors/fs-fault.js';
25
+ import { edgeBigintStatSync } from '../link-following.js';
26
+ import { canonicalPath } from '../path-containment.js';
27
+ import { safePath, toForwardSlash, toNfc } from '../path-core.js';
28
+ import { normalizePath } from '../path-utils.js';
29
+ import { identityOf, identityOracleOver, insideBy, sameBy } from './identity-compare.js';
30
+ /** Run `work` for `entry`; a filesystem refusal is rethrown classified on `side`, anything else as it was. */
31
+ function guarded(entry, side, work) {
32
+ try {
33
+ return work();
34
+ }
35
+ catch (error) {
36
+ throw classifyFsFault(error, { side, action: 'examine the entry', path: entry });
37
+ }
38
+ }
39
+ /** The fold of `real` (an absolute path): its real parent, a `/`, its base name, NFC then lower-cased. */
40
+ function foldedRealPath(entry, side, real) {
41
+ return guarded(entry, side, () => ({
42
+ foldedRealPath: toNfc(`${toForwardSlash(normalizePath(path.dirname(real), path.dirname(real)))}/${path.basename(real)}`).toLowerCase(),
43
+ }));
44
+ }
45
+ /**
46
+ * `stat(entry)` for an entry that may be absent: `undefined` when nothing is there — believed only when
47
+ * the parent's listing agrees — and a classified fault when the OS refuses.
48
+ */
49
+ function examine(entry, side, follows) {
50
+ const ctx = { side, action: 'examine the entry', path: entry };
51
+ try {
52
+ return follows ? edgeBigintStatSync(entry) : lstatSync(entry, { bigint: true });
53
+ }
54
+ catch (error) {
55
+ if (!isPathAbsentError(error))
56
+ throw classifyFsFault(error, ctx);
57
+ // A `stat` follows a link (a dangling one is absent); an `lstat` sees the link itself.
58
+ requireConfirmedAbsent(entry, error, { action: ctx.action, path: entry }, { follows, sideOf: everywhere(side) });
59
+ return undefined;
60
+ }
61
+ }
62
+ /**
63
+ * Every identity `entry` answers to: `[]` when nothing is there; its own (`lstat`) for
64
+ * anything; plus its target's (`stat`) when it is a link that resolves.
65
+ *
66
+ * @param entry - Absolute path to identify
67
+ * @param side - The side of the verb that named `entry`; every fault (the `lstat`, the `stat`, the real path of a fold) is raised on it
68
+ * @throws FsFaultError on `side` when the OS refuses to examine `entry`
69
+ */
70
+ export function entryIdentities(entry, side) {
71
+ const own = examine(entry, side, false);
72
+ if (own === undefined)
73
+ return [];
74
+ const identities = [identityOf(own, () => foldedRealPath(entry, side, entry))];
75
+ if (!own.isSymbolicLink())
76
+ return identities;
77
+ // Every entry identified here is one the verb itself names (a destination it writes or removes):
78
+ // the operator's own path, its own link the edge the kernel answers.
79
+ const target = examine(entry, side, true);
80
+ if (target !== undefined) {
81
+ identities.push(identityOf(target, () => foldedRealPath(entry, side, guarded(entry, side, () => normalizePath(entry, entry)))));
82
+ }
83
+ return identities;
84
+ }
85
+ /**
86
+ * The identities of `entry`, or `undefined` when the OS refuses to examine it. The
87
+ * side is irrelevant here: a refusal becomes `unknown` and never surfaces.
88
+ */
89
+ function identitiesOrUndecided(entry) {
90
+ try {
91
+ return entryIdentities(entry, 'destination');
92
+ }
93
+ catch (error) {
94
+ if (isFsFaultError(error))
95
+ return undefined;
96
+ throw error;
97
+ }
98
+ }
99
+ /**
100
+ * Where `entry` physically is — its own place (its real parent directory, then its name) and, for a
101
+ * link, its target's — or `undefined` when the OS refuses to resolve it. `entry` need not exist: it
102
+ * is resolved from its deepest existing ancestor.
103
+ */
104
+ function physicalSpellings(entry) {
105
+ try {
106
+ return [safePath.join(canonicalPath(path.dirname(entry), path.dirname(entry)), path.basename(entry)), canonicalPath(entry, entry)];
107
+ }
108
+ catch (error) {
109
+ // A refusal is `unknown` to the caller, exactly as an entry it cannot examine is; a defect stays loud.
110
+ if (isFsFaultError(classifyFsFault(error, { side: 'destination', action: 'resolve the entry', path: entry })))
111
+ return undefined;
112
+ throw error;
113
+ }
114
+ }
115
+ /**
116
+ * Whether `a` and `b` are one entry. Any shared identity is `same`; identities on
117
+ * both sides with a filesystem id each and none shared is `different`; a folded path
118
+ * on either side that does not match is `unknown`; an entry the OS refuses to
119
+ * examine is `unknown`; an absent entry is `different` (nothing there can alias).
120
+ *
121
+ * @param a - Absolute path
122
+ * @param b - Absolute path
123
+ */
124
+ export function sameEntry(a, b) {
125
+ return sameBy(identitiesOrUndecided, a, b);
126
+ }
127
+ /**
128
+ * Whether `child` lies strictly under `ancestor`, judged by identity: some directory
129
+ * on the way up from `child` — along its spelling as given, and along where it
130
+ * physically is — is the same entry as `ancestor`. So a linked or case-aliased
131
+ * spelling of the ancestor is found, and so is a directory of `child`'s spelling that
132
+ * is a link to somewhere BELOW the ancestor. `child` need not exist.
133
+ * `unknown` when no ancestor proved `same` and one could not be examined.
134
+ *
135
+ * ⚠️ On a filesystem that reports `ino` 0 the answer for an existing `ancestor` is
136
+ * `unknown`, never `outside`: a fold cannot prove two names apart. A caller that
137
+ * refuses on `inside` must not refuse on `unknown`, or every copy there is refused.
138
+ *
139
+ * @param child - Absolute path
140
+ * @param ancestor - Absolute path
141
+ */
142
+ export function isInsideByIdentity(child, ancestor) {
143
+ return insideBy(identitiesOrUndecided, child, ancestor, physicalSpellings);
144
+ }
145
+ /**
146
+ * {@link sameEntry} and {@link isInsideByIdentity} over one memo: each entry is examined once
147
+ * (`entryIdentities(entry, 'destination')`), so every answer of a plan is built from one observation.
148
+ */
149
+ export function identityOracle() {
150
+ return identityOracleOver((entry) => entryIdentities(entry, 'destination'), physicalSpellings);
151
+ }
152
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/tree-change/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /** The plan/apply tree-change primitive: entry identity, the readable-tree proof and the copy, the planner, the applier and the file helpers. */
2
+ export { entryIdentities, isInsideByIdentity, sameEntry } from './identity.js';
3
+ export type { EntryContainment, EntrySameness, Identity } from './identity-compare.js';
4
+ export { COPY_LINK_ESCAPES_SOURCE_CODE, CopyLinkEscapesSourceError, DIRECTORY_WALK_REVISITED_CODE, DirectoryWalkRevisitedError, FollowedWalk, } from './followed-walk.js';
5
+ export { proveTreeReadable, readRegularFile } from './readable-tree.js';
6
+ export type { ProveTreeReadableOptions } from './readable-tree.js';
7
+ export { copyRegularFile, copyTree } from './copy-tree.js';
8
+ export type { CopyRegularFileOptions, CopyTreeOptions } from './copy-tree.js';
9
+ export type { CopyOnto } from './copy-decisions.js';
10
+ export type { LinkPolicy, TreeWalkOptions } from './tree-walk.js';
11
+ export { refuseEscapingLinks, treeChain, treeContainment } from './link-containment.js';
12
+ export type { LinkContainmentCheck } from './link-containment.js';
13
+ export { isLinkChainRefusedError } from './host-chain.js';
14
+ export { HOST_PATH_FLAVOR, isWithinUnder, untrustedLinkRefusal, untrustedTextRefusal, win32FoldedName } from './link-chain.js';
15
+ export { planTreeChanges, TREE_DEST_HOLDS_SOURCE_CODE, TREE_DEST_NOT_OWNED_CODE, TREE_DEST_OCCUPIED_CODE, TREE_DESTS_OVERLAP_CODE, TREE_SOURCE_HOLDS_DEST_CODE, } from './plan.js';
16
+ export type { EntryKind, FileContents, Ownership, OwnershipVerdict, PlannedAction, PlannedChange, TreeChange, TreeFill, TreePlan } from './plan.js';
17
+ export { applyTreePlan, applyTreePlanOrLeftover, TREE_CLEANUP_INCOMPLETE_CODE } from './apply.js';
18
+ export { TREE_ROLLBACK_INCOMPLETE_CODE, TreeRollbackIncompleteError } from './rollback-error.js';
19
+ export type { TreeRollbackStranded } from './rollback-error.js';
20
+ export type { ApplyOptions, ApplyOutcome, ApplyResult, TreeChangeWarning } from './apply.js';
21
+ export type { TempDirOutcome } from './files.js';
22
+ export { disposeTempDir, disposeTempDirAfterFailure, disposeTempDirSync, makeDirectoryUnder, renameFileAtomic, replaceFile, TEMP_DIR_OUTSIDE_TMPDIR_CODE, withTempDir, writeFileUnder } from './files.js';
23
+ export { isParkedTreeEntry, isTreeChangeResidue } from './staging-names.js';
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tree-change/index.ts"],"names":[],"mappings":"AAAA,iJAAiJ;AACjJ,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9E,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxF,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/H,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpJ,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7F,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,4BAA4B,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1M,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** The plan/apply tree-change primitive: entry identity, the readable-tree proof and the copy, the planner, the applier and the file helpers. */
2
+ export { entryIdentities, isInsideByIdentity, sameEntry } from './identity.js';
3
+ export { COPY_LINK_ESCAPES_SOURCE_CODE, CopyLinkEscapesSourceError, DIRECTORY_WALK_REVISITED_CODE, DirectoryWalkRevisitedError, FollowedWalk, } from './followed-walk.js';
4
+ export { proveTreeReadable, readRegularFile } from './readable-tree.js';
5
+ export { copyRegularFile, copyTree } from './copy-tree.js';
6
+ export { refuseEscapingLinks, treeChain, treeContainment } from './link-containment.js';
7
+ export { isLinkChainRefusedError } from './host-chain.js';
8
+ export { HOST_PATH_FLAVOR, isWithinUnder, untrustedLinkRefusal, untrustedTextRefusal, win32FoldedName } from './link-chain.js';
9
+ export { planTreeChanges, TREE_DEST_HOLDS_SOURCE_CODE, TREE_DEST_NOT_OWNED_CODE, TREE_DEST_OCCUPIED_CODE, TREE_DESTS_OVERLAP_CODE, TREE_SOURCE_HOLDS_DEST_CODE, } from './plan.js';
10
+ export { applyTreePlan, applyTreePlanOrLeftover, TREE_CLEANUP_INCOMPLETE_CODE } from './apply.js';
11
+ export { TREE_ROLLBACK_INCOMPLETE_CODE, TreeRollbackIncompleteError } from './rollback-error.js';
12
+ export { disposeTempDir, disposeTempDirAfterFailure, disposeTempDirSync, makeDirectoryUnder, renameFileAtomic, replaceFile, TEMP_DIR_OUTSIDE_TMPDIR_CODE, withTempDir, writeFileUnder } from './files.js';
13
+ export { isParkedTreeEntry, isTreeChangeResidue } from './staging-names.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tree-change/index.ts"],"names":[],"mappings":"AAAA,iJAAiJ;AACjJ,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI3D,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/H,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAIjG,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,4BAA4B,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1M,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -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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"}