@hawkeyexl/manni 4.9.0 → 4.11.0

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 (196) hide show
  1. package/README.md +1 -1
  2. package/dist/{batch-W5GXL2P4.js → batch-UAULUMGH.js} +18 -14
  3. package/dist/{build-Q56FEXR6.js → build-QSPNK5AL.js} +14 -11
  4. package/dist/calibrate-KXN4URPM.js +40 -0
  5. package/dist/{capture-GMYH4ATL.js → capture-JHTSUPUC.js} +16 -13
  6. package/dist/{check-OAJWC3WD.js → check-MDGXEUWL.js} +5 -5
  7. package/dist/{check-KUBELO5G.js → check-VELUQAUW.js} +17 -14
  8. package/dist/{check-KUBELO5G.js.map → check-VELUQAUW.js.map} +1 -1
  9. package/dist/check-WOQRKNZK.js +29 -0
  10. package/dist/{check-ZE5HIDOW.js → check-YEOYBFST.js} +14 -11
  11. package/dist/{chunk-FGUXMZXO.js → chunk-2DRHT73U.js} +7 -7
  12. package/dist/{chunk-XHFHXOQQ.js → chunk-2IAZEB45.js} +8 -8
  13. package/dist/{chunk-35WTFPQS.js → chunk-2IR4T5PO.js} +2 -2
  14. package/dist/{chunk-KX5IYIOO.js → chunk-2MJ62AYQ.js} +2 -2
  15. package/dist/chunk-2MJ62AYQ.js.map +1 -0
  16. package/dist/{chunk-OWSNXPPX.js → chunk-4CK6ZSOK.js} +6 -6
  17. package/dist/{chunk-YUZ2ECZY.js → chunk-4D5IZSXM.js} +3 -3
  18. package/dist/{chunk-RFSGKF4O.js → chunk-4VBSTXN6.js} +4 -4
  19. package/dist/{chunk-3XMG4CZY.js → chunk-54F74XR3.js} +15 -13
  20. package/dist/{chunk-3XMG4CZY.js.map → chunk-54F74XR3.js.map} +1 -1
  21. package/dist/{chunk-IN6DWNVE.js → chunk-6HWBRZIN.js} +47 -191
  22. package/dist/chunk-6HWBRZIN.js.map +1 -0
  23. package/dist/{chunk-GEMXGXPR.js → chunk-ADMEPUBT.js} +3 -3
  24. package/dist/{chunk-23UAGEKP.js → chunk-B7XYNRJQ.js} +2 -2
  25. package/dist/{chunk-QKE3XS2L.js → chunk-BBT6AVUG.js} +2 -2
  26. package/dist/{chunk-VKWZCEWB.js → chunk-BKVHX6FU.js} +4 -4
  27. package/dist/{chunk-X6WLCNKC.js → chunk-D4N2V43Z.js} +4 -4
  28. package/dist/{chunk-XXDXIIQ4.js → chunk-EFW7CAG4.js} +2 -2
  29. package/dist/{chunk-UDUHRXJ4.js → chunk-EIFCP6CZ.js} +2 -2
  30. package/dist/{chunk-KPXOJMQ6.js → chunk-FBMZJEB3.js} +2 -2
  31. package/dist/{chunk-HRXDM6H6.js → chunk-FQXWVS6J.js} +8 -8
  32. package/dist/{chunk-76I6I5N2.js → chunk-FUCPZVRY.js} +2 -2
  33. package/dist/{chunk-DTGIUMZV.js → chunk-GDVZ5VII.js} +4 -4
  34. package/dist/{chunk-QCCAHV6A.js → chunk-GV56WGL2.js} +321 -3308
  35. package/dist/chunk-GV56WGL2.js.map +1 -0
  36. package/dist/{chunk-TKTRIZT4.js → chunk-HA6MQMOY.js} +6 -6
  37. package/dist/{chunk-ECVQNGTH.js → chunk-HQR2ZBHW.js} +3 -3
  38. package/dist/{chunk-MHAYLDBK.js → chunk-IGSHYP74.js} +4 -4
  39. package/dist/{chunk-YEUQ7OKT.js → chunk-J6IDQGJL.js} +4 -4
  40. package/dist/{chunk-5LBEQ4ZU.js → chunk-JPQDNYXH.js} +2 -2
  41. package/dist/{chunk-6KKZXB2O.js → chunk-JROBZ7JA.js} +2 -2
  42. package/dist/{chunk-TMI2YF6D.js → chunk-KGEQJ5CC.js} +15 -26
  43. package/dist/{chunk-TMI2YF6D.js.map → chunk-KGEQJ5CC.js.map} +1 -1
  44. package/dist/{chunk-2EHDJ47Y.js → chunk-LBSCW232.js} +4 -4
  45. package/dist/{chunk-M6GH3FGJ.js → chunk-LKP7QDFN.js} +4 -4
  46. package/dist/{chunk-5EB6RIXJ.js → chunk-LPIF5LIE.js} +2 -2
  47. package/dist/chunk-M42DZCOU.js +164 -0
  48. package/dist/chunk-M42DZCOU.js.map +1 -0
  49. package/dist/{chunk-ND2S5BXB.js → chunk-MSRU4D4P.js} +2 -2
  50. package/dist/{chunk-BSTXARMY.js → chunk-NSBXVPZL.js} +11 -11
  51. package/dist/{chunk-Q45PZX2D.js → chunk-ODRPL3WB.js} +2 -2
  52. package/dist/{chunk-FSBIPCWE.js → chunk-OPXWPJDA.js} +10 -10
  53. package/dist/chunk-PCQ7ELJH.js +3081 -0
  54. package/dist/chunk-PCQ7ELJH.js.map +1 -0
  55. package/dist/{chunk-QAKJNPY3.js → chunk-PCS4VJBV.js} +6 -6
  56. package/dist/{chunk-GRILZZGS.js → chunk-PWBH7DCQ.js} +3 -3
  57. package/dist/{chunk-XKBEHKWO.js → chunk-QLWC7MKD.js} +34 -32
  58. package/dist/chunk-QLWC7MKD.js.map +1 -0
  59. package/dist/{chunk-XTAHZTES.js → chunk-R4WXQIOZ.js} +6 -6
  60. package/dist/{chunk-3CDZWY6M.js → chunk-RLT5SEML.js} +5 -5
  61. package/dist/{chunk-XG6PVB5S.js → chunk-SSQZB5SQ.js} +1 -11
  62. package/dist/chunk-SSQZB5SQ.js.map +1 -0
  63. package/dist/chunk-T7JAOYE5.js +393 -0
  64. package/dist/chunk-T7JAOYE5.js.map +1 -0
  65. package/dist/chunk-TU2WUBVZ.js +2 -0
  66. package/dist/{chunk-Q2H7J3FF.js → chunk-U7CTFN2J.js} +12 -10
  67. package/dist/{chunk-Q2H7J3FF.js.map → chunk-U7CTFN2J.js.map} +1 -1
  68. package/dist/{chunk-3XGGF5JW.js → chunk-VSEBFHAY.js} +10 -7
  69. package/dist/{chunk-3XGGF5JW.js.map → chunk-VSEBFHAY.js.map} +1 -1
  70. package/dist/{chunk-OVOYKWCM.js → chunk-WDWXAYVH.js} +4 -4
  71. package/dist/{chunk-5WME4CB6.js → chunk-Y6DOMTQD.js} +10 -8
  72. package/dist/{chunk-5WME4CB6.js.map → chunk-Y6DOMTQD.js.map} +1 -1
  73. package/dist/chunk-ZMECS2IJ.js +27 -0
  74. package/dist/chunk-ZMECS2IJ.js.map +1 -0
  75. package/dist/chunk-ZPLVANY2.js +15 -0
  76. package/dist/chunk-ZPLVANY2.js.map +1 -0
  77. package/dist/{cli-MEPJTS2V.js → cli-3BTCK4AB.js} +18 -15
  78. package/dist/{cli-MEPJTS2V.js.map → cli-3BTCK4AB.js.map} +1 -1
  79. package/dist/{cli-EMQXRN6E.js → cli-4MF37HAQ.js} +16 -12
  80. package/dist/{cli-EMQXRN6E.js.map → cli-4MF37HAQ.js.map} +1 -1
  81. package/dist/{cli-YDMMIL2U.js → cli-6KZTHWGP.js} +17 -14
  82. package/dist/{cli-YDMMIL2U.js.map → cli-6KZTHWGP.js.map} +1 -1
  83. package/dist/{cli-ZJRHU7MO.js → cli-DRMZBZLB.js} +39 -34
  84. package/dist/cli-DRMZBZLB.js.map +1 -0
  85. package/dist/{cli-J35OPSTP.js → cli-I62SRH3R.js} +10 -8
  86. package/dist/cli-JJMOVIKS.js +83 -0
  87. package/dist/cli-JJMOVIKS.js.map +1 -0
  88. package/dist/{cli-TPONWND4.js → cli-O4ABTN4Z.js} +25 -21
  89. package/dist/{cli-TPONWND4.js.map → cli-O4ABTN4Z.js.map} +1 -1
  90. package/dist/{cli-URNPOMOH.js → cli-ONTC5PKT.js} +3 -3
  91. package/dist/{cli-BJ7JXJ2U.js → cli-RORDPAPE.js} +17 -14
  92. package/dist/{cli-BJ7JXJ2U.js.map → cli-RORDPAPE.js.map} +1 -1
  93. package/dist/{cli-B7SFJDZA.js → cli-VID7PJS3.js} +17 -13
  94. package/dist/{cli-B7SFJDZA.js.map → cli-VID7PJS3.js.map} +1 -1
  95. package/dist/cli-VUKMWSSF.js +839 -0
  96. package/dist/cli-VUKMWSSF.js.map +1 -0
  97. package/dist/cli.js +24 -11
  98. package/dist/cli.js.map +1 -1
  99. package/dist/{config-YWSNBFEE.js → config-XDA42RJL.js} +3 -3
  100. package/dist/docmeta.js +9 -7
  101. package/dist/docmeta.js.map +1 -1
  102. package/dist/github-RY3FRA6P.js +29 -0
  103. package/dist/github-YKQSH7FS.js +34 -0
  104. package/dist/index.d.ts +1023 -915
  105. package/dist/index.js +107 -81
  106. package/dist/index.js.map +1 -1
  107. package/dist/{json-JIUPGOUQ.js → json-BXY44ED7.js} +13 -10
  108. package/dist/lint-YJ2L22BW.js +33 -0
  109. package/dist/list-NSG24VVX.js +31 -0
  110. package/dist/list-V6EGCSHX.js +34 -0
  111. package/dist/{page-WQ3GQF76.js → page-EV7OUBAU.js} +12 -9
  112. package/dist/{prepare-ZUTYXTUZ.js → prepare-JHJDJB6I.js} +17 -14
  113. package/dist/{prepare-ZUTYXTUZ.js.map → prepare-JHJDJB6I.js.map} +1 -1
  114. package/dist/{pretty-Q3C433DW.js → pretty-Q5Y2PA64.js} +14 -11
  115. package/dist/{pretty-CBV5XSMZ.js → pretty-SCRK66M5.js} +13 -10
  116. package/dist/{reporters-DH5HPFXJ.js → reporters-A5ADKDYR.js} +12 -9
  117. package/dist/{reporters-TTYGOUAF.js → reporters-RBYTASMI.js} +5 -5
  118. package/dist/run-BUE2DDXN.js +36 -0
  119. package/dist/{term-DL3YFL43.js → term-CQAGNG3Q.js} +17 -14
  120. package/dist/{term-DL3YFL43.js.map → term-CQAGNG3Q.js.map} +1 -1
  121. package/dist/{validate-UVDMC5LT.js → validate-ZBW44RHT.js} +6 -5
  122. package/dist/validate-ZBW44RHT.js.map +1 -0
  123. package/package.json +1 -1
  124. package/plugin/manni/.claude-plugin/plugin.json +1 -1
  125. package/dist/calibrate-XFE56P4M.js +0 -36
  126. package/dist/check-HBRX4FOR.js +0 -26
  127. package/dist/chunk-IN6DWNVE.js.map +0 -1
  128. package/dist/chunk-KX5IYIOO.js.map +0 -1
  129. package/dist/chunk-QCCAHV6A.js.map +0 -1
  130. package/dist/chunk-XG6PVB5S.js.map +0 -1
  131. package/dist/chunk-XKBEHKWO.js.map +0 -1
  132. package/dist/cli-ZJRHU7MO.js.map +0 -1
  133. package/dist/github-63CZJGZS.js +0 -31
  134. package/dist/github-IYOVHWHG.js +0 -26
  135. package/dist/lint-TBE35CCW.js +0 -29
  136. package/dist/list-IZ4RIM72.js +0 -30
  137. package/dist/list-TNEJ3J77.js +0 -28
  138. package/dist/run-LZXKVTM7.js +0 -32
  139. /package/dist/{batch-W5GXL2P4.js.map → batch-UAULUMGH.js.map} +0 -0
  140. /package/dist/{build-Q56FEXR6.js.map → build-QSPNK5AL.js.map} +0 -0
  141. /package/dist/{calibrate-XFE56P4M.js.map → calibrate-KXN4URPM.js.map} +0 -0
  142. /package/dist/{capture-GMYH4ATL.js.map → capture-JHTSUPUC.js.map} +0 -0
  143. /package/dist/{check-HBRX4FOR.js.map → check-MDGXEUWL.js.map} +0 -0
  144. /package/dist/{check-OAJWC3WD.js.map → check-WOQRKNZK.js.map} +0 -0
  145. /package/dist/{check-ZE5HIDOW.js.map → check-YEOYBFST.js.map} +0 -0
  146. /package/dist/{chunk-FGUXMZXO.js.map → chunk-2DRHT73U.js.map} +0 -0
  147. /package/dist/{chunk-XHFHXOQQ.js.map → chunk-2IAZEB45.js.map} +0 -0
  148. /package/dist/{chunk-35WTFPQS.js.map → chunk-2IR4T5PO.js.map} +0 -0
  149. /package/dist/{chunk-OWSNXPPX.js.map → chunk-4CK6ZSOK.js.map} +0 -0
  150. /package/dist/{chunk-YUZ2ECZY.js.map → chunk-4D5IZSXM.js.map} +0 -0
  151. /package/dist/{chunk-RFSGKF4O.js.map → chunk-4VBSTXN6.js.map} +0 -0
  152. /package/dist/{chunk-GEMXGXPR.js.map → chunk-ADMEPUBT.js.map} +0 -0
  153. /package/dist/{chunk-23UAGEKP.js.map → chunk-B7XYNRJQ.js.map} +0 -0
  154. /package/dist/{chunk-QKE3XS2L.js.map → chunk-BBT6AVUG.js.map} +0 -0
  155. /package/dist/{chunk-VKWZCEWB.js.map → chunk-BKVHX6FU.js.map} +0 -0
  156. /package/dist/{chunk-X6WLCNKC.js.map → chunk-D4N2V43Z.js.map} +0 -0
  157. /package/dist/{chunk-XXDXIIQ4.js.map → chunk-EFW7CAG4.js.map} +0 -0
  158. /package/dist/{chunk-UDUHRXJ4.js.map → chunk-EIFCP6CZ.js.map} +0 -0
  159. /package/dist/{chunk-KPXOJMQ6.js.map → chunk-FBMZJEB3.js.map} +0 -0
  160. /package/dist/{chunk-HRXDM6H6.js.map → chunk-FQXWVS6J.js.map} +0 -0
  161. /package/dist/{chunk-76I6I5N2.js.map → chunk-FUCPZVRY.js.map} +0 -0
  162. /package/dist/{chunk-DTGIUMZV.js.map → chunk-GDVZ5VII.js.map} +0 -0
  163. /package/dist/{chunk-TKTRIZT4.js.map → chunk-HA6MQMOY.js.map} +0 -0
  164. /package/dist/{chunk-ECVQNGTH.js.map → chunk-HQR2ZBHW.js.map} +0 -0
  165. /package/dist/{chunk-MHAYLDBK.js.map → chunk-IGSHYP74.js.map} +0 -0
  166. /package/dist/{chunk-YEUQ7OKT.js.map → chunk-J6IDQGJL.js.map} +0 -0
  167. /package/dist/{chunk-5LBEQ4ZU.js.map → chunk-JPQDNYXH.js.map} +0 -0
  168. /package/dist/{chunk-6KKZXB2O.js.map → chunk-JROBZ7JA.js.map} +0 -0
  169. /package/dist/{chunk-2EHDJ47Y.js.map → chunk-LBSCW232.js.map} +0 -0
  170. /package/dist/{chunk-M6GH3FGJ.js.map → chunk-LKP7QDFN.js.map} +0 -0
  171. /package/dist/{chunk-5EB6RIXJ.js.map → chunk-LPIF5LIE.js.map} +0 -0
  172. /package/dist/{chunk-ND2S5BXB.js.map → chunk-MSRU4D4P.js.map} +0 -0
  173. /package/dist/{chunk-BSTXARMY.js.map → chunk-NSBXVPZL.js.map} +0 -0
  174. /package/dist/{chunk-Q45PZX2D.js.map → chunk-ODRPL3WB.js.map} +0 -0
  175. /package/dist/{chunk-FSBIPCWE.js.map → chunk-OPXWPJDA.js.map} +0 -0
  176. /package/dist/{chunk-QAKJNPY3.js.map → chunk-PCS4VJBV.js.map} +0 -0
  177. /package/dist/{chunk-GRILZZGS.js.map → chunk-PWBH7DCQ.js.map} +0 -0
  178. /package/dist/{chunk-XTAHZTES.js.map → chunk-R4WXQIOZ.js.map} +0 -0
  179. /package/dist/{chunk-3CDZWY6M.js.map → chunk-RLT5SEML.js.map} +0 -0
  180. /package/dist/{cli-J35OPSTP.js.map → chunk-TU2WUBVZ.js.map} +0 -0
  181. /package/dist/{chunk-OVOYKWCM.js.map → chunk-WDWXAYVH.js.map} +0 -0
  182. /package/dist/{config-YWSNBFEE.js.map → cli-I62SRH3R.js.map} +0 -0
  183. /package/dist/{cli-URNPOMOH.js.map → cli-ONTC5PKT.js.map} +0 -0
  184. /package/dist/{github-63CZJGZS.js.map → config-XDA42RJL.js.map} +0 -0
  185. /package/dist/{github-IYOVHWHG.js.map → github-RY3FRA6P.js.map} +0 -0
  186. /package/dist/{json-JIUPGOUQ.js.map → github-YKQSH7FS.js.map} +0 -0
  187. /package/dist/{lint-TBE35CCW.js.map → json-BXY44ED7.js.map} +0 -0
  188. /package/dist/{list-IZ4RIM72.js.map → lint-YJ2L22BW.js.map} +0 -0
  189. /package/dist/{list-TNEJ3J77.js.map → list-NSG24VVX.js.map} +0 -0
  190. /package/dist/{page-WQ3GQF76.js.map → list-V6EGCSHX.js.map} +0 -0
  191. /package/dist/{pretty-CBV5XSMZ.js.map → page-EV7OUBAU.js.map} +0 -0
  192. /package/dist/{pretty-Q3C433DW.js.map → pretty-Q5Y2PA64.js.map} +0 -0
  193. /package/dist/{reporters-DH5HPFXJ.js.map → pretty-SCRK66M5.js.map} +0 -0
  194. /package/dist/{reporters-TTYGOUAF.js.map → reporters-A5ADKDYR.js.map} +0 -0
  195. /package/dist/{run-LZXKVTM7.js.map → reporters-RBYTASMI.js.map} +0 -0
  196. /package/dist/{validate-UVDMC5LT.js.map → run-BUE2DDXN.js.map} +0 -0
@@ -12,7 +12,7 @@ import {
12
12
  shortCommit,
13
13
  toValidationResult,
14
14
  unitText
15
- } from "./chunk-OVOYKWCM.js";
15
+ } from "./chunk-WDWXAYVH.js";
16
16
  import {
17
17
  SEVERITIES
18
18
  } from "./chunk-77AINBYM.js";
@@ -34,7 +34,7 @@ import {
34
34
  sliceLines,
35
35
  sourceRange,
36
36
  spellSource
37
- } from "./chunk-VKWZCEWB.js";
37
+ } from "./chunk-BKVHX6FU.js";
38
38
  import {
39
39
  MAX_RANGE_LINES,
40
40
  MOVE_BUDGET_BYTES,
@@ -56,7 +56,7 @@ import {
56
56
  spellLines,
57
57
  splitLines,
58
58
  toBodyLines
59
- } from "./chunk-QCCAHV6A.js";
59
+ } from "./chunk-GV56WGL2.js";
60
60
  import {
61
61
  STDIN_LABEL,
62
62
  STDIN_TOKEN,
@@ -70,7 +70,7 @@ import {
70
70
  resolveTargetSet,
71
71
  settleBaseline,
72
72
  supportedExtensions
73
- } from "./chunk-3XGGF5JW.js";
73
+ } from "./chunk-VSEBFHAY.js";
74
74
  import {
75
75
  decryptValue,
76
76
  encryptValue,
@@ -81,7 +81,7 @@ import {
81
81
  pageManifestPath,
82
82
  readConfigFile,
83
83
  selectCollections
84
- } from "./chunk-XKBEHKWO.js";
84
+ } from "./chunk-QLWC7MKD.js";
85
85
  import {
86
86
  warn
87
87
  } from "./chunk-HX44GB4Z.js";
@@ -1648,4 +1648,4 @@ export {
1648
1648
  sayNotices,
1649
1649
  runCheck
1650
1650
  };
1651
- //# sourceMappingURL=chunk-QAKJNPY3.js.map
1651
+ //# sourceMappingURL=chunk-PCS4VJBV.js.map
@@ -3,12 +3,12 @@ import {
3
3
  renderCalibration,
4
4
  resolveBatchTraces,
5
5
  runBatch
6
- } from "./chunk-3XMG4CZY.js";
6
+ } from "./chunk-54F74XR3.js";
7
7
  import {
8
8
  discoverConfig,
9
9
  labelKey,
10
10
  loadLabels
11
- } from "./chunk-KPXOJMQ6.js";
11
+ } from "./chunk-FBMZJEB3.js";
12
12
  import {
13
13
  assertProviderSelection,
14
14
  selectProvider
@@ -378,4 +378,4 @@ export {
378
378
  score,
379
379
  runCalibrate
380
380
  };
381
- //# sourceMappingURL=chunk-GRILZZGS.js.map
381
+ //# sourceMappingURL=chunk-PWBH7DCQ.js.map
@@ -595,9 +595,7 @@ function isGlob(entry) {
595
595
 
596
596
  // src/shared/tools.ts
597
597
  import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
598
- var TOOL_KEYS = ["vale", "dita-ot"];
599
- var VALE_KEYS = ["config"];
600
- var DITA_OT_KEYS = ["home"];
598
+ var TOOL_KEYS = ["vale", "dita-ot", "doc-detective"];
601
599
  function isMapping2(value) {
602
600
  return typeof value === "object" && value !== null && !Array.isArray(value);
603
601
  }
@@ -618,52 +616,54 @@ function parseTools(raw, source, toError) {
618
616
  rejectUnknownKeys2(raw, TOOL_KEYS, "tools", source, toError);
619
617
  const tools = {};
620
618
  if (Object.hasOwn(raw, "vale")) {
621
- tools.vale = parseVale(raw["vale"], source, toError);
619
+ tools.vale = parseStringKey(raw["vale"], "vale", "config", source, toError);
622
620
  }
623
621
  if (Object.hasOwn(raw, "dita-ot")) {
624
- tools["dita-ot"] = parseDitaOt(raw["dita-ot"], source, toError);
622
+ tools["dita-ot"] = parseStringKey(raw["dita-ot"], "dita-ot", "home", source, toError);
623
+ }
624
+ if (Object.hasOwn(raw, "doc-detective")) {
625
+ tools["doc-detective"] = parseStringKey(
626
+ raw["doc-detective"],
627
+ "doc-detective",
628
+ "config",
629
+ source,
630
+ toError
631
+ );
625
632
  }
626
633
  return tools;
627
634
  }
628
- function parseVale(raw, source, toError) {
635
+ function parseStringKey(raw, tool, key, source, toError) {
629
636
  if (!isMapping2(raw)) {
630
- throw toError(`${source}: tools.vale must be a mapping.`);
637
+ throw toError(`${source}: tools.${tool} must be a mapping.`);
631
638
  }
632
- rejectUnknownKeys2(raw, VALE_KEYS, "tools.vale", source, toError);
633
- const vale = {};
634
- if (Object.hasOwn(raw, "config")) {
635
- const config = raw["config"];
636
- if (typeof config !== "string" || config.trim() === "") {
637
- throw toError(`${source}: tools.vale.config must be a non-empty string.`);
639
+ rejectUnknownKeys2(raw, [key], `tools.${tool}`, source, toError);
640
+ const out = {};
641
+ if (Object.hasOwn(raw, key)) {
642
+ const value = raw[key];
643
+ if (typeof value !== "string" || value.trim() === "") {
644
+ throw toError(`${source}: tools.${tool}.${key} must be a non-empty string.`);
638
645
  }
639
- vale.config = config;
646
+ out[key] = value;
640
647
  }
641
- return vale;
648
+ return out;
642
649
  }
643
- function parseDitaOt(raw, source, toError) {
644
- if (!isMapping2(raw)) {
645
- throw toError(`${source}: tools.dita-ot must be a mapping.`);
646
- }
647
- rejectUnknownKeys2(raw, DITA_OT_KEYS, "tools.dita-ot", source, toError);
648
- const ditaOt = {};
649
- if (Object.hasOwn(raw, "home")) {
650
- const home = raw["home"];
651
- if (typeof home !== "string" || home.trim() === "") {
652
- throw toError(`${source}: tools.dita-ot.home must be a non-empty string.`);
653
- }
654
- ditaOt.home = home;
655
- }
656
- return ditaOt;
650
+ function fromConfigDir(path, configDir) {
651
+ return isAbsolute2(path) ? path : resolve2(configDir, path);
657
652
  }
658
653
  function valeConfigPath(tools, configDir) {
659
654
  const config = tools.vale?.config;
660
655
  if (config === void 0) return void 0;
661
- return isAbsolute2(config) ? config : resolve2(configDir, config);
656
+ return fromConfigDir(config, configDir);
662
657
  }
663
658
  function ditaOtHome(tools, configDir) {
664
659
  const home = tools["dita-ot"]?.home;
665
660
  if (home === void 0) return void 0;
666
- return isAbsolute2(home) ? home : resolve2(configDir, home);
661
+ return fromConfigDir(home, configDir);
662
+ }
663
+ function docDetectiveConfigPath(tools, configDir) {
664
+ const config = tools["doc-detective"]?.config;
665
+ if (config === void 0) return void 0;
666
+ return fromConfigDir(config, configDir);
667
667
  }
668
668
 
669
669
  // src/shared/config-file.ts
@@ -952,8 +952,10 @@ export {
952
952
  isMember,
953
953
  matchesCollectionPaths,
954
954
  findGitRoot,
955
+ searchPath,
955
956
  valeConfigPath,
956
957
  ditaOtHome,
958
+ docDetectiveConfigPath,
957
959
  FAMILY_CONFIG_NAMES,
958
960
  ENCRYPTION_KEY_FIELD,
959
961
  ENCRYPTION_KEY_PREVIOUS_FIELD,
@@ -964,4 +966,4 @@ export {
964
966
  findFamilyConfigFile,
965
967
  readFamilyConfigFile
966
968
  };
967
- //# sourceMappingURL=chunk-XKBEHKWO.js.map
969
+ //# sourceMappingURL=chunk-QLWC7MKD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/encryption.ts","../src/shared/collections.ts","../src/shared/globs.ts","../src/shared/page-manifest.ts","../src/shared/tools.ts","../src/shared/config-file.ts","../src/shared/git-root.ts"],"sourcesContent":["/**\n * The family's value encryption (proposal 0045).\n *\n * One ciphertext format for every tool: meta frontmatter values and cite\n * source paths encrypt the same way, under different contexts, so rotation\n * has one rule. Pure, and `node:crypto` only.\n *\n * The construction is SIV-style on AES-256-GCM, because Node has no SIV mode\n * (`aes-256-gcm-siv` and `aes-*-siv` are unknown ciphers in Node 24):\n *\n * - Three 32-byte subkeys come from the configured key with HKDF-SHA256 and an\n * empty salt, labelled `manni/v1/encrypt`, `manni/v1/nonce` and\n * `manni/v1/pin`. The key is expanded, not stretched: a hand-picked value is\n * only as strong as it is random, which is why `manni key set` generates 256\n * bits.\n * - The plaintext is the value's JSON, padded ISO/IEC 7816-4 style (`0x80`,\n * then `0x00` up to the next multiple of 32, always at least one pad byte),\n * so a token's length reveals a size class, not the value.\n * - The nonce is the first 12 bytes of `HMAC(nonceKey, context ‖ 0x00 ‖\n * padded)`. Equal plaintexts give equal tokens, which is what a join needs;\n * decryption checks the nonce is that HMAC, so every valid token is\n * canonical.\n * - The associated data is `0x01 ‖ context`, so the same text in two contexts\n * never reuses a nonce under different tags. The property name is not\n * bound: a value moved between fields stays readable.\n *\n * A token is `~` then unpadded base64url of `0x01 ‖ nonce ‖ ciphertext ‖\n * tag`: 61 bytes at the smallest, so 82 characters after the `~`.\n */\nimport {\n createCipheriv,\n createDecipheriv,\n createHmac,\n hkdfSync,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { TextDecoder } from \"node:util\";\n\n/** Which kind of value a token holds. Bound as associated data. */\nexport type EncryptionContext = \"meta\" | \"cite-src\";\n\n/** An encrypted value: `~` and at least 82 base64url characters. */\nexport const ENCRYPTED_VALUE = /^~[A-Za-z0-9_-]{82,}$/;\n\n/** Whether `value` is shaped like a token. Says nothing about which key. */\nexport function isEncryptedValue(value: unknown): value is string {\n return typeof value === \"string\" && ENCRYPTED_VALUE.test(value);\n}\n\n/** A configured key: hex or base64url, at least 32 characters. */\nexport const ENCRYPTION_KEY_SHAPE = /^[A-Za-z0-9_-]{32,}$/;\n\nexport function isValidEncryptionKey(value: unknown): value is string {\n return typeof value === \"string\" && ENCRYPTION_KEY_SHAPE.test(value);\n}\n\n/** 256 random bits as 64 lowercase hex characters. */\nexport function generateEncryptionKey(): string {\n return randomBytes(32).toString(\"hex\");\n}\n\nconst VERSION = 0x01;\nconst NONCE_BYTES = 12;\nconst TAG_BYTES = 16;\nconst BLOCK = 32;\n/** Version byte, nonce, one padded block, tag. */\nconst MIN_BYTES = 1 + NONCE_BYTES + BLOCK + TAG_BYTES;\n\ninterface Subkeys {\n encrypt: Buffer;\n nonce: Buffer;\n pin: Buffer;\n}\n\n/**\n * Subkeys per configured key. A run sees one key, or two while rotating, so\n * the cache stays tiny; it saves three HKDF calls per value on a large tree.\n */\nconst subkeyCache = new Map<string, Subkeys>();\n\nfunction subkeys(key: string): Subkeys {\n const cached = subkeyCache.get(key);\n if (cached !== undefined) return cached;\n const ikm = Buffer.from(key, \"utf8\");\n const derive = (info: string): Buffer =>\n Buffer.from(hkdfSync(\"sha256\", ikm, Buffer.alloc(0), info, 32));\n const made: Subkeys = {\n encrypt: derive(\"manni/v1/encrypt\"),\n nonce: derive(\"manni/v1/nonce\"),\n pin: derive(\"manni/v1/pin\"),\n };\n subkeyCache.set(key, made);\n return made;\n}\n\nfunction requireKey(key: string, caller: string): void {\n if (!isValidEncryptionKey(key)) {\n // Never echo the value: it is key material.\n throw new Error(\n `${caller}: the key must be at least 32 hex or base64url characters.`,\n );\n }\n}\n\nfunction pad(data: Buffer): Buffer {\n const out = Buffer.alloc((Math.floor(data.length / BLOCK) + 1) * BLOCK);\n data.copy(out);\n out[data.length] = 0x80;\n return out;\n}\n\n/** The data before the padding, or `null` when the padding is not canonical. */\nfunction unpad(padded: Buffer): Buffer | null {\n let i = padded.length - 1;\n while (i >= 0 && padded[i] === 0) i--;\n if (i < 0 || padded[i] !== 0x80) return null;\n // Exactly the padding `pad` would have added: never a spare block.\n if (padded.length !== (Math.floor(i / BLOCK) + 1) * BLOCK) return null;\n return padded.subarray(0, i);\n}\n\nfunction nonceFor(keys: Subkeys, context: Buffer, padded: Buffer): Buffer {\n return createHmac(\"sha256\", keys.nonce)\n .update(context)\n .update(Buffer.from([0x00]))\n .update(padded)\n .digest()\n .subarray(0, NONCE_BYTES);\n}\n\nfunction aad(context: Buffer): Buffer {\n return Buffer.concat([Buffer.from([VERSION]), context]);\n}\n\n/**\n * Encrypt any JSON value. Throws for a value that is not JSON (`undefined`, a\n * function) and for a key that is not key-shaped: both are programming\n * errors, since keys arrive through `resolveEncryptionKey`.\n */\nexport function encryptValue(\n value: unknown,\n key: string,\n context: EncryptionContext,\n): string {\n requireKey(key, \"encryptValue\");\n // lib.d.ts types the result as `string`, but `undefined`, a function or a\n // symbol stringify to `undefined` at run time.\n const json = JSON.stringify(value) as string | undefined;\n if (json === undefined) {\n throw new TypeError(\"encryptValue: the value is not JSON.\");\n }\n const keys = subkeys(key);\n const ctx = Buffer.from(context, \"utf8\");\n const padded = pad(Buffer.from(json, \"utf8\"));\n const nonce = nonceFor(keys, ctx, padded);\n const cipher = createCipheriv(\"aes-256-gcm\", keys.encrypt, nonce, {\n authTagLength: TAG_BYTES,\n });\n cipher.setAAD(aad(ctx));\n const sealed = Buffer.concat([cipher.update(padded), cipher.final()]);\n const tag = cipher.getAuthTag();\n const bytes = Buffer.concat([Buffer.from([VERSION]), nonce, sealed, tag]);\n return `~${bytes.toString(\"base64url\")}`;\n}\n\nconst FAILED = { ok: false } as const;\n\n/**\n * Decrypt a token. `{ ok: false }` for anything that is not a canonical token\n * under this key and context; never throws.\n */\nexport function decryptValue(\n token: string,\n key: string,\n context: EncryptionContext,\n): { ok: true; value: unknown } | { ok: false } {\n try {\n return open(token, key, context);\n } catch {\n return FAILED;\n }\n}\n\nfunction open(\n token: string,\n key: string,\n context: EncryptionContext,\n): { ok: true; value: unknown } | { ok: false } {\n if (!isEncryptedValue(token) || !isValidEncryptionKey(key)) return FAILED;\n const body = token.slice(1);\n const bytes = Buffer.from(body, \"base64url\");\n // Buffer's decoder ignores stray trailing bits; a token is canonical only\n // if it is exactly what the encoder writes for these bytes.\n if (bytes.toString(\"base64url\") !== body) return FAILED;\n if (bytes.length < MIN_BYTES) return FAILED;\n if ((bytes.length - 1 - NONCE_BYTES - TAG_BYTES) % BLOCK !== 0) return FAILED;\n if (bytes[0] !== VERSION) return FAILED;\n\n const nonce = bytes.subarray(1, 1 + NONCE_BYTES);\n const sealed = bytes.subarray(1 + NONCE_BYTES, bytes.length - TAG_BYTES);\n const tag = bytes.subarray(bytes.length - TAG_BYTES);\n const keys = subkeys(key);\n const ctx = Buffer.from(context, \"utf8\");\n const decipher = createDecipheriv(\"aes-256-gcm\", keys.encrypt, nonce, {\n authTagLength: TAG_BYTES,\n });\n decipher.setAAD(aad(ctx));\n decipher.setAuthTag(tag);\n // `final` throws on a bad tag; the caller turns that into `{ ok: false }`.\n const padded = Buffer.concat([decipher.update(sealed), decipher.final()]);\n\n if (!timingSafeEqual(nonceFor(keys, ctx, padded), nonce)) return FAILED;\n const data = unpad(padded);\n if (data === null) return FAILED;\n const json = new TextDecoder(\"utf-8\", { fatal: true }).decode(data);\n const value: unknown = JSON.parse(json);\n return { ok: true, value };\n}\n\n/** The prefix a keyed pin carries, so it never reads as a plain hash of the text. */\nexport const KEYED_PIN_PREFIX = \"hmac-sha256-\";\n\n/**\n * A keyed fingerprint of `text`: `hmac-sha256-` and the hex HMAC-SHA256 under\n * the pin subkey. Stable per key, so it can be compared without being\n * reversible. The prefix says it is an HMAC: hashing the text by hand gives\n * a different value, and the prefix is why.\n */\nexport function keyedPin(text: string, key: string): string {\n requireKey(key, \"keyedPin\");\n const mac = createHmac(\"sha256\", subkeys(key).pin)\n .update(text, \"utf8\")\n .digest(\"hex\");\n return `${KEYED_PIN_PREFIX}${mac}`;\n}\n","/**\n * `collections:`, the family-level home for document sets (proposal 0041).\n *\n * A collection names a set of documents once, at the top level of\n * `manni.config.yaml`, and every tool reads the same declaration: the metadata\n * tool validates its members, `query` exposes it as a view, and `a11y check`\n * seeds from its `url`. Only the *declaration* is shared. Loading a manifest\n * and merging it into `ExtractedMetadata` stays with the metadata tool,\n * because that type is the metadata tool's.\n *\n * Every refusal in this parser is a shape that would otherwise read as\n * configured and do nothing, or do something nobody can see. The manifest\n * rules are 0037's, moved here unchanged in substance: an entry with an empty\n * `keys` list owns nothing, one key under two manifests of a collection would need a\n * tiebreak (0020 refuses tiebreaks), and `$schema` from external metadata\n * would let a private file pick the schema a public document is judged by\n * (0015) with the provenance lost at the merge.\n */\nimport picomatch from \"picomatch\";\nimport { matchesFileGlob } from \"./globs.js\";\nimport { posix, win32 } from \"node:path\";\nimport {\n PAGE_PLACEHOLDER,\n hasPagePlaceholder,\n normalizeManifestPattern,\n} from \"./page-manifest.js\";\n\n/** One external-metadata manifest (proposals 0037, 0038, 0039) joined to a collection. */\nexport interface ExternalMetadataConfig {\n file: string;\n /**\n * The top-level keys the manifest owns. Absent (proposal 0068), it owns\n * what each page's schemas mark `x-manni-location: external`, as `ownsKey`\n * decides.\n */\n keys?: string[];\n tokenEnv?: string;\n join?: string;\n}\n\n/** One named document set, as declared under the top-level `collections:` key. */\nexport interface CollectionConfig {\n name: string;\n paths: string[];\n exclude: string[];\n externalMetadata: ExternalMetadataConfig[];\n /** Where the collection is published; `manni a11y check` seeds from it. */\n url?: string;\n}\n\nconst COLLECTION_KEYS = [\n \"name\",\n \"paths\",\n \"exclude\",\n \"url\",\n \"externalMetadata\",\n] as const;\n\nconst EXTERNAL_METADATA_KEYS = [\"file\", \"keys\", \"tokenEnv\", \"join\"] as const;\n\n/**\n * The join that names documents by path, and the default. The metadata tool\n * spells it `PATH_JOIN` in `src/meta/core/external-metadata.ts`; the literal\n * is repeated here for the same reason as `SCHEMA_KEY` below.\n */\nconst PATH_JOIN = \"path\";\n\n/**\n * The document-level schema key. The metadata tool spells it\n * `FILE_SCHEMA_KEY` in `src/meta/core/resolve-schema.ts`; the literal is\n * repeated here so the shared layer does not import a tool's module. Keep the\n * two in step.\n */\nconst SCHEMA_KEY = \"$schema\";\n\n/**\n * The label `load-files.ts` gives stdin (`STDIN_LABEL`). Repeated as a literal\n * for the same reason as `SCHEMA_KEY`: the shared layer does not depend on the\n * metadata tool. Stdin has no path, so it is a member of nothing.\n */\nconst STDIN = \"<stdin>\";\n\n/** Is this manifest reference a URL? The `url` branch of `classifyRef`. */\nfunction isUrlRef(ref: string): boolean {\n return /^https?:\\/\\//i.test(ref);\n}\n\n/**\n * Whether a URL may name an external-metadata manifest: `https://` anywhere,\n * or `http://` on a loopback host only, where plaintext leaks nothing.\n * Userinfo is refused because the URL is printed in every diagnostic and a\n * secret in it would print too. Returns the reason it may not, or `null`.\n */\nexport function externalMetadataUrlProblem(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return \"is not a valid URL\";\n }\n if (parsed.username !== \"\" || parsed.password !== \"\") {\n return 'carries a credential in the URL; put the token in an environment variable and name it with \"tokenEnv\"';\n }\n if (parsed.protocol === \"https:\") return null;\n if (parsed.protocol === \"http:\" && isLoopback(parsed.hostname)) return null;\n if (parsed.protocol === \"http:\") {\n return \"is plain http://; a bearer token over plaintext is a leak, and a public manifest is served over https too\";\n }\n return `has the unsupported scheme \"${parsed.protocol}\"`;\n}\n\nfunction isLoopback(host: string): boolean {\n return (\n host === \"localhost\" ||\n host === \"127.0.0.1\" ||\n host === \"[::1]\" ||\n host === \"::1\" ||\n host.startsWith(\"127.\")\n );\n}\n\nfunction rejectUnknownKeys(\n raw: Record<string, unknown>,\n allowed: readonly string[],\n where: string,\n source: string,\n toError: (message: string) => Error,\n): void {\n for (const key of Object.keys(raw)) {\n if (!allowed.includes(key)) {\n throw toError(\n `${source}: ${where} has unknown key \"${key}\". Supported keys: ${allowed.join(\", \")}.`,\n );\n }\n }\n}\n\nfunction isMapping(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\n/**\n * Parse the top-level `collections:` value. Errors are built with `toError`,\n * so each tool reports a bad family file in its own error class.\n */\nexport function parseCollections(\n raw: unknown,\n source: string,\n toError: (message: string) => Error,\n): CollectionConfig[] {\n if (!Array.isArray(raw)) {\n throw toError(`${source}: \"collections\" must be a list.`);\n }\n if (raw.length === 0) {\n throw toError(\n `${source}: \"collections\" must name at least one collection; remove the key if there are none.`,\n );\n }\n // Names become SQL view names, and SQLite compares identifiers without\n // regard to case, so two names that differ only in case are one view.\n const taken = new Map<string, number>();\n return raw.map((entry, i) => {\n const where = `collections[${i}]`;\n if (!isMapping(entry)) {\n throw toError(`${source}: ${where} must be a mapping.`);\n }\n rejectUnknownKeys(entry, COLLECTION_KEYS, where, source, toError);\n\n if (!isNonEmptyString(entry.name)) {\n throw toError(`${source}: ${where}.name must be a non-empty string.`);\n }\n const name = entry.name;\n const folded = name.toLowerCase();\n if (folded === \"docs\") {\n throw toError(\n `${source}: ${where}.name \"${name}\" collides with the docs table every query reads. Pick another name, such as \"site\" or \"pages\".`,\n );\n }\n // The derived table (proposal 0040) is a view named `derived` over a\n // backing table named `_derived_rows`, built beside `docs` when a\n // statement asks for it, and `resolved` (proposal 0043) is the view over\n // `docs` and those rows. Any of the three would shadow it the same way.\n if (folded === \"derived\" || folded === \"_derived_rows\" || folded === \"resolved\") {\n throw toError(\n `${source}: ${where}.name \"${name}\" collides with the derived table a query builds beside docs (the derived view, its _derived_rows table, and the resolved view over both). Pick another name.`,\n );\n }\n if (folded.startsWith(\"sqlite_\")) {\n throw toError(\n `${source}: ${where}.name \"${name}\" starts with \"sqlite_\", which SQLite reserves for its own objects. Pick another name.`,\n );\n }\n const owner = taken.get(folded);\n if (owner !== undefined) {\n throw toError(\n `${source}: ${where}.name \"${name}\" is already taken by collections[${owner}]; names are compared case-insensitively because they become SQL views.`,\n );\n }\n taken.set(folded, i);\n\n if (\n !Array.isArray(entry.paths) ||\n entry.paths.length === 0 ||\n entry.paths.some((p) => !isNonEmptyString(p))\n ) {\n throw toError(\n `${source}: ${where}.paths must be a non-empty list of files, directories or globs.`,\n );\n }\n const paths = entry.paths as string[];\n\n let exclude: string[] = [];\n if (entry.exclude !== undefined) {\n if (\n !Array.isArray(entry.exclude) ||\n entry.exclude.some((g) => !isNonEmptyString(g))\n ) {\n throw toError(`${source}: ${where}.exclude must be a list of globs.`);\n }\n exclude = entry.exclude as string[];\n }\n\n let url: string | undefined;\n if (entry.url !== undefined) {\n if (typeof entry.url !== \"string\" || !isHttpUrl(entry.url)) {\n throw toError(`${source}: ${where}.url must be an http(s) URL.`);\n }\n url = entry.url;\n }\n\n let externalMetadata: ExternalMetadataConfig[] = [];\n if (entry.externalMetadata !== undefined) {\n if (!Array.isArray(entry.externalMetadata)) {\n throw toError(`${source}: ${where}.externalMetadata must be a list.`);\n }\n externalMetadata = parseExternalMetadata(\n entry.externalMetadata,\n where,\n source,\n toError,\n );\n }\n\n return {\n name,\n paths,\n exclude,\n externalMetadata,\n ...(url === undefined ? {} : { url }),\n };\n });\n}\n\nfunction isHttpUrl(value: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n return false;\n }\n return parsed.protocol === \"http:\" || parsed.protocol === \"https:\";\n}\n\n/**\n * Parse one collection's `externalMetadata:`. 0037's rules, with the error\n * path rewritten and the noun changed: ownership is now disjoint within a\n * collection rather than across the whole config, because two collections may\n * each own the same key.\n */\nfunction parseExternalMetadata(\n raw: readonly unknown[],\n collectionWhere: string,\n source: string,\n toError: (message: string) => Error,\n): ExternalMetadataConfig[] {\n const owners = new Map<string, number>();\n const patterns = new Map<string, number>();\n /** The one entry that omits `keys` (proposal 0068), once one does. */\n let keyless: number | undefined;\n const parsedEntries = raw.map((entry, i): ExternalMetadataConfig => {\n const where = `${collectionWhere}.externalMetadata[${i}]`;\n if (!isMapping(entry)) {\n throw toError(`${source}: ${where} must be a mapping.`);\n }\n rejectUnknownKeys(entry, EXTERNAL_METADATA_KEYS, where, source, toError);\n if (!isNonEmptyString(entry.file)) {\n throw toError(\n `${source}: ${where}.file must be a non-empty string naming the manifest, relative to the config file.`,\n );\n }\n const placeholderProblem = pagePlaceholderProblem(entry.file);\n if (placeholderProblem !== null) {\n throw toError(`${source}: ${where}.file ${placeholderProblem}`);\n }\n const perPage = hasPagePlaceholder(entry.file);\n if (perPage && (posix.isAbsolute(entry.file) || win32.isAbsolute(entry.file))) {\n throw toError(\n `${source}: ${where}.file uses ${PAGE_PLACEHOLDER} in an absolute path, so every manifest would sit outside the repository. Make it relative to the config file.`,\n );\n }\n // Absent keys (proposal 0068) is an ownership rule, not an empty list:\n // the manifest owns what each page's schemas mark external. Written, the\n // list still has to name something.\n if (\n entry.keys !== undefined &&\n (!Array.isArray(entry.keys) ||\n entry.keys.length === 0 ||\n entry.keys.some((k) => !isNonEmptyString(k)))\n ) {\n throw toError(\n `${source}: ${where}.keys must be a non-empty list of key names.`,\n );\n }\n if (entry.keys === undefined) {\n if (keyless !== undefined) {\n throw toError(\n `${source}: ${collectionWhere}.externalMetadata: entries ${String(keyless)} and ${String(i)} both omit keys; at most one manifest may own the external-marked fields`,\n );\n }\n keyless = i;\n }\n const isUrl = isUrlRef(entry.file);\n if (isUrl) {\n const problem = externalMetadataUrlProblem(entry.file);\n if (problem !== null) {\n throw toError(`${source}: ${where}.file ${problem}.`);\n }\n if (perPage) {\n throw toError(\n `${source}: ${where}.file uses ${PAGE_PLACEHOLDER} in a URL. A placeholder names a file this run can write, and a URL is not one.`,\n );\n }\n }\n if (entry.tokenEnv !== undefined) {\n if (!isNonEmptyString(entry.tokenEnv)) {\n throw toError(\n `${source}: ${where}.tokenEnv must be the name of an environment variable.`,\n );\n }\n if (!isUrl) {\n throw toError(\n `${source}: ${where}.tokenEnv is set, but \"file\" is a path, so no request is made and the token would go nowhere. Remove it, or make \"file\" a URL.`,\n );\n }\n }\n const keys = entry.keys === undefined ? undefined : (entry.keys as string[]);\n if (entry.join !== undefined) {\n if (!isNonEmptyString(entry.join)) {\n throw toError(\n `${source}: ${where}.join must be \"path\" or the name of a top-level frontmatter field.`,\n );\n }\n if (entry.join === SCHEMA_KEY) {\n throw toError(`${source}: ${where}.join may not be \"${SCHEMA_KEY}\".`);\n }\n if (keys?.includes(entry.join) === true) {\n throw toError(\n `${source}: ${where}.join names \"${entry.join}\", which the same entry owns — the value that selects an entry cannot come from the entry.`,\n );\n }\n }\n // An explicit `join: path` is the default spelled out, and a per-page\n // manifest is found by that path. A field would name the file by one\n // thing and key its entry by another (0058 § 6).\n if (perPage && typeof entry.join === \"string\" && entry.join !== PATH_JOIN) {\n throw toError(\n `${source}: ${where}.file uses ${PAGE_PLACEHOLDER}, and join is \"${entry.join}\". A per-page manifest is found by path, so it cannot be keyed by a field. Remove ${PAGE_PLACEHOLDER}, or remove join.`,\n );\n }\n const seen = new Set<string>();\n for (const key of keys ?? []) {\n if (key === SCHEMA_KEY) {\n throw toError(\n `${source}: ${where}.keys may not include \"${SCHEMA_KEY}\" — a manifest never chooses the schema a document is judged by; use \"overrides\".`,\n );\n }\n if (seen.has(key)) {\n throw toError(`${source}: ${where}.keys lists \"${key}\" twice.`);\n }\n seen.add(key);\n const owner = owners.get(key);\n if (owner !== undefined) {\n throw toError(\n `${source}: ${where}.keys claims \"${key}\", which externalMetadata[${owner}] already owns — a key has exactly one manifest in a collection.`,\n );\n }\n owners.set(key, i);\n }\n // Two spellings of one pattern are one manifest for every page (0058\n // stress test 10), compared once, with no page known yet. Two concrete\n // entries naming one file stay legal, as they were before placeholders.\n if (perPage) {\n const pattern = normalizeManifestPattern(entry.file);\n const first = patterns.get(pattern);\n if (first !== undefined) {\n throw toError(\n `${source}: ${where}.file resolves to the same manifest as externalMetadata[${String(first)}].file for every page. Give each entry its own file name.`,\n );\n }\n patterns.set(pattern, i);\n }\n const parsed: ExternalMetadataConfig = {\n file: entry.file,\n ...(keys === undefined ? {} : { keys }),\n ...(typeof entry.tokenEnv === \"string\" ? { tokenEnv: entry.tokenEnv } : {}),\n ...(typeof entry.join === \"string\" ? { join: entry.join } : {}),\n };\n origins.set(parsed, { source, where });\n return parsed;\n });\n // What each keyless manifest can never own, fixed here against the whole\n // declaration. A caller that narrows a collection to some of its manifests\n // still gets the answer the full collection gives.\n for (const manifest of parsedEntries) {\n if (manifest.keys === undefined) declaredNever.set(manifest, neverOwnedIn(parsedEntries, manifest));\n }\n return parsedEntries;\n}\n\n/**\n * The problem with `file`'s `{page}` placeholder grammar (0058 § 1), as the\n * tail of a sentence that starts with the entry's path, or `null` when there\n * is none. One token, spelled exactly, at most once; every other brace is\n * refused rather than read as a literal.\n */\nfunction pagePlaceholderProblem(file: string): string | null {\n for (const match of file.matchAll(/\\{[^{}]*\\}/g)) {\n if (match[0] !== PAGE_PLACEHOLDER) {\n return `uses \"${match[0]}\", which is not a placeholder. The only placeholder is ${PAGE_PLACEHOLDER}.`;\n }\n }\n const tokens = file.split(PAGE_PLACEHOLDER);\n const stray = /[{}]/.exec(tokens.join(\"\"));\n if (stray !== null) {\n return `contains \"${stray[0]}\" outside a ${PAGE_PLACEHOLDER} placeholder. Remove it, or write ${PAGE_PLACEHOLDER}.`;\n }\n if (tokens.length > 2) {\n return `uses ${PAGE_PLACEHOLDER} twice. One manifest names one page.`;\n }\n return null;\n}\n\n/**\n * Where a parsed `externalMetadata:` entry was declared: the config file as\n * the parse was told to name it, and the entry's\n * `collections[c].externalMetadata[i]` path. Kept beside the entry rather\n * than on it, so the entry's shape, its equality, and anything that\n * serializes it stay what they were. A copy made with a spread has no origin.\n */\nconst origins = new WeakMap<ExternalMetadataConfig, { source: string; where: string }>();\n\n/**\n * The config file and path an entry was parsed from, for a refusal that is\n * known only once a page is: a `{page}` manifest resolving above the config\n * directory. `undefined` for an entry `parseCollections` did not build.\n */\nexport function externalMetadataOrigin(\n entry: ExternalMetadataConfig,\n): { source: string; where: string } | undefined {\n return origins.get(entry);\n}\n\n/**\n * Does `manifest`, one of `collection`'s, own `key` for one page? The one\n * answer every reader and writer asks for (proposal 0068).\n *\n * A manifest with `keys` owns exactly those, for every page. One without owns\n * what `marked` holds: the top-level keys the page's resolved schema set marks\n * `x-manni-location: external`. It never owns a key another manifest of the\n * collection names in its own `keys`, because written keys win. Nor its own\n * join field, which the page carries to name its entry, nor `$schema`. With\n * `marked` undefined, because the page's schemas are not known, a keyless\n * manifest owns nothing.\n */\nexport function ownsKey(\n collection: Pick<CollectionConfig, \"externalMetadata\">,\n manifest: ExternalMetadataConfig,\n key: string,\n marked: ReadonlySet<string> | undefined,\n): boolean {\n if (manifest.keys !== undefined) return manifest.keys.includes(key);\n if (marked === undefined || !marked.has(key)) return false;\n return !neverOwned(collection, manifest).has(key);\n}\n\n/**\n * The keys a keyless manifest never owns, whatever a page marks: the keys\n * the collection's other manifests name, its join field, and `$schema`. An\n * entry setting one of them is refused when the manifest is read.\n */\nexport function neverOwned(\n collection: Pick<CollectionConfig, \"externalMetadata\">,\n manifest: ExternalMetadataConfig,\n): ReadonlySet<string> {\n return declaredNever.get(manifest) ?? neverOwnedIn(collection.externalMetadata, manifest);\n}\n\nfunction neverOwnedIn(\n manifests: readonly ExternalMetadataConfig[],\n manifest: ExternalMetadataConfig,\n): Set<string> {\n const out = new Set<string>([SCHEMA_KEY]);\n if (manifest.join !== undefined && manifest.join !== PATH_JOIN) out.add(manifest.join);\n for (const other of manifests) {\n if (other === manifest) continue;\n for (const key of other.keys ?? []) out.add(key);\n }\n return out;\n}\n\n/**\n * `neverOwned` for each keyless manifest `parseCollections` built, computed\n * over its whole collection. Kept beside the entry, as `origins` is, so a\n * copy of the collection filtered to some of its manifests does not forget a\n * key a manifest it left out names.\n */\nconst declaredNever = new WeakMap<ExternalMetadataConfig, ReadonlySet<string>>();\n\n/**\n * The collections a run covers: every one when `names` is undefined, else the\n * named ones in **declaration** order, however they were named on the command\n * line. Repeats collapse; an unknown name is an error, because a typo that\n * silently narrowed the run to nothing would read as a clean pass.\n */\nexport function selectCollections(\n collections: readonly CollectionConfig[],\n names: readonly string[] | undefined,\n source: string,\n toError: (message: string) => Error,\n): CollectionConfig[] {\n // An empty list means the same as no list: every collection. Commander's\n // repeatable-option collector defaults to `[]`, so \"the user passed no\n // --collection\" reaches this function as an empty array at least as often as\n // it reaches it as `undefined`. Were `[]` to select nothing, a plain\n // `manni meta validate` would validate zero files and exit 0 the moment a\n // caller forwarded the raw commander value — the false green proposal 0014\n // exists to end, and invisible in the exit code. Nothing needs to express\n // \"select no collections\", so the ambiguity is resolved in the safe\n // direction. Do not tighten this back.\n if (names === undefined || names.length === 0) return [...collections];\n const wanted = new Set(names);\n for (const name of wanted) {\n if (!collections.some((c) => c.name === name)) {\n const configured =\n collections.length === 0\n ? \"(none)\"\n : collections.map((c) => c.name).join(\", \");\n // Names are unique case-insensitively (they become SQL views) but are\n // selected by their one spelling. When the miss is only a casing miss,\n // say so — \"Configured: guides\" beside \"Guides\" reads as a contradiction\n // to anyone who does not already know the rule.\n const nearMiss = collections.find(\n (c) => c.name.toLowerCase() === name.toLowerCase(),\n );\n const hint =\n nearMiss === undefined\n ? \"\"\n : ` Names are case-sensitive; did you mean \"${nearMiss.name}\"?`;\n throw toError(\n `no collection named \"${name}\" in ${source}. Configured: ${configured}.${hint}`,\n );\n }\n }\n return collections.filter((c) => wanted.has(c.name));\n}\n\n/**\n * Is this file a member of the collection? `relPath` is posix and already\n * relative to the config file's directory, so this is arithmetic on a string:\n * no `stat`, nothing that can fail, and nothing that costs per file. A path\n * that climbed out of the config directory, and stdin, are members of nothing.\n */\nexport function isMember(\n collection: CollectionConfig,\n relPath: string,\n): boolean {\n if (relPath === STDIN) return false;\n if (relPath.startsWith(\"../\")) return false;\n return matchesCollectionPaths(collection, relPath);\n}\n\n/**\n * Do the collection's `paths:` and `exclude:` claim this path, before the two\n * guards `isMember` adds? Only a caller that has to *say* why a claimed path\n * is not a member needs this: a page above the config directory that a\n * `{page}` manifest would resolve outside the tree (proposal 0058). Everyone\n * else wants `isMember`.\n */\nexport function matchesCollectionPaths(\n collection: CollectionConfig,\n relPath: string,\n): boolean {\n const matched = collection.paths.some((entry) => {\n const normalized = normalizeEntry(entry);\n if (normalized === \"\") return false;\n if (isGlob(normalized)) return matchesFileGlob(relPath, normalized);\n return relPath === normalized || relPath.startsWith(`${normalized}/`);\n });\n if (!matched) return false;\n // `exclude` is globs only, whatever `paths` was written as.\n return !collection.exclude.some((glob) => matchesFileGlob(relPath, glob));\n}\n\n/** A `paths` entry as a posix path with no `./` prefix and no trailing `/`. */\nfunction normalizeEntry(entry: string): string {\n const posix = entry.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n return posix.endsWith(\"/\") ? posix.slice(0, -1) : posix;\n}\n\n/**\n * Is this `paths` entry a glob, by picomatch's own definition, rather than a\n * bare file or directory to match by prefix?\n *\n * picomatch decides, not a character class of our own: an extended glob such\n * as `docs/!(drafts)/**` or `@(a|b)` has no `*`, `?`, `[` or `{` of its own\n * and a hand-rolled test read it as a literal path — matching nothing and\n * excluding nothing, silently. The one cost is that a literal directory whose\n * name carries parentheses, `docs/notes (old)`, is also read as a glob and so\n * matches itself only, not everything beneath it. Spell such a path as\n * `docs/notes (old)/**` to get the prefix behaviour back.\n */\nfunction isGlob(entry: string): boolean {\n return picomatch.scan(entry).isGlob;\n}\n","/**\n * Glob matching, shared by every tool.\n *\n * One matcher, so that the two questions that look alike really are alike: is\n * this file in a collection (proposal 0041), and does it match a schema\n * override's `files:` (0015). It lived in the metadata tool's schema resolver\n * until membership needed it too; a second copy is how the two would start\n * disagreeing about a dot file or a `\\`.\n */\nimport picomatch from \"picomatch\";\n\n/**\n * One compiled matcher per distinct glob, kept for the life of the process.\n * Never evicted, on purpose: the set of globs a run sees is the config's plus\n * a handful of flags, which is dozens at most, and a compile is the expensive\n * step. This module is shared across the family, so if it ever sits inside a\n * long-lived host that feeds it unbounded user globs, this is the line to\n * revisit — bound it, or key it per run. Until then an eviction policy would\n * be machinery for a case that does not exist.\n */\nconst matcherCache = new Map<string, (p: string) => boolean>();\n\n/**\n * Does this file path match the glob — or, for the list form, any of them — by\n * the shared rules (picomatch, dot files included, `\\` normalized to `/`)?\n */\nexport function matchesFileGlob(\n relPath: string,\n glob: string | readonly string[],\n): boolean {\n // picomatch takes a pattern array natively, so the list form needs no loop\n // here — but the cache does need a *string* key. Keyed on the array itself\n // this Map would compare by identity: a hit only for the very same object,\n // and an entry retained for every config ever parsed. JSON.stringify is the\n // key because it separates the two shapes on its own — a lone \"a/**\" and\n // [\"a/**\"] serialize differently — with no separator char to collide with\n // one inside a glob.\n const key = JSON.stringify(glob);\n let m = matcherCache.get(key);\n if (!m) {\n m = picomatch(glob as string | string[], { dot: true });\n matcherCache.set(key, m);\n }\n // Normalize Windows separators so globs written with `/` still match.\n return m(relPath.replace(/\\\\/g, \"/\"));\n}\n","/**\n * The `{page}` placeholder in `externalMetadata[].file` (proposal 0058).\n *\n * A `file` holding `{page}` is a rule rather than a file: each page gets its\n * own manifest, named by substituting the page's path, relative to the config\n * file's directory and without its extension. `\"{page}.citations.yaml\"` puts\n * `docs/x/index.mdx`'s manifest at `docs/x/index.citations.yaml`.\n *\n * One normalization is used wherever a pattern or a page path is compared or\n * substituted (0058 § 1): strip one leading `./`, and write every separator as\n * a forward slash. It is deliberately that small. It is not `path.resolve`, so\n * `./a/../{page}.yaml` stays its own pattern, and it does not fold case.\n *\n * The grammar itself (one token, spelled exactly, never in a URL or an\n * absolute path) is enforced by the config parser in `collections.ts`, so\n * everything here may assume a `file` that passed it.\n *\n * Shared, so it imports nothing from a tool.\n */\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { isAbsolute, posix, relative, resolve, sep } from \"node:path\";\nimport fg from \"fast-glob\";\nimport { LineCounter, isMap, isNode, isScalar, parseDocument } from \"yaml\";\n\n/** The one placeholder `externalMetadata[].file` accepts. */\nexport const PAGE_PLACEHOLDER = \"{page}\";\n\n/** True when `file` contains the {page} token (assumes it already passed parse validation). */\nexport function hasPagePlaceholder(file: string): boolean {\n return file.includes(PAGE_PLACEHOLDER);\n}\n\n/** Every separator written as a forward slash. */\nfunction slashes(path: string): string {\n return path.split(sep).join(\"/\").replace(/\\\\/g, \"/\");\n}\n\n/**\n * 0058 § 1 normalization: strip one leading \"./\", backslashes to \"/\". No case\n * folding, no path.resolve, no \"..\" collapsing.\n */\nexport function normalizeManifestPattern(file: string): string {\n const forward = file.replace(/\\\\/g, \"/\");\n return forward.startsWith(\"./\") ? forward.slice(2) : forward;\n}\n\n/**\n * The manifest a page resolves to. `configDir` absolute; `pageAbs` absolute.\n *\n * `{page}` is `relative(configDir, pageAbs)` with its extension removed,\n * normalized as above, substituted into the normalized pattern. Returns the\n * manifest's absolute path and its posix path relative to `configDir`, or\n * `{ outside: true, pageRel }` when the page is not under the config\n * directory: its relative path climbs with `../`, or, across Windows drives,\n * there is no relative path at all.\n */\nexport function pageManifestPath(\n file: string,\n configDir: string,\n pageAbs: string,\n): { abs: string; rel: string } | { outside: true; pageRel: string } {\n const between = relative(configDir, pageAbs);\n const pageRel = slashes(between);\n if (isAbsolute(between) || pageRel === \"..\" || pageRel.startsWith(\"../\")) {\n return { outside: true, pageRel };\n }\n const ext = posix.extname(pageRel);\n const page = ext === \"\" ? pageRel : pageRel.slice(0, -ext.length);\n const named = normalizeManifestPattern(file).replace(PAGE_PLACEHOLDER, () => page);\n const abs = resolve(configDir, named);\n return { abs, rel: slashes(relative(configDir, abs)) };\n}\n\n/**\n * 0058 § 5: the glob every manifest of a pattern matches. The normalized\n * pattern is split at `{page}` and `**` + `/*` inserted, keeping the prefix\n * and the suffix. No page path reaches it, but the prefix and suffix are the\n * config author's own literal text, and a directory name may hold glob\n * syntax: read raw, `meta (v2)/` is an extglob group that matches nothing. So\n * both are escaped, and only the inserted `**` + `/*` is live.\n * `\"{page}.citations.yaml\"` gives `**` + `/*.citations.yaml`, and\n * `\"./meta/{page}.citations.yaml\"` gives `meta/**` + `/*.citations.yaml`.\n */\nexport function strayManifestGlob(file: string): string {\n const normalized = normalizeManifestPattern(file);\n const at = normalized.indexOf(PAGE_PLACEHOLDER);\n if (at < 0) return literal(normalized);\n const prefix = literal(normalized.slice(0, at));\n const suffix = literal(normalized.slice(at + PAGE_PLACEHOLDER.length));\n return `${prefix}**/*${suffix}`;\n}\n\n/** Text a glob matches exactly. fast-glob refuses to escape an empty string. */\nfunction literal(text: string): string {\n return text === \"\" ? \"\" : fg.escapePath(text);\n}\n\n/** One entry of a per-page manifest found on disk, naming the page it is for. */\nexport interface FoundPageEntry {\n /** The key exactly as the manifest spells it. */\n spelled: string;\n /** The page it names, absolute. */\n pageAbs: string;\n /** 1-based line of the key in the manifest, when known. */\n line?: number;\n}\n\n/** A manifest file a `{page}` pattern names, and the entries it holds for its own page. */\nexport interface FoundPageManifest {\n /** The manifest, absolute. */\n abs: string;\n entries: FoundPageEntry[];\n}\n\n/**\n * The manifests on disk that `file`, a `{page}` pattern, names (0058 § 5):\n * every file the pattern's `strayManifestGlob` matches under `configDir`,\n * kept when it holds an entry the pattern resolves back to that very file,\n * for a page `isMember` claims. `isMember` takes the page's posix path\n * relative to `configDir`. A file with no such entry is not this pattern's\n * manifest, whatever its name: a copy of the tree elsewhere, a fixture, or a\n * file that only looks like one.\n *\n * The walk does not consult `.gitignore`, because a manifest is config named\n * input rather than a discovered document. It skips `node_modules`, `.git`\n * and dot directories, as the document walk does, so a checkout nested under\n * a dot directory is never read as this one's. A file that cannot be read or\n * parsed is not recognizably a manifest, and is skipped.\n *\n * Synchronous, so a synchronous orphan check can call it. It runs once per\n * whole-corpus run, never for a run given paths. A match in `skip` (absolute\n * paths) is neither read nor returned: the orphan check passes the manifests\n * the run already loaded, which would otherwise be parsed a second time.\n */\nexport function findPageManifests(\n file: string,\n configDir: string,\n isMember: (relPath: string) => boolean,\n skip: ReadonlySet<string> = new Set(),\n): FoundPageManifest[] {\n const matched = fg\n .sync(strayManifestGlob(file), {\n cwd: configDir,\n absolute: true,\n onlyFiles: true,\n dot: false,\n ignore: [\"**/node_modules/**\", \"**/.git/**\"],\n })\n .map((path) => resolve(path))\n .sort();\n const found: FoundPageManifest[] = [];\n for (const abs of matched) {\n if (skip.has(abs)) continue;\n const entries = ownEntries(abs, file, configDir, isMember);\n if (entries.length > 0) found.push({ abs, entries });\n }\n return found;\n}\n\n/** The entries of the manifest at `abs` that `file` resolves back to `abs`. */\nfunction ownEntries(\n abs: string,\n file: string,\n configDir: string,\n isMember: (relPath: string) => boolean,\n): FoundPageEntry[] {\n let text: string;\n try {\n text = readFileSync(abs, \"utf8\");\n } catch {\n return [];\n }\n const lc = new LineCounter();\n const doc = parseDocument(text, { lineCounter: lc, uniqueKeys: false });\n const root = doc.contents;\n if (doc.errors.length > 0 || !isMap(root)) return [];\n const entries: FoundPageEntry[] = [];\n for (const pair of root.items) {\n const spelled = isScalar(pair.key) ? String(pair.key.value) : String(pair.key);\n const pageAbs = resolve(configDir, spelled);\n if (!isMember(slashes(relative(configDir, pageAbs)))) continue;\n const resolved = pageManifestPath(file, configDir, pageAbs);\n if (\"outside\" in resolved || !sameFile(resolved.abs, abs)) continue;\n const range = isNode(pair.key) ? pair.key.range : undefined;\n entries.push({\n spelled,\n pageAbs,\n ...(range ? { line: lc.linePos(range[0]).line } : {}),\n });\n }\n return entries;\n}\n\n/**\n * Are these one file, as the filesystem resolves them? Compared as files and\n * not as text (0058 stress test 8), so a case-only difference is one file on\n * a case-insensitive filesystem and two elsewhere.\n */\nexport function sameFile(a: string, b: string): boolean {\n if (a === b) return true;\n try {\n return realpathSync(a) === realpathSync(b);\n } catch {\n return false;\n }\n}\n","/**\n * `tools:`, the family-level home for an outside tool's settings (proposal\n * 0052, in the shape lint's proposal 0050 recorded).\n *\n * An outside tool is not any one domain's, so its settings do not sit under a\n * domain's key. Vale is the case in point: `manni term lint` runs it, and so\n * does a docevals grader. Each should read one description of where Vale's\n * config lives, not two that can disagree.\n *\n * Each tool gets a namespace of its own keys rather than a shared\n * `{tool, config}` shape, because tools do not have the same settings. A\n * lowest-common-denominator shape would either lie or grow a passthrough blob.\n *\n * Only the declaration is shared. Running a tool stays with the domain that\n * runs it. Paths are kept as the user spelled them and resolved on use,\n * against the config file's directory, the way `collections:` keeps its globs;\n * whether the file exists is a question for the run that needs it.\n */\nimport { isAbsolute, resolve } from \"node:path\";\n\n/** Vale's settings under `tools.vale`. */\nexport interface ValeToolConfig {\n /** Vale's config file, as written, relative to `manni.config.yaml`. */\n config?: string;\n}\n\n/** DITA Open Toolkit's settings under `tools.dita-ot`. */\nexport interface DitaOtToolConfig {\n /**\n * DITA-OT's installation directory, as written, relative to\n * `manni.config.yaml`. Unset means the `dita` on PATH.\n *\n * A directory rather than a path to the launcher, because that is the unit\n * DITA-OT is distributed and documented as: the download unpacks to one\n * directory, and `bin/dita` inside it will not run from anywhere else.\n */\n home?: string;\n}\n\n/** Doc Detective's settings under `tools.doc-detective`. */\nexport interface DocDetectiveToolConfig {\n /**\n * Doc Detective's config file, as written, relative to `manni.config.yaml`.\n * Unset means Doc Detective finds its own in the working directory.\n */\n config?: string;\n}\n\n/** The document's top-level `tools:`, one namespace per outside tool. */\nexport interface ToolsConfig {\n vale?: ValeToolConfig;\n \"dita-ot\"?: DitaOtToolConfig;\n \"doc-detective\"?: DocDetectiveToolConfig;\n}\n\nconst TOOL_KEYS = [\"vale\", \"dita-ot\", \"doc-detective\"] as const;\n\nfunction isMapping(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction rejectUnknownKeys(\n raw: Record<string, unknown>,\n allowed: readonly string[],\n where: string,\n source: string,\n toError: (message: string) => Error,\n): void {\n for (const key of Object.keys(raw)) {\n if (!allowed.includes(key)) {\n throw toError(\n `${source}: ${where} has unknown key \"${key}\". Supported keys: ${allowed.join(\", \")}.`,\n );\n }\n }\n}\n\n/**\n * Parse the value under the top-level `tools:` key. A bare `tools:` (YAML\n * null) is no tools. Every refusal is a shape that would otherwise read as\n * configured and do nothing: a misspelled tool, or a key a tool never reads.\n */\nexport function parseTools(\n raw: unknown,\n source: string,\n toError: (message: string) => Error,\n): ToolsConfig {\n if (raw === null || raw === undefined) return {};\n if (!isMapping(raw)) {\n throw toError(`${source}: \"tools\" must be a mapping.`);\n }\n rejectUnknownKeys(raw, TOOL_KEYS, \"tools\", source, toError);\n\n const tools: ToolsConfig = {};\n if (Object.hasOwn(raw, \"vale\")) {\n tools.vale = parseStringKey(raw[\"vale\"], \"vale\", \"config\", source, toError);\n }\n if (Object.hasOwn(raw, \"dita-ot\")) {\n tools[\"dita-ot\"] = parseStringKey(raw[\"dita-ot\"], \"dita-ot\", \"home\", source, toError);\n }\n if (Object.hasOwn(raw, \"doc-detective\")) {\n tools[\"doc-detective\"] = parseStringKey(\n raw[\"doc-detective\"],\n \"doc-detective\",\n \"config\",\n source,\n toError,\n );\n }\n return tools;\n}\n\n/**\n * One tool's namespace, whose single key is a non-empty string. Every tool\n * read today has that shape; a tool with a second key or another type gets a\n * parser of its own.\n */\nfunction parseStringKey<K extends string>(\n raw: unknown,\n tool: string,\n key: K,\n source: string,\n toError: (message: string) => Error,\n): Partial<Record<K, string>> {\n if (!isMapping(raw)) {\n throw toError(`${source}: tools.${tool} must be a mapping.`);\n }\n rejectUnknownKeys(raw, [key], `tools.${tool}`, source, toError);\n\n const out: Partial<Record<K, string>> = {};\n if (Object.hasOwn(raw, key)) {\n const value = raw[key];\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw toError(`${source}: tools.${tool}.${key} must be a non-empty string.`);\n }\n out[key] = value;\n }\n return out;\n}\n\n/** A path as written in the config, made absolute against the config file's directory. */\nfunction fromConfigDir(path: string, configDir: string): string {\n return isAbsolute(path) ? path : resolve(configDir, path);\n}\n\n/**\n * The absolute path of Vale's config file, resolved against the directory of\n * the config file that declared it. `undefined` when none is set, which means\n * Vale finds its own config, as it does when a person runs it.\n */\nexport function valeConfigPath(tools: ToolsConfig, configDir: string): string | undefined {\n const config = tools.vale?.config;\n if (config === undefined) return undefined;\n return fromConfigDir(config, configDir);\n}\n\n/**\n * The absolute path of DITA-OT's installation directory, resolved against the\n * directory of the config file that declared it. `undefined` when none is set,\n * which means the `dita` on PATH, as when a person runs it.\n */\nexport function ditaOtHome(tools: ToolsConfig, configDir: string): string | undefined {\n const home = tools[\"dita-ot\"]?.home;\n if (home === undefined) return undefined;\n return fromConfigDir(home, configDir);\n}\n\n/**\n * The absolute path of Doc Detective's config file, resolved against the\n * directory of the config file that declared it. `undefined` when none is set,\n * which means Doc Detective finds its own in the working directory.\n */\nexport function docDetectiveConfigPath(\n tools: ToolsConfig,\n configDir: string,\n): string | undefined {\n const config = tools[\"doc-detective\"]?.config;\n if (config === undefined) return undefined;\n return fromConfigDir(config, configDir);\n}\n","/**\n * The family config file.\n *\n * One file per repository, `manni.config.yaml`, with one top-level key per\n * tool: `meta:` for the metadata tool, `docevals:`, `tracevals:`, `lint:`,\n * `graph:` as each lands. A tool reads its own key and leaves its siblings alone,\n * so adding a tool never invalidates a config that predates it.\n *\n * This module only finds the file and hands a tool its slice. What the slice\n * may contain is the tool's business; it validates the value with its own\n * parser and its own error class, which is why `toError` is an option rather\n * than an import. Four top-level keys belong to the family rather than to a\n * tool, and are parsed here once for every tool. They are `collections:`\n * (proposal 0041), `tools:` (proposal 0052) and `encryptionKey:` (proposal\n * 0045). The fourth is `providers:`, the inference provider settings every\n * tool that sends content to a model reads.\n *\n * One older spelling is still read, with a warning on discovery: each tool's\n * pre-family file (`docmeta.config.yaml` for the metadata tool), whose whole\n * document is the tool's section with no wrapper key.\n *\n * Within one directory the order is manni, then the legacy names, and\n * `.yaml` before `.yml`. A family file that exists but has neither a key for\n * this tool nor a family key is not this tool's config: discovery keeps\n * looking, first at the legacy names beside it (a repository mid-migration,\n * where a sibling tool moved to the family file first) and then up the tree.\n * The walk stops at the nearest `.git` boundary, nearest directory first, and\n * the first hit wins; ancestor files are never merged.\n *\n * An explicit path is read the same way minus the warning: the section key is\n * unwrapped when present, a document carrying a family key is a family file\n * with an empty section, and the whole document is taken otherwise, so a\n * `-c ./anything.yaml` needs no filename sniffing and no flag.\n *\n * The core is synchronous: some tools load config from synchronous library\n * entry points, and one small file read is not worth an `await` through their\n * public API. `findConfigFileSync` and `readConfigFileSync` expose it; the\n * async spellings are wrappers over the same core for callers that await.\n */\nimport { readFileSync } from \"node:fs\";\nimport { basename, dirname, join, relative, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { parseCollections, type CollectionConfig } from \"./collections.js\";\nimport { isValidEncryptionKey } from \"./encryption.js\";\nimport { PROVIDERS_KEY, parseProviders, type ProvidersConfig } from \"./providers.js\";\nimport { searchPath } from \"./git-root.js\";\nimport { parseTools, type ToolsConfig } from \"./tools.js\";\nimport { warn } from \"./warn.js\";\nimport { errorMessage } from \"./errors.js\";\n\n/** The canonical family filenames, in discovery order. */\nexport const FAMILY_CONFIG_NAMES: readonly string[] = [\n \"manni.config.yaml\",\n \"manni.config.yml\",\n];\n\n/** The top-level key holding the family encryption key (proposal 0045). */\nexport const ENCRYPTION_KEY_FIELD = \"encryptionKey\";\n\n/**\n * The top-level key holding the key a rotation is replacing (proposal 0045).\n * Present only while a rotation is unfinished: `manni key rotate` writes it\n * beside the new key before it touches a page, and removes it once every page\n * is written. Read only by `manni key rotate`, which finishes the rotation.\n */\nexport const ENCRYPTION_KEY_PREVIOUS_FIELD = \"encryptionKeyPrevious\";\n\nexport interface ConfigFileOptions {\n /** The tool's key in a family file, e.g. `\"meta\"`. */\n section: string;\n /**\n * The tool's own filenames from before the family file, in discovery order.\n * Their whole document is the tool's section.\n */\n legacyNames: readonly string[];\n /** Build the error for an unreadable document, in the tool's own class. */\n toError: (message: string) => Error;\n}\n\nexport interface ConfigFile {\n /** Absolute path to the file. */\n path: string;\n /** Directory holding it; relative paths in the config resolve from here. */\n dir: string;\n /**\n * The file as the user would name it: relative to `cwd`, posix separators,\n * or the spelling they typed for an explicit path. For error messages.\n */\n source: string;\n /** The file's text, for a tool that rewrites the file in place. */\n text: string;\n /**\n * The tool's slice: the value under `section:` when `wrapped`, otherwise\n * the whole document. `null` for an empty file or an empty section; the\n * tool decides what empty means.\n */\n value: unknown;\n /** Whether `value` came from under `section:`. */\n wrapped: boolean;\n /** How the file was found. Explicit paths are never warned about. */\n kind: \"manni\" | \"legacy\" | \"explicit\";\n /**\n * The document's top-level `collections:` (proposal 0041), parsed once here\n * because every tool reads the same declaration. `[]` when the key is\n * absent, and always `[]` for a `legacy` file: a per-tool file's whole\n * document *is* the tool's section, so it carries no family-wide keys.\n */\n collections: CollectionConfig[];\n /**\n * The document's top-level `tools:` (proposal 0052), an outside tool's\n * settings, parsed once here for the same reason as `collections`. `{}` when\n * the key is absent, and always `{}` for a `legacy` file.\n */\n tools: ToolsConfig;\n /**\n * The document's top-level `encryptionKey:` (proposal 0045), validated\n * here. Absent when the key is, and always absent for a `legacy` file, for\n * the same reason as `collections`. `MANNI_ENCRYPTION_KEY` wins over it;\n * read it through `resolveEncryptionKey`, not directly.\n */\n encryptionKey?: string;\n /**\n * The document's top-level `encryptionKeyPrevious:`, validated here as\n * `encryptionKey` is. Present only while a rotation is unfinished, and read\n * only by `manni key rotate`, which finishes it.\n */\n encryptionKeyPrevious?: string;\n /**\n * The document's top-level `providers:`, parsed here with a relative\n * `llama-cpp.modelsDir` resolved against `dir`. Absent when the key is, and\n * always absent for a `legacy` file, for the same reason as `collections`.\n */\n providers?: ProvidersConfig;\n}\n\ntype ToError = (message: string) => Error;\n\ninterface Document {\n text: string;\n /** Parsed YAML: a mapping, or `null` for an empty file. */\n doc: Record<string, unknown> | null;\n}\n\nfunction readDocument(\n path: string,\n source: string,\n toError: ToError,\n): Document | null {\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n return { text, doc: parseMapping(text, source, toError) };\n}\n\n/**\n * Parse outside any \"not found\" handling. A discovered file with a typo must\n * be reported, not skipped: falling through to the next candidate, or to no\n * config at all, would run the tool on defaults and read as a clean pass.\n */\nfunction parseMapping(\n text: string,\n source: string,\n toError: ToError,\n): Record<string, unknown> | null {\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (err) {\n throw toError(`${source}: invalid YAML: ${errorMessage(err)}`);\n }\n if (raw == null) return null;\n if (typeof raw !== \"object\" || Array.isArray(raw)) {\n throw toError(`${source}: top level must be a mapping.`);\n }\n return raw as Record<string, unknown>;\n}\n\n/** The top-level key holding the family's document sets. */\nconst COLLECTIONS_KEY = \"collections\";\n\n/** The top-level key holding outside tools' settings (proposal 0052). */\nconst TOOLS_KEY = \"tools\";\n\n/** The keys that belong to the family, not to any one tool. */\nconst FAMILY_KEYS: readonly string[] = [\n COLLECTIONS_KEY,\n TOOLS_KEY,\n ENCRYPTION_KEY_FIELD,\n PROVIDERS_KEY,\n];\n\nfunction slice(\n document: Document,\n opts: ConfigFileOptions,\n): { value: unknown; wrapped: boolean } | null {\n const { doc } = document;\n if (doc !== null && Object.hasOwn(doc, opts.section)) {\n return { value: doc[opts.section] ?? null, wrapped: true };\n }\n // A family file carrying a family key and no section is still this tool's\n // config: the documents are declared (0041), or the key is (0045), or the\n // provider settings are, and the tool just has no options of its own.\n // Handing back an empty section is what stops discovery walking past it.\n // For the key that matters twice: a\n // file the key prompt created holds nothing else, and the next run must\n // find it.\n if (doc !== null && FAMILY_KEYS.some((key) => Object.hasOwn(doc, key))) {\n return { value: null, wrapped: true };\n }\n return null;\n}\n\n/**\n * The document's collections. Parsed with the tool's own `toError`, so a bad\n * `collections:` is reported in the class the tool already catches.\n */\nfunction collectionsOf(\n document: Document,\n source: string,\n toError: ToError,\n): CollectionConfig[] {\n const { doc } = document;\n if (doc === null || !Object.hasOwn(doc, COLLECTIONS_KEY)) return [];\n return parseCollections(doc[COLLECTIONS_KEY], source, toError);\n}\n\n/**\n * The document's outside-tool settings. Parsed with the tool's own `toError`,\n * as `collections:` is.\n */\nfunction toolsOf(document: Document, source: string, toError: ToError): ToolsConfig {\n const { doc } = document;\n if (doc === null || !Object.hasOwn(doc, TOOLS_KEY)) return {};\n return parseTools(doc[TOOLS_KEY], source, toError);\n}\n\n/**\n * The document's encryption key, as a spreadable field. A malformed one is an\n * error in the tool's class, and the message never repeats the value: it is\n * key material, and a CI log is not the place for it.\n */\nfunction encryptionKeyOf(\n document: Document,\n source: string,\n toError: ToError,\n): { encryptionKey?: string; encryptionKeyPrevious?: string } {\n const { doc } = document;\n if (doc === null) return {};\n const keys: { encryptionKey?: string; encryptionKeyPrevious?: string } = {};\n if (Object.hasOwn(doc, ENCRYPTION_KEY_FIELD)) {\n const value = doc[ENCRYPTION_KEY_FIELD];\n if (!isValidEncryptionKey(value)) {\n throw toError(\n `${source}: \"${ENCRYPTION_KEY_FIELD}\" must be at least 32 hex or base64url characters. Run \\`manni key set\\` to generate one.`,\n );\n }\n keys.encryptionKey = value;\n }\n // The key an unfinished rotation is replacing. Key material too, so the\n // same shape rule and the same silence about the value. There is no advice\n // to generate one: `manni key rotate` writes it, and nothing else should.\n if (Object.hasOwn(doc, ENCRYPTION_KEY_PREVIOUS_FIELD)) {\n const value = doc[ENCRYPTION_KEY_PREVIOUS_FIELD];\n if (!isValidEncryptionKey(value)) {\n throw toError(\n `${source}: \"${ENCRYPTION_KEY_PREVIOUS_FIELD}\" must be at least 32 hex or base64url characters.`,\n );\n }\n keys.encryptionKeyPrevious = value;\n }\n return keys;\n}\n\n/**\n * The document's provider settings, as a spreadable field. Parsed with the\n * tool's own `toError`, and a relative path in them resolves from `dir`.\n */\nfunction providersOf(\n document: Document,\n source: string,\n dir: string,\n toError: ToError,\n): { providers?: ProvidersConfig } {\n const { doc } = document;\n if (doc === null || !Object.hasOwn(doc, PROVIDERS_KEY)) return {};\n return { providers: parseProviders(doc[PROVIDERS_KEY], source, dir, toError) };\n}\n\n/**\n * The family keys as `manni key` reads them: only valid ones, and a malformed\n * one is no error, because replacing it is what the caller is for.\n */\nfunction tolerantKeysOf(document: Document): {\n encryptionKey?: string;\n encryptionKeyPrevious?: string;\n} {\n const key = document.doc?.[ENCRYPTION_KEY_FIELD];\n const previous = document.doc?.[ENCRYPTION_KEY_PREVIOUS_FIELD];\n return {\n ...(isValidEncryptionKey(key) ? { encryptionKey: key } : {}),\n ...(isValidEncryptionKey(previous) ? { encryptionKeyPrevious: previous } : {}),\n };\n}\n\nfunction relativeSource(cwd: string, path: string): string {\n return relative(cwd, path).replace(/\\\\/g, \"/\");\n}\n\n/**\n * Discover the tool's config from `cwd` upward. `null` when nothing exists.\n */\nexport function findConfigFileSync(\n cwd: string,\n opts: ConfigFileOptions,\n): ConfigFile | null {\n const start = resolve(cwd);\n for (const dir of searchPath(start)) {\n for (const name of FAMILY_CONFIG_NAMES) {\n const path = join(dir, name);\n const source = relativeSource(start, path);\n const document = readDocument(path, source, opts.toError);\n if (document === null) continue;\n const found = slice(document, opts);\n // A family file without this tool's key belongs to a sibling. Keep\n // looking, rather than treating the file as an empty config.\n if (found === null) continue;\n return {\n path,\n dir,\n source,\n text: document.text,\n kind: \"manni\",\n collections: collectionsOf(document, source, opts.toError),\n tools: toolsOf(document, source, opts.toError),\n ...encryptionKeyOf(document, source, opts.toError),\n ...providersOf(document, source, dir, opts.toError),\n ...found,\n };\n }\n for (const name of opts.legacyNames) {\n const path = join(dir, name);\n const source = relativeSource(start, path);\n const document = readDocument(path, source, opts.toError);\n if (document === null) continue;\n warn(\n `\"${name}\" is a deprecated config file name. Move its keys under \\`${opts.section}:\\` in \"${FAMILY_CONFIG_NAMES[0] ?? \"manni.config.yaml\"}\", and its paths, exclude and sidecars keys to a top-level collections: list, where sidecars becomes externalMetadata.`,\n );\n return {\n path,\n dir,\n source,\n text: document.text,\n value: document.doc,\n wrapped: false,\n kind: \"legacy\",\n collections: [],\n tools: {},\n };\n }\n }\n return null;\n}\n\n/**\n * Read the config at an explicit path. Missing is an error: a `-c` pointing\n * at a file that is not there is a mistake worth failing on, not a reason to\n * quietly run against something else.\n */\nexport function readConfigFileSync(\n explicitPath: string,\n cwd: string,\n opts: ConfigFileOptions,\n): ConfigFile {\n const path = resolve(cwd, explicitPath);\n // Report the spelling the user typed, not the resolved absolute path.\n const document = readDocument(path, explicitPath, opts.toError);\n if (document === null) {\n throw opts.toError(`Config file not found: \"${explicitPath}\".`);\n }\n const found = slice(document, opts) ?? {\n value: document.doc,\n wrapped: false,\n };\n return {\n path,\n dir: dirname(path),\n source: explicitPath,\n text: document.text,\n kind: \"explicit\",\n collections: collectionsOf(document, explicitPath, opts.toError),\n tools: toolsOf(document, explicitPath, opts.toError),\n // An unwrapped document carries no family key (else it would be wrapped),\n // so this only ever reads a family file's key.\n ...encryptionKeyOf(document, explicitPath, opts.toError),\n ...providersOf(document, explicitPath, dirname(path), opts.toError),\n ...found,\n };\n}\n\n/** `findConfigFileSync`, for callers that await. */\nexport function findConfigFile(\n cwd: string,\n opts: ConfigFileOptions,\n): Promise<ConfigFile | null> {\n return Promise.resolve().then(() => findConfigFileSync(cwd, opts));\n}\n\n/** `readConfigFileSync`, for callers that await. */\nexport function readConfigFile(\n explicitPath: string,\n cwd: string,\n opts: ConfigFileOptions,\n): Promise<ConfigFile> {\n return Promise.resolve().then(() =>\n readConfigFileSync(explicitPath, cwd, opts),\n );\n}\n\n/**\n * The nearest family file from `cwd` upward, whatever it carries: any tool's\n * section, a family key, or nothing at all. For `manni key`, which writes the\n * family's key and so belongs to no one tool's section. Never a legacy\n * per-tool file, whose whole document is one tool's section.\n *\n * `value` is `null` and `wrapped` is `true`: no tool's section is handed\n * over. `encryptionKey` is set only when the file holds a valid one. A\n * malformed key is not an error here, unlike in `findConfigFile`, because\n * replacing it is what the caller is for.\n */\nexport function findFamilyConfigFile(\n cwd: string,\n toError: ToError,\n): Promise<ConfigFile | null> {\n return Promise.resolve().then(() => findFamilyConfigFileSync(cwd, toError));\n}\n\nfunction findFamilyConfigFileSync(\n cwd: string,\n toError: ToError,\n): ConfigFile | null {\n const start = resolve(cwd);\n for (const dir of searchPath(start)) {\n for (const name of FAMILY_CONFIG_NAMES) {\n const path = join(dir, name);\n const source = relativeSource(start, path);\n const document = readDocument(path, source, toError);\n if (document === null) continue;\n return {\n path,\n dir,\n source,\n text: document.text,\n value: null,\n wrapped: true,\n kind: \"manni\",\n collections: collectionsOf(document, source, toError),\n tools: toolsOf(document, source, toError),\n ...tolerantKeysOf(document),\n };\n }\n }\n return null;\n}\n\n/**\n * Read an explicit path as a family file, for `manni key set -c`, which writes\n * the family's key and so belongs to no one tool's section.\n *\n * The file is a family file when it is empty, carries a family key, or is\n * named as one (`manni.config.yaml`, `manni.config.yml`): its top-level keys\n * are then sections, whatever they are, and `value` is `null`. Anything else\n * is read whole as one tool's section (`wrapped: false`), which the key writer\n * refuses rather than turn into a family file under that tool's feet. Missing\n * is an error, as it is for `readConfigFile`. Keys are carried as\n * `findFamilyConfigFile` carries them.\n */\nexport function readFamilyConfigFile(\n explicitPath: string,\n cwd: string,\n toError: ToError,\n): Promise<ConfigFile> {\n return Promise.resolve().then(() =>\n readFamilyConfigFileSync(explicitPath, cwd, toError),\n );\n}\n\nfunction readFamilyConfigFileSync(\n explicitPath: string,\n cwd: string,\n toError: ToError,\n): ConfigFile {\n const path = resolve(cwd, explicitPath);\n const document = readDocument(path, explicitPath, toError);\n if (document === null) {\n throw toError(`Config file not found: \"${explicitPath}\".`);\n }\n const { doc } = document;\n const named = FAMILY_CONFIG_NAMES.includes(basename(path));\n const family =\n doc === null || named || FAMILY_KEYS.some((key) => Object.hasOwn(doc, key));\n return {\n path,\n dir: dirname(path),\n source: explicitPath,\n text: document.text,\n value: family ? null : doc,\n wrapped: family,\n kind: \"explicit\",\n collections: collectionsOf(document, explicitPath, toError),\n tools: toolsOf(document, explicitPath, toError),\n ...tolerantKeysOf(document),\n };\n}\n","/**\n * The project boundary: the nearest directory at or above `cwd` holding a\n * `.git` entry. A file or a directory, since a worktree's `.git` is a file.\n *\n * Two callers need it for opposite reasons and only one of them wants a\n * chain. Config discovery walks the chain and stops here. The SARIF reporter\n * needs the root itself, and needs \"there is no repository\" to be\n * distinguishable from \"the repository root is where you are standing\" — a\n * one-element chain conflates the two, and getting that wrong means emitting\n * paths GitHub silently drops.\n */\nimport { existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\n\nexport function findGitRoot(cwd: string): string | null {\n let dir = resolve(cwd);\n for (;;) {\n if (existsSync(join(dir, \".git\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * The directories a discovery walk may look in, nearest first.\n *\n * The walk stops at the project boundary `findGitRoot` reports, which is\n * included in the search. Only that one call touches the filesystem; the chain\n * itself is then assembled from path strings.\n *\n * With **no** boundary anywhere above cwd, only cwd is considered. A\n * project-scoped config has no meaning without a project, and walking on would\n * let a stray config file in a home or temp directory silently govern\n * unrelated runs — including this repo's own tests, which work in OS temp\n * directories under the user's home.\n */\nexport function searchPath(cwd: string): string[] {\n const start = resolve(cwd);\n const root = findGitRoot(start);\n if (root === null) return [start];\n const chain: string[] = [];\n let dir = start;\n for (;;) {\n chain.push(dir);\n if (dir === root) return chain;\n const parent = dirname(dir);\n // `root` is an ancestor of `start` by construction, so this is unreachable\n // — but a filesystem race (the boundary removed mid-walk) must not loop.\n if (parent === dir) return chain;\n dir = parent;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAMrB,IAAM,kBAAkB;AAGxB,SAAS,iBAAiB,OAAiC;AAChE,SAAO,OAAO,UAAU,YAAY,gBAAgB,KAAK,KAAK;AAChE;AAGO,IAAM,uBAAuB;AAE7B,SAAS,qBAAqB,OAAiC;AACpE,SAAO,OAAO,UAAU,YAAY,qBAAqB,KAAK,KAAK;AACrE;AAGO,SAAS,wBAAgC;AAC9C,SAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACvC;AAEA,IAAM,UAAU;AAChB,IAAM,cAAc;AACpB,IAAM,YAAY;AAClB,IAAM,QAAQ;AAEd,IAAM,YAAY,IAAI,cAAc,QAAQ;AAY5C,IAAM,cAAc,oBAAI,IAAqB;AAE7C,SAAS,QAAQ,KAAsB;AACrC,QAAM,SAAS,YAAY,IAAI,GAAG;AAClC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,MAAM,OAAO,KAAK,KAAK,MAAM;AACnC,QAAM,SAAS,CAAC,SACd,OAAO,KAAK,SAAS,UAAU,KAAK,OAAO,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;AAChE,QAAM,OAAgB;AAAA,IACpB,SAAS,OAAO,kBAAkB;AAAA,IAClC,OAAO,OAAO,gBAAgB;AAAA,IAC9B,KAAK,OAAO,cAAc;AAAA,EAC5B;AACA,cAAY,IAAI,KAAK,IAAI;AACzB,SAAO;AACT;AAEA,SAAS,WAAW,KAAa,QAAsB;AACrD,MAAI,CAAC,qBAAqB,GAAG,GAAG;AAE9B,UAAM,IAAI;AAAA,MACR,GAAG,MAAM;AAAA,IACX;AAAA,EACF;AACF;AAEA,SAAS,IAAI,MAAsB;AACjC,QAAM,MAAM,OAAO,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI,KAAK,KAAK;AACtE,OAAK,KAAK,GAAG;AACb,MAAI,KAAK,MAAM,IAAI;AACnB,SAAO;AACT;AAGA,SAAS,MAAM,QAA+B;AAC5C,MAAI,IAAI,OAAO,SAAS;AACxB,SAAO,KAAK,KAAK,OAAO,CAAC,MAAM,EAAG;AAClC,MAAI,IAAI,KAAK,OAAO,CAAC,MAAM,IAAM,QAAO;AAExC,MAAI,OAAO,YAAY,KAAK,MAAM,IAAI,KAAK,IAAI,KAAK,MAAO,QAAO;AAClE,SAAO,OAAO,SAAS,GAAG,CAAC;AAC7B;AAEA,SAAS,SAAS,MAAe,SAAiB,QAAwB;AACxE,SAAO,WAAW,UAAU,KAAK,KAAK,EACnC,OAAO,OAAO,EACd,OAAO,OAAO,KAAK,CAAC,CAAI,CAAC,CAAC,EAC1B,OAAO,MAAM,EACb,OAAO,EACP,SAAS,GAAG,WAAW;AAC5B;AAEA,SAAS,IAAI,SAAyB;AACpC,SAAO,OAAO,OAAO,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AACxD;AAOO,SAAS,aACd,OACA,KACA,SACQ;AACR,aAAW,KAAK,cAAc;AAG9B,QAAM,OAAO,KAAK,UAAU,KAAK;AACjC,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,UAAU,sCAAsC;AAAA,EAC5D;AACA,QAAM,OAAO,QAAQ,GAAG;AACxB,QAAM,MAAM,OAAO,KAAK,SAAS,MAAM;AACvC,QAAM,SAAS,IAAI,OAAO,KAAK,MAAM,MAAM,CAAC;AAC5C,QAAM,QAAQ,SAAS,MAAM,KAAK,MAAM;AACxC,QAAM,SAAS,eAAe,eAAe,KAAK,SAAS,OAAO;AAAA,IAChE,eAAe;AAAA,EACjB,CAAC;AACD,SAAO,OAAO,IAAI,GAAG,CAAC;AACtB,QAAM,SAAS,OAAO,OAAO,CAAC,OAAO,OAAO,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AACpE,QAAM,MAAM,OAAO,WAAW;AAC9B,QAAM,QAAQ,OAAO,OAAO,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,QAAQ,GAAG,CAAC;AACxE,SAAO,IAAI,MAAM,SAAS,WAAW,CAAC;AACxC;AAEA,IAAM,SAAS,EAAE,IAAI,MAAM;AAMpB,SAAS,aACd,OACA,KACA,SAC8C;AAC9C,MAAI;AACF,WAAO,KAAK,OAAO,KAAK,OAAO;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KACP,OACA,KACA,SAC8C;AAC9C,MAAI,CAAC,iBAAiB,KAAK,KAAK,CAAC,qBAAqB,GAAG,EAAG,QAAO;AACnE,QAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,QAAM,QAAQ,OAAO,KAAK,MAAM,WAAW;AAG3C,MAAI,MAAM,SAAS,WAAW,MAAM,KAAM,QAAO;AACjD,MAAI,MAAM,SAAS,UAAW,QAAO;AACrC,OAAK,MAAM,SAAS,IAAI,cAAc,aAAa,UAAU,EAAG,QAAO;AACvE,MAAI,MAAM,CAAC,MAAM,QAAS,QAAO;AAEjC,QAAM,QAAQ,MAAM,SAAS,GAAG,IAAI,WAAW;AAC/C,QAAM,SAAS,MAAM,SAAS,IAAI,aAAa,MAAM,SAAS,SAAS;AACvE,QAAM,MAAM,MAAM,SAAS,MAAM,SAAS,SAAS;AACnD,QAAM,OAAO,QAAQ,GAAG;AACxB,QAAM,MAAM,OAAO,KAAK,SAAS,MAAM;AACvC,QAAM,WAAW,iBAAiB,eAAe,KAAK,SAAS,OAAO;AAAA,IACpE,eAAe;AAAA,EACjB,CAAC;AACD,WAAS,OAAO,IAAI,GAAG,CAAC;AACxB,WAAS,WAAW,GAAG;AAEvB,QAAM,SAAS,OAAO,OAAO,CAAC,SAAS,OAAO,MAAM,GAAG,SAAS,MAAM,CAAC,CAAC;AAExE,MAAI,CAAC,gBAAgB,SAAS,MAAM,KAAK,MAAM,GAAG,KAAK,EAAG,QAAO;AACjE,QAAM,OAAO,MAAM,MAAM;AACzB,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,EAAE,OAAO,IAAI;AAClE,QAAM,QAAiB,KAAK,MAAM,IAAI;AACtC,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAGO,IAAM,mBAAmB;AAQzB,SAAS,SAAS,MAAc,KAAqB;AAC1D,aAAW,KAAK,UAAU;AAC1B,QAAM,MAAM,WAAW,UAAU,QAAQ,GAAG,EAAE,GAAG,EAC9C,OAAO,MAAM,MAAM,EACnB,OAAO,KAAK;AACf,SAAO,GAAG,gBAAgB,GAAG,GAAG;AAClC;;;ACzNA,OAAOA,gBAAe;;;ACTtB,OAAO,eAAe;AAWtB,IAAM,eAAe,oBAAI,IAAoC;AAMtD,SAAS,gBACd,SACA,MACS;AAQT,QAAM,MAAM,KAAK,UAAU,IAAI;AAC/B,MAAI,IAAI,aAAa,IAAI,GAAG;AAC5B,MAAI,CAAC,GAAG;AACN,QAAI,UAAU,MAA2B,EAAE,KAAK,KAAK,CAAC;AACtD,iBAAa,IAAI,KAAK,CAAC;AAAA,EACzB;AAEA,SAAO,EAAE,QAAQ,QAAQ,OAAO,GAAG,CAAC;AACtC;;;ADzBA,SAAS,SAAAC,QAAO,aAAa;;;AED7B,SAAS,cAAc,oBAAoB;AAC3C,SAAS,YAAY,OAAO,UAAU,SAAS,WAAW;AAC1D,OAAO,QAAQ;AACf,SAAS,aAAa,OAAO,QAAQ,UAAU,qBAAqB;AAG7D,IAAM,mBAAmB;AAGzB,SAAS,mBAAmB,MAAuB;AACxD,SAAO,KAAK,SAAS,gBAAgB;AACvC;AAGA,SAAS,QAAQ,MAAsB;AACrC,SAAO,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG;AACrD;AAMO,SAAS,yBAAyB,MAAsB;AAC7D,QAAM,UAAU,KAAK,QAAQ,OAAO,GAAG;AACvC,SAAO,QAAQ,WAAW,IAAI,IAAI,QAAQ,MAAM,CAAC,IAAI;AACvD;AAYO,SAAS,iBACd,MACA,WACA,SACmE;AACnE,QAAM,UAAU,SAAS,WAAW,OAAO;AAC3C,QAAM,UAAU,QAAQ,OAAO;AAC/B,MAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,QAAQ,WAAW,KAAK,GAAG;AACxE,WAAO,EAAE,SAAS,MAAM,QAAQ;AAAA,EAClC;AACA,QAAM,MAAM,MAAM,QAAQ,OAAO;AACjC,QAAM,OAAO,QAAQ,KAAK,UAAU,QAAQ,MAAM,GAAG,CAAC,IAAI,MAAM;AAChE,QAAM,QAAQ,yBAAyB,IAAI,EAAE,QAAQ,kBAAkB,MAAM,IAAI;AACjF,QAAM,MAAM,QAAQ,WAAW,KAAK;AACpC,SAAO,EAAE,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG,CAAC,EAAE;AACvD;AAYO,SAAS,kBAAkB,MAAsB;AACtD,QAAM,aAAa,yBAAyB,IAAI;AAChD,QAAM,KAAK,WAAW,QAAQ,gBAAgB;AAC9C,MAAI,KAAK,EAAG,QAAO,QAAQ,UAAU;AACrC,QAAM,SAAS,QAAQ,WAAW,MAAM,GAAG,EAAE,CAAC;AAC9C,QAAM,SAAS,QAAQ,WAAW,MAAM,KAAK,iBAAiB,MAAM,CAAC;AACrE,SAAO,GAAG,MAAM,OAAO,MAAM;AAC/B;AAGA,SAAS,QAAQ,MAAsB;AACrC,SAAO,SAAS,KAAK,KAAK,GAAG,WAAW,IAAI;AAC9C;AAuCO,SAAS,kBACd,MACA,WACAC,WACA,OAA4B,oBAAI,IAAI,GACf;AACrB,QAAM,UAAU,GACb,KAAK,kBAAkB,IAAI,GAAG;AAAA,IAC7B,KAAK;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,KAAK;AAAA,IACL,QAAQ,CAAC,sBAAsB,YAAY;AAAA,EAC7C,CAAC,EACA,IAAI,CAAC,SAAS,QAAQ,IAAI,CAAC,EAC3B,KAAK;AACR,QAAM,QAA6B,CAAC;AACpC,aAAW,OAAO,SAAS;AACzB,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,UAAM,UAAU,WAAW,KAAK,MAAM,WAAWA,SAAQ;AACzD,QAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,EAAE,KAAK,QAAQ,CAAC;AAAA,EACrD;AACA,SAAO;AACT;AAGA,SAAS,WACP,KACA,MACA,WACAA,WACkB;AAClB,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,KAAK,MAAM;AAAA,EACjC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,KAAK,IAAI,YAAY;AAC3B,QAAM,MAAM,cAAc,MAAM,EAAE,aAAa,IAAI,YAAY,MAAM,CAAC;AACtE,QAAM,OAAO,IAAI;AACjB,MAAI,IAAI,OAAO,SAAS,KAAK,CAAC,MAAM,IAAI,EAAG,QAAO,CAAC;AACnD,QAAM,UAA4B,CAAC;AACnC,aAAW,QAAQ,KAAK,OAAO;AAC7B,UAAM,UAAU,SAAS,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,GAAG;AAC7E,UAAM,UAAU,QAAQ,WAAW,OAAO;AAC1C,QAAI,CAACA,UAAS,QAAQ,SAAS,WAAW,OAAO,CAAC,CAAC,EAAG;AACtD,UAAM,WAAW,iBAAiB,MAAM,WAAW,OAAO;AAC1D,QAAI,aAAa,YAAY,CAAC,SAAS,SAAS,KAAK,GAAG,EAAG;AAC3D,UAAM,QAAQ,OAAO,KAAK,GAAG,IAAI,KAAK,IAAI,QAAQ;AAClD,YAAQ,KAAK;AAAA,MACX;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,EAAE,MAAM,GAAG,QAAQ,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,IACrD,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAOO,SAAS,SAAS,GAAW,GAAoB;AACtD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI;AACF,WAAO,aAAa,CAAC,MAAM,aAAa,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AF3JA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,yBAAyB,CAAC,QAAQ,QAAQ,YAAY,MAAM;AAOlE,IAAM,YAAY;AAQlB,IAAM,aAAa;AAOnB,IAAM,QAAQ;AAGd,SAAS,SAAS,KAAsB;AACtC,SAAO,gBAAgB,KAAK,GAAG;AACjC;AAQO,SAAS,2BAA2B,KAA4B;AACrE,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,GAAG;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,aAAa,MAAM,OAAO,aAAa,IAAI;AACpD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,aAAa,SAAU,QAAO;AACzC,MAAI,OAAO,aAAa,WAAW,WAAW,OAAO,QAAQ,EAAG,QAAO;AACvE,MAAI,OAAO,aAAa,SAAS;AAC/B,WAAO;AAAA,EACT;AACA,SAAO,+BAA+B,OAAO,QAAQ;AACvD;AAEA,SAAS,WAAW,MAAuB;AACzC,SACE,SAAS,eACT,SAAS,eACT,SAAS,WACT,SAAS,SACT,KAAK,WAAW,MAAM;AAE1B;AAEA,SAAS,kBACP,KACA,SACA,OACA,QACA,SACM;AACN,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,QAAI,CAAC,QAAQ,SAAS,GAAG,GAAG;AAC1B,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,qBAAqB,GAAG,sBAAsB,QAAQ,KAAK,IAAI,CAAC;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAAkD;AACnE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,iBAAiB,OAAiC;AACzD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM;AACvD;AAMO,SAAS,iBACd,KACA,QACA,SACoB;AACpB,MAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,UAAM,QAAQ,GAAG,MAAM,iCAAiC;AAAA,EAC1D;AACA,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM;AAAA,MACJ,GAAG,MAAM;AAAA,IACX;AAAA,EACF;AAGA,QAAM,QAAQ,oBAAI,IAAoB;AACtC,SAAO,IAAI,IAAI,CAAC,OAAO,MAAM;AAC3B,UAAM,QAAQ,eAAe,CAAC;AAC9B,QAAI,CAAC,UAAU,KAAK,GAAG;AACrB,YAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,qBAAqB;AAAA,IACxD;AACA,sBAAkB,OAAO,iBAAiB,OAAO,QAAQ,OAAO;AAEhE,QAAI,CAAC,iBAAiB,MAAM,IAAI,GAAG;AACjC,YAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,mCAAmC;AAAA,IACtE;AACA,UAAM,OAAO,MAAM;AACnB,UAAM,SAAS,KAAK,YAAY;AAChC,QAAI,WAAW,QAAQ;AACrB,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,UAAU,IAAI;AAAA,MACnC;AAAA,IACF;AAKA,QAAI,WAAW,aAAa,WAAW,mBAAmB,WAAW,YAAY;AAC/E,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,UAAU,IAAI;AAAA,MACnC;AAAA,IACF;AACA,QAAI,OAAO,WAAW,SAAS,GAAG;AAChC,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,UAAU,IAAI;AAAA,MACnC;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,IAAI,MAAM;AAC9B,QAAI,UAAU,QAAW;AACvB,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,UAAU,IAAI,qCAAqC,KAAK;AAAA,MAC7E;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,CAAC;AAEnB,QACE,CAAC,MAAM,QAAQ,MAAM,KAAK,KAC1B,MAAM,MAAM,WAAW,KACvB,MAAM,MAAM,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAC5C;AACA,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK;AAAA,MACrB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM;AAEpB,QAAI,UAAoB,CAAC;AACzB,QAAI,MAAM,YAAY,QAAW;AAC/B,UACE,CAAC,MAAM,QAAQ,MAAM,OAAO,KAC5B,MAAM,QAAQ,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAC9C;AACA,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,mCAAmC;AAAA,MACtE;AACA,gBAAU,MAAM;AAAA,IAClB;AAEA,QAAI;AACJ,QAAI,MAAM,QAAQ,QAAW;AAC3B,UAAI,OAAO,MAAM,QAAQ,YAAY,CAAC,UAAU,MAAM,GAAG,GAAG;AAC1D,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,8BAA8B;AAAA,MACjE;AACA,YAAM,MAAM;AAAA,IACd;AAEA,QAAI,mBAA6C,CAAC;AAClD,QAAI,MAAM,qBAAqB,QAAW;AACxC,UAAI,CAAC,MAAM,QAAQ,MAAM,gBAAgB,GAAG;AAC1C,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,mCAAmC;AAAA,MACtE;AACA,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,SAAY,CAAC,IAAI,EAAE,IAAI;AAAA,IACrC;AAAA,EACF,CAAC;AACH;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,OAAO,aAAa,WAAW,OAAO,aAAa;AAC5D;AAQA,SAAS,sBACP,KACA,iBACA,QACA,SAC0B;AAC1B,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,WAAW,oBAAI,IAAoB;AAEzC,MAAI;AACJ,QAAM,gBAAgB,IAAI,IAAI,CAAC,OAAO,MAA8B;AAClE,UAAM,QAAQ,GAAG,eAAe,qBAAqB,CAAC;AACtD,QAAI,CAAC,UAAU,KAAK,GAAG;AACrB,YAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,qBAAqB;AAAA,IACxD;AACA,sBAAkB,OAAO,wBAAwB,OAAO,QAAQ,OAAO;AACvE,QAAI,CAAC,iBAAiB,MAAM,IAAI,GAAG;AACjC,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK;AAAA,MACrB;AAAA,IACF;AACA,UAAM,qBAAqB,uBAAuB,MAAM,IAAI;AAC5D,QAAI,uBAAuB,MAAM;AAC/B,YAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,SAAS,kBAAkB,EAAE;AAAA,IAChE;AACA,UAAM,UAAU,mBAAmB,MAAM,IAAI;AAC7C,QAAI,YAAYC,OAAM,WAAW,MAAM,IAAI,KAAK,MAAM,WAAW,MAAM,IAAI,IAAI;AAC7E,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,cAAc,gBAAgB;AAAA,MACnD;AAAA,IACF;AAIA,QACE,MAAM,SAAS,WACd,CAAC,MAAM,QAAQ,MAAM,IAAI,KACxB,MAAM,KAAK,WAAW,KACtB,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAC7C;AACA,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK;AAAA,MACrB;AAAA,IACF;AACA,QAAI,MAAM,SAAS,QAAW;AAC5B,UAAI,YAAY,QAAW;AACzB,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,eAAe,8BAA8B,OAAO,OAAO,CAAC,QAAQ,OAAO,CAAC,CAAC;AAAA,QAC7F;AAAA,MACF;AACA,gBAAU;AAAA,IACZ;AACA,UAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,QAAI,OAAO;AACT,YAAM,UAAU,2BAA2B,MAAM,IAAI;AACrD,UAAI,YAAY,MAAM;AACpB,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,SAAS,OAAO,GAAG;AAAA,MACtD;AACA,UAAI,SAAS;AACX,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK,cAAc,gBAAgB;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM,aAAa,QAAW;AAChC,UAAI,CAAC,iBAAiB,MAAM,QAAQ,GAAG;AACrC,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK;AAAA,QACrB;AAAA,MACF;AACA,UAAI,CAAC,OAAO;AACV,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,MAAM,SAAS,SAAY,SAAa,MAAM;AAC3D,QAAI,MAAM,SAAS,QAAW;AAC5B,UAAI,CAAC,iBAAiB,MAAM,IAAI,GAAG;AACjC,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK;AAAA,QACrB;AAAA,MACF;AACA,UAAI,MAAM,SAAS,YAAY;AAC7B,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,qBAAqB,UAAU,IAAI;AAAA,MACtE;AACA,UAAI,MAAM,SAAS,MAAM,IAAI,MAAM,MAAM;AACvC,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK,gBAAgB,MAAM,IAAI;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAIA,QAAI,WAAW,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,WAAW;AACzE,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,cAAc,gBAAgB,kBAAkB,MAAM,IAAI,qFAAqF,gBAAgB;AAAA,MACpL;AAAA,IACF;AACA,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,OAAO,QAAQ,CAAC,GAAG;AAC5B,UAAI,QAAQ,YAAY;AACtB,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK,0BAA0B,UAAU;AAAA,QACzD;AAAA,MACF;AACA,UAAI,KAAK,IAAI,GAAG,GAAG;AACjB,cAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,gBAAgB,GAAG,UAAU;AAAA,MAChE;AACA,WAAK,IAAI,GAAG;AACZ,YAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,UAAI,UAAU,QAAW;AACvB,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK,iBAAiB,GAAG,6BAA6B,KAAK;AAAA,QAC3E;AAAA,MACF;AACA,aAAO,IAAI,KAAK,CAAC;AAAA,IACnB;AAIA,QAAI,SAAS;AACX,YAAM,UAAU,yBAAyB,MAAM,IAAI;AACnD,YAAM,QAAQ,SAAS,IAAI,OAAO;AAClC,UAAI,UAAU,QAAW;AACvB,cAAM;AAAA,UACJ,GAAG,MAAM,KAAK,KAAK,2DAA2D,OAAO,KAAK,CAAC;AAAA,QAC7F;AAAA,MACF;AACA,eAAS,IAAI,SAAS,CAAC;AAAA,IACzB;AACA,UAAM,SAAiC;AAAA,MACrC,MAAM,MAAM;AAAA,MACZ,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK;AAAA,MACrC,GAAI,OAAO,MAAM,aAAa,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAAA,MACzE,GAAI,OAAO,MAAM,SAAS,WAAW,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IAC/D;AACA,YAAQ,IAAI,QAAQ,EAAE,QAAQ,MAAM,CAAC;AACrC,WAAO;AAAA,EACT,CAAC;AAID,aAAW,YAAY,eAAe;AACpC,QAAI,SAAS,SAAS,OAAW,eAAc,IAAI,UAAU,aAAa,eAAe,QAAQ,CAAC;AAAA,EACpG;AACA,SAAO;AACT;AAQA,SAAS,uBAAuB,MAA6B;AAC3D,aAAW,SAAS,KAAK,SAAS,aAAa,GAAG;AAChD,QAAI,MAAM,CAAC,MAAM,kBAAkB;AACjC,aAAO,SAAS,MAAM,CAAC,CAAC,0DAA0D,gBAAgB;AAAA,IACpG;AAAA,EACF;AACA,QAAM,SAAS,KAAK,MAAM,gBAAgB;AAC1C,QAAM,QAAQ,OAAO,KAAK,OAAO,KAAK,EAAE,CAAC;AACzC,MAAI,UAAU,MAAM;AAClB,WAAO,aAAa,MAAM,CAAC,CAAC,eAAe,gBAAgB,qCAAqC,gBAAgB;AAAA,EAClH;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO,QAAQ,gBAAgB;AAAA,EACjC;AACA,SAAO;AACT;AASA,IAAM,UAAU,oBAAI,QAAmE;AAOhF,SAAS,uBACd,OAC+C;AAC/C,SAAO,QAAQ,IAAI,KAAK;AAC1B;AAcO,SAAS,QACd,YACA,UACA,KACA,QACS;AACT,MAAI,SAAS,SAAS,OAAW,QAAO,SAAS,KAAK,SAAS,GAAG;AAClE,MAAI,WAAW,UAAa,CAAC,OAAO,IAAI,GAAG,EAAG,QAAO;AACrD,SAAO,CAAC,WAAW,YAAY,QAAQ,EAAE,IAAI,GAAG;AAClD;AAOO,SAAS,WACd,YACA,UACqB;AACrB,SAAO,cAAc,IAAI,QAAQ,KAAK,aAAa,WAAW,kBAAkB,QAAQ;AAC1F;AAEA,SAAS,aACP,WACA,UACa;AACb,QAAM,MAAM,oBAAI,IAAY,CAAC,UAAU,CAAC;AACxC,MAAI,SAAS,SAAS,UAAa,SAAS,SAAS,UAAW,KAAI,IAAI,SAAS,IAAI;AACrF,aAAW,SAAS,WAAW;AAC7B,QAAI,UAAU,SAAU;AACxB,eAAW,OAAO,MAAM,QAAQ,CAAC,EAAG,KAAI,IAAI,GAAG;AAAA,EACjD;AACA,SAAO;AACT;AAQA,IAAM,gBAAgB,oBAAI,QAAqD;AAQxE,SAAS,kBACd,aACA,OACA,QACA,SACoB;AAUpB,MAAI,UAAU,UAAa,MAAM,WAAW,EAAG,QAAO,CAAC,GAAG,WAAW;AACrE,QAAM,SAAS,IAAI,IAAI,KAAK;AAC5B,aAAW,QAAQ,QAAQ;AACzB,QAAI,CAAC,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG;AAC7C,YAAM,aACJ,YAAY,WAAW,IACnB,WACA,YAAY,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AAK9C,YAAM,WAAW,YAAY;AAAA,QAC3B,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,KAAK,YAAY;AAAA,MACnD;AACA,YAAM,OACJ,aAAa,SACT,KACA,4CAA4C,SAAS,IAAI;AAC/D,YAAM;AAAA,QACJ,wBAAwB,IAAI,QAAQ,MAAM,iBAAiB,UAAU,IAAI,IAAI;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACA,SAAO,YAAY,OAAO,CAAC,MAAM,OAAO,IAAI,EAAE,IAAI,CAAC;AACrD;AAQO,SAAS,SACd,YACA,SACS;AACT,MAAI,YAAY,MAAO,QAAO;AAC9B,MAAI,QAAQ,WAAW,KAAK,EAAG,QAAO;AACtC,SAAO,uBAAuB,YAAY,OAAO;AACnD;AASO,SAAS,uBACd,YACA,SACS;AACT,QAAM,UAAU,WAAW,MAAM,KAAK,CAAC,UAAU;AAC/C,UAAM,aAAa,eAAe,KAAK;AACvC,QAAI,eAAe,GAAI,QAAO;AAC9B,QAAI,OAAO,UAAU,EAAG,QAAO,gBAAgB,SAAS,UAAU;AAClE,WAAO,YAAY,cAAc,QAAQ,WAAW,GAAG,UAAU,GAAG;AAAA,EACtE,CAAC;AACD,MAAI,CAAC,QAAS,QAAO;AAErB,SAAO,CAAC,WAAW,QAAQ,KAAK,CAAC,SAAS,gBAAgB,SAAS,IAAI,CAAC;AAC1E;AAGA,SAAS,eAAe,OAAuB;AAC7C,QAAMA,SAAQ,MAAM,QAAQ,OAAO,GAAG,EAAE,QAAQ,SAAS,EAAE;AAC3D,SAAOA,OAAM,SAAS,GAAG,IAAIA,OAAM,MAAM,GAAG,EAAE,IAAIA;AACpD;AAcA,SAAS,OAAO,OAAwB;AACtC,SAAOC,WAAU,KAAK,KAAK,EAAE;AAC/B;;;AGjmBA,SAAS,cAAAC,aAAY,WAAAC,gBAAe;AAqCpC,IAAM,YAAY,CAAC,QAAQ,WAAW,eAAe;AAErD,SAASC,WAAU,OAAkD;AACnE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAASC,mBACP,KACA,SACA,OACA,QACA,SACM;AACN,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,QAAI,CAAC,QAAQ,SAAS,GAAG,GAAG;AAC1B,YAAM;AAAA,QACJ,GAAG,MAAM,KAAK,KAAK,qBAAqB,GAAG,sBAAsB,QAAQ,KAAK,IAAI,CAAC;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,WACd,KACA,QACA,SACa;AACb,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO,CAAC;AAC/C,MAAI,CAACD,WAAU,GAAG,GAAG;AACnB,UAAM,QAAQ,GAAG,MAAM,8BAA8B;AAAA,EACvD;AACA,EAAAC,mBAAkB,KAAK,WAAW,SAAS,QAAQ,OAAO;AAE1D,QAAM,QAAqB,CAAC;AAC5B,MAAI,OAAO,OAAO,KAAK,MAAM,GAAG;AAC9B,UAAM,OAAO,eAAe,IAAI,MAAM,GAAG,QAAQ,UAAU,QAAQ,OAAO;AAAA,EAC5E;AACA,MAAI,OAAO,OAAO,KAAK,SAAS,GAAG;AACjC,UAAM,SAAS,IAAI,eAAe,IAAI,SAAS,GAAG,WAAW,QAAQ,QAAQ,OAAO;AAAA,EACtF;AACA,MAAI,OAAO,OAAO,KAAK,eAAe,GAAG;AACvC,UAAM,eAAe,IAAI;AAAA,MACvB,IAAI,eAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,eACP,KACA,MACA,KACA,QACA,SAC4B;AAC5B,MAAI,CAACD,WAAU,GAAG,GAAG;AACnB,UAAM,QAAQ,GAAG,MAAM,WAAW,IAAI,qBAAqB;AAAA,EAC7D;AACA,EAAAC,mBAAkB,KAAK,CAAC,GAAG,GAAG,SAAS,IAAI,IAAI,QAAQ,OAAO;AAE9D,QAAM,MAAkC,CAAC;AACzC,MAAI,OAAO,OAAO,KAAK,GAAG,GAAG;AAC3B,UAAM,QAAQ,IAAI,GAAG;AACrB,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI;AACpD,YAAM,QAAQ,GAAG,MAAM,WAAW,IAAI,IAAI,GAAG,8BAA8B;AAAA,IAC7E;AACA,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAGA,SAAS,cAAc,MAAc,WAA2B;AAC9D,SAAOH,YAAW,IAAI,IAAI,OAAOC,SAAQ,WAAW,IAAI;AAC1D;AAOO,SAAS,eAAe,OAAoB,WAAuC;AACxF,QAAM,SAAS,MAAM,MAAM;AAC3B,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,cAAc,QAAQ,SAAS;AACxC;AAOO,SAAS,WAAW,OAAoB,WAAuC;AACpF,QAAM,OAAO,MAAM,SAAS,GAAG;AAC/B,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,cAAc,MAAM,SAAS;AACtC;AAOO,SAAS,uBACd,OACA,WACoB;AACpB,QAAM,SAAS,MAAM,eAAe,GAAG;AACvC,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,cAAc,QAAQ,SAAS;AACxC;;;AC5IA,SAAS,gBAAAG,qBAAoB;AAC7B,SAAS,UAAU,WAAAC,UAAS,QAAAC,OAAM,YAAAC,WAAU,WAAAC,gBAAe;AAC3D,SAAS,SAAS,iBAAiB;;;AC9BnC,SAAS,kBAAkB;AAC3B,SAAS,SAAS,MAAM,WAAAC,gBAAe;AAEhC,SAAS,YAAY,KAA4B;AACtD,MAAI,MAAMA,SAAQ,GAAG;AACrB,aAAS;AACP,QAAI,WAAW,KAAK,KAAK,MAAM,CAAC,EAAG,QAAO;AAC1C,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAeO,SAAS,WAAW,KAAuB;AAChD,QAAM,QAAQA,SAAQ,GAAG;AACzB,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,SAAS,KAAM,QAAO,CAAC,KAAK;AAChC,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAM;AACV,aAAS;AACP,UAAM,KAAK,GAAG;AACd,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,SAAS,QAAQ,GAAG;AAG1B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;;;ADDO,IAAM,sBAAyC;AAAA,EACpD;AAAA,EACA;AACF;AAGO,IAAM,uBAAuB;AAQ7B,IAAM,gCAAgC;AA8E7C,SAAS,aACP,MACA,QACA,SACiB;AACjB,MAAI;AACJ,MAAI;AACF,WAAOC,cAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,EAAE,MAAM,KAAK,aAAa,MAAM,QAAQ,OAAO,EAAE;AAC1D;AAOA,SAAS,aACP,MACA,QACA,SACgC;AAChC,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,IAAI;AAAA,EACtB,SAAS,KAAK;AACZ,UAAM,QAAQ,GAAG,MAAM,mBAAmB,aAAa,GAAG,CAAC,EAAE;AAAA,EAC/D;AACA,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,QAAQ,GAAG,MAAM,gCAAgC;AAAA,EACzD;AACA,SAAO;AACT;AAGA,IAAM,kBAAkB;AAGxB,IAAM,YAAY;AAGlB,IAAM,cAAiC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,MACP,UACA,MAC6C;AAC7C,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,QAAQ,QAAQ,OAAO,OAAO,KAAK,KAAK,OAAO,GAAG;AACpD,WAAO,EAAE,OAAO,IAAI,KAAK,OAAO,KAAK,MAAM,SAAS,KAAK;AAAA,EAC3D;AAQA,MAAI,QAAQ,QAAQ,YAAY,KAAK,CAAC,QAAQ,OAAO,OAAO,KAAK,GAAG,CAAC,GAAG;AACtE,WAAO,EAAE,OAAO,MAAM,SAAS,KAAK;AAAA,EACtC;AACA,SAAO;AACT;AAMA,SAAS,cACP,UACA,QACA,SACoB;AACpB,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,QAAQ,QAAQ,CAAC,OAAO,OAAO,KAAK,eAAe,EAAG,QAAO,CAAC;AAClE,SAAO,iBAAiB,IAAI,eAAe,GAAG,QAAQ,OAAO;AAC/D;AAMA,SAAS,QAAQ,UAAoB,QAAgB,SAA+B;AAClF,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,QAAQ,QAAQ,CAAC,OAAO,OAAO,KAAK,SAAS,EAAG,QAAO,CAAC;AAC5D,SAAO,WAAW,IAAI,SAAS,GAAG,QAAQ,OAAO;AACnD;AAOA,SAAS,gBACP,UACA,QACA,SAC4D;AAC5D,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,QAAQ,KAAM,QAAO,CAAC;AAC1B,QAAM,OAAmE,CAAC;AAC1E,MAAI,OAAO,OAAO,KAAK,oBAAoB,GAAG;AAC5C,UAAM,QAAQ,IAAI,oBAAoB;AACtC,QAAI,CAAC,qBAAqB,KAAK,GAAG;AAChC,YAAM;AAAA,QACJ,GAAG,MAAM,MAAM,oBAAoB;AAAA,MACrC;AAAA,IACF;AACA,SAAK,gBAAgB;AAAA,EACvB;AAIA,MAAI,OAAO,OAAO,KAAK,6BAA6B,GAAG;AACrD,UAAM,QAAQ,IAAI,6BAA6B;AAC/C,QAAI,CAAC,qBAAqB,KAAK,GAAG;AAChC,YAAM;AAAA,QACJ,GAAG,MAAM,MAAM,6BAA6B;AAAA,MAC9C;AAAA,IACF;AACA,SAAK,wBAAwB;AAAA,EAC/B;AACA,SAAO;AACT;AAMA,SAAS,YACP,UACA,QACA,KACA,SACiC;AACjC,QAAM,EAAE,IAAI,IAAI;AAChB,MAAI,QAAQ,QAAQ,CAAC,OAAO,OAAO,KAAK,aAAa,EAAG,QAAO,CAAC;AAChE,SAAO,EAAE,WAAW,eAAe,IAAI,aAAa,GAAG,QAAQ,KAAK,OAAO,EAAE;AAC/E;AAMA,SAAS,eAAe,UAGtB;AACA,QAAM,MAAM,SAAS,MAAM,oBAAoB;AAC/C,QAAM,WAAW,SAAS,MAAM,6BAA6B;AAC7D,SAAO;AAAA,IACL,GAAI,qBAAqB,GAAG,IAAI,EAAE,eAAe,IAAI,IAAI,CAAC;AAAA,IAC1D,GAAI,qBAAqB,QAAQ,IAAI,EAAE,uBAAuB,SAAS,IAAI,CAAC;AAAA,EAC9E;AACF;AAEA,SAAS,eAAe,KAAa,MAAsB;AACzD,SAAOC,UAAS,KAAK,IAAI,EAAE,QAAQ,OAAO,GAAG;AAC/C;AAKO,SAAS,mBACd,KACA,MACmB;AACnB,QAAM,QAAQC,SAAQ,GAAG;AACzB,aAAW,OAAO,WAAW,KAAK,GAAG;AACnC,eAAW,QAAQ,qBAAqB;AACtC,YAAM,OAAOC,MAAK,KAAK,IAAI;AAC3B,YAAM,SAAS,eAAe,OAAO,IAAI;AACzC,YAAM,WAAW,aAAa,MAAM,QAAQ,KAAK,OAAO;AACxD,UAAI,aAAa,KAAM;AACvB,YAAM,QAAQ,MAAM,UAAU,IAAI;AAGlC,UAAI,UAAU,KAAM;AACpB,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,SAAS;AAAA,QACf,MAAM;AAAA,QACN,aAAa,cAAc,UAAU,QAAQ,KAAK,OAAO;AAAA,QACzD,OAAO,QAAQ,UAAU,QAAQ,KAAK,OAAO;AAAA,QAC7C,GAAG,gBAAgB,UAAU,QAAQ,KAAK,OAAO;AAAA,QACjD,GAAG,YAAY,UAAU,QAAQ,KAAK,KAAK,OAAO;AAAA,QAClD,GAAG;AAAA,MACL;AAAA,IACF;AACA,eAAW,QAAQ,KAAK,aAAa;AACnC,YAAM,OAAOA,MAAK,KAAK,IAAI;AAC3B,YAAM,SAAS,eAAe,OAAO,IAAI;AACzC,YAAM,WAAW,aAAa,MAAM,QAAQ,KAAK,OAAO;AACxD,UAAI,aAAa,KAAM;AACvB;AAAA,QACE,IAAI,IAAI,6DAA6D,KAAK,OAAO,WAAW,oBAAoB,CAAC,KAAK,mBAAmB;AAAA,MAC3I;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,SAAS;AAAA,QACf,OAAO,SAAS;AAAA,QAChB,SAAS;AAAA,QACT,MAAM;AAAA,QACN,aAAa,CAAC;AAAA,QACd,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,mBACd,cACA,KACA,MACY;AACZ,QAAM,OAAOD,SAAQ,KAAK,YAAY;AAEtC,QAAM,WAAW,aAAa,MAAM,cAAc,KAAK,OAAO;AAC9D,MAAI,aAAa,MAAM;AACrB,UAAM,KAAK,QAAQ,2BAA2B,YAAY,IAAI;AAAA,EAChE;AACA,QAAM,QAAQ,MAAM,UAAU,IAAI,KAAK;AAAA,IACrC,OAAO,SAAS;AAAA,IAChB,SAAS;AAAA,EACX;AACA,SAAO;AAAA,IACL;AAAA,IACA,KAAKE,SAAQ,IAAI;AAAA,IACjB,QAAQ;AAAA,IACR,MAAM,SAAS;AAAA,IACf,MAAM;AAAA,IACN,aAAa,cAAc,UAAU,cAAc,KAAK,OAAO;AAAA,IAC/D,OAAO,QAAQ,UAAU,cAAc,KAAK,OAAO;AAAA;AAAA;AAAA,IAGnD,GAAG,gBAAgB,UAAU,cAAc,KAAK,OAAO;AAAA,IACvD,GAAG,YAAY,UAAU,cAAcA,SAAQ,IAAI,GAAG,KAAK,OAAO;AAAA,IAClE,GAAG;AAAA,EACL;AACF;AAGO,SAAS,eACd,KACA,MAC4B;AAC5B,SAAO,QAAQ,QAAQ,EAAE,KAAK,MAAM,mBAAmB,KAAK,IAAI,CAAC;AACnE;AAGO,SAAS,eACd,cACA,KACA,MACqB;AACrB,SAAO,QAAQ,QAAQ,EAAE;AAAA,IAAK,MAC5B,mBAAmB,cAAc,KAAK,IAAI;AAAA,EAC5C;AACF;AAaO,SAAS,qBACd,KACA,SAC4B;AAC5B,SAAO,QAAQ,QAAQ,EAAE,KAAK,MAAM,yBAAyB,KAAK,OAAO,CAAC;AAC5E;AAEA,SAAS,yBACP,KACA,SACmB;AACnB,QAAM,QAAQF,SAAQ,GAAG;AACzB,aAAW,OAAO,WAAW,KAAK,GAAG;AACnC,eAAW,QAAQ,qBAAqB;AACtC,YAAM,OAAOC,MAAK,KAAK,IAAI;AAC3B,YAAM,SAAS,eAAe,OAAO,IAAI;AACzC,YAAM,WAAW,aAAa,MAAM,QAAQ,OAAO;AACnD,UAAI,aAAa,KAAM;AACvB,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,SAAS;AAAA,QACf,OAAO;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,QACN,aAAa,cAAc,UAAU,QAAQ,OAAO;AAAA,QACpD,OAAO,QAAQ,UAAU,QAAQ,OAAO;AAAA,QACxC,GAAG,eAAe,QAAQ;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAcO,SAAS,qBACd,cACA,KACA,SACqB;AACrB,SAAO,QAAQ,QAAQ,EAAE;AAAA,IAAK,MAC5B,yBAAyB,cAAc,KAAK,OAAO;AAAA,EACrD;AACF;AAEA,SAAS,yBACP,cACA,KACA,SACY;AACZ,QAAM,OAAOD,SAAQ,KAAK,YAAY;AACtC,QAAM,WAAW,aAAa,MAAM,cAAc,OAAO;AACzD,MAAI,aAAa,MAAM;AACrB,UAAM,QAAQ,2BAA2B,YAAY,IAAI;AAAA,EAC3D;AACA,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,QAAQ,oBAAoB,SAAS,SAAS,IAAI,CAAC;AACzD,QAAM,SACJ,QAAQ,QAAQ,SAAS,YAAY,KAAK,CAAC,QAAQ,OAAO,OAAO,KAAK,GAAG,CAAC;AAC5E,SAAO;AAAA,IACL;AAAA,IACA,KAAKE,SAAQ,IAAI;AAAA,IACjB,QAAQ;AAAA,IACR,MAAM,SAAS;AAAA,IACf,OAAO,SAAS,OAAO;AAAA,IACvB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,aAAa,cAAc,UAAU,cAAc,OAAO;AAAA,IAC1D,OAAO,QAAQ,UAAU,cAAc,OAAO;AAAA,IAC9C,GAAG,eAAe,QAAQ;AAAA,EAC5B;AACF;","names":["picomatch","posix","isMember","posix","picomatch","isAbsolute","resolve","isMapping","rejectUnknownKeys","readFileSync","dirname","join","relative","resolve","resolve","readFileSync","relative","resolve","join","dirname"]}
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- configDir
4
- } from "./chunk-4TSWU2SU.js";
5
2
  import {
6
3
  MANIFEST_VERSION
7
4
  } from "./chunk-CU57HT2E.js";
5
+ import {
6
+ configDir
7
+ } from "./chunk-4TSWU2SU.js";
8
8
  import {
9
9
  metaProvenanceEntries
10
- } from "./chunk-IN6DWNVE.js";
10
+ } from "./chunk-M42DZCOU.js";
11
11
  import {
12
12
  __default4 as __default,
13
13
  extractFrontmatter
14
- } from "./chunk-3XGGF5JW.js";
14
+ } from "./chunk-VSEBFHAY.js";
15
15
 
16
16
  // src/tracevals/capture/manifest.ts
17
17
  import { createHash } from "node:crypto";
@@ -1168,4 +1168,4 @@ export {
1168
1168
  ARTIFACT_EVALS_SCHEMA_ID,
1169
1169
  extractEvals
1170
1170
  };
1171
- //# sourceMappingURL=chunk-XTAHZTES.js.map
1171
+ //# sourceMappingURL=chunk-R4WXQIOZ.js.map
@@ -22,7 +22,7 @@ import {
22
22
  updateConfigEval,
23
23
  updateManifestEval,
24
24
  updatePageEval
25
- } from "./chunk-TKTRIZT4.js";
25
+ } from "./chunk-HA6MQMOY.js";
26
26
  import {
27
27
  DEFAULT_CONFIG_FILENAME,
28
28
  assertRegisteredGraders,
@@ -41,10 +41,10 @@ import {
41
41
  sha256,
42
42
  splitBody,
43
43
  withExternalMetadata
44
- } from "./chunk-Q2H7J3FF.js";
44
+ } from "./chunk-U7CTFN2J.js";
45
45
  import {
46
46
  isTurnBudgetSkip
47
- } from "./chunk-XG6PVB5S.js";
47
+ } from "./chunk-SSQZB5SQ.js";
48
48
  import {
49
49
  SUMMARY_FORMATS,
50
50
  parseFormat
@@ -57,7 +57,7 @@ import {
57
57
  } from "./chunk-77AINBYM.js";
58
58
  import {
59
59
  mergeMetaProvenance
60
- } from "./chunk-IN6DWNVE.js";
60
+ } from "./chunk-M42DZCOU.js";
61
61
  import {
62
62
  palette
63
63
  } from "./chunk-RNVYGWIC.js";
@@ -1284,4 +1284,4 @@ export {
1284
1284
  renderCalibration,
1285
1285
  runInit
1286
1286
  };
1287
- //# sourceMappingURL=chunk-3CDZWY6M.js.map
1287
+ //# sourceMappingURL=chunk-RLT5SEML.js.map
@@ -1,13 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/shared/exec.ts
4
- import { realExec } from "@hawkeyexl/inference";
5
- function outputTail(result, maxChars = 400) {
6
- const text = (result.stderr.trim() || result.stdout.trim()).trim();
7
- if (text.length <= maxChars) return text;
8
- return `\u2026${text.slice(-maxChars)}`;
9
- }
10
-
11
3
  // src/docevals/judge/budget.ts
12
4
  var TURN_BUDGET_SKIP = "turn budget";
13
5
  function turnBudgetSkipReason(maxTurns) {
@@ -18,10 +10,8 @@ function isTurnBudgetSkip(reason) {
18
10
  }
19
11
 
20
12
  export {
21
- outputTail,
22
- realExec,
23
13
  TURN_BUDGET_SKIP,
24
14
  turnBudgetSkipReason,
25
15
  isTurnBudgetSkip
26
16
  };
27
- //# sourceMappingURL=chunk-XG6PVB5S.js.map
17
+ //# sourceMappingURL=chunk-SSQZB5SQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/docevals/judge/budget.ts"],"sourcesContent":["/**\n * The turn-budget skip marker (ADR 01019), in one place.\n *\n * `judge.ts` produces the skip reason; `core/engine.ts` and `commands/\n * calibrate.ts` both have to tell a budget truncation apart from every other\n * reason an eval went unjudged, because that distinction is what stops a\n * partial run from reporting as a complete one. They did it by matching the\n * bare substring `\"turn budget\"` against a message written in a third file,\n * so a reword in the producer would have left both detectors matching nothing\n * — silently, since the tests that exercise truncation supply the skip reason\n * themselves rather than getting it from the judge.\n *\n * Kept in its own module rather than exported from `judge.ts`: the detectors\n * need the marker, not the judge, and `engine.ts` deliberately takes the judge\n * by injection.\n */\n\n/** The substring every budget-truncation skip reason carries. */\nexport const TURN_BUDGET_SKIP = \"turn budget\";\n\n/** The skip reason recorded for an eval the budget stopped before dispatch. */\nexport function turnBudgetSkipReason(maxTurns: number): string {\n return `judge ${TURN_BUDGET_SKIP} exhausted (${maxTurns})`;\n}\n\n/**\n * Whether a skip reason is the budget's doing rather than the judge's.\n *\n * A type predicate, not a plain boolean: a budget skip always carries a\n * reason, and callers interpolate it into the message they report.\n */\nexport function isTurnBudgetSkip(reason: string | undefined): reason is string {\n return reason !== undefined && reason.includes(TURN_BUDGET_SKIP);\n}\n"],"mappings":";;;AAkBO,IAAM,mBAAmB;AAGzB,SAAS,qBAAqB,UAA0B;AAC7D,SAAO,SAAS,gBAAgB,eAAe,QAAQ;AACzD;AAQO,SAAS,iBAAiB,QAA8C;AAC7E,SAAO,WAAW,UAAa,OAAO,SAAS,gBAAgB;AACjE;","names":[]}