@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 @@
1
+ {"version":3,"file":"copy-tree.d.ts","sourceRoot":"","sources":["../../src/tree-change/copy-tree.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAKrE,OAAO,EAAgC,KAAK,QAAQ,EAAwE,MAAM,qBAAqB,CAAC;AACxJ,OAAO,EAAE,KAAK,gBAAgB,EAAqC,MAAM,YAAY,CAAC;AAEtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAA8B,KAAK,WAAW,EAAY,MAAM,gBAAgB,CAAC;AAqDxF,mGAAmG;AACnG,UAAU,eAAe;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA+BD,wHAAwH;AACxH,MAAM,WAAW,eAAgB,SAAQ,wBAAwB;IAC/D,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAgE7G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtH;AAED,mIAAmI;AACnI,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBpI"}
@@ -0,0 +1,279 @@
1
+ /**
2
+ * `copyTree`: copy a directory tree by the same walk `proveTreeReadable` proves it
3
+ * with, reading each file from the very handle the walk judged a regular file.
4
+ */
5
+ import fs from 'node:fs/promises';
6
+ import { isAlreadyExistsError, isPathAbsentError } from '../errors/errno-table.js';
7
+ import { classifyFsFault } from '../errors/fs-fault.js';
8
+ import { linkCopyType } from '../link-following.js';
9
+ import { safePath } from '../path-core.js';
10
+ import { openForReading } from '../text-file.js';
11
+ import { aliasFault, foldedNameIsTwin, mayTakeOver, nameOf, parentOf, sameNameWhereFolded } from './copy-decisions.js';
12
+ import { makeDirectoryUnder, openFileUnder } from './files.js';
13
+ import { sameEntry } from './identity.js';
14
+ import { readingTree, treeReadFault, walkTree } from './tree-walk.js';
15
+ /** The owner's read, write and search bits, kept on every copied directory so its owner can always fill and remove it. */
16
+ const OWNER_RWX = 0o700;
17
+ /** The bytes of an open file being copied; a read failure is `readFault`'s. A write failure never reaches this catch: the writer's loop ends the generator with `return()`, not `throw()`. */
18
+ async function* sourceBytes(source, readFault) {
19
+ try {
20
+ yield* source.createReadStream({ autoClose: false, start: 0 });
21
+ }
22
+ catch (error) {
23
+ throw readFault(error);
24
+ }
25
+ }
26
+ /**
27
+ * Copy the bytes of an open file — one already judged a regular file on that very handle — into
28
+ * `claimed`, a file this copy has just made exclusively (opened `wx`), and carry its permission bits
29
+ * over, so a script stays executable — never its setuid, setgid or sticky bit. The ONE file copy of the tree copy and the
30
+ * single-file copy: bytes and mode both go to that very handle, never to a path, so nothing swapped
31
+ * in after the claim — a link least of all — is written or chmod-ed through. Closes `claimed`.
32
+ */
33
+ async function copyOpenFileInto(handle, claimed, mode, readFault) {
34
+ try {
35
+ // On the handle, not through a stream of it: a stream owns the handle it writes and closes it
36
+ // with itself, before the mode could be set on it.
37
+ await claimed.writeFile(sourceBytes(handle, readFault));
38
+ // The permission bits only: setuid, setgid and sticky never travel with a copy.
39
+ await claimed.chmod(mode & 0o777);
40
+ }
41
+ catch (error) {
42
+ // The copy's own failure is the answer: a close refused after it is not a second one.
43
+ await claimed.close().catch(() => undefined);
44
+ throw error;
45
+ }
46
+ await claimed.close();
47
+ }
48
+ /**
49
+ * Make the root of a copy: with its parents when nothing is there, adopted when a real directory
50
+ * is. A link there — to a directory included — or a file is never adopted: the plain `mkdir` is the
51
+ * OS's own `EEXIST` for it, raw, as for any entry of a `fresh` destination.
52
+ */
53
+ async function claimRoot(into) {
54
+ const there = await fs.lstat(into).catch((error) => {
55
+ if (isPathAbsentError(error))
56
+ return undefined;
57
+ throw error;
58
+ });
59
+ if (there === undefined)
60
+ await fs.mkdir(into, { recursive: true });
61
+ else if (!there.isDirectory())
62
+ await fs.mkdir(into);
63
+ }
64
+ /**
65
+ * Make the directory a copy goes into ({@link CopyDestination}): the root as the caller's, every
66
+ * component under it as a real directory or not at all.
67
+ *
68
+ * @param copied - What is copied, for a refusal's message
69
+ * @returns The directory's path
70
+ */
71
+ async function claimDestination(into, copied) {
72
+ await claimRoot(into.root);
73
+ return into.relative === '' ? into.root : await makeDirectoryUnder(into.root, into.relative, `the copy of ${copied}`);
74
+ }
75
+ /**
76
+ * Make the directory a copy's own entry goes IN, for a source that is not a directory at all — a
77
+ * root that is a link, kept as one (`links: 'preserve'`): the destination root's parents as the
78
+ * caller's, every component under the root as a real directory or not at all.
79
+ *
80
+ * @param copied - What is copied, for a refusal's message
81
+ */
82
+ async function claimParentOfDestination(into, copied) {
83
+ if (into.relative === '') {
84
+ await fs.mkdir(parentOf(into.root), { recursive: true });
85
+ return;
86
+ }
87
+ await claimRoot(into.root);
88
+ const parent = parentOf(into.relative);
89
+ if (parent !== '')
90
+ await makeDirectoryUnder(into.root, parent, `the copy of ${copied}`);
91
+ }
92
+ /**
93
+ * The visitor that writes the copy of each entry the walk accepts under `dest`; every read of the
94
+ * copied tree is on `side`.
95
+ *
96
+ * Every create below the root is EXCLUSIVE and never follows a link: a file is opened `wx`, a
97
+ * directory made by a plain `mkdir`, a link made only where nothing is. So a create that finds
98
+ * something answers `EEXIST`, and what happens then is decided in this order:
99
+ *
100
+ * 1. The entry there is one THIS copy made — the same entry by identity, or (where the OS will not
101
+ * say) a sibling whose name is the same once case and Unicode form are folded: two source names,
102
+ * one destination name. Refused as the source's layout (`aliasFault`), under either mode.
103
+ * 2. `fresh`: refused as it is (`EEXIST`), for the caller's boundary to classify.
104
+ * 3. `merge`: a real directory is adopted; a file or a link is removed (the link, never its target)
105
+ * and the entry made anew; anything else — a directory where a file goes, a link where a
106
+ * directory goes — is refused as it is (`mayTakeOver`).
107
+ *
108
+ * Where the copy goes is made by {@link claimDestination}: the root is the caller's (made with its
109
+ * parents when absent, adopted when a real directory, refused `EEXIST` when a link or a file — a link
110
+ * taken for it would put the whole copy where it points), and every directory from the root down to
111
+ * the copy is a real one or is refused as the layout of the tree it is in.
112
+ *
113
+ * Exported for `copyTree` and for the suite that hands it two entries by name — a real alias needs
114
+ * a case-sensitive source and a case-folding destination, which no one temp directory offers.
115
+ */
116
+ export function copyVisitor(where, copied, side, onto) {
117
+ const dest = where.relative === '' ? where.root : safePath.join(where.root, where.relative);
118
+ const target = (relative) => (relative === '' ? dest : safePath.join(dest, relative));
119
+ /** What this copy made, by the directory (relative to the root) it made it in. */
120
+ const made = new Map();
121
+ const madeIn = (parent) => {
122
+ const list = made.get(parent) ?? [];
123
+ made.set(parent, list);
124
+ return list;
125
+ };
126
+ const twinOf = async (entry, into) => {
127
+ const siblings = madeIn(parentOf(entry.relative));
128
+ const byIdentity = siblings.find((each) => sameEntry(target(each.relative), into) === 'same');
129
+ if (byIdentity !== undefined)
130
+ return byIdentity;
131
+ const byName = siblings.find((each) => sameNameWhereFolded(nameOf(each.relative), nameOf(entry.relative)));
132
+ if (byName === undefined)
133
+ return undefined;
134
+ // Absent or unexaminable alike: then the collision is all there is to go on, and the name decides.
135
+ const present = await fs.lstat(into).then(() => true, () => false);
136
+ return foldedNameIsTwin(sameEntry(target(byName.relative), into), present) ? byName : undefined;
137
+ };
138
+ /**
139
+ * Claim `entry`'s place by `claim` — an exclusive create, and nothing else, so it can be asked
140
+ * twice — and hand back what it made (`undefined`: a directory a merge adopted).
141
+ */
142
+ const create = async (entry, kind, claim) => {
143
+ const into = target(entry.relative);
144
+ const claimed = await claim(into).catch(async (error) => {
145
+ if (!isAlreadyExistsError(error))
146
+ throw error;
147
+ const twin = await twinOf(entry, into);
148
+ if (twin !== undefined)
149
+ throw aliasFault(twin, entry, error);
150
+ const takeOver = mayTakeOver(onto, kind, onto === 'merge' ? (await fs.lstat(into)).isDirectory() : false);
151
+ if (takeOver === 'refuse')
152
+ throw error;
153
+ if (takeOver === 'adopt')
154
+ return undefined;
155
+ await fs.unlink(into);
156
+ return claim(into);
157
+ });
158
+ madeIn(parentOf(entry.relative)).push(entry);
159
+ return claimed;
160
+ };
161
+ return {
162
+ async directory(entry, stats) {
163
+ const into = target(entry.relative);
164
+ if (entry.relative === '')
165
+ await claimDestination(where, copied);
166
+ else
167
+ await create(entry, 'directory', (path) => fs.mkdir(path));
168
+ await fs.chmod(into, (stats.mode & 0o7777) | OWNER_RWX);
169
+ },
170
+ async file(entry, handle, stats) {
171
+ const claimed = await create(entry, 'leaf', (into) => fs.open(into, 'wx'));
172
+ if (claimed !== undefined)
173
+ await copyOpenFileInto(handle, claimed, stats.mode, (error) => treeReadFault(side, entry.path, error));
174
+ },
175
+ async link(entry) {
176
+ const linkTarget = await readingTree(side, entry.path, () => fs.readlink(entry.path));
177
+ // Typed from the chain read by its text, before anything is made: an untyped `symlink` has
178
+ // Node on Windows ask the kernel to follow the target to pick `file` or `dir`, so a network or
179
+ // device text — at any hop — is refused here, on the copied tree's side, never handed on.
180
+ const type = await readingTree(side, entry.path, () => Promise.resolve().then(() => linkCopyType(entry.path, copied)));
181
+ // The source itself is a link: there is no copied directory to hold it, so its place is made first.
182
+ if (entry.relative === '')
183
+ await claimParentOfDestination(where, copied);
184
+ // Verbatim. A host that cannot make the link (Windows without the privilege: EPERM; a
185
+ // filesystem with none: ENOTSUP) raises the raw errno, a write like any other, for the
186
+ // caller's boundary to classify.
187
+ await create(entry, 'leaf', (into) => fs.symlink(linkTarget, into, type));
188
+ },
189
+ };
190
+ }
191
+ /**
192
+ * Copy the directory `source` to `relative` under `root` ({@link CopyDestination}).
193
+ *
194
+ * `root` is a directory the caller made or its user named: created, with its parents, when absent;
195
+ * adopted when a real directory; refused, `EEXIST`, when a link or a file stands there. `relative`
196
+ * is `''` for a copy onto `root` itself. A copy that goes INSIDE a tree — a pool skill into a staged
197
+ * plugin that earlier copies filled, links kept — names that tree's root and where under it, and
198
+ * every directory between the two must then be a real one (made when absent): a link or a file
199
+ * there is a `source` fault (origin `content`, class `occupied`) naming it, never followed. So a
200
+ * caller cannot reach through a link by how deep its destination is: the only directory taken on
201
+ * trust is the one it declares as `root`.
202
+ *
203
+ * Entries are walked, and refused, exactly as `proveTreeReadable` proves them (see
204
+ * `tree-walk.ts`): a `follow-contained` link is copied as what it points at, inside
205
+ * the source only; a `preserve` link is copied as a link, its target string
206
+ * verbatim and never followed — its chain is read by text alone to give the copy its `file`/`dir`
207
+ * type, and one through a network or device path is refused (a `refused` fault on `options.side`)
208
+ * before anything is made; an entry `filter` excludes is never touched; a named
209
+ * pipe, socket or device is refused unopened. Each file is copied from the handle
210
+ * `fstat` judged, with its mode. Each directory takes its source's mode with the
211
+ * owner's rwx kept (`| 0o700`): a read-only source must not become a copy nothing
212
+ * can fill or remove.
213
+ *
214
+ * Below the copy's own directory nothing already there is ever adopted or written through, and two source
215
+ * entries that are one name there are refused as the source's layout (a `source` fault
216
+ * naming both): see {@link copyVisitor} and `options.onto`.
217
+ *
218
+ * The copy names no side for what it WRITES: a failure writing the copy is the raw
219
+ * errno, for the caller's boundary to classify — a link this host cannot create, and an
220
+ * `EEXIST` for something already at a name of a `fresh` destination, included. What it READS is on the side the caller names, as the proof's reads
221
+ * are: a refusal is a classified fault on `options.side` (origin `content`) naming
222
+ * the entry. Only the caller knows whether the tree is an input (`source`), VAT's
223
+ * staging (`environment`) or a copy already made into user state (`destination`).
224
+ *
225
+ * @param source - The directory to copy
226
+ * @param root - The directory the caller made or owns, which the copy goes into or under
227
+ * @param relative - Where under `root` the copy goes, forward slashes; `''` for `root` itself
228
+ * @param options - `links`, `filter` and `side`, exactly as `proveTreeReadable` was given them, and `onto`
229
+ */
230
+ export async function copyTree(source, root, relative, options) {
231
+ await walkTree(source, options, options.side, copyVisitor({ root, relative }, source, options.side, options.onto));
232
+ }
233
+ /**
234
+ * Copy ONE regular file to `relative` under `root`, as {@link copyTree} copies a file: opened
235
+ * without blocking and judged by `fstat` on that handle (a named pipe, socket or device is refused
236
+ * before a byte is read, never waited on), its bytes copied from that very handle, its mode
237
+ * carried over.
238
+ *
239
+ * The copy is made as `writeFileUnder` makes a file — it is the same primitive: every directory of
240
+ * `relative` must be a real directory (made by a plain `mkdir` when absent), the file is created
241
+ * exclusively, and its bytes and mode go to that handle. So it never writes through a link, at the
242
+ * file's name or on the way to it; `options.existing` decides a regular file already there.
243
+ *
244
+ * Every read is a classified fault on `options.side` (origin `content`), naming what was read. An
245
+ * entry in the copy's way is a `source` fault (origin `content`, class `occupied`) naming it; any
246
+ * other failure writing is the raw errno, for the caller's boundary to classify.
247
+ *
248
+ * @param source - The file to copy
249
+ * @param root - The root of the tree the copy goes into, which the caller made
250
+ * @param relative - Where the copy goes, relative to `root`, forward slashes
251
+ * @param options - The side `source` is on and what it is; `existing` and `writing`, for the copy
252
+ */
253
+ export async function copyRegularFile(source, root, relative, options) {
254
+ // Every read — the open, the `fstat`, the stream, the close — names what was read, on its side.
255
+ const readFault = (error) => classifyFsFault(error, { side: options.side, origin: 'content', action: `read ${options.reading}`, path: source });
256
+ const reading = async (read) => {
257
+ try {
258
+ return await read();
259
+ }
260
+ catch (error) {
261
+ throw readFault(error);
262
+ }
263
+ };
264
+ const handle = await reading(() => openForReading(source));
265
+ try {
266
+ const stats = await reading(() => handle.stat());
267
+ // Only once the source is open and judged: a `replace` removes nothing for a copy that cannot be read.
268
+ const claimed = await openFileUnder(root, relative, options);
269
+ await copyOpenFileInto(handle, claimed, stats.mode, readFault);
270
+ }
271
+ catch (error) {
272
+ // The copy's own failure is the answer: a close refused after it is not a second one.
273
+ await handle.close().catch(() => undefined);
274
+ throw error;
275
+ }
276
+ // The close is a read of the source, too: a refusal of it is on the source's side.
277
+ await reading(() => handle.close());
278
+ }
279
+ //# sourceMappingURL=copy-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-tree.js","sourceRoot":"","sources":["../../src/tree-change/copy-tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * The file-level half of the tree-change primitive: the staging names every
3
+ * change sits beside its destination under, the one rename (retried where Windows
4
+ * makes a rename race a scanner), the one removal (a read-only tree included), a
5
+ * whole-file replace that never truncates what it replaces, and a temp directory
6
+ * that is always disposed of.
7
+ */
8
+ import { type FileHandle } from 'node:fs/promises';
9
+ export { TEMP_DIR_OUTSIDE_TMPDIR_CODE } from './apply-decisions.js';
10
+ /**
11
+ * Rename `from` to `to` — a file or a whole tree — in one step. Under win32 a
12
+ * rename the OS refuses with contention (`EPERM`, `EBUSY`, `EACCES`: a scanner,
13
+ * an indexer, a handle still closing) is retried, up to 6 tries with a backoff of
14
+ * 50·2ⁿ ms (1.55 s in all); any other errno, and any errno on another platform,
15
+ * is final.
16
+ *
17
+ * @param from - The entry to move
18
+ * @param to - Its new name, on the same filesystem
19
+ * @throws the rename's raw errno, for the caller to classify on the side it knows
20
+ */
21
+ export declare function renameFileAtomic(from: string, to: string): Promise<void>;
22
+ /**
23
+ * Remove `entry` — a tree, a file or a link (never what it points at) — whatever
24
+ * the modes under it: the root gets the owner's rwx first, and a removal still
25
+ * refused (`EACCES` / `EPERM`: a read-only directory deeper down refuses the
26
+ * removal of its entries) is retried once every directory under it has been given
27
+ * them too. Nothing there is not a failure. A root the OS will not chmod is still
28
+ * removed when the removal itself is allowed. Done means not there: a removal that
29
+ * resolved with the entry still present (`rm`'s `force` reading its own listing's
30
+ * ENOENT as "gone") is run once more, and a plain `rmdir` then throws why it stays.
31
+ *
32
+ * @throws the raw errno of the chmod, listing or removal that failed
33
+ */
34
+ export declare function removeEntry(entry: string): Promise<void>;
35
+ /**
36
+ * Replace the file `dest` with `contents` in one step: the bytes go to a temp
37
+ * name beside it, which is then renamed over it ({@link renameFileAtomic}). A
38
+ * write that fails — a full disk mid-write — leaves `dest` byte-equal and the
39
+ * temp removed (a temp that will not go is recorded beside the thrown error:
40
+ * `suppressedFaultsOf`). An existing file keeps its mode, and must be writable as it is: one
41
+ * a write in place would be refused (a read-only file) is refused before anything is written,
42
+ * never replaced by the rename. A link is written through, so it stays a link to the replaced file.
43
+ *
44
+ * @param dest - The file to replace or create
45
+ * @param contents - Its new bytes
46
+ * @throws the raw errno, for the caller to classify on the side it knows
47
+ */
48
+ export declare function replaceFile(dest: string, contents: string | Uint8Array): Promise<void>;
49
+ /**
50
+ * Make the directory `relative` under `root` — a tree VAT is building, which may hold whatever its
51
+ * source shipped, links included — without ever going through a link: every component must be a real
52
+ * directory (adopted when there, made by a plain `mkdir` when absent). What a recursive `mkdir`
53
+ * would do instead is follow a link standing where a component goes, out of the tree.
54
+ *
55
+ * @param root - The tree's root, which the caller made
56
+ * @param relative - The directory, relative to `root`, forward slashes
57
+ * @param writing - What the directory is for, for a refusal's message
58
+ * @returns The directory's path
59
+ * @throws FsFaultError side `source`, origin `content`, class `occupied`, naming the link or file
60
+ * standing where a directory goes; any other failure is the raw errno
61
+ */
62
+ export declare function makeDirectoryUnder(root: string, relative: string, writing: string): Promise<string>;
63
+ /** What decides the entry already AT the name of a file VAT makes under a tree, and what a refusal calls the file. */
64
+ export interface FileUnderOptions {
65
+ /**
66
+ * - `refuse`: anything there refuses the file.
67
+ * - `replace`: a regular file there is removed first (VAT's own earlier output, or a file the source
68
+ * shipped that VAT's supersedes); a link or a directory still refuses.
69
+ */
70
+ readonly existing: 'refuse' | 'replace';
71
+ /** What the file is, for a refusal's message. */
72
+ readonly writing: string;
73
+ }
74
+ /**
75
+ * Write a file VAT makes into a tree it is building, never over or through what is already there
76
+ * (see {@link createUnder}: real directories only, an exclusive create, `existing` deciding the
77
+ * entry at the file's own name).
78
+ *
79
+ * @param root - The tree's root, which the caller made
80
+ * @param relative - The file, relative to `root`, forward slashes
81
+ * @param contents - Its bytes
82
+ * @param options - `existing`, and `writing`: what the file is, for a refusal's message
83
+ * @throws FsFaultError side `source`, origin `content`, class `occupied`, naming the entry in the
84
+ * way; any other failure is the raw errno, for the caller's boundary to classify
85
+ */
86
+ export declare function writeFileUnder(root: string, relative: string, contents: string | Uint8Array, options: FileUnderOptions): Promise<void>;
87
+ /**
88
+ * {@link writeFileUnder} for bytes that are streamed: the file is made the same way and handed back
89
+ * OPEN, for the caller to fill and close. What the copy of one file into such a tree is built on.
90
+ *
91
+ * @returns The new file's handle, open for writing; the caller closes it
92
+ */
93
+ export declare function openFileUnder(root: string, relative: string, options: FileUnderOptions): Promise<FileHandle>;
94
+ /**
95
+ * Remove a temporary directory, its modes notwithstanding — only one strictly
96
+ * under the temp directory (`normalizedTmpdir()`): this removal makes a read-only
97
+ * tree writable before it deletes it, so a wrong argument (an empty string, a
98
+ * project directory) must be refused before anything is touched. A directory
99
+ * elsewhere that VAT owns goes through a `remove` plan.
100
+ *
101
+ * @param dir - The directory
102
+ * @returns `undefined` when it is gone (or was never there); otherwise the leftover: the
103
+ * classified fault (an `FsFaultError`, side `environment`) whose message names the directory,
104
+ * for the caller to report as the one `TREE_CLEANUP_INCOMPLETE` warning beside its work
105
+ * @throws VatError {@link TEMP_DIR_OUTSIDE_TMPDIR_CODE} — a defect in the caller — for a
106
+ * directory not strictly under the temp directory, with nothing removed
107
+ */
108
+ export declare function disposeTempDir(dir: string): Promise<unknown>;
109
+ /**
110
+ * {@link disposeTempDir}, synchronously — for a process on its way OUT, where no promise will ever
111
+ * settle: a signal handler about to re-raise, an `exit` listener. Same refusal of any directory not
112
+ * strictly under the temp directory, same answer.
113
+ *
114
+ * ⚠️ No mode repair: it is for a directory the process filled with its own ordinary files. A
115
+ * SHORT bounded retry ({@link SYNC_DISPOSAL_RETRIES} × {@link SYNC_DISPOSAL_RETRY_DELAY_MS}, linear
116
+ * backoff, at most ~0.4 s) covers the one transient refusal a dying process meets: on Windows a
117
+ * child it has just terminated still holds a handle in the directory for a moment, and removal
118
+ * answers `EBUSY`/`EPERM` until the OS lets go. Anything still there after it is answered as the
119
+ * leftover fault.
120
+ *
121
+ * @param dir - The directory
122
+ * @returns `undefined` when it is gone (or was never there); otherwise the leftover fault
123
+ * @throws VatError {@link TEMP_DIR_OUTSIDE_TMPDIR_CODE} — a defect in the caller — for a
124
+ * directory not strictly under the temp directory, with nothing removed
125
+ */
126
+ export declare function disposeTempDirSync(dir: string): unknown;
127
+ /**
128
+ * Dispose of a temporary directory because the work that owned it failed — for a directory
129
+ * whose lifetime is not one call (one handed back to the caller on success, removed only on
130
+ * failure), where {@link withTempDir} cannot hold it. The failure stays the answer: a
131
+ * disposal fault is recorded beside it (`suppressedFaultsOf`), never thrown in its place.
132
+ *
133
+ * @param dir - The directory, strictly under the temp directory
134
+ * @param failure - What the work threw; the caller rethrows it
135
+ * @returns `undefined` when the directory went; otherwise the leftover fault, already recorded
136
+ * beside `failure` — for a caller that must also say so where no report will carry the failure
137
+ * @throws VatError {@link TEMP_DIR_OUTSIDE_TMPDIR_CODE} — a defect in the caller — for a
138
+ * directory not strictly under the temp directory, with nothing removed
139
+ */
140
+ export declare function disposeTempDirAfterFailure(dir: string, failure: unknown): Promise<unknown>;
141
+ /** What {@link withTempDir} hands back once its work SUCCEEDED: the work's value, and what the disposal left. */
142
+ export interface TempDirOutcome<T> {
143
+ /** What `work` returned. */
144
+ readonly value: T;
145
+ /**
146
+ * `undefined` when the directory went; otherwise the classified fault (an `FsFaultError`, side
147
+ * `environment`, its message naming the directory) of a directory the OS would not remove. The
148
+ * work is DONE: this is never its refusal, but the one `TREE_CLEANUP_INCOMPLETE` warning a verb
149
+ * reports beside the work it finished.
150
+ */
151
+ readonly leftover: unknown;
152
+ }
153
+ /**
154
+ * Run `work` in a fresh directory under the temp dir (`normalizedTmpdir()`), and
155
+ * dispose of it after, whatever happened.
156
+ *
157
+ * The work's error always wins: rethrown unchanged — never wrapped, never given a
158
+ * cause, never mutated — with a disposal failure recorded beside it
159
+ * (`suppressedFaultsOf`), so a report still names the directory left behind.
160
+ * When the work succeeded, nothing is thrown: the value comes back with the
161
+ * disposal's `leftover` (see {@link TempDirOutcome}), for the caller to report.
162
+ *
163
+ * @param prefix - `mkdtemp` prefix, so a leaked directory names its owner
164
+ * @param work - Given the directory's forward-slash path
165
+ */
166
+ export declare function withTempDir<T>(prefix: string, work: (dir: string) => Promise<T>): Promise<TempDirOutcome<T>>;
167
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/tree-change/files.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAW,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAmBvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAWpE;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExE;AA4BD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9D;AA+DD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5F;AAkCD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzG;AAED,sHAAsH;AACtH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IACxC,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AA6BD;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5I;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAE5G;AAoBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGlE;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQvD;AASD;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhG;AAED,iHAAiH;AACjH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAWlH"}