@hawkeyexl/manni 4.11.0 → 4.12.1

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 (238) hide show
  1. package/README.md +1 -1
  2. package/dist/{batch-UAULUMGH.js → batch-KMGZMIAQ.js} +20 -19
  3. package/dist/build-NO4AWCYG.js +36 -0
  4. package/dist/calibrate-XQUG5XAK.js +41 -0
  5. package/dist/capture-5XE3ZMXK.js +38 -0
  6. package/dist/{check-YEOYBFST.js → check-HTNPLWOV.js} +19 -18
  7. package/dist/check-RJ45IBA6.js +34 -0
  8. package/dist/{check-VELUQAUW.js → check-W24BQNCT.js} +35 -34
  9. package/dist/{check-VELUQAUW.js.map → check-W24BQNCT.js.map} +1 -1
  10. package/dist/check-WFXSXICD.js +30 -0
  11. package/dist/{chunk-IGSHYP74.js → chunk-23P7KMJM.js} +4 -4
  12. package/dist/{chunk-HA6MQMOY.js → chunk-24J7TYPU.js} +6 -6
  13. package/dist/{chunk-PCS4VJBV.js → chunk-37QJLVS3.js} +9 -9
  14. package/dist/{chunk-4CK6ZSOK.js → chunk-3P6KCU36.js} +3 -3
  15. package/dist/{chunk-54F74XR3.js → chunk-5JBVFWVP.js} +19 -19
  16. package/dist/{chunk-B7XYNRJQ.js → chunk-6KIYVVKL.js} +2 -2
  17. package/dist/{chunk-J6IDQGJL.js → chunk-6TVH4FI7.js} +469 -85
  18. package/dist/chunk-6TVH4FI7.js.map +1 -0
  19. package/dist/{chunk-GDVZ5VII.js → chunk-7A452BPZ.js} +4 -4
  20. package/dist/{chunk-M42DZCOU.js → chunk-7FCLXEAQ.js} +2 -2
  21. package/dist/{chunk-2MJ62AYQ.js → chunk-7MCQHTQ5.js} +4 -3
  22. package/dist/chunk-7MCQHTQ5.js.map +1 -0
  23. package/dist/{chunk-LBSCW232.js → chunk-7NXPQKSB.js} +7 -7
  24. package/dist/{chunk-OPXWPJDA.js → chunk-A3TEDWBZ.js} +14 -14
  25. package/dist/{chunk-BBT6AVUG.js → chunk-BHHXU2D2.js} +2 -2
  26. package/dist/{chunk-D4N2V43Z.js → chunk-CEI2J42X.js} +13 -7
  27. package/dist/chunk-CEI2J42X.js.map +1 -0
  28. package/dist/{chunk-FQXWVS6J.js → chunk-CMMBWHT2.js} +9 -9
  29. package/dist/{chunk-EIFCP6CZ.js → chunk-CO47QJKZ.js} +2 -2
  30. package/dist/{chunk-2DRHT73U.js → chunk-CVZMKWZ5.js} +913 -1398
  31. package/dist/chunk-CVZMKWZ5.js.map +1 -0
  32. package/dist/{chunk-ADMEPUBT.js → chunk-CXWTNT2O.js} +3 -3
  33. package/dist/{chunk-LKP7QDFN.js → chunk-DAQTRAP5.js} +4 -4
  34. package/dist/chunk-DSWKBZWW.js +16 -0
  35. package/dist/chunk-DSWKBZWW.js.map +1 -0
  36. package/dist/{chunk-PWBH7DCQ.js → chunk-EESH6COU.js} +4 -4
  37. package/dist/{chunk-JPQDNYXH.js → chunk-EM67O2ET.js} +8 -2
  38. package/dist/chunk-EM67O2ET.js.map +1 -0
  39. package/dist/{chunk-HQR2ZBHW.js → chunk-I2XQRRP7.js} +36 -7
  40. package/dist/chunk-I2XQRRP7.js.map +1 -0
  41. package/dist/{chunk-NCPWXHDO.js → chunk-JEUFYSFQ.js} +4 -4
  42. package/dist/{chunk-2IR4T5PO.js → chunk-JJ4RSDSY.js} +2 -2
  43. package/dist/chunk-KCB5JCK4.js +2272 -0
  44. package/dist/chunk-KCB5JCK4.js.map +1 -0
  45. package/dist/{chunk-VSEBFHAY.js → chunk-KFKL4VTL.js} +11369 -10218
  46. package/dist/chunk-KFKL4VTL.js.map +1 -0
  47. package/dist/{chunk-PCQ7ELJH.js → chunk-L2TD7DZB.js} +109 -5
  48. package/dist/chunk-L2TD7DZB.js.map +1 -0
  49. package/dist/{chunk-WDWXAYVH.js → chunk-L3RLGGRK.js} +4 -4
  50. package/dist/{chunk-T7JAOYE5.js → chunk-L75CAZG4.js} +6 -6
  51. package/dist/{chunk-U7CTFN2J.js → chunk-MYRPMSOX.js} +7 -7
  52. package/dist/{chunk-Y6DOMTQD.js → chunk-NCF5NQV5.js} +27 -27
  53. package/dist/chunk-NCF5NQV5.js.map +1 -0
  54. package/dist/{chunk-WQWKYAAK.js → chunk-NUQF5F5J.js} +2 -2
  55. package/dist/{chunk-FUCPZVRY.js → chunk-OEN6FBUW.js} +2 -2
  56. package/dist/{chunk-FBMZJEB3.js → chunk-OKMTUAWO.js} +4 -4
  57. package/dist/{chunk-QLWC7MKD.js → chunk-OTJOBZJ5.js} +135 -135
  58. package/dist/chunk-OTJOBZJ5.js.map +1 -0
  59. package/dist/{chunk-R4WXQIOZ.js → chunk-PCEM5FOZ.js} +3 -3
  60. package/dist/{chunk-4D5IZSXM.js → chunk-PXNCRGUL.js} +3 -3
  61. package/dist/chunk-Q2QC5AZB.js +1868 -0
  62. package/dist/chunk-Q2QC5AZB.js.map +1 -0
  63. package/dist/{chunk-ODRPL3WB.js → chunk-Q3ODX5PF.js} +2 -2
  64. package/dist/{chunk-KGEQJ5CC.js → chunk-RRFFUWDL.js} +8 -8
  65. package/dist/{chunk-NSBXVPZL.js → chunk-S3XUYW5K.js} +17 -17
  66. package/dist/{chunk-LPIF5LIE.js → chunk-SAU2WQFC.js} +2 -2
  67. package/dist/{chunk-MSRU4D4P.js → chunk-SFLICIQV.js} +2 -2
  68. package/dist/{chunk-NGTVREIH.js → chunk-U6IHLEWT.js} +2 -2
  69. package/dist/{chunk-QCHVR4ZM.js → chunk-VM7VAFJW.js} +33 -7
  70. package/dist/chunk-VM7VAFJW.js.map +1 -0
  71. package/dist/{chunk-6HWBRZIN.js → chunk-VQYGLU4C.js} +252 -193
  72. package/dist/chunk-VQYGLU4C.js.map +1 -0
  73. package/dist/{chunk-2IAZEB45.js → chunk-WGMFDG6U.js} +229 -821
  74. package/dist/chunk-WGMFDG6U.js.map +1 -0
  75. package/dist/{chunk-GV56WGL2.js → chunk-WO4JYWZK.js} +1036 -883
  76. package/dist/chunk-WO4JYWZK.js.map +1 -0
  77. package/dist/chunk-XYRUDFKS.js +685 -0
  78. package/dist/chunk-XYRUDFKS.js.map +1 -0
  79. package/dist/{chunk-VQK4VHLF.js → chunk-YQV75FIL.js} +2 -2
  80. package/dist/chunk-YUTVAMWW.js +2 -0
  81. package/dist/{chunk-RLT5SEML.js → chunk-Z5MZCCTT.js} +6 -6
  82. package/dist/{chunk-BKVHX6FU.js → chunk-ZIRBFXDW.js} +5 -10
  83. package/dist/chunk-ZIRBFXDW.js.map +1 -0
  84. package/dist/{cli-O4ABTN4Z.js → cli-236TGVJG.js} +31 -30
  85. package/dist/{cli-O4ABTN4Z.js.map → cli-236TGVJG.js.map} +1 -1
  86. package/dist/{cli-3BTCK4AB.js → cli-3FAPJBEY.js} +27 -26
  87. package/dist/{cli-3BTCK4AB.js.map → cli-3FAPJBEY.js.map} +1 -1
  88. package/dist/{cli-JJMOVIKS.js → cli-BS43EVCD.js} +14 -14
  89. package/dist/{cli-RORDPAPE.js → cli-CMR4FYF6.js} +59 -38
  90. package/dist/cli-CMR4FYF6.js.map +1 -0
  91. package/dist/{cli-ONTC5PKT.js → cli-FX53PBQ3.js} +9 -9
  92. package/dist/{cli-6KZTHWGP.js → cli-IQNJLKSN.js} +37 -36
  93. package/dist/{cli-6KZTHWGP.js.map → cli-IQNJLKSN.js.map} +1 -1
  94. package/dist/{cli-4MF37HAQ.js → cli-KOMXVPXX.js} +26 -25
  95. package/dist/{cli-4MF37HAQ.js.map → cli-KOMXVPXX.js.map} +1 -1
  96. package/dist/{cli-I62SRH3R.js → cli-NGL63E53.js} +14 -13
  97. package/dist/{cli-DRMZBZLB.js → cli-QPGS2MR5.js} +43 -42
  98. package/dist/{cli-DRMZBZLB.js.map → cli-QPGS2MR5.js.map} +1 -1
  99. package/dist/{cli-VID7PJS3.js → cli-RKQX4TI3.js} +23 -22
  100. package/dist/{cli-VID7PJS3.js.map → cli-RKQX4TI3.js.map} +1 -1
  101. package/dist/{cli-VUKMWSSF.js → cli-WNEI5L7P.js} +4 -4
  102. package/dist/cli.js +19 -13
  103. package/dist/cli.js.map +1 -1
  104. package/dist/{config-XDA42RJL.js → config-JJW75SBX.js} +5 -5
  105. package/dist/docmeta.js +13 -12
  106. package/dist/docmeta.js.map +1 -1
  107. package/dist/github-4UR67MEN.js +35 -0
  108. package/dist/github-UPGKMVVF.js +30 -0
  109. package/dist/graph/runtime.d.ts +4 -1
  110. package/dist/graph/runtime.js +20 -9
  111. package/dist/graph/runtime.js.map +1 -1
  112. package/dist/{host-6GURH5HE.js → host-YSTYO5DX.js} +3 -3
  113. package/dist/index.d.ts +192 -26
  114. package/dist/index.js +129 -124
  115. package/dist/index.js.map +1 -1
  116. package/dist/{json-BXY44ED7.js → json-DQIELR36.js} +18 -17
  117. package/dist/labels-XSTJJMSG.js +73 -0
  118. package/dist/labels-XSTJJMSG.js.map +1 -0
  119. package/dist/lint-BLBGGOYM.js +34 -0
  120. package/dist/list-B3RRRWFQ.js +32 -0
  121. package/dist/list-ZFRT3JQH.js +35 -0
  122. package/dist/{page-EV7OUBAU.js → page-5ISEVJR5.js} +18 -16
  123. package/dist/{prepare-JHJDJB6I.js → prepare-KFXXG2CJ.js} +22 -21
  124. package/dist/{prepare-JHJDJB6I.js.map → prepare-KFXXG2CJ.js.map} +1 -1
  125. package/dist/{pretty-Q5Y2PA64.js → pretty-Y7CGQLAF.js} +18 -17
  126. package/dist/{pretty-SCRK66M5.js → pretty-ZISH3Q4F.js} +18 -17
  127. package/dist/{provider-527CO4DM.js → provider-LBX4IXGX.js} +3 -3
  128. package/dist/{release-3D5PK6M7.js → release-VV3UTKE5.js} +3 -3
  129. package/dist/{reporters-M6LJ2IYU.js → reporters-26H3N2KW.js} +3 -3
  130. package/dist/{reporters-A5ADKDYR.js → reporters-4STXJJE2.js} +16 -15
  131. package/dist/{reporters-RBYTASMI.js → reporters-74B4U2GZ.js} +7 -7
  132. package/dist/run-DVGTKM52.js +37 -0
  133. package/dist/{term-CQAGNG3Q.js → term-PLJEYJQV.js} +981 -141
  134. package/dist/term-PLJEYJQV.js.map +1 -0
  135. package/dist/validate-CJNFQHMD.js +19 -0
  136. package/package.json +3 -2
  137. package/plugin/manni/.claude-plugin/plugin.json +1 -1
  138. package/shapes/graph/shapes-1.1.0.ttl +785 -0
  139. package/dist/build-QSPNK5AL.js +0 -35
  140. package/dist/calibrate-KXN4URPM.js +0 -40
  141. package/dist/capture-JHTSUPUC.js +0 -37
  142. package/dist/check-MDGXEUWL.js +0 -20
  143. package/dist/check-WOQRKNZK.js +0 -29
  144. package/dist/chunk-2DRHT73U.js.map +0 -1
  145. package/dist/chunk-2IAZEB45.js.map +0 -1
  146. package/dist/chunk-2MJ62AYQ.js.map +0 -1
  147. package/dist/chunk-4VBSTXN6.js +0 -392
  148. package/dist/chunk-4VBSTXN6.js.map +0 -1
  149. package/dist/chunk-6HWBRZIN.js.map +0 -1
  150. package/dist/chunk-BKVHX6FU.js.map +0 -1
  151. package/dist/chunk-D4N2V43Z.js.map +0 -1
  152. package/dist/chunk-EFW7CAG4.js +0 -387
  153. package/dist/chunk-EFW7CAG4.js.map +0 -1
  154. package/dist/chunk-GV56WGL2.js.map +0 -1
  155. package/dist/chunk-HQR2ZBHW.js.map +0 -1
  156. package/dist/chunk-J6IDQGJL.js.map +0 -1
  157. package/dist/chunk-JPQDNYXH.js.map +0 -1
  158. package/dist/chunk-JROBZ7JA.js +0 -818
  159. package/dist/chunk-JROBZ7JA.js.map +0 -1
  160. package/dist/chunk-PCQ7ELJH.js.map +0 -1
  161. package/dist/chunk-QCHVR4ZM.js.map +0 -1
  162. package/dist/chunk-QLWC7MKD.js.map +0 -1
  163. package/dist/chunk-TU2WUBVZ.js +0 -2
  164. package/dist/chunk-VSEBFHAY.js.map +0 -1
  165. package/dist/chunk-Y6DOMTQD.js.map +0 -1
  166. package/dist/cli-RORDPAPE.js.map +0 -1
  167. package/dist/github-RY3FRA6P.js +0 -29
  168. package/dist/github-YKQSH7FS.js +0 -34
  169. package/dist/lint-YJ2L22BW.js +0 -33
  170. package/dist/list-NSG24VVX.js +0 -31
  171. package/dist/list-V6EGCSHX.js +0 -34
  172. package/dist/run-BUE2DDXN.js +0 -36
  173. package/dist/term-CQAGNG3Q.js.map +0 -1
  174. package/dist/validate-ZBW44RHT.js +0 -18
  175. /package/dist/{batch-UAULUMGH.js.map → batch-KMGZMIAQ.js.map} +0 -0
  176. /package/dist/{build-QSPNK5AL.js.map → build-NO4AWCYG.js.map} +0 -0
  177. /package/dist/{calibrate-KXN4URPM.js.map → calibrate-XQUG5XAK.js.map} +0 -0
  178. /package/dist/{capture-JHTSUPUC.js.map → capture-5XE3ZMXK.js.map} +0 -0
  179. /package/dist/{check-MDGXEUWL.js.map → check-HTNPLWOV.js.map} +0 -0
  180. /package/dist/{check-WOQRKNZK.js.map → check-RJ45IBA6.js.map} +0 -0
  181. /package/dist/{check-YEOYBFST.js.map → check-WFXSXICD.js.map} +0 -0
  182. /package/dist/{chunk-IGSHYP74.js.map → chunk-23P7KMJM.js.map} +0 -0
  183. /package/dist/{chunk-HA6MQMOY.js.map → chunk-24J7TYPU.js.map} +0 -0
  184. /package/dist/{chunk-PCS4VJBV.js.map → chunk-37QJLVS3.js.map} +0 -0
  185. /package/dist/{chunk-4CK6ZSOK.js.map → chunk-3P6KCU36.js.map} +0 -0
  186. /package/dist/{chunk-54F74XR3.js.map → chunk-5JBVFWVP.js.map} +0 -0
  187. /package/dist/{chunk-B7XYNRJQ.js.map → chunk-6KIYVVKL.js.map} +0 -0
  188. /package/dist/{chunk-GDVZ5VII.js.map → chunk-7A452BPZ.js.map} +0 -0
  189. /package/dist/{chunk-M42DZCOU.js.map → chunk-7FCLXEAQ.js.map} +0 -0
  190. /package/dist/{chunk-LBSCW232.js.map → chunk-7NXPQKSB.js.map} +0 -0
  191. /package/dist/{chunk-OPXWPJDA.js.map → chunk-A3TEDWBZ.js.map} +0 -0
  192. /package/dist/{chunk-BBT6AVUG.js.map → chunk-BHHXU2D2.js.map} +0 -0
  193. /package/dist/{chunk-FQXWVS6J.js.map → chunk-CMMBWHT2.js.map} +0 -0
  194. /package/dist/{chunk-EIFCP6CZ.js.map → chunk-CO47QJKZ.js.map} +0 -0
  195. /package/dist/{chunk-ADMEPUBT.js.map → chunk-CXWTNT2O.js.map} +0 -0
  196. /package/dist/{chunk-LKP7QDFN.js.map → chunk-DAQTRAP5.js.map} +0 -0
  197. /package/dist/{chunk-PWBH7DCQ.js.map → chunk-EESH6COU.js.map} +0 -0
  198. /package/dist/{chunk-NCPWXHDO.js.map → chunk-JEUFYSFQ.js.map} +0 -0
  199. /package/dist/{chunk-2IR4T5PO.js.map → chunk-JJ4RSDSY.js.map} +0 -0
  200. /package/dist/{chunk-WDWXAYVH.js.map → chunk-L3RLGGRK.js.map} +0 -0
  201. /package/dist/{chunk-T7JAOYE5.js.map → chunk-L75CAZG4.js.map} +0 -0
  202. /package/dist/{chunk-U7CTFN2J.js.map → chunk-MYRPMSOX.js.map} +0 -0
  203. /package/dist/{chunk-WQWKYAAK.js.map → chunk-NUQF5F5J.js.map} +0 -0
  204. /package/dist/{chunk-FUCPZVRY.js.map → chunk-OEN6FBUW.js.map} +0 -0
  205. /package/dist/{chunk-FBMZJEB3.js.map → chunk-OKMTUAWO.js.map} +0 -0
  206. /package/dist/{chunk-R4WXQIOZ.js.map → chunk-PCEM5FOZ.js.map} +0 -0
  207. /package/dist/{chunk-4D5IZSXM.js.map → chunk-PXNCRGUL.js.map} +0 -0
  208. /package/dist/{chunk-ODRPL3WB.js.map → chunk-Q3ODX5PF.js.map} +0 -0
  209. /package/dist/{chunk-KGEQJ5CC.js.map → chunk-RRFFUWDL.js.map} +0 -0
  210. /package/dist/{chunk-NSBXVPZL.js.map → chunk-S3XUYW5K.js.map} +0 -0
  211. /package/dist/{chunk-LPIF5LIE.js.map → chunk-SAU2WQFC.js.map} +0 -0
  212. /package/dist/{chunk-MSRU4D4P.js.map → chunk-SFLICIQV.js.map} +0 -0
  213. /package/dist/{chunk-NGTVREIH.js.map → chunk-U6IHLEWT.js.map} +0 -0
  214. /package/dist/{chunk-VQK4VHLF.js.map → chunk-YQV75FIL.js.map} +0 -0
  215. /package/dist/{chunk-TU2WUBVZ.js.map → chunk-YUTVAMWW.js.map} +0 -0
  216. /package/dist/{chunk-RLT5SEML.js.map → chunk-Z5MZCCTT.js.map} +0 -0
  217. /package/dist/{cli-JJMOVIKS.js.map → cli-BS43EVCD.js.map} +0 -0
  218. /package/dist/{cli-ONTC5PKT.js.map → cli-FX53PBQ3.js.map} +0 -0
  219. /package/dist/{cli-I62SRH3R.js.map → cli-NGL63E53.js.map} +0 -0
  220. /package/dist/{cli-VUKMWSSF.js.map → cli-WNEI5L7P.js.map} +0 -0
  221. /package/dist/{config-XDA42RJL.js.map → config-JJW75SBX.js.map} +0 -0
  222. /package/dist/{github-RY3FRA6P.js.map → github-4UR67MEN.js.map} +0 -0
  223. /package/dist/{github-YKQSH7FS.js.map → github-UPGKMVVF.js.map} +0 -0
  224. /package/dist/{host-6GURH5HE.js.map → host-YSTYO5DX.js.map} +0 -0
  225. /package/dist/{json-BXY44ED7.js.map → json-DQIELR36.js.map} +0 -0
  226. /package/dist/{lint-YJ2L22BW.js.map → lint-BLBGGOYM.js.map} +0 -0
  227. /package/dist/{list-NSG24VVX.js.map → list-B3RRRWFQ.js.map} +0 -0
  228. /package/dist/{list-V6EGCSHX.js.map → list-ZFRT3JQH.js.map} +0 -0
  229. /package/dist/{page-EV7OUBAU.js.map → page-5ISEVJR5.js.map} +0 -0
  230. /package/dist/{pretty-Q5Y2PA64.js.map → pretty-Y7CGQLAF.js.map} +0 -0
  231. /package/dist/{pretty-SCRK66M5.js.map → pretty-ZISH3Q4F.js.map} +0 -0
  232. /package/dist/{provider-527CO4DM.js.map → provider-LBX4IXGX.js.map} +0 -0
  233. /package/dist/{release-3D5PK6M7.js.map → release-VV3UTKE5.js.map} +0 -0
  234. /package/dist/{reporters-A5ADKDYR.js.map → reporters-26H3N2KW.js.map} +0 -0
  235. /package/dist/{reporters-M6LJ2IYU.js.map → reporters-4STXJJE2.js.map} +0 -0
  236. /package/dist/{reporters-RBYTASMI.js.map → reporters-74B4U2GZ.js.map} +0 -0
  237. /package/dist/{run-BUE2DDXN.js.map → run-DVGTKM52.js.map} +0 -0
  238. /package/dist/{validate-ZBW44RHT.js.map → validate-CJNFQHMD.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/term/commands/check.ts","../src/term/core/severity.ts","../src/term/core/check.ts","../src/term/commands/findings.ts"],"sourcesContent":["/**\n * `manni term check`: the referential integrity of the set (proposal 0052 § 7),\n * with the baseline ratchet meta and cite use.\n *\n * `--baseline` is one flag for both halves of the ratchet. When the baseline\n * file is not there yet, the run records one and exits 0: that is how a docset\n * whose terms were never checked ramps in. When it is there, the run fails only\n * on findings it does not hold. A configured `term.baseline` compares without\n * the flag, as `cite.baseline` does.\n */\nimport { existsSync } from \"node:fs\";\nimport { resolveBaselineRequest, settleBaseline } from \"../../meta/internal.js\";\nimport { checkTermSet } from \"../core/check.js\";\nimport { DEFAULT_TERM_BASELINE_PATH } from \"../core/config.js\";\nimport { TermError } from \"../errors.js\";\nimport { reportedFindings, summarize, toResults, type TermReport } from \"./findings.js\";\nimport { loadTerms, type TermCommandOptions } from \"./run.js\";\n\nexport interface CheckOptions extends TermCommandOptions {\n /** `--baseline`. */\n baseline?: boolean;\n}\n\nexport async function runCheck(opts: CheckOptions): Promise<TermReport> {\n const { cwd, run, set } = await loadTerms(opts);\n const config = run.config;\n const findings = checkTermSet(set, {\n ...(config?.severity === undefined ? {} : { severity: config.severity }),\n ...(config?.abstractMaxLength === undefined ? {} : { abstractMaxLength: config.abstractMaxLength }),\n ...(config?.fill?.crawl?.minPages === undefined ? {} : { minPages: config.fill.crawl.minPages }),\n });\n const { results, origin } = toResults(set, findings);\n\n // Fingerprints must not depend on where the command was run from.\n const frame = { cwd, base: run.configDir ?? cwd, runBase: run.base };\n let request = resolveBaselineRequest(\n opts.baseline === true ? { baseline: true } : {},\n config?.baseline,\n run.configDir,\n cwd,\n { current: DEFAULT_TERM_BASELINE_PATH },\n );\n if (request !== null && !existsSync(request.absPath)) {\n if (opts.baseline !== true) {\n throw new TermError(\n `Baseline \"${request.label}\" not found. Record one with \\`manni term check --baseline\\`.`,\n );\n }\n request = { ...request, write: true };\n }\n const { results: reported, baseline } = await settleBaseline(results, request, frame);\n\n return {\n findings: reportedFindings(reported, origin),\n results: reported,\n summary: summarize(reported, baseline),\n frame,\n terms: set.terms.length,\n references: set.references.length,\n ...(baseline === undefined ? {} : { baseline }),\n found: findings.length,\n cwd,\n };\n}\n","/**\n * Rule ids and levels for the term tool (proposal 0052). `check` findings are\n * `manni:term/<rule>`. A `lint` finding keeps Vale's own rule name,\n * `manni:term/prose/<Style.Rule>`, in the shape lint's proposal 0050 set for\n * its prose job, because that is the name Vale's documentation uses.\n */\nimport type { Severity } from \"../../shared/severity.js\";\nimport { TERM_RULES, type TermRule, type TermSeverity, type ValeSeverity } from \"../types.js\";\n\nexport const RULE_ID_PREFIX = \"manni:term\";\n\nexport const DEFAULT_SEVERITY: Readonly<Record<TermRule, TermSeverity>> = {\n \"undefined-term\": \"error\",\n \"duplicate-id\": \"error\",\n \"label-collision\": \"error\",\n \"alt-label-collision\": \"error\",\n \"label-overlap\": \"error\",\n \"dangling-reference\": \"error\",\n \"broader-cycle\": \"error\",\n \"see-not-empty\": \"error\",\n \"missing-definition\": \"error\",\n \"conflicting-matches\": \"error\",\n \"asymmetric-hierarchy\": \"warning\",\n \"asymmetric-related\": \"warning\",\n \"related-in-hierarchy\": \"warning\",\n \"alt-label-reference\": \"warning\",\n \"ambiguous-alt-label\": \"warning\",\n \"deprecated-label\": \"warning\",\n \"abstract-too-long\": \"notice\",\n \"unused-term\": \"notice\",\n \"hierarchy-outside-termbase\": \"notice\",\n \"related-siblings\": \"notice\",\n \"orphan-term\": \"notice\",\n \"keyword-candidate\": \"notice\",\n};\n\nexport function ruleId(rule: TermRule): string {\n return `${RULE_ID_PREFIX}/${rule}`;\n}\n\nexport function proseRuleId(check: string): string {\n return `${RULE_ID_PREFIX}/prose/${check}`;\n}\n\nexport function isTermRule(value: string): value is TermRule {\n return (TERM_RULES as readonly string[]).includes(value);\n}\n\n/** The defaults with config's overrides laid over them. */\nexport function resolveSeverity(\n overrides?: Partial<Record<TermRule, TermSeverity>>,\n): Record<TermRule, TermSeverity> {\n return { ...DEFAULT_SEVERITY, ...overrides };\n}\n\n/**\n * Vale's scale onto the family's. A finding keeps Vale's own value beside the\n * folded one, as an a11y violation keeps axe's `impact`.\n */\nexport function foldValeSeverity(severity: ValeSeverity): Severity {\n return severity === \"suggestion\" ? \"notice\" : severity;\n}\n","/**\n * `term check`'s rules (proposal 0052 § 7): the referential integrity of a term\n * set. Pure: it reads records and references and returns findings, so the\n * command core owns every file and the rules are tested on sets built inline.\n *\n * Every label comparison ignores case, because the Vale style `write -f vale`\n * renders is a `swap` map keyed on the lowercased term. Two labels that differ\n * only in case collide there silently, so they collide here out loud. Ids\n * compare ignoring case too, because references resolve them that way.\n *\n * A set may hold one term per language, joined on `id` (proposal 0084). A term\n * with no language counts as `en`, and languages compare ignoring case. Across\n * languages, two labels collide only when they differ in spelling but not in\n * case: `API` in en and de is one word, `Kubernetes`/`kubernetes` is a clash.\n *\n * The hierarchy is the concept scheme's (proposal 0086): every term's\n * `broader` and `narrower`, plus every page's `graph:` block. A name on a page\n * resolves as the graph resolves it, by label, then id, then alt-label. The\n * thesaurus rules (proposal 0086 § 4) read that union too, and\n * `keyword-candidate` reads the free `keywords:` of every page loaded.\n */\nimport { TERM_RELATION_FIELDS } from \"../../shared/reference-fields.js\";\nimport { DEFAULT_ABSTRACT_MAX_LENGTH, DEFAULT_CRAWL_MIN_PAGES } from \"./config.js\";\nimport { languageKey as language } from \"./writers/render-util.js\";\nimport { resolveSeverity, ruleId } from \"./severity.js\";\nimport {\n TERM_MATCH_FIELDS,\n type Term,\n type TermField,\n type TermFinding,\n type TermReference,\n type TermRule,\n type TermSet,\n type TermSeverity,\n} from \"../types.js\";\n\nexport interface CheckTermSetOptions {\n severity?: Partial<Record<TermRule, TermSeverity>>;\n /** Default `DEFAULT_ABSTRACT_MAX_LENGTH`. */\n abstractMaxLength?: number;\n /** The pages a keyword needs to be a `keyword-candidate`: `term.fill.crawl.minPages`. Default `DEFAULT_CRAWL_MIN_PAGES`. */\n minPages?: number;\n}\n\n/** The `administrative-status` values that retire a designation, as 0084 reads them from TBX. */\nconst RETIRED_STATUSES: ReadonlySet<string> = new Set([\"deprecated\", \"superseded\"]);\n\n/** The fields whose values name other entries. */\nconst REFERENCE_FIELDS = TERM_RELATION_FIELDS;\ntype ReferenceField = (typeof REFERENCE_FIELDS)[number];\n\ninterface Draft {\n rule: TermRule;\n message: string;\n file: string;\n line?: number;\n id?: string;\n field?: TermField;\n}\n\nexport function checkTermSet(set: TermSet, opts: CheckTermSetOptions = {}): TermFinding[] {\n const levels = resolveSeverity(opts.severity);\n const maxLength = opts.abstractMaxLength ?? DEFAULT_ABSTRACT_MAX_LENGTH;\n const { terms } = set;\n const drafts: Draft[] = [];\n\n const fold = (value: string): string => value.toLowerCase();\n const where = (t: Term): string => `${t.location.file}:${String(t.location.line)}`;\n const onEntry = (t: Term, rule: TermRule, message: string, field?: TermField): Draft => ({\n rule,\n message,\n file: t.location.file,\n line: (field === undefined ? undefined : t.location.fieldLines[field]) ?? t.location.line,\n id: t.id,\n ...(field === undefined ? {} : { field }),\n });\n\n // The first entry claiming each folded label, id and alt-label, overall\n // and per language.\n const byLabel = new Map<string, number>();\n const byId = new Map<string, number>();\n const inLanguage = new Map<string, number>();\n const byAlt = new Map<string, number>();\n // Every entry per folded label, and per folded alt-label, in set order, for\n // the collision rules.\n const labelled = new Map<string, number[]>();\n const altHeld = new Map<string, Set<number>>();\n const claim = (map: Map<string, number>, key: string, index: number): void => {\n if (!map.has(key)) map.set(key, index);\n };\n terms.forEach((t, index) => {\n claim(byLabel, fold(t.record.label), index);\n const group = labelled.get(fold(t.record.label));\n if (group === undefined) labelled.set(fold(t.record.label), [index]);\n else group.push(index);\n claim(byId, fold(t.id), index);\n claim(inLanguage, `${language(t)} label ${fold(t.record.label)}`, index);\n claim(inLanguage, `${language(t)} id ${fold(t.id)}`, index);\n for (const alt of t.record[\"alt-labels\"] ?? []) {\n claim(byAlt, fold(alt), index);\n claim(inLanguage, `${language(t)} alt ${fold(alt)}`, index);\n const holders = altHeld.get(fold(alt));\n if (holders === undefined) altHeld.set(fold(alt), new Set([index]));\n else holders.add(index);\n }\n });\n /** Whether `t`'s label `a` collides with `other`'s label `b`: within a language when they fold alike, across languages only when their spelling differs too. */\n const clash = (t: Term, a: string, other: Term, b: string): boolean =>\n fold(a) === fold(b) && (language(t) === language(other) || a !== b);\n /** The first entry other than `index` whose label collides with `value` on `t`, among those `before` allows. */\n const collider = (t: Term, index: number, value: string, before: boolean): Term | undefined => {\n for (const otherIndex of labelled.get(fold(value)) ?? []) {\n if (otherIndex === index || (before && otherIndex > index)) continue;\n const other = terms[otherIndex];\n if (other !== undefined && clash(t, value, other, other.record.label)) return other;\n }\n return undefined;\n };\n /** As `collider`, against other entries' alt-labels. */\n const altCollider = (t: Term, index: number, value: string, before: boolean): Term | undefined => {\n for (const otherIndex of altHeld.get(fold(value)) ?? []) {\n if (otherIndex === index || (before && otherIndex > index)) continue;\n const other = terms[otherIndex];\n const alt = other?.record[\"alt-labels\"]?.find((a) => fold(a) === fold(value));\n if (other !== undefined && alt !== undefined && clash(t, value, other, alt)) return other;\n }\n return undefined;\n };\n\n // A same-language label, then a same-language id, then any language.\n const resolveReference = (from: Term, value: string): number | undefined =>\n inLanguage.get(`${language(from)} label ${fold(value)}`) ??\n inLanguage.get(`${language(from)} id ${fold(value)}`) ??\n byLabel.get(fold(value)) ??\n byId.get(fold(value));\n\n // A name on a page, in the graph's order (`termIndex` in\n // src/graph/core/concepts.ts): the page's language, then its primary\n // language, then any, and within each a label, an id, then an alt-label. The\n // two must change together, or term check and the graph judge different\n // hierarchies. So here, unlike the other rules, a page or term with no\n // language takes part only in the last pass, as in the graph.\n const tagOf = (tag: string | undefined): string | undefined =>\n tag === undefined || tag === \"\" ? undefined : fold(tag);\n // `split` always yields one element; the `?? \"\"` only satisfies the index type.\n const primaryOf = (tag: string): string => tag.split(\"-\")[0] ?? \"\";\n const ids = new Map<string, number[]>();\n const alts = new Map<string, number[]>();\n const list = (map: Map<string, number[]>, key: string, index: number): void => {\n const held = map.get(key);\n if (held === undefined) map.set(key, [index]);\n else held.push(index);\n };\n terms.forEach((t, index) => {\n list(ids, fold(t.id), index);\n for (const alt of t.record[\"alt-labels\"] ?? []) list(alts, fold(alt), index);\n });\n const tiers: [Map<string, number[]>, boolean][] = [\n [labelled, false],\n [ids, false],\n [alts, true],\n ];\n const resolvePageName = (value: string, lang: string | undefined): { index: number; alt: boolean } | undefined => {\n const wanted = tagOf(lang);\n const passes: ((t: Term) => boolean)[] =\n wanted === undefined\n ? [() => true]\n : [\n (t) => tagOf(t.language) === wanted,\n (t) => {\n const own = tagOf(t.language);\n return own !== undefined && primaryOf(own) === primaryOf(wanted);\n },\n () => true,\n ];\n for (const pass of passes) {\n for (const [map, alt] of tiers) {\n const index = map.get(fold(value))?.find((i) => {\n const t = terms[i];\n return t !== undefined && pass(t);\n });\n if (index !== undefined) return { index, alt };\n }\n }\n return undefined;\n };\n const resolveName = (value: string, lang: string | undefined): number | undefined =>\n resolvePageName(value, lang)?.index;\n\n // undefined-term and alt-label-reference, and the ids some page names, for unused-term.\n const usedIds = new Set<string>();\n for (const reference of set.references) {\n const field = reference.field ?? \"concepts\";\n const at = { file: reference.file, ...(reference.line === undefined ? {} : { line: reference.line }) };\n const hit = resolvePageName(reference.label, reference.language);\n const target = terms[hit?.index ?? -1];\n if (target !== undefined) usedIds.add(fold(target.id));\n if (hit !== undefined && !hit.alt) continue;\n drafts.push(\n target === undefined\n ? { rule: \"undefined-term\", message: `${field}: \"${reference.label}\" names no entry.`, ...at }\n : {\n rule: \"alt-label-reference\",\n message: `${field}: \"${reference.label}\" is an alt-label of \"${target.record.label}\", so name the label.`,\n ...at,\n },\n );\n }\n\n // duplicate-id: ignoring case, as references resolve ids, on every entry\n // sharing the id in one language.\n const sharing = new Map<string, Term[]>();\n for (const t of terms) {\n const id = `${language(t)} ${fold(t.id)}`;\n const group = sharing.get(id);\n if (group === undefined) sharing.set(id, [t]);\n else group.push(t);\n }\n for (const group of sharing.values()) {\n if (group.length < 2) continue;\n for (const t of group) {\n const others = group.filter((other) => other !== t).map(where);\n drafts.push(onEntry(t, \"duplicate-id\", `id: \"${t.id}\" is also used by ${others.join(\", \")}`));\n }\n }\n\n terms.forEach((t, index) => {\n // label-collision: on each later entry, naming the first.\n const firstTerm = collider(t, index, t.record.label, true);\n if (firstTerm !== undefined) {\n drafts.push(\n onEntry(\n t,\n \"label-collision\",\n `\"${t.record.label}\" is claimed by ${where(firstTerm)} as \"${firstTerm.record.label}\"`,\n \"label\",\n ),\n );\n }\n\n // alt-label-collision: an alt-label that is another entry's label.\n for (const alt of t.record[\"alt-labels\"] ?? []) {\n const owner = collider(t, index, alt, false);\n if (owner !== undefined) {\n drafts.push(\n onEntry(t, \"alt-label-collision\", `alt-labels: \"${alt}\" is the label of ${where(owner)}`, \"alt-labels\"),\n );\n }\n // label-overlap: an alt-label that repeats the entry's own label.\n if (fold(alt) === fold(t.record.label)) {\n drafts.push(onEntry(t, \"label-overlap\", `alt-labels: \"${alt}\" repeats this entry's label.`, \"alt-labels\"));\n }\n // ambiguous-alt-label: on each later entry, naming the first.\n const sharer = altCollider(t, index, alt, true);\n if (sharer !== undefined) {\n drafts.push(\n onEntry(\n t,\n \"ambiguous-alt-label\",\n `alt-labels: \"${alt}\" is also an alt-label of ${where(sharer)}, so a reader can't tell which term it means.`,\n \"alt-labels\",\n ),\n );\n }\n }\n\n // label-overlap: a hidden-label is a name the entry or another one goes by.\n const ownAlts = new Set((t.record[\"alt-labels\"] ?? []).map(fold));\n for (const hidden of t.record[\"hidden-labels\"] ?? []) {\n const labelOf = collider(t, index, hidden, false);\n const altOf = labelOf === undefined ? altCollider(t, index, hidden, false) : undefined;\n const overlap =\n fold(hidden) === fold(t.record.label)\n ? \"repeats this entry's label.\"\n : ownAlts.has(fold(hidden))\n ? \"repeats this entry's alt-label.\"\n : labelOf !== undefined\n ? `is the label of ${where(labelOf)}.`\n : altOf !== undefined\n ? `is an alt-label of ${where(altOf)}.`\n : undefined;\n if (overlap !== undefined) {\n drafts.push(onEntry(t, \"label-overlap\", `hidden-labels: \"${hidden}\" ${overlap}`, \"hidden-labels\"));\n }\n }\n\n // deprecated-label: the designation of the label itself is retired.\n const status = t.record.designations?.find((d) => fold(d.term) === fold(t.record.label))?.[\"administrative-status\"];\n if (t.record.see === undefined && status !== undefined && RETIRED_STATUSES.has(fold(status))) {\n drafts.push(\n onEntry(\n t,\n \"deprecated-label\",\n `label: \"${t.record.label}\" is deprecated in designations, so promote another label or redirect with see:.`,\n \"label\",\n ),\n );\n }\n\n // dangling-reference: one finding per value that resolves to nothing.\n for (const field of REFERENCE_FIELDS) {\n for (const value of referenceValues(t, field)) {\n if (resolveReference(t, value) === undefined) {\n drafts.push(onEntry(t, \"dangling-reference\", `${field}: \"${value}\" names no entry`, field));\n }\n }\n }\n\n // see-not-empty: a redirect carries no definition.\n if (t.record.see !== undefined && t.record.definition !== undefined) {\n drafts.push(\n onEntry(t, \"see-not-empty\", `see: \"${t.record.see}\" redirects this entry, so remove its definition`, \"see\"),\n );\n }\n\n // missing-definition: an entry that neither defines its term nor redirects.\n if (t.record.see === undefined && t.record.definition === undefined) {\n drafts.push(\n onEntry(t, \"missing-definition\", \"definition: missing, so write one or run manni term fill.\", \"definition\"),\n );\n }\n\n // conflicting-matches: once per IRI, naming the first kind it is under and the next.\n const kindOf = new Map<string, TermField>();\n const conflicting = new Set<string>();\n for (const field of TERM_MATCH_FIELDS) {\n for (const iri of t.record[field] ?? []) {\n const first = kindOf.get(iri);\n if (first === undefined) kindOf.set(iri, field);\n else if (first !== field && !conflicting.has(iri)) {\n conflicting.add(iri);\n drafts.push(\n onEntry(t, \"conflicting-matches\", `${first}: \"${iri}\" is also in ${field}, and a mapping has one kind.`, first),\n );\n }\n }\n }\n\n // abstract-too-long.\n const abstract = t.record.abstract;\n if (abstract !== undefined && abstract.length > maxLength) {\n drafts.push(\n onEntry(\n t,\n \"abstract-too-long\",\n `abstract: ${String(abstract.length)} characters is over the limit of ${String(maxLength)}, so shorten it`,\n \"abstract\",\n ),\n );\n }\n\n // unused-term: no reference resolves to any term sharing the id.\n if (!usedIds.has(fold(t.id))) {\n drafts.push(onEntry(t, \"unused-term\", \"no page's concepts: names this term\"));\n }\n });\n\n // The resolved hierarchy, one edge per pair.\n const edges = (field: \"broader\" | \"narrower\" | \"related-terms\"): Set<number>[] =>\n terms.map((t) => {\n const targets = new Set<number>();\n for (const value of referenceValues(t, field)) {\n const target = resolveReference(t, value);\n if (target !== undefined) targets.add(target);\n }\n return targets;\n });\n const broader = edges(\"broader\");\n const narrower = edges(\"narrower\");\n const related = edges(\"related-terms\");\n\n // asymmetric-related: on the entry that omits the other half.\n terms.forEach((t, index) => {\n for (const other of related[index] ?? []) {\n const otherTerm = terms[other];\n if (otherTerm !== undefined && !(related[other]?.has(index) ?? false)) {\n drafts.push(\n onEntry(\n otherTerm,\n \"asymmetric-related\",\n `related-terms: omits \"${t.record.label}\", which lists this entry as related.`,\n \"related-terms\",\n ),\n );\n }\n }\n });\n\n // asymmetric-hierarchy: each missing half, on the entry that should carry it.\n terms.forEach((t, index) => {\n for (const parent of broader[index] ?? []) {\n const parentTerm = terms[parent];\n if (parentTerm !== undefined && !(narrower[parent]?.has(index) ?? false)) {\n drafts.push(\n onEntry(\n parentTerm,\n \"asymmetric-hierarchy\",\n `narrower: omits \"${t.record.label}\", which lists this entry as broader`,\n \"narrower\",\n ),\n );\n }\n }\n for (const child of narrower[index] ?? []) {\n const childTerm = terms[child];\n if (childTerm !== undefined && !(broader[child]?.has(index) ?? false)) {\n drafts.push(\n onEntry(\n childTerm,\n \"asymmetric-hierarchy\",\n `broader: omits \"${t.record.label}\", which lists this entry as narrower`,\n \"broader\",\n ),\n );\n }\n }\n });\n\n // The concept scheme's hierarchy, child to parent: every term's broader and\n // narrower, then every graph block's. A name no term claims is a freeform\n // node of its own, after the terms.\n const names = terms.map((t) => t.record.label);\n const freeform = new Map<string, number>();\n const node = (value: string, lang: string | undefined): number => {\n const index = resolveName(value, lang) ?? freeform.get(fold(value));\n if (index !== undefined) return index;\n freeform.set(fold(value), names.length);\n names.push(value);\n return names.length - 1;\n };\n const up: Set<number>[] = [];\n const termEdges = new Set<string>();\n const blockEdges = new Map<string, { file: string; line?: number }>();\n const link = (child: number, parent: number): string => {\n (up[child] ??= new Set()).add(parent);\n return `${String(child)}>${String(parent)}`;\n };\n broader.forEach((parents, child) => {\n for (const parent of parents) termEdges.add(link(child, parent));\n });\n narrower.forEach((children, parent) => {\n for (const child of children) termEdges.add(link(child, parent));\n });\n for (const block of set.blocks) {\n const subject = node(block.label, block.language);\n for (const edge of block.edges) {\n if (edge.field === \"related-concepts\") continue;\n const other = node(edge.value, block.language);\n const key = edge.field === \"broader\" ? link(subject, other) : link(other, subject);\n const line = edge.line ?? block.line;\n if (!blockEdges.has(key)) blockEdges.set(key, { file: block.file, ...(line === undefined ? {} : { line }) });\n }\n }\n\n // broader-cycle: every elementary cycle once, found from its earliest node.\n // A cycle the terms close alone sits on that entry; one a block edge closes\n // sits on the first such edge's page.\n for (const [start, path] of broaderCycles(up)) {\n const chain = [...path, start];\n const message = `broader: ${chain.map((index) => names[index] ?? \"\").join(\" > \")}`;\n const hops = path.map((from, i) => `${String(from)}>${String(chain[i + 1])}`);\n const viaBlock = hops.find((hop) => !termEdges.has(hop));\n const startTerm = terms[start];\n if (viaBlock === undefined && startTerm !== undefined) {\n drafts.push(onEntry(startTerm, \"broader-cycle\", message, \"broader\"));\n } else {\n const at = blockEdges.get(viaBlock ?? \"\");\n if (at !== undefined) drafts.push({ rule: \"broader-cycle\", message, ...at });\n }\n }\n\n // hierarchy-outside-termbase: a block whose label is a term gives it an edge\n // the term's own field does not list. Values compare as concepts, by id.\n const concept = (index: number | undefined, value: string): string => {\n const t = index === undefined ? undefined : terms[index];\n return t === undefined ? `name ${fold(value)}` : `id ${fold(t.id)}`;\n };\n for (const block of set.blocks) {\n const t = terms[resolveName(block.label, block.language) ?? -1];\n if (t === undefined) continue;\n for (const edge of block.edges) {\n const field = edge.field === \"related-concepts\" ? \"related-terms\" : edge.field;\n const listed = referenceValues(t, field).map((value) => concept(resolveReference(t, value), value));\n if (listed.includes(concept(resolveName(edge.value, block.language), edge.value))) continue;\n const kind = t.location.construct === \"page\" ? \"page\" : \"entry\";\n const line = edge.line ?? block.line;\n drafts.push({\n rule: \"hierarchy-outside-termbase\",\n message: `graph: ${edge.field} \"${edge.value}\" on \"${t.record.label}\" is not on its term ${kind} (${t.location.file}), so add it there.`,\n file: block.file,\n ...(line === undefined ? {} : { line }),\n });\n }\n }\n\n // Every concept above a node, at any depth, over the whole union.\n const ancestors = (start: number): Set<number> => {\n const seen = new Set<number>();\n const stack = [...(up[start] ?? [])];\n for (let next = stack.pop(); next !== undefined; next = stack.pop()) {\n if (seen.has(next)) continue;\n seen.add(next);\n stack.push(...(up[next] ?? []));\n }\n return seen;\n };\n\n // related-in-hierarchy on the entry that lists the link; related-siblings\n // once per pair, on the first entry that lists the other.\n const siblingPairs = new Set<string>();\n terms.forEach((t, index) => {\n const above = ancestors(index);\n const seen = new Set<number>();\n for (const value of referenceValues(t, \"related-terms\")) {\n const other = resolveReference(t, value);\n if (other === undefined || other === index || seen.has(other)) continue;\n seen.add(other);\n const direction = above.has(other) ? \"broader\" : ancestors(other).has(index) ? \"narrower\" : undefined;\n if (direction !== undefined) {\n drafts.push(\n onEntry(\n t,\n \"related-in-hierarchy\",\n `related-terms: \"${value}\" is also ${direction} than this entry, so keep one of the two.`,\n \"related-terms\",\n ),\n );\n continue;\n }\n // Z39.19 siblings share an immediate broader term, not just an ancestor.\n const parent = [...(up[index] ?? [])].find((p) => up[other]?.has(p) ?? false);\n const pair = `${String(Math.min(index, other))}~${String(Math.max(index, other))}`;\n if (parent === undefined || siblingPairs.has(pair)) continue;\n siblingPairs.add(pair);\n drafts.push(\n onEntry(\n t,\n \"related-siblings\",\n `related-terms: \"${value}\" shares the broader term \"${names[parent] ?? \"\"}\", which already relates them.`,\n \"related-terms\",\n ),\n );\n }\n });\n\n // orphan-term: only once a term sits in the hierarchy at all, so a flat\n // glossary never hears of it, nor one whose only edges join names no term\n // defines (those sit past the terms in `names`). A concept is placed in\n // every language.\n const isTerm = (index: number): boolean => index < terms.length;\n if (up.some((parents, child) => isTerm(child) || [...parents].some(isTerm))) {\n const placed = new Set<string>();\n const place = (index: number | undefined): void => {\n if (index === undefined) return;\n const t = terms[index];\n if (t !== undefined) placed.add(fold(t.id));\n };\n up.forEach((parents, child) => {\n place(child);\n for (const parent of parents) place(parent);\n });\n related.forEach((others, index) => {\n if (others.size > 0) place(index);\n for (const other of others) place(other);\n });\n for (const block of set.blocks) {\n for (const edge of block.edges) {\n if (edge.field !== \"related-concepts\") continue;\n place(resolveName(block.label, block.language));\n place(resolveName(edge.value, block.language));\n }\n }\n for (const t of terms) {\n if (t.record.see === undefined && !placed.has(fold(t.id))) {\n drafts.push(onEntry(t, \"orphan-term\", \"no broader, narrower or related term, so place it in the hierarchy.\"));\n }\n }\n }\n\n // keyword-candidate: once per keyword, at its first page in sort order.\n const minPages = opts.minPages ?? DEFAULT_CRAWL_MIN_PAGES;\n const byKeyword = new Map<string, TermReference[]>();\n for (const keyword of set.keywords ?? []) {\n const uses = byKeyword.get(fold(keyword.label));\n if (uses === undefined) byKeyword.set(fold(keyword.label), [keyword]);\n else uses.push(keyword);\n }\n for (const [key, uses] of byKeyword) {\n if (byLabel.has(key) || byId.has(key) || byAlt.has(key)) continue;\n const pages = new Set(uses.map((use) => use.file)).size;\n const first = [...uses].sort((a, b) => compareText(a.file, b.file) || (a.line ?? 0) - (b.line ?? 0))[0];\n if (pages < minPages || first === undefined) continue;\n drafts.push({\n rule: \"keyword-candidate\",\n message: `keywords: \"${first.label}\" is on ${String(pages)} pages and names no term, so add it to concepts: or run manni term fill --crawl.`,\n file: first.file,\n ...(first.line === undefined ? {} : { line: first.line }),\n });\n }\n\n const findings: TermFinding[] = [];\n for (const draft of drafts) {\n const level = levels[draft.rule];\n if (level === \"off\") continue;\n findings.push({\n rule: draft.rule,\n ruleId: ruleId(draft.rule),\n severity: level,\n message: draft.message,\n file: draft.file,\n ...(draft.line === undefined ? {} : { line: draft.line }),\n ...(draft.id === undefined ? {} : { id: draft.id }),\n ...(draft.field === undefined ? {} : { field: draft.field }),\n });\n }\n return findings.sort(compareFindings);\n}\n\nfunction referenceValues(t: Term, field: ReferenceField): string[] {\n const value = t.record[field];\n if (value === undefined) return [];\n return typeof value === \"string\" ? [value] : value;\n}\n\n/**\n * Each elementary cycle of the `broader` graph, as its start and the path from\n * it. A search from `start` only visits later entries, so a cycle is found\n * exactly once, from the entry of it that comes first in set order.\n */\nfunction broaderCycles(broader: readonly Set<number>[]): [number, number[]][] {\n const cycles: [number, number[]][] = [];\n for (let start = 0; start < broader.length; start++) {\n const path: number[] = [start];\n const onPath = new Set<number>([start]);\n const walk = (node: number): void => {\n for (const next of broader[node] ?? []) {\n if (next === start) {\n cycles.push([start, [...path]]);\n } else if (next > start && !onPath.has(next)) {\n path.push(next);\n onPath.add(next);\n walk(next);\n path.pop();\n onPath.delete(next);\n }\n }\n };\n walk(start);\n }\n return cycles;\n}\n\nfunction compareText(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nfunction compareFindings(a: TermFinding, b: TermFinding): number {\n return (\n compareText(a.file, b.file) ||\n (a.line ?? 0) - (b.line ?? 0) ||\n compareText(a.ruleId, b.ruleId) ||\n compareText(a.message, b.message)\n );\n}\n","/**\n * The report `check` and `lint` share, and the bridge from term findings to\n * meta's `ValidationResult`, so the SARIF and JUnit renderers and the baseline\n * ratchet work unchanged, as cite's adapter does for citations.\n *\n * FieldError: schema `manni:term` (a builtin-shaped ref, so fingerprints and\n * rule ids never depend on where the command ran), keyword = the rule id after\n * `manni:term/`, instancePath `/<field>` when the finding concerns one field\n * and `\"\"` when it concerns none (`undefined-term`, `duplicate-id`,\n * `unused-term`). A check finding and a lint finding carry the field the same\n * way. The subject is the entry, the field, and what the finding says, with any\n * `file:line` taken out: a line moves when a page is edited above the entry,\n * and a moved finding is not a new one.\n */\nimport {\n isErrorSeverity,\n type BaselineSummary,\n type FieldError,\n type FingerprintContext,\n type RunSummary,\n type ValidationResult,\n} from \"../../meta/index.js\";\nimport { RULE_ID_PREFIX } from \"../core/severity.js\";\nimport type { TermFinding, TermSet } from \"../types.js\";\n\nexport const FINDING_FORMATS = [\"pretty\", \"json\", \"github\", \"sarif\", \"junit\"] as const;\nexport type FindingFormat = (typeof FINDING_FORMATS)[number];\n\n/** JUnit `classname` for the term tool's findings. */\nexport const JUNIT_CLASSNAME = \"manni.term\";\n\n/** What `check` and `lint` return. */\nexport interface TermReport {\n /** The findings reported: a baselined one is not among them. */\n findings: TermFinding[];\n /** The same findings as meta's results, for SARIF and JUnit. One per file. */\n results: ValidationResult[];\n summary: RunSummary;\n frame: FingerprintContext;\n /** Entries in the set. */\n terms: number;\n /** `concepts:` values the run read. */\n references: number;\n /** How a baseline shaped the run, when one did. */\n baseline?: BaselineSummary;\n /** Findings before the baseline, for the line a recording run prints. */\n found: number;\n /** Where the run stood, for the paths the reporters print. */\n cwd: string;\n}\n\nconst LINE_REFERENCE = /:\\d+\\b/g;\n\nfunction toFieldError(finding: TermFinding): FieldError {\n const keyword = finding.ruleId.startsWith(`${RULE_ID_PREFIX}/`)\n ? finding.ruleId.slice(RULE_ID_PREFIX.length + 1)\n : finding.ruleId;\n const { field } = finding;\n const subject = [finding.id ?? \"\", field ?? \"\", finding.message.replace(LINE_REFERENCE, \"\")].join(\"\\u0000\");\n const error: FieldError = {\n schema: RULE_ID_PREFIX,\n instancePath: field === undefined ? \"\" : `/${field}`,\n message: finding.message,\n keyword,\n subject,\n severity: finding.severity,\n };\n if (finding.line !== undefined) error.line = finding.line;\n return error;\n}\n\n/**\n * One result per file: every file a term was read from, clean or not, and\n * every file a finding sits in. `origin` maps each error back to its finding.\n */\nexport function toResults(\n set: TermSet,\n findings: readonly TermFinding[],\n): { results: ValidationResult[]; origin: Map<FieldError, TermFinding> } {\n const origin = new Map<FieldError, TermFinding>();\n const byFile = new Map<string, FieldError[]>();\n for (const term of set.terms) {\n if (!byFile.has(term.location.file)) byFile.set(term.location.file, []);\n }\n for (const finding of findings) {\n const error = toFieldError(finding);\n origin.set(error, finding);\n const list = byFile.get(finding.file) ?? [];\n list.push(error);\n byFile.set(finding.file, list);\n }\n const results = [...byFile.keys()].sort().map((file): ValidationResult => {\n const errors = byFile.get(file) ?? [];\n return { file, format: \"term\", ok: !errors.some(isErrorSeverity), schemas: [RULE_ID_PREFIX], errors };\n });\n return { results, origin };\n}\n\n/** The run summary meta's renderers read, from results a baseline may have thinned. */\nexport function summarize(results: readonly ValidationResult[], baseline?: BaselineSummary): RunSummary {\n const failed = results.filter((r) => !r.ok).length;\n const count = (keep: (e: FieldError) => boolean): number =>\n results.reduce((n, r) => n + r.errors.filter(keep).length, 0);\n const warnings = count((e) => e.severity === \"warning\");\n const notices = count((e) => e.severity === \"notice\");\n return {\n files: results.length,\n passed: results.length - failed,\n failed,\n errors: count(isErrorSeverity),\n ...(warnings > 0 ? { warnings } : {}),\n ...(notices > 0 ? { notices } : {}),\n ...(baseline ? { baseline } : {}),\n };\n}\n\n/** The findings still reported once a baseline has thinned the results. */\nexport function reportedFindings(\n results: readonly ValidationResult[],\n origin: ReadonlyMap<FieldError, TermFinding>,\n): TermFinding[] {\n const kept: TermFinding[] = [];\n for (const result of results) {\n for (const error of result.errors) {\n const finding = origin.get(error);\n if (finding !== undefined) kept.push(finding);\n }\n }\n return kept.sort(compareFindings);\n}\n\nfunction compareFindings(a: TermFinding, b: TermFinding): number {\n if (a.file !== b.file) return a.file < b.file ? -1 : 1;\n const lineA = a.line ?? 0;\n const lineB = b.line ?? 0;\n if (lineA !== lineB) return lineA - lineB;\n if (a.ruleId !== b.ruleId) return a.ruleId < b.ruleId ? -1 : 1;\n return a.message < b.message ? -1 : a.message > b.message ? 1 : 0;\n}\n"],"mappings":"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","names":["compareFindings"]}
@@ -10,7 +10,7 @@ import {
10
10
  constructProvider,
11
11
  resolveSelectionIdentity,
12
12
  selectProvider
13
- } from "./chunk-NGTVREIH.js";
13
+ } from "./chunk-U6IHLEWT.js";
14
14
  import {
15
15
  TracevalsError
16
16
  } from "./chunk-UKZNLVJW.js";
@@ -499,4 +499,4 @@ export {
499
499
  localGate,
500
500
  assertDownloaded
501
501
  };
502
- //# sourceMappingURL=chunk-VQK4VHLF.js.map
502
+ //# sourceMappingURL=chunk-YQV75FIL.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ //# sourceMappingURL=chunk-YUTVAMWW.js.map
@@ -22,7 +22,7 @@ import {
22
22
  updateConfigEval,
23
23
  updateManifestEval,
24
24
  updatePageEval
25
- } from "./chunk-HA6MQMOY.js";
25
+ } from "./chunk-24J7TYPU.js";
26
26
  import {
27
27
  DEFAULT_CONFIG_FILENAME,
28
28
  assertRegisteredGraders,
@@ -41,7 +41,7 @@ import {
41
41
  sha256,
42
42
  splitBody,
43
43
  withExternalMetadata
44
- } from "./chunk-U7CTFN2J.js";
44
+ } from "./chunk-MYRPMSOX.js";
45
45
  import {
46
46
  isTurnBudgetSkip
47
47
  } from "./chunk-SSQZB5SQ.js";
@@ -52,12 +52,12 @@ import {
52
52
  import {
53
53
  DocevalsError
54
54
  } from "./chunk-SE4JN5XN.js";
55
+ import {
56
+ mergeMetaProvenance
57
+ } from "./chunk-7FCLXEAQ.js";
55
58
  import {
56
59
  SEVERITIES
57
60
  } from "./chunk-77AINBYM.js";
58
- import {
59
- mergeMetaProvenance
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-RLT5SEML.js.map
1287
+ //# sourceMappingURL=chunk-Z5MZCCTT.js.map
@@ -6,12 +6,13 @@ import {
6
6
  import {
7
7
  LineRangeError,
8
8
  MAX_RANGE_LINES,
9
+ bodyLineOf,
9
10
  hashRange,
10
11
  isKeyedPin,
11
12
  lineSpec,
12
13
  parseLines,
13
14
  sliceLines
14
- } from "./chunk-GV56WGL2.js";
15
+ } from "./chunk-WO4JYWZK.js";
15
16
  import {
16
17
  __default3 as __default,
17
18
  extractorByName,
@@ -19,10 +20,10 @@ import {
19
20
  hasFrontmatterFence,
20
21
  locateFrontmatter,
21
22
  supportedExtensions
22
- } from "./chunk-VSEBFHAY.js";
23
+ } from "./chunk-KFKL4VTL.js";
23
24
  import {
24
25
  isEncryptedValue
25
- } from "./chunk-QLWC7MKD.js";
26
+ } from "./chunk-OTJOBZJ5.js";
26
27
  import {
27
28
  ToolError
28
29
  } from "./chunk-G3T47CFV.js";
@@ -688,11 +689,6 @@ function pickExtractor(file, format) {
688
689
  function byLine(a, b) {
689
690
  return (a.line ?? Number.MAX_SAFE_INTEGER) - (b.line ?? Number.MAX_SAFE_INTEGER);
690
691
  }
691
- function bodyLineOf(content, bodyOffset) {
692
- if (bodyOffset <= 0) return 1;
693
- const line = lineAt(content, bodyOffset);
694
- return content.charCodeAt(bodyOffset - 1) === 10 ? line : line + 1;
695
- }
696
692
  function citationInputs(file, extracted, injected) {
697
693
  if (injected !== void 0) return [...injected];
698
694
  const raw = extracted.data.citations;
@@ -972,8 +968,7 @@ export {
972
968
  ownedMessage,
973
969
  validateEntry,
974
970
  pickExtractor,
975
- bodyLineOf,
976
971
  citationInputs,
977
972
  readPage
978
973
  };
979
- //# sourceMappingURL=chunk-BKVHX6FU.js.map
974
+ //# sourceMappingURL=chunk-ZIRBFXDW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cite/core/page.ts","../src/cite/errors.ts","../src/cite/core/hash.ts","../src/cite/core/range.ts","../src/cite/types.ts","../src/cite/core/severity.ts","../src/cite/core/statements.ts"],"sourcesContent":["/**\n * Read a page's citations and its markers.\n *\n * Entries come from the frontmatter (via meta's extractor for the format), or\n * from `CheckPageOptions.citations` when a manifest owns them; either way each\n * one is validated against the registered `manni:citations:1.0.0` schema and\n * carries where it sits.\n * Markers are `cite <id> [<id>…]` comments in the body, resolved to the\n * entries they name, so each citation knows what anchors it. Every id one\n * marker names anchors the same text, so every claim under it pins the same\n * bytes and holds the same hash (proposal 0056).\n *\n * The findings made here are the ones that need no source: `entry-invalid`,\n * `marker-invalid`, `marker-orphan`, `marker-repeated` and `anchor-invalid`.\n */\nimport { extname } from \"node:path\";\nimport * as Ajv2020Ns from \"ajv/dist/2020.js\";\nimport type { ValidateFunction } from \"ajv/dist/2020.js\";\nimport citationsSchema from \"../../meta/schemas/citations/1.0.0.json\" with { type: \"json\" };\nimport {\n extractorForExtension,\n locateFrontmatter,\n supportedExtensions,\n type ExtractedMetadata,\n type MetadataExtractor,\n} from \"../../meta/index.js\";\nimport { extractorByName, hasFrontmatterFence } from \"../../meta/internal.js\";\nimport { isEncryptedValue } from \"../../shared/encryption.js\";\nimport { CiteError } from \"../errors.js\";\nimport type {\n Citation,\n CitationFinding,\n CitationInput,\n CitationOrigin,\n CiteRule,\n PageCitation,\n PageCitations,\n} from \"../types.js\";\nimport { isKeyedPin } from \"./hash.js\";\nimport { parseLines, spellSource, tooWide } from \"./range.js\";\nimport { DEFAULT_SEVERITY, ruleId } from \"./severity.js\";\nimport { bodyLineOf } from \"../../shared/pin.js\";\nimport { parseStatements } from \"./statements.js\";\n\nexport { MAX_IDS_PER_MARKER } from \"./statements.js\";\nexport { bodyLineOf };\n\nexport const MAX_MARKERS_PER_PAGE = 500;\n\n/** Formats whose leading fenced block is frontmatter meta would read. */\nconst ELEMENT_BACKED = new Set([\"html\", \"xml\"]);\n/** Formats whose only metadata channel is a fenced block. */\nconst FENCE_ONLY = new Set([\"markdown\", \"mdx\"]);\n\n/** What a marker with a JSON payload is told, since an entry never lives in the body. */\nexport const MARKER_JSON =\n \"A marker names an entry by id. Write the entry in frontmatter or the sidecar.\";\n\nexport interface ReadPageOptions {\n /** Extractor name; derived from the file's extension when absent. */\n format?: string;\n /**\n * The page's citations as a manifest owns them, already merged. Given, the\n * page's own frontmatter `citations` is not read.\n */\n citations?: readonly CitationInput[];\n /**\n * The manifest that owns this page's `citations`, when one does. A page\n * that also carries its own is `entry-invalid`: neither channel wins, and\n * the discarded value would be exactly the one nobody checked.\n */\n owned?: { file: string; collection: string };\n /**\n * `false` reads the entries alone, for a caller that only counts them: no\n * marker is parsed, so no marker finding is raised and none is anchored.\n */\n markers?: boolean;\n}\n\n/** Meta's `external:owned` sentence, said by `cite check` under `entry-invalid`. */\nexport function ownedMessage(owner: { file: string; collection: string }): string {\n return `\"citations\" is owned by manifest ${owner.file} (collection ${owner.collection}); remove it from the document`;\n}\n\n// ajv is CommonJS with a default export; under NodeNext the constructor lives\n// on `.default`, as src/meta/core/validator.ts notes.\ntype AjvCtor = typeof import(\"ajv/dist/2020.js\").default;\nconst Ajv2020 = Ajv2020Ns.default as unknown as AjvCtor;\n\nlet entryValidator: ValidateFunction | undefined;\n\nfunction compileEntryValidator(): ValidateFunction {\n if (entryValidator) return entryValidator;\n const ajv = new Ajv2020({ allErrors: false, strict: false });\n ajv.addSchema(citationsSchema);\n entryValidator = ajv.compile({\n $ref: `${citationsSchema.$id}#/$defs/citationEntry`,\n });\n return entryValidator;\n}\n\n/** Ajv validation of one entry object against `$defs.citationEntry`; the first error text, or undefined. */\nexport function validateEntry(entry: unknown): string | undefined {\n const validate = compileEntryValidator();\n if (validate(entry)) return undefined;\n const first = validate.errors?.[0];\n if (!first) return \"invalid entry\";\n return `${first.instancePath} ${first.message ?? \"is invalid\"}`.trim();\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Copy a validated entry into the `Citation` shape, field by field. */\nfunction toCitation(entry: Record<string, unknown>): Citation {\n const source = isRecord(entry.source) ? entry.source : {};\n const citation: Citation = {\n source: {\n file: String(source.file),\n integrity: String(source.integrity),\n },\n };\n if (typeof source.lines === \"number\" || typeof source.lines === \"string\") {\n citation.source.lines = source.lines;\n }\n if (typeof source[\"commit-sha\"] === \"string\") {\n citation.source[\"commit-sha\"] = source[\"commit-sha\"];\n }\n if (typeof entry.id === \"string\") citation.id = entry.id;\n if (isRecord(entry.claim)) {\n const claim = entry.claim;\n citation.claim = { integrity: String(claim.integrity) };\n if (typeof claim.lines === \"number\" || typeof claim.lines === \"string\") {\n citation.claim.lines = claim.lines;\n }\n }\n if (typeof entry.quote === \"boolean\") citation.quote = entry.quote;\n return citation;\n}\n\ntype FindingExtra = Pick<CitationFinding, \"line\" | \"id\" | \"src\" | \"index\" | \"file\">;\n\nfunction finding(rule: CiteRule, message: string, extra: FindingExtra): CitationFinding {\n const level = DEFAULT_SEVERITY[rule];\n const out: CitationFinding = {\n rule,\n ruleId: ruleId(rule),\n // The caller re-applies the configured severity table; no page-side rule\n // defaults to `off`, so there is nothing to collapse.\n severity: level === \"off\" ? \"error\" : level,\n message,\n };\n if (extra.line !== undefined) out.line = extra.line;\n if (extra.file !== undefined) out.file = extra.file;\n if (extra.id !== undefined) out.id = extra.id;\n if (extra.src !== undefined) out.src = extra.src;\n if (extra.index !== undefined) out.index = extra.index;\n return out;\n}\n\n/** `<id>: <text>`, or the text alone for an entry with no id. */\nfunction named(id: string | undefined, text: string): string {\n return id === undefined ? text : `${id}: ${text}`;\n}\n\n/** The `id`/`src` a finding carries for an entry that may not have validated. */\nfunction subjectOf(entry: unknown): Pick<FindingExtra, \"id\" | \"src\"> {\n const out: Pick<FindingExtra, \"id\" | \"src\"> = {};\n if (!isRecord(entry)) return out;\n if (typeof entry.id === \"string\") out.id = entry.id;\n if (isRecord(entry.source) && typeof entry.source.file === \"string\") out.src = entry.source.file;\n return out;\n}\n\nexport function pickExtractor(file: string, format: string | undefined): MetadataExtractor {\n if (format !== undefined) {\n const forced = extractorByName(format);\n if (forced === undefined) {\n throw new CiteError(\n `Unknown format \"${format}\". Supported extensions: ${supportedExtensions().join(\", \")}.`,\n );\n }\n return forced;\n }\n const extension = extname(file);\n const byExt = extractorForExtension(extension);\n if (!byExt) {\n throw new CiteError(\n `Unsupported file type \"${extension}\" for \"${file}\". Supported: ${supportedExtensions().join(\", \")}. Use --as to override.`,\n );\n }\n return byExt;\n}\n\nfunction byLine(a: CitationFinding, b: CitationFinding): number {\n return (a.line ?? Number.MAX_SAFE_INTEGER) - (b.line ?? Number.MAX_SAFE_INTEGER);\n}\n\n\n/**\n * A page's citation entries as they are written, each with where it sits: the\n * manifest's when one owns them, else the page's own frontmatter. The entries\n * are raw, so one the schema refuses is still in the list at its own index,\n * which is how `remove` reaches an entry `check` can only report.\n */\nexport function citationInputs(\n file: string,\n extracted: ExtractedMetadata,\n injected?: readonly CitationInput[],\n): CitationInput[] {\n if (injected !== undefined) return [...injected];\n const raw: unknown = extracted.data.citations;\n const list: unknown[] = Array.isArray(raw) ? (raw as unknown[]) : [];\n return list.map((entry, index) => {\n const origin: CitationInput[\"origin\"] = { kind: \"frontmatter\", file };\n const line = extracted.lineFor(`/citations/${String(index)}`);\n if (line !== undefined) origin.line = line;\n return { entry, origin };\n });\n}\n\nexport function readPage(\n file: string,\n content: string,\n opts?: ReadPageOptions,\n): PageCitations {\n const extractor = pickExtractor(file, opts?.format);\n const format = extractor.name;\n // An opening fence with no close is not a block, so the extractor reads no\n // metadata and every entry the page carries would silently vanish. rst and\n // asciidoc read a stray fence as body on purpose, ahead of their native\n // headers; the fence-only formats have no such reading.\n if (FENCE_ONLY.has(format) && hasFrontmatterFence(content) && locateFrontmatter(content) === null) {\n throw new CiteError(\n `${file}: Unterminated front matter fence: the opening fence has no matching close, so the page's citations cannot be read. Add a closing fence.`,\n );\n }\n const extracted = extractor.extract(content, file);\n const data = extracted.data;\n const bodyOffset = ELEMENT_BACKED.has(format)\n ? 0\n : (locateFrontmatter(content)?.closeEnd ?? 0);\n const bodyLine = bodyLineOf(content, bodyOffset);\n\n const findings: CitationFinding[] = [];\n const citations: PageCitation[] = [];\n /** Entries by id, for marker resolution. */\n const byId = new Map<string, PageCitation>();\n /** Every id seen, for uniqueness. */\n const ids = new Set<string>();\n /** The first marker naming an entry, by entry. */\n const markedAt = new Map<PageCitation, number>();\n\n // The page root reserves the whole `citation-` prefix, so a typo fails\n // loudly rather than passing as a key of somebody else's.\n for (const key of Object.keys(data)) {\n if (!key.startsWith(\"citation-\")) continue;\n findings.push(\n finding(\n \"entry-invalid\",\n `\"${key}\" is not a citations key; the page root reserves the citation- prefix`,\n { line: extracted.lineFor(`/${key}`) },\n ),\n );\n }\n\n const injected = opts?.citations;\n const rawCitations = data.citations;\n // The page carries a key a manifest owns (0020 across files). The page's\n // own entries are still read and checked, so the report shows what the\n // page would publish; the finding says the manifest is the one authority.\n if (opts?.owned !== undefined && rawCitations !== undefined) {\n findings.push(\n finding(\"entry-invalid\", ownedMessage(opts.owned), {\n line: extracted.lineFor(\"/citations\"),\n }),\n );\n }\n if (injected === undefined && rawCitations !== undefined && !Array.isArray(rawCitations)) {\n findings.push(\n finding(\"entry-invalid\", \"citations must be an array of entries\", {\n line: extracted.lineFor(\"/citations\"),\n }),\n );\n }\n const inputs = citationInputs(file, extracted, injected);\n\n inputs.forEach((input, index) => {\n const origin: CitationOrigin = { ...input.origin, index };\n /** Where a finding about the entry itself sits: the entry's own line, in its own file. */\n const where: FindingExtra = { index };\n if (origin.line !== undefined) where.line = origin.line;\n if (origin.kind === \"manifest\") where.file = origin.file;\n\n const entry = input.entry;\n const error = validateEntry(entry);\n if (error !== undefined || !isRecord(entry)) {\n findings.push(finding(\"entry-invalid\", error ?? \"must be object\", { ...where, ...subjectOf(entry) }));\n return;\n }\n const citation = toCitation(entry);\n const subject: FindingExtra = { ...where, src: spellSource(citation.source) };\n if (citation.id !== undefined) subject.id = citation.id;\n\n // A range that ends before it starts: the schema cannot compare two\n // numbers, so the rule is kept here.\n const bad = ([\"claim\", \"source\"] as const).find((end) => {\n const lines = end === \"claim\" ? citation.claim?.lines : citation.source.lines;\n return lines !== undefined && parseLines(lines) === undefined;\n });\n if (bad !== undefined) {\n const lines = bad === \"claim\" ? citation.claim?.lines : citation.source.lines;\n findings.push(\n finding(\n \"entry-invalid\",\n named(citation.id, `${bad}.lines \"${String(lines)}\" ends before it starts`),\n subject,\n ),\n );\n return;\n }\n\n // A range wider than the cap is refused at either end (proposal 0044).\n const wide = ([\"claim\", \"source\"] as const)\n .map((end) => {\n const lines = end === \"claim\" ? citation.claim?.lines : citation.source.lines;\n const parsed = lines === undefined ? undefined : parseLines(lines);\n const reason = parsed === undefined ? undefined : tooWide(parsed);\n return reason === undefined ? undefined : `${end}.lines \"${String(lines)}\" ${reason}`;\n })\n .find((message) => message !== undefined);\n if (wide !== undefined) {\n findings.push(finding(\"entry-invalid\", named(citation.id, wide), subject));\n return;\n }\n\n // The pin's prefix says how it was taken, and only an encrypted source\n // carries a keyed one.\n const encrypted = isEncryptedValue(citation.source.file);\n if (encrypted !== isKeyedPin(citation.source.integrity)) {\n findings.push(\n finding(\n \"entry-invalid\",\n named(\n citation.id,\n encrypted\n ? \"an encrypted source is pinned with hmac-sha256-, not sha256-.\"\n : \"a plain source is pinned with sha256-, not hmac-sha256-.\",\n ),\n subject,\n ),\n );\n return;\n }\n\n const page: PageCitation = { citation, origin };\n citations.push(page);\n if (citation.id !== undefined) {\n if (ids.has(citation.id)) {\n findings.push(finding(\"entry-invalid\", `duplicate id \"${citation.id}\"`, subject));\n } else {\n ids.add(citation.id);\n byId.set(citation.id, page);\n }\n }\n });\n\n // Markers.\n const body = content.slice(bodyOffset);\n const statements =\n opts?.markers === false ? [] : parseStatements(body, format, { offset: bodyOffset, line: bodyLine });\n if (statements.length > MAX_MARKERS_PER_PAGE) {\n findings.push(\n finding(\n \"marker-invalid\",\n `more than ${String(MAX_MARKERS_PER_PAGE)} markers on one page (${String(statements.length)}); the rest are not read`,\n { line: statements[MAX_MARKERS_PER_PAGE]?.line },\n ),\n );\n }\n\n for (const statement of statements.slice(0, MAX_MARKERS_PER_PAGE)) {\n const { payload } = statement;\n if (payload.kind === \"bad\") {\n findings.push(\n finding(\n \"marker-invalid\",\n payload.json === true ? MARKER_JSON : `invalid marker: ${payload.reason}`,\n { line: statement.line },\n ),\n );\n continue;\n }\n // Each named id reports on its own: an orphan is a well-formed id with a\n // missing entry, so each one is its own subject and its own fix. A\n // malformed marker is one line and one fix, and was handled above.\n // The entries this marker actually anchors. An id the marker repeats is\n // reported and left to the marker that won it, so it never lands here.\n const anchored: PageCitation[] = [];\n for (const id of payload.ids) {\n const target = byId.get(id);\n if (target === undefined) {\n findings.push(\n finding(\"marker-orphan\", `no entry has id \"${id}\"`, {\n line: statement.line,\n id,\n }),\n );\n continue;\n }\n const first = markedAt.get(target);\n if (first !== undefined) {\n findings.push(\n finding(\n \"marker-repeated\",\n `${id} is named by markers at lines ${String(first)} and ${String(statement.line)}; the first anchors it.`,\n {\n line: statement.line,\n id,\n index: target.origin.index,\n src: spellSource(target.citation.source),\n },\n ),\n );\n continue;\n }\n markedAt.set(target, statement.line);\n target.marker = statement;\n anchored.push(target);\n }\n // A quote entry anchors the next fenced block and a plain one anchors the\n // paragraph, so a list that mixes them would mean two spans on one line,\n // and the page would not say which id got which.\n const quoted = anchored.find((entry) => entry.citation.quote === true);\n const plain = anchored.find((entry) => entry.citation.quote !== true);\n if (quoted !== undefined && plain !== undefined) {\n const mixed: FindingExtra = {\n line: statement.line,\n index: quoted.origin.index,\n src: spellSource(quoted.citation.source),\n };\n if (quoted.citation.id !== undefined) mixed.id = quoted.citation.id;\n findings.push(\n finding(\n \"anchor-invalid\",\n named(\n quoted.citation.id,\n \"a quote entry shares a marker with a non-quote entry. Give the quote its own marker.\",\n ),\n mixed,\n ),\n );\n }\n }\n\n // Anchors: exactly one way in, and a quote needs one of them.\n for (const entry of citations) {\n const { citation, origin, marker } = entry;\n const subject: FindingExtra = { index: origin.index, src: spellSource(citation.source) };\n if (citation.id !== undefined) subject.id = citation.id;\n if (origin.line !== undefined) subject.line = origin.line;\n if (origin.kind === \"manifest\") subject.file = origin.file;\n\n if (citation.claim?.lines !== undefined && marker !== undefined) {\n // A marker names an id, so an entry with both always has one. The\n // finding sits on the marker, which is the half a reader can see.\n const onPage: FindingExtra = { index: origin.index, line: marker.line, src: subject.src };\n if (citation.id !== undefined) onPage.id = citation.id;\n findings.push(\n finding(\n \"anchor-invalid\",\n `${citation.id ?? \"the entry\"} has claim lines and a marker. Keep one.`,\n onPage,\n ),\n );\n continue;\n }\n if (citation.quote === true && citation.claim?.lines === undefined && marker === undefined) {\n findings.push(\n finding(\"anchor-invalid\", named(citation.id, \"quote needs a claim or a marker.\"), subject),\n );\n }\n }\n\n return {\n file,\n format,\n content,\n bodyOffset,\n bodyLine,\n citations,\n statements,\n findings: findings.sort(byLine),\n };\n}\n","import { ToolError } from \"../shared/errors.js\";\nimport { LineRangeError } from \"../shared/pin.js\";\n\n/** An operational or usage error in the citation tool: exit 2. */\nexport class CiteError extends ToolError {\n constructor(message: string) {\n super(message);\n this.name = \"CiteError\";\n }\n}\n\n/**\n * Run a shared pin-engine call under cite's error contract: a\n * `LineRangeError` comes back as a `CiteError` with the same message, which is\n * what cite's callers (and library users of the `cite` namespace) match on.\n */\nexport function asCiteError<T>(call: () => T): T {\n try {\n return call();\n } catch (error) {\n if (error instanceof LineRangeError) throw new CiteError(error.message);\n throw error;\n }\n}\n","/**\n * The hashing rule, as cite uses it. The engine is `src/shared/pin.ts`, shared\n * with meta; this module keeps cite's import path and its error contract: a\n * range past the end of the text throws `CiteError`.\n */\nimport {\n hashRange as sharedHashRange,\n sliceLines as sharedSliceLines,\n type LineRange,\n} from \"../../shared/pin.js\";\nimport { asCiteError } from \"../errors.js\";\n\nexport { hashLines, isKeyedPin, normalizeText, splitLines } from \"../../shared/pin.js\";\n\n/**\n * The cited lines joined by LF. Throws `CiteError` when the range runs past\n * the end of the text (`<path> has N lines; line M is out of range`).\n */\nexport function sliceLines(lines: readonly string[], range?: LineRange, label?: string): string {\n return asCiteError(() => sharedSliceLines(lines, range, label));\n}\n\n/** The pin of the cited lines: plain, or the keyed pin under `key` when one is given. Throws `CiteError` past the end. */\nexport function hashRange(text: string, range?: LineRange, key?: string): string {\n return asCiteError(() => sharedHashRange(text, range, key));\n}\n","/**\n * Source and line grammar.\n *\n * An entry's `source.file` is a path or an encrypted source (`~` and at least\n * 82 base64url characters, proposal 0045), and its `lines` an integer or\n * `\"L1-L2\"`. `FILE_PATTERN` is the schema's `$defs.fileRef.pattern`, spelled\n * once here and once in the registered `manni:citations:1.0.0` schema\n * (`src/meta/schemas/citations/1.0.0.json`); `test/cite/range.test.ts` pins\n * the two equal.\n *\n * The command line spells a source as one string, `path`, `path:L` or\n * `path:L1-L2` (`SRC_PATTERN`), and reports spell it that way too.\n */\nimport { isEncryptedValue } from \"../../shared/encryption.js\";\nimport { MAX_RANGE_LINES, lineSpec, parseLines } from \"../../shared/pin.js\";\nimport { CiteError } from \"../errors.js\";\nimport type { CitationSource, LineSpec, PageLines, SourceRange } from \"../types.js\";\n\n// The line grammar is the shared pin engine's; this path keeps cite's imports working.\nexport { lineSpec, parseLines, spellLines } from \"../../shared/pin.js\";\n\n/** The schema's `$defs.fileRef.pattern`, as a RegExp. */\nexport const FILE_PATTERN =\n /^(?:~[A-Za-z0-9_-]{82,}|(?!~)(?:(?!\\.\\.?(?:\\/|$))[^/\\\\:\\r\\n\\t]+)(?:\\/(?:(?!\\.\\.?(?:\\/|$))[^/\\\\:\\r\\n\\t]+))*)$/;\n\n/** A source on the command line: a `file`, then an optional `:L` or `:L1-L2`. */\nexport const SRC_PATTERN =\n /^(?:~[A-Za-z0-9_-]{82,}|(?!~)(?:(?!\\.\\.?(?:\\/|:|$))[^/\\\\:\\r\\n\\t]+)(?:\\/(?:(?!\\.\\.?(?:\\/|:|$))[^/\\\\:\\r\\n\\t]+))*)(?::[1-9][0-9]*(?:-[1-9][0-9]*)?)?$/;\n\n/**\n * Parse a command-line source. Throws `CiteError` on bad grammar or an end\n * line before the start. A one-line `path:L` comes back with `end` equal to\n * `start`, so a range's width is always `end - start + 1`.\n */\nexport function parseSrc(src: string): SourceRange {\n if (!SRC_PATTERN.test(src)) {\n throw new CiteError(\n `Invalid src \"${src}\": expected path, path:L, path:L1-L2, or an encrypted source (~ and at least 82 base64url characters) with the same line forms; ` +\n \"the path is repo-root-relative, posix, and free of . and .. segments.\",\n );\n }\n // The grammar keeps `:` out of every path segment and out of the base64url\n // alphabet, so the first one starts the line suffix.\n const colon = src.indexOf(\":\");\n const path = colon === -1 ? src : src.slice(0, colon);\n const range: SourceRange = { path, encrypted: isEncryptedValue(path) };\n if (colon === -1) return range;\n const [startText, endText] = src.slice(colon + 1).split(\"-\");\n const start = Number(startText);\n const end = endText === undefined ? start : Number(endText);\n if (end < start) {\n throw new CiteError(`Invalid range \"${src}\": end line ${end} is before start line ${start}.`);\n }\n range.start = start;\n range.end = end;\n return range;\n}\n\n/**\n * How a range too wide to cite is refused: `spans N lines, more than 5000`.\n * Undefined for a range of at most `MAX_RANGE_LINES` lines.\n */\nexport function tooWide(lines: PageLines): string | undefined {\n const width = lines.end - lines.start + 1;\n return width > MAX_RANGE_LINES\n ? `spans ${String(width)} lines, more than ${String(MAX_RANGE_LINES)}`\n : undefined;\n}\n\n/** The canonical spelling: `path:L` for one line, `path:L1-L2` otherwise. */\nexport function formatSrc(range: SourceRange): string {\n const { path, start, end } = range;\n if (start === undefined) return path;\n if (end === undefined || end === start) return `${path}:${start}`;\n return `${path}:${start}-${end}`;\n}\n\n/** An entry's source as a range. Its lines must already be valid (see `parseLines`). */\nexport function sourceRange(source: CitationSource): SourceRange {\n const range: SourceRange = { path: source.file, encrypted: isEncryptedValue(source.file) };\n if (source.lines === undefined) return range;\n const lines = parseLines(source.lines);\n if (lines !== undefined) {\n range.start = lines.start;\n range.end = lines.end;\n }\n return range;\n}\n\n/** An entry's source spelled as one string, `path:L1-L2`, the way the command line takes it. */\nexport function spellSource(source: CitationSource): string {\n return formatSrc(sourceRange(source));\n}\n\n/** A range's lines as an entry writes them; undefined for a whole file. */\nexport function rangeLines(range: SourceRange): LineSpec | undefined {\n if (range.start === undefined) return undefined;\n return lineSpec({ start: range.start, end: range.end ?? range.start });\n}\n","/**\n * The citation tool's types. Proposal 0044 is the record, and stress test 25\n * the shape: an entry has two ends, `claim` and `source`, each a line range\n * and a hash, and a check classifies both with one set of rules.\n *\n * Vocabulary: a page's `citations` live in its frontmatter, or in a manifest\n * a collection declares (handed in through `CheckPageOptions.citations`). A\n * body marker, `cite <id>` in the format's comment syntax, is the other way\n * to anchor an entry's claim.\n */\nimport type { ValidationResult } from \"../meta/index.js\";\nimport type { CollectionConfig } from \"../shared/collections.js\";\nimport type { ConfigFile } from \"../shared/config-file.js\";\nimport type { KeySource } from \"../shared/encryption-key.js\";\nimport type { LineSpec, PageLines } from \"../shared/pin.js\";\nimport type { Confirm } from \"../shared/prompt.js\";\nimport type { Severity } from \"../shared/severity.js\";\n\n/** A parsed `<src>` or `source`: a path or `~` ciphertext, and an optional line range. */\nexport interface SourceRange {\n /** Repo-root-relative posix path, or the `~` ciphertext when `encrypted`. */\n path: string;\n /** `true` when `path` is an encrypted source (`~` and 82+ base64url characters) rather than a path. */\n encrypted: boolean;\n /** 1-based, inclusive. Both absent for a whole-file citation. */\n start?: number;\n end?: number;\n}\n\n// The line shapes are the shared pin engine's, re-exported so cite's imports and its namespace keep them.\nexport type { LineSpec, PageLines };\n\n/** The page text a citation supports. */\nexport interface CitationClaim {\n /** Body lines: counted from the first line after the frontmatter. Absent when a marker anchors the entry. */\n lines?: LineSpec;\n /** `sha256-<64 hex>` over the claimed lines, always plain. */\n integrity: string;\n}\n\n/** The lines a claim rests on. */\nexport interface CitationSource {\n /** Root-relative posix path, or `~` and the path encrypted with the family key. */\n file: string;\n /** File lines. Absent pins the whole file. */\n lines?: LineSpec;\n /** `sha256-<64 hex>`, or `hmac-sha256-<64 hex>` exactly when `file` is encrypted. */\n integrity: string;\n /** The git commit hash the pin was taken at: 7 to 64 lowercase hex. */\n \"commit-sha\"?: string;\n}\n\n/** One entry of `citations`, as the schema (`manni:citations:1.0.0`) spells it. */\nexport interface Citation {\n id?: string;\n claim?: CitationClaim;\n source: CitationSource;\n quote?: boolean;\n}\n\n/** Where an entry is kept: the page's frontmatter, or a manifest a collection declares. */\nexport type OriginKind = \"frontmatter\" | \"manifest\";\n\n/** Where an entry sits. */\nexport interface CitationOrigin {\n kind: OriginKind;\n /** The file the entry sits in: the page for frontmatter, the manifest otherwise. */\n file: string;\n /** Line of the entry in that file. */\n line?: number;\n /** Index in the page's `citations` list. */\n index: number;\n}\n\n/**\n * One entry handed to `checkCitations` from outside the page, already merged\n * from a manifest. `entry` is the raw value and is validated as a frontmatter\n * entry is; its index is its position in the list.\n */\nexport interface CitationInput {\n entry: unknown;\n origin: { kind: OriginKind; file: string; line?: number };\n}\n\n/** How a citation's claim is anchored: by its lines, by a body marker, or not at all. */\nexport type CitationAnchor = \"claim\" | \"marker\" | null;\n\nexport type SourceStatus =\n | \"current\"\n | \"moved\"\n | \"moved-ambiguous\"\n | \"changed\"\n | \"missing\"\n | \"never-true\"\n | \"skipped\";\n\nexport type ClaimStatus =\n | \"current\"\n | \"moved\"\n | \"moved-ambiguous\"\n | \"reanchored\"\n | \"changed\"\n | \"skipped\";\n\n/** Where a misplaced marker is, and where it belongs (proposal 0054). */\nexport interface MisplacedMarkerReport {\n /** The paragraph the marker's run splits, in file lines. */\n unit: string;\n /** The file line the marker takes once its run has moved. */\n to: number;\n}\n\n/** The marker that anchors a citation: its line, and its misplacement when it has one. */\nexport interface MarkerEnd {\n /** File line of the marker. */\n line: number;\n /** Present only when the marker line sits inside a paragraph. */\n misplaced?: MisplacedMarkerReport;\n}\n\n/**\n * Why a source could not be read: not a tracked file under the root, an\n * encrypted source with no key to decrypt it, one that does not decrypt under\n * the key there is, or a tracked file that could not be read.\n */\nexport type MissingReason = \"untracked\" | \"no-key\" | \"undecryptable\" | \"unreadable\";\n\n/**\n * Every rule a finding can carry, stated once. The union and the default\n * severity table are derived from this array so the three cannot disagree.\n */\nexport const CITE_RULES = [\n \"source-moved\",\n \"source-moved-ambiguous\",\n \"source-changed\",\n \"source-never-true\",\n \"source-missing\",\n \"claim-moved\",\n \"claim-moved-ambiguous\",\n \"claim-reanchored\",\n \"claim-changed\",\n \"marker-orphan\",\n \"marker-invalid\",\n \"marker-repeated\",\n \"marker-misplaced\",\n \"anchor-invalid\",\n \"entry-invalid\",\n \"quote-drift\",\n] as const;\nexport type CiteRule = (typeof CITE_RULES)[number];\n\n/**\n * A rule's level: the family scale (`src/shared/severity.ts`) plus `off`,\n * which drops the rule's findings altogether.\n */\nexport type CiteSeverity = Severity | \"off\";\n\n/** The claim end of a classified citation. Line specs are `\"L\"` or `\"L1-L2\"`. */\nexport interface ClaimEnd {\n /** The entry's body lines. Absent when a marker anchors the claim. */\n lines?: string;\n /** Where the claim is judged, in file lines: the recorded lines, or what the marker anchors. */\n fileLines?: string;\n status: ClaimStatus;\n /** For `moved`: the new body lines. */\n newLines?: string;\n /** For `moved-ambiguous`: every candidate, in body lines. */\n candidates?: string[];\n /** For `moved`: the new file lines. Pretty-only. */\n newFileLines?: string;\n /** For `moved-ambiguous`: every candidate, in file lines. Pretty-only. */\n candidateFileLines?: string[];\n /** For `changed`: the page lines at the claim now, for `--show-diff`. Pretty-only. */\n text?: string[];\n /**\n * For `changed` and `reanchored`: the baseline, the newest commit whose\n * page held the pin. Derived from the page's history, never stored (0053).\n */\n commitSha?: string;\n /** For `changed`: `false` when the walk hit a shallow boundary or its cap. */\n historyAvailable?: boolean;\n /** Subjects of the commits that touched the page since the baseline. Pretty-only. */\n commitsSince?: string[];\n /** Unified diff of the claim's lines since the baseline. Pretty-only, `--show-diff`. */\n diff?: string;\n /**\n * The claim's lines as they were at the baseline. Never serialized: it is\n * what `update --accept` reads to tell an edit from a replacement.\n */\n baselineText?: string[];\n}\n\n/** The source end of a classified citation. */\nexport interface SourceEnd {\n /** `file` and `lines` as the entry spells them: `path`, `path:L`, `path:L1-L2`, the ciphertext for `path`. */\n src: string;\n status: SourceStatus;\n /**\n * The path an encrypted source decrypted to. Never printed except by the\n * pretty reporter under `--reveal`; never serialized to a machine format.\n */\n resolvedPath?: string;\n /**\n * The path a moved encrypted source's `newSrc` decrypts to. Printed only by\n * the pretty reporter under `--reveal`; never serialized to a machine format.\n */\n resolvedNewPath?: string;\n /** For `missing`: why the source could not be read. Composes the message, which never names a path. */\n missingReason?: MissingReason;\n /**\n * For `moved`: the new file lines. For `changed`: the span the pinned\n * range's old first and last lines now cover, when each sits exactly once\n * and in order (proposal 0055).\n */\n newLines?: string;\n /**\n * For `moved`: the new `src`, spelled as the entry spells sources. It names\n * another file when the pin followed its text across one, and carries the\n * new path encrypted for an encrypted source.\n */\n newSrc?: string;\n /** For `moved-ambiguous`: every candidate `src`. */\n candidates?: string[];\n /** The commit the pin was taken at. */\n commitSha?: string;\n /** For `changed` with a commit: whether git could show that commit. */\n historyAvailable?: boolean;\n /** Subjects of commits touching the path since the commit. Pretty-only. */\n commitsSince?: string[];\n /** Unified diff of the path since the commit. Pretty-only, `--show-diff`. */\n diff?: string;\n /** The move search hit its byte budget before covering the file. */\n truncatedSearch?: boolean;\n /**\n * The truncation happened across the files a commit touched rather than\n * inside one file, which is the other half of the notice a run raises. Never\n * serialized: `truncatedSearch` stays the one boolean the output carries.\n */\n truncatedAcross?: boolean;\n}\n\n/** One classified citation: both ends, and how it is anchored. */\nexport interface CitationResult {\n citation: Citation;\n origin: CitationOrigin;\n anchor: CitationAnchor;\n /** File line of the marker that names the entry, when one does. */\n markerLine?: number;\n /**\n * File line the citation anchors to now: the claim's first line, or the\n * first line of the text a marker anchors. Where its findings sit.\n */\n anchorLine?: number;\n /** The marker that names the entry, when one does, and its misplacement. */\n marker?: MarkerEnd;\n /** `null` for an entry with no `claim`: a bare pin, or a marker with no drift check. */\n claim: ClaimEnd | null;\n source: SourceEnd;\n}\n\nexport interface CitationFinding {\n rule: CiteRule;\n /** `manni:cite/<rule>`. */\n ruleId: string;\n /** On the family scale: an `error` fails the file; a `warning` or a `notice` is reported and never does. */\n severity: Severity;\n message: string;\n /** Line the finding anchors to: in the page, or in `file` when that is set. */\n line?: number;\n /** The manifest, when the finding sits there rather than on the page. */\n file?: string;\n id?: string;\n /** The source, spelled as the entry spells it. */\n src?: string;\n newSrc?: string;\n /** Index in the page's `citations` for a finding about one entry. */\n index?: number;\n}\n\nexport interface PageCitationReport {\n file: string;\n format: string;\n citations: CitationResult[];\n findings: CitationFinding[];\n /** Run-level advice, said once per run by the caller: shallow clone, root fallback. */\n notices: string[];\n}\n\n/** What `showFile` answers: the bytes, or why it could not. */\nexport type ShownFile = { text: string } | { missing: \"commit\" | \"path\" };\n\n/** One commit that touched a path. */\nexport interface PageCommit {\n /** The full hash. */\n sha: string;\n subject: string;\n}\n\n/**\n * What `pageCommits` answers: the commits that touched a path, newest first.\n * `shallow` is a repository whose oldest commit may have an unfetched parent,\n * and `truncated` a list the cap cut short. Either means a walk that exhausts\n * the list cannot say the pin never held.\n */\nexport interface PageHistory {\n commits: PageCommit[];\n shallow: boolean;\n truncated: boolean;\n}\n\n/**\n * A thin git client over `execFile(\"git\", …)`, memoized per call shape so a\n * page with a thousand citations against one path runs one `git show`.\n */\nexport interface GitClient {\n available(): Promise<boolean>;\n head(): Promise<string | null>;\n /** Tracked files, posix, relative to the root. */\n lsFiles(): Promise<string[]>;\n showFile(commit: string, path: string): Promise<ShownFile>;\n subjectsSince(commit: string, path: string): Promise<string[]>;\n diffSince(commit: string, path: string): Promise<string>;\n /**\n * Paths whose content differs between `commit` and `HEAD`: committed\n * changes only. The candidate list a pin is followed across (proposal\n * 0055). A client without it follows no source out of its own file.\n */\n changedSince?(commit: string): Promise<string[]>;\n /**\n * The commits that touched `path`, newest first, at most `cap` of them. A\n * client without it reads no claim history, so a claim that no longer holds\n * is `changed` with no baseline.\n */\n pageCommits?(path: string, cap: number): Promise<PageHistory>;\n /**\n * Whether the clone is shallow, so a commit it lacks may still be in the\n * history. A client without it, or without `isAncestor`, never re-records\n * a commit for being outside the history.\n */\n shallow?(): Promise<boolean>;\n /**\n * Whether `commit` is `of` or one of its ancestors. A commit the clone does\n * not have is not.\n */\n isAncestor?(commit: string, of: string): Promise<boolean>;\n}\n\n/**\n * The files a source may resolve to. Built from `git ls-files` when git is\n * available, else from a walk that follows no symlinks. Every entry is\n * realpath-contained in the root.\n */\nexport interface SourceIndex {\n files(): readonly string[];\n /** Whether `path` is a tracked file under the root. An encrypted source is decrypted first, then asked here. */\n has(path: string): boolean;\n}\n\n/**\n * What one run has read so far: each tracked file once, with its lines split\n * once. A run makes one with `createSourceCache` and passes it to every\n * `readSource` it makes, so a source cited a hundred times is read once. It\n * holds whatever the files said when first read, so a run that rewrites files\n * it may later read as sources makes none.\n */\nexport type SourceCache = Map<string, Promise<{ text: string; lines: readonly string[] } | undefined>>;\n\nexport interface CheckPageOptions {\n /** Absolute directory source paths resolve from. */\n root: string;\n /** Default true. False: page-side rules only; every source status is `skipped`. Claim ends are still judged. */\n checkSources?: boolean;\n /**\n * The encryption key: decrypts encrypted sources and keys their pins.\n * Absent, an encrypted citation is `missing` (no key to decrypt it).\n */\n key?: string;\n severity?: Partial<Record<CiteRule, CiteSeverity>>;\n /**\n * The git client the source index and history are read through. Defaults\n * to one over the root, which is used whenever git is on PATH and the root\n * is inside a work tree. A caller that wants no git at all passes `noGit()`.\n */\n gitClient?: GitClient;\n /**\n * Directory the page's own path resolves from, and so the repository its\n * history is read in. Defaults to `root`, the same directory unless\n * `--root` sent the sources to another checkout.\n */\n pageRoot?: string;\n /** The client the page's history is read through. Defaults to one over `pageRoot`. */\n pageGitClient?: GitClient;\n /** How many commits the claim history walk reads. Defaults to `MAX_PAGE_COMMITS`. */\n pageCommitCap?: number;\n sourceIndex?: SourceIndex;\n /** Source files already read this run (`createSourceCache`); absent, each citation reads its own. */\n sourceCache?: SourceCache;\n /**\n * The page's citations as merged from a manifest, each with where it sits.\n * Given, the page's own frontmatter `citations` is not read. Without it, a\n * grader calling `checkCitations` on a sidecar page would find no citations.\n */\n citations?: readonly CitationInput[];\n /**\n * The manifest that owns this page's `citations`, when one does. A page\n * that carries its own anyway is `entry-invalid`, in meta's `external:owned`\n * words.\n */\n owned?: { file: string; collection: string };\n}\n\nexport interface MintOptions {\n root: string;\n /** `path`, `path:L`, `path:L1-L2`, or an encrypted `~file` with the same line forms. */\n src: string;\n id?: string;\n /** The claim pin, already taken over the page lines. */\n claim?: CitationClaim;\n quote?: boolean;\n /** A commit to record; `false` records none; absent records HEAD when git has one. */\n commitSha?: string | false;\n /**\n * Write `file` encrypted and pin the lines with the keyed pin; needs `key`.\n * A source that is already encrypted stays encrypted either way.\n */\n encrypt?: boolean;\n /** The encryption key: encrypts, decrypts an encrypted source, and keys its pin. */\n key?: string;\n gitClient?: GitClient;\n sourceIndex?: SourceIndex;\n}\n\n/**\n * One replacement against a fixed text: the bytes `[start, end)` become\n * `text`. A writer that has a position from a parse hands back an edit rather\n * than a rewritten page, so several of them can be collected against that one\n * parse and applied together (`applyEdits`). Nothing is then read out of a\n * text that has already been written to.\n */\nexport interface TextEdit {\n start: number;\n end: number;\n text: string;\n}\n\n/** One marker found in a page body: `cite <id>` in the format's comment syntax. */\nexport interface InlineStatement {\n /** File line of the marker. */\n line: number;\n /** File line of the anchored text: the rest of the marker's line, else the paragraph or block that follows. */\n anchorLine?: number;\n /**\n * The ids the marker names, in the order written, or why the marker is not\n * read. A payload of one id is what proposal 0044 shipped; 0056 widened it\n * to a space-separated list, and every id in the list anchors the same text.\n */\n payload:\n | { kind: \"ref\"; ids: [string, ...string[]] }\n | { kind: \"bad\"; reason: string; json?: boolean };\n /** The marker text between the delimiters, trimmed. */\n raw: string;\n /** Character offsets of the whole marker in the file, for rewriting. */\n start: number;\n end: number;\n}\n\n/** A validated citation with where it sits and the marker that names it, before classification. */\nexport interface PageCitation {\n citation: Citation;\n origin: CitationOrigin;\n /** The first marker naming the entry's id. */\n marker?: InlineStatement;\n}\n\n/** What `readPage` returns: the entries, the markers, and page-side findings. */\nexport interface PageCitations {\n file: string;\n format: string;\n content: string;\n /** Offset where the body starts (just past the closing fence, or 0). */\n bodyOffset: number;\n /** File line of body line 1: the first line after the frontmatter, or 1. */\n bodyLine: number;\n citations: PageCitation[];\n statements: InlineStatement[];\n findings: CitationFinding[];\n}\n\nexport interface CheckOptions {\n /** Positional inputs; empty means the configured collections' `paths:` (0041). */\n inputs: string[];\n /**\n * `--collection <name>`, repeatable: the collections this run covers. Empty\n * or absent means every declared one; see `selectCollections`.\n */\n collection?: string[];\n exts?: string[];\n /** `--exclude`, appended to the selected collections' `exclude:` when the run reads from them. */\n exclude?: string[];\n as?: string;\n configPath?: string;\n noConfig?: boolean;\n cwd?: string;\n stdinContent?: string;\n allowEmpty?: boolean;\n respectGitignore?: boolean;\n onNotice?: (message: string) => void;\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n baseline?: string | boolean;\n writeBaseline?: string | boolean;\n /** `--no-check-sources` (false): page-side rules only. Absent leaves `checkSources:` in charge. */\n checkSources?: boolean;\n /**\n * `--show-diff`: the report will want diffs and commit subjects. The check\n * is the same either way; the run only says, once, when git is not there\n * to give them.\n */\n showDiff?: boolean;\n root?: string;\n /**\n * The git client every page is checked through. Defaults to one over the\n * root, used whenever git is on PATH and the root is inside a work tree; a\n * test hands in a fake.\n */\n gitClient?: GitClient;\n /**\n * The client every page's own history is read through, over the directory\n * the page paths resolve from. Defaults to one there; a test hands in a fake.\n */\n pageGitClient?: GitClient;\n /** Defaults to `process.env`; read for `MANNI_ENCRYPTION_KEY`. A test hands in its own. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface CheckRun {\n results: ValidationResult[];\n summary: import(\"../meta/index.js\").RunSummary;\n frame: import(\"../meta/index.js\").FingerprintContext;\n pages: PageCitationReport[];\n warnings: number;\n notices: number;\n}\n\n\nexport interface AddOptions {\n page: string;\n /** The claim's lines as an editor numbers them (`page:L`, `page:L1-L2`). Absent: a bare pin. */\n pageLines?: PageLines;\n src: string;\n id?: string;\n /** Write a marker above the page lines instead of claim lines, and pin the text it anchors. Needs `pageLines` and `id`. */\n marker?: boolean;\n /** The page lines are a fenced block that reproduces the source. Needs `pageLines`. */\n quote?: boolean;\n /**\n * `--encrypt`: write `file` encrypted, with a keyed pin, even when no key is\n * available yet, in which case `confirm` is asked whether to generate one.\n * An available key encrypts every add without it, so `false` and absent\n * are the same.\n */\n encrypt?: boolean;\n /** `false` records no `commit-sha`. */\n commitSha?: boolean;\n dryRun?: boolean;\n as?: string;\n configPath?: string;\n noConfig?: boolean;\n cwd?: string;\n stdinContent?: string;\n root?: string;\n /**\n * The git client HEAD and the source index are read through. Defaults to\n * one over the root; a test hands in a fake.\n */\n gitClient?: GitClient;\n /** Defaults to `process.env`; read for `MANNI_ENCRYPTION_KEY`. A test hands in its own. */\n env?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n /**\n * Asked when `encrypt` needs a key and there is none; the CLI passes\n * `terminalConfirm()`. Absent, the add refuses instead of asking.\n */\n confirm?: Confirm;\n}\n\n/** The manifest half of a write: what it says now, and whether it was saved. */\nexport interface ManifestWrite {\n /** The manifest as the run reports it. */\n file: string;\n /** 1-based line the written entry now sits on. */\n line: number;\n /** The whole manifest, rewritten. */\n content: string;\n diff: string;\n written: boolean;\n}\n\nexport interface AddResult {\n file: string;\n citation: Citation;\n /**\n * Where the entry was written: the page's frontmatter, or the manifest the\n * page's collection declares. The config decides, not a flag.\n */\n placed: \"frontmatter\" | \"manifest\";\n /** The manifest the entry went to, under `placed: \"manifest\"`. */\n manifest?: ManifestWrite;\n /** File lines of the claim after the write: the claim lines, or the text the marker anchors. */\n claimLines?: PageLines;\n /** File line of the marker that names the entry, under `marker`. */\n markerLine?: number;\n /**\n * Whether the id joined the list of a marker already there, rather than\n * getting a marker line of its own. A join moves no line on the page.\n */\n markerJoined?: boolean;\n /**\n * The first pinned source line, as the file holds it, so a mis-typed range\n * is visible at write time. Absent for a whole file, which has no first\n * line worth naming, and for an encrypted source, which no output reveals.\n */\n sourceLine?: string;\n /** The rewritten page. */\n content: string;\n diff: string;\n written: boolean;\n}\n\nexport interface UpdateOptions extends Omit<CheckOptions, \"baseline\" | \"writeBaseline\"> {\n accept?: boolean;\n only?: string[];\n dryRun?: boolean;\n /**\n * Re-record `commit-sha` where the recorded commit does not contain the\n * pinned lines. Rewrites that one field and nothing else.\n */\n recommit?: boolean;\n}\n\n/**\n * One end `update` rewrote.\n *\n * Read the typed pairs: `fromLines`/`toLines` for lines, `fromPin`/`toPin`\n * for pins. `from` and `to` carry the same values, but which of the two a row\n * holds depends on the row, so they are easy to misuse.\n */\n/** Why `--recommit` re-recorded a commit. */\nexport type RecommitReason = \"not-contained\" | \"not-in-history\";\n\nexport interface UpdateRewrite {\n id?: string;\n /** Index in the page's `citations`. */\n index: number;\n /** File line of the entry. */\n line?: number;\n end: \"claim\" | \"source\" | \"marker\";\n /**\n * `re-anchored` covers both re-pins: the pin held over another span (0054),\n * and the words held while the anchor moved (0053). `status` is what tells\n * those apart. `replaced` is a claim `--accept` refused to re-pin.\n */\n reason: \"moved\" | \"accepted\" | \"re-anchored\" | \"shifted\" | \"replaced\" | \"recommitted\";\n /**\n * The status that was repaired. `replaced` is a claim whose line held\n * wholly other text, accepted because `--only` named it.\n */\n status: \"moved\" | \"changed\" | \"reanchored\" | \"replaced\" | \"never-true\" | \"misplaced\" | \"current\";\n /**\n * Before and after. A moved or shifted claim, and a moved marker: file\n * lines. A moved source: its `src`. An accepted or re-anchored end: its pin.\n * The typed pairs below say which, so prefer them.\n */\n from: string;\n to: string;\n /** A moved marker, or a moved or shifted claim: its file lines, before and after. */\n fromLines?: string;\n toLines?: string;\n /** An accepted or re-anchored end: its pin, before and after. */\n fromPin?: string;\n toPin?: string;\n /** A re-committed source: the commit it recorded, and the one it records now. */\n fromCommit?: string;\n toCommit?: string;\n /**\n * A re-committed source: why the recorded commit could not stand.\n * `not-contained`: it does not hold the pinned lines. `not-in-history`: it\n * holds them, but is not in the history of HEAD.\n */\n because?: RecommitReason;\n /** A re-anchored claim: the span that held, in file lines. */\n lines?: string;\n /** A re-anchored claim: the span now pinned, in file lines. */\n newLines?: string;\n /** An accepted, re-anchored or refused claim: the file line of its first body line. */\n at?: number;\n /** An accepted claim a marker anchors: the marker's file line. Pretty-only. */\n markerLine?: number;\n /** An accepted claim: the text now pinned, whitespace collapsed. */\n text?: string;\n /** An accepted source: its `src`. */\n src?: string;\n /**\n * An accepted source: the commit recorded in the entry, when it records\n * one. A re-anchored or refused claim: the baseline its words were read\n * against.\n */\n commitSha?: string;\n /**\n * A refused claim the word-overlap test refused: the share of the claim's\n * words, from 0 to 1, that its line still holds. Absent on a claim the\n * sentence test refused, which is how a report tells the two apart.\n */\n wordShare?: number;\n}\n\nexport interface UpdatePage {\n file: string;\n rewritten: UpdateRewrite[];\n /**\n * Claims `--accept` refused to re-pin, because the stored first line now\n * holds text that shares no sentence with the claim at its baseline, or too\n * few of its words. Each carries `reason: \"replaced\"`, and each is work left\n * undone: exit 1.\n */\n refused: UpdateRewrite[];\n skipped: CitationFinding[];\n diff: string;\n written: boolean;\n /** The rewritten page, for the stdin input only: it has no file to be written to. */\n content?: string;\n}\n\n/** One manifest a run rewrote: what changed in it, and whether it was saved. */\nexport interface ManifestChange {\n /** The manifest as the run reports it. */\n file: string;\n diff: string;\n written: boolean;\n}\n\nexport interface UpdateRun {\n pages: UpdatePage[];\n rewritten: number;\n skipped: number;\n /** The manifests this run rewrote, each written once however many pages it holds. */\n manifests?: ManifestChange[];\n exitCode: 0 | 1;\n}\n\nexport interface RemoveOptions\n extends Omit<CheckOptions, \"baseline\" | \"writeBaseline\" | \"checkSources\" | \"showDiff\"> {\n /**\n * `--only <id>`, repeatable: the entries to remove, each named by its id or\n * by its pointer (`/citations/N`). Required, and an empty list is a\n * refusal: `remove` never takes every citation off a page.\n */\n only: string[];\n dryRun?: boolean;\n}\n\n/** One entry `remove` took out, and the markers that went with it. */\nexport interface Removal {\n id?: string;\n /** Index in the page's `citations`. Absent for a marker that named no entry. */\n index?: number;\n /** Where the entry was kept. Absent for a marker that named no entry. */\n origin?: { kind: OriginKind; file: string; line?: number };\n /** File lines of the markers removed with it, in the page as it was. */\n markerLines: number[];\n}\n\nexport interface RemovePage {\n file: string;\n removed: Removal[];\n diff: string;\n written: boolean;\n /** The rewritten page, for the stdin input only: it has no file to be written to. */\n content?: string;\n}\n\nexport interface RemoveRun {\n pages: RemovePage[];\n /** Entries removed, counting a marker that named no entry as one. */\n removed: number;\n /** The manifests this run rewrote, each written once however many pages it holds. */\n manifests?: ManifestChange[];\n}\n\n/** One citation `reencryptCitations` rewrote: where it sits, and its source before and after. */\nexport interface ReencryptedCitation {\n id?: string;\n /** Index in `citations`. */\n index?: number;\n /** File line of the entry. */\n line?: number;\n /** `file` and its line suffix, under the old key and under the new. */\n from: string;\n to: string;\n}\n\n/** What `reencryptCitations` did to one page. */\nexport interface ReencryptCitationsResult {\n /** The page with every re-encrypted citation rewritten; the input itself when none was. */\n content: string;\n rewritten: ReencryptedCitation[];\n /** Citations that could not be re-encrypted, and why. A message never names a path. */\n skipped: { id?: string; index?: number; line?: number; message: string }[];\n}\n\n/**\n * Config under `cite:` in manni.config.yaml, camelCase as `meta:` is. The\n * document set is not here: proposal 0041 moved `paths` and `exclude` to the\n * family-level `collections:` list, which every tool reads. Neither is the\n * encryption key, the top-level `encryptionKey:` every tool reads (0045).\n */\nexport interface CiteConfig {\n allowEmpty?: boolean;\n respectGitignore?: boolean;\n root?: string;\n baseline?: string;\n checkSources?: boolean;\n severity?: Partial<Record<CiteRule, CiteSeverity>>;\n}\n\nexport interface LoadedCiteConfig {\n config: CiteConfig;\n path: string;\n dir: string;\n /** The file as the user would name it, for messages. */\n source: string;\n /** The family file's top-level `collections:`, parsed by the shared layer (0041). */\n collections: CollectionConfig[];\n}\n\n/** What every command core resolves before touching a file. */\nexport interface CiteRun {\n config: CiteConfig | null;\n /** The positional inputs, or the selected collections' `paths:` in declaration order. */\n inputs: string[];\n /** Directory the inputs resolve from: cwd for positional inputs, the config directory for collections. */\n base: string;\n /**\n * The collections this run covers: every declared one, or the ones\n * `--collection` named. `[]` when no config governs the run.\n */\n collections: CollectionConfig[];\n /** Whether `inputs` came from the collections rather than the command line. */\n fromCollections: boolean;\n configDir?: string;\n configPath?: string;\n /** The config file as the user would name it, for messages. */\n configSource?: string;\n /** Absolute root source paths resolve from. */\n root: string;\n /**\n * The family encryption key (proposal 0045): `MANNI_ENCRYPTION_KEY`, else\n * the config's top-level `encryptionKey:`. Absent when neither is set.\n */\n key?: string;\n /** Where `key` came from. The environment wins over the config. */\n keySource: KeySource;\n /**\n * The config file that governs the run, as discovery found it. A write\n * that needs a key (`add --encrypt` with none) puts one there.\n */\n configFile?: ConfigFile;\n}\n","/**\n * Which rules fail a run, which only warn, and which are notices.\n *\n * Warnings and notices never affect the exit code (docevals' rule).\n * `source-moved` is a warning because `cite update` fixes it mechanically,\n * and a PR job that failed on it would make the ratchet noisy. `claim-moved`\n * is a notice: the text is found verbatim, so nothing drifted.\n * `claim-reanchored` is a notice for the same reason (proposal 0053): the\n * words the pin covered are found unchanged since the baseline, so only the\n * layout moved, and plain `cite update` re-pins it.\n * `claim-changed` is a warning, since it fires on any edit to a pinned\n * paragraph, a typo fix beside the cited sentence included. A repository that\n * wants either hard sets `severity: { claim-changed: error }`.\n * `marker-misplaced` is a warning too: it annotates the pull request, and one\n * `cite update` clears it, so a job that failed on it would block on a page\n * that was fine yesterday (proposal 0054, stress test 15).\n */\nimport { CITE_RULES, type CiteRule, type CiteSeverity } from \"../types.js\";\n\nexport const DEFAULT_SEVERITY: Readonly<Record<CiteRule, CiteSeverity>> = {\n \"source-moved\": \"warning\",\n \"source-moved-ambiguous\": \"error\",\n \"source-changed\": \"error\",\n \"source-never-true\": \"error\",\n \"source-missing\": \"error\",\n \"claim-moved\": \"notice\",\n \"claim-moved-ambiguous\": \"warning\",\n \"claim-reanchored\": \"notice\",\n \"claim-changed\": \"warning\",\n \"marker-orphan\": \"error\",\n \"marker-invalid\": \"error\",\n \"marker-repeated\": \"warning\",\n \"marker-misplaced\": \"warning\",\n \"anchor-invalid\": \"error\",\n \"entry-invalid\": \"error\",\n \"quote-drift\": \"error\",\n};\n\nexport const RULE_ID_PREFIX = \"manni:cite\";\n\n/** `manni:cite/<rule>`: the SARIF rule id and the JUnit failure type. */\nexport function ruleId(rule: CiteRule): string {\n return `${RULE_ID_PREFIX}/${rule}`;\n}\n\nexport function isCiteRule(value: string): value is CiteRule {\n return (CITE_RULES as readonly string[]).includes(value);\n}\n\n/** The effective severity table: defaults with the config's overrides. */\nexport function resolveSeverity(\n overrides?: Partial<Record<CiteRule, CiteSeverity>>,\n): Record<CiteRule, CiteSeverity> {\n return { ...DEFAULT_SEVERITY, ...(overrides ?? {}) };\n}\n","/**\n * Markers: `cite <id> [<id>…]` inside the format's comment syntax.\n *\n * | Format | Forms |\n * |---------------|------------------------------------------------------------------------------------|\n * | markdown, mdx | `<!-- cite a b -->`, `{/* cite a b *\\/}`, `[comment]: # (cite a b)` |\n * | html, xml | `<!-- cite a b -->` |\n * | asciidoc | `// (cite a b)` |\n * | rst | `.. (cite a b)` |\n *\n * A marker carries one or more ids, separated by spaces, and nothing else\n * (`cite true` names id `true`). Every id in the list anchors the text the\n * marker anchors, so a paragraph resting on four sources carries one comment\n * rather than four (proposal 0056). An id cannot hold `(`, `)`, `{`, `}`, `<`,\n * `>` or a leading `-`, so no list can close a parenthesised comment early.\n *\n * A word starting with `{` was the inline entry of proposal 0044's first\n * draft; it is `marker-invalid` now, because an entry lives in frontmatter or\n * a manifest and a source is never written into the body. The scanner uses\n * `indexOf` for the delimiters, never a regex over the whole page, and is\n * handed the body only, so a `cite` inside frontmatter is never matched.\n *\n * It does not read code. An opener inside a fenced block or a backtick span,\n * as `src/shared/code-regions.ts` finds them, is skipped, so a page that\n * documents the syntax carries no statements.\n *\n * Every form parses in both markdown and mdx. The first form is what\n * `formatStatement` writes: the html comment for markdown, the jsx comment\n * for mdx, which rejects an html comment.\n */\nimport type { InlineStatement, TextEdit } from \"../types.js\";\nimport { codeEndAt, codeRegions } from \"../../shared/code-regions.js\";\nimport { CiteError } from \"../errors.js\";\n\nexport interface StatementForm {\n open: string;\n close: string;\n}\n\nconst HTML_COMMENT: StatementForm = { open: \"<!--\", close: \"-->\" };\nconst JSX_COMMENT: StatementForm = { open: \"{/*\", close: \"*/}\" };\nconst LINK_REFERENCE: StatementForm = { open: \"[comment]: # (\", close: \")\" };\nconst MARKDOWN_FORMS: readonly StatementForm[] = [HTML_COMMENT, JSX_COMMENT, LINK_REFERENCE];\nconst MDX_FORMS: readonly StatementForm[] = [JSX_COMMENT, HTML_COMMENT, LINK_REFERENCE];\nconst COMMENT_FORMS: readonly StatementForm[] = [{ open: \"<!--\", close: \"-->\" }];\nconst ASCIIDOC_FORMS: readonly StatementForm[] = [{ open: \"// (\", close: \")\" }];\nconst RST_FORMS: readonly StatementForm[] = [{ open: \".. (\", close: \")\" }];\n\nconst ID = /^[a-z0-9][a-z0-9-]*$/;\n\n/**\n * A fence opener in any format. `paragraphAfter` reads it whether or not it\n * was given a format, and so does the claim search (`claims.ts`), so every\n * family's opener counts; the per-format locators below are what `quote`\n * anchoring uses.\n * A markdown fence keeps its meaning when indented, as inside a list item, so\n * it is read there too, matching the code skip in `src/shared/code-regions.ts`; the\n * asciidoc `----` stays at column 0.\n */\nexport const ANY_FENCE = /^(?:[ \\t]*(?:`{3,}|~{3,})|-{4,})/;\nconst MARKDOWN_FENCE = /^(`{3,}|~{3,})/;\nconst ASCIIDOC_FENCE = /^(-{4,})/;\n/** The forms per extractor name. Unknown formats have none. */\nexport function statementForms(format: string): readonly StatementForm[] {\n switch (format) {\n case \"markdown\":\n return MARKDOWN_FORMS;\n case \"mdx\":\n return MDX_FORMS;\n case \"html\":\n case \"xml\":\n return COMMENT_FORMS;\n case \"asciidoc\":\n return ASCIIDOC_FORMS;\n case \"rst\":\n return RST_FORMS;\n default:\n return [];\n }\n}\n\n/** The fence-opener pattern for a format, or undefined when it has no locator. */\nfunction fenceFor(format: string): RegExp | undefined {\n switch (format) {\n case \"markdown\":\n case \"mdx\":\n return MARKDOWN_FENCE;\n case \"asciidoc\":\n return ASCIIDOC_FENCE;\n default:\n return undefined;\n }\n}\n\n/** Offset of the terminator of the line containing `pos` (or the text length). */\nfunction lineEnd(content: string, pos: number): number {\n const nl = content.indexOf(\"\\n\", pos);\n return nl === -1 ? content.length : nl;\n}\n\n/** The text of the line `[pos, end)`, without a trailing CR. */\nfunction lineText(content: string, pos: number, end: number): string {\n const text = content.slice(pos, end);\n return text.endsWith(\"\\r\") ? text.slice(0, -1) : text;\n}\n\n/** `end` pulled back over a CR so a CRLF terminator is excluded whole. */\nfunction beforeCr(content: string, end: number): number {\n return end > 0 && content.charCodeAt(end - 1) === 13 ? end - 1 : end;\n}\n\n/**\n * Where a forward scan of whole lines starts from `offset`: the next line when\n * `offset` sits inside one (the rest of a statement's own line), the line\n * itself when `offset` is already at its start.\n */\nfunction nextLineStart(content: string, offset: number): number {\n if (offset === 0 || content.charCodeAt(offset - 1) === 10) return offset;\n return lineEnd(content, offset) + 1;\n}\n\n/**\n * A marker holds at most 25 ids. Past that it is `marker-invalid`, on the\n * same grounds as the 500 markers a page may hold: an unbounded list is an\n * unbounded allocation from a file the tool did not write. The cap counts\n * ids rather than characters, because a character cap moves with how long\n * the ids happen to be. Proposal 0056 is the record.\n */\nexport const MAX_IDS_PER_MARKER = 25;\n\n/**\n * The payload, as one or more ids separated by spaces. The checks run in a\n * stated order and the first defect is the finding, so one marker raises one\n * `marker-invalid` however many of its words are wrong.\n *\n * A run of spaces separates as one, which is what a soft-wrapped editor and a\n * shell both do. A comma is never a separator: `cite a,b` is one token, that\n * token is not an id, and the message quotes it back (proposal 0034's one\n * separator per list).\n */\nfunction payloadOf(payload: string): InlineStatement[\"payload\"] {\n if (payload === \"\") return { kind: \"bad\", reason: \"empty payload\" };\n // A marker is one line: `isMarkerLine` tests one line, so half a wrapped\n // marker would land inside the pin of the text below it.\n if (/[\\r\\n]/.test(payload)) {\n return { kind: \"bad\", reason: \"a marker is one line; write two markers\" };\n }\n // Destructured rather than cast: a `ref` carries at least one id, and the\n // head is what proves it to the compiler.\n const [head, ...rest] = payload.split(\" \").filter((word) => word !== \"\");\n if (head === undefined) return { kind: \"bad\", reason: \"empty payload\" };\n const words: [string, ...string[]] = [head, ...rest];\n // An entry lives in frontmatter or a manifest; the body carries names. This\n // runs before the id test so the more actionable message fires.\n if (words.some((word) => word.startsWith(\"{\"))) {\n return { kind: \"bad\", reason: \"a JSON payload\", json: true };\n }\n if (words.length > MAX_IDS_PER_MARKER) {\n return {\n kind: \"bad\",\n reason: `more than ${String(MAX_IDS_PER_MARKER)} ids in one marker (${String(words.length)}); write a second marker`,\n };\n }\n const bad = words.find((word) => !ID.test(word));\n if (bad !== undefined) return { kind: \"bad\", reason: `\"${bad}\" is not an id` };\n const seen = new Set<string>();\n for (const word of words) {\n // One id named twice inside one marker has no second anchor and no winner\n // to pick, so it is a typo in one line rather than `marker-repeated`.\n if (seen.has(word)) {\n return { kind: \"bad\", reason: `\"${word}\" is named twice in one marker` };\n }\n seen.add(word);\n }\n return { kind: \"ref\", ids: words };\n}\n\n/** Whether the text between a form's delimiters is a cite statement. */\nfunction isCite(inner: string): boolean {\n return inner === \"cite\" || (inner.startsWith(\"cite\") && /\\s/.test(inner.charAt(4)));\n}\n\n/**\n * Whether a line holds one marker and nothing else, in any of the format's\n * forms. Such a line is never text a marker anchors: markers stacked above a\n * paragraph all anchor the paragraph, and none of them is part of its pin.\n */\nexport function isMarkerLine(line: string, format: string): boolean {\n const text = line.trim();\n return statementForms(format).some((form) => {\n if (text.length < form.open.length + form.close.length) return false;\n if (!text.startsWith(form.open)) return false;\n // The first close is the last thing on the line, so there is one marker.\n const closeAt = text.indexOf(form.close, form.open.length);\n if (closeAt !== text.length - form.close.length) return false;\n return isCite(text.slice(form.open.length, closeAt).trim());\n });\n}\n\n/** An ATX heading: one to six `#`, then a space or the end of the line. */\nconst ATX_HEADING = /^[ \\t]*#{1,6}([ \\t]|$)/;\n/** An asciidoc section title: one to six `=`, then a space. */\nconst ASCIIDOC_TITLE = /^={1,6}[ \\t]/;\n/**\n * A line of one punctuation character repeated three or more times, spaces\n * allowed: a setext underline, a thematic break, an rst adornment.\n */\nconst ADORNMENT = /^[ \\t]*([-=~^\"'*+#_:<>])(?:[ \\t]*\\1){2,}[ \\t]*$/;\n/** A line that is nothing but one JSX or HTML tag, opening or closing. */\nconst TAG_LINE = /^[ \\t]*<\\/?[A-Za-z][^<>]*>[ \\t]*$/;\n/** A table row: a line whose first non-blank character is a pipe. */\nconst TABLE_ROW = /^[ \\t]*\\|/;\n\n/**\n * Whether a line is a table row. The header row, the `|---|---|` delimiter row\n * and every body row all open with a pipe, so all three read as rows. Leading\n * whitespace is allowed, because a table inside a list item is indented and is\n * still a table.\n *\n * A row is a statement of its own. Nothing else separates one row from the\n * next, so a paragraph walk reads a whole table as one paragraph, and a claim\n * pinned to one row would re-pin every row below it. `unitAt` in\n * `src/cite/core/claims.ts` reads this instead, so a re-pin covers the row and\n * no more.\n */\nexport function isTableRow(line: string): boolean {\n return TABLE_ROW.test(line);\n}\n\n/**\n * A table rule: the `|---|---|` line under a header row. The outer pipes are\n * optional, a cell may carry `:` alignment markers, and the whole line may be\n * indented, because a table inside a list item is indented and is still a\n * table.\n *\n * A rule has to carry at least one `|`, which is what the leading lookahead\n * asks. Every pipe in the pattern that follows is optional on its own, so\n * without that test a bare `---` reads as a table rule, and `---` is a\n * thematic break or the underline of a setext heading instead.\n *\n * `isTableRow` reads a rule as a row, deliberately: a claim over a header, its\n * rule and a body row covers three rows and keeps all three. This asks the\n * narrower question `unitAt` needs, which is whether a span holds nothing but\n * rules. Such a span carries no words, so `update --accept` refuses to pin one.\n */\nconst TABLE_RULE = /^(?=[^|]*\\|)[ \\t]*\\|?(?:[ \\t]*:?-+:?[ \\t]*\\|)*[ \\t]*:?-+:?[ \\t]*\\|?[ \\t]*$/;\n\nexport function isTableSeparator(line: string): boolean {\n return TABLE_RULE.test(line);\n}\n\n/**\n * Whether a line is a **bound line**: one the format makes a block on its own,\n * so no paragraph continues across it, and no pin covers it beside text.\n *\n * | Format | Bound lines |\n * |---|---|\n * | markdown, mdx | An ATX heading, and a line that is nothing but one tag. |\n * | html, xml | A line that is nothing but one tag. |\n * | asciidoc | A section title. |\n * | every format | An adornment: one of `-=~^\"'*+#_:<>` repeated three or more times. |\n *\n * Proposal 0054 is the record, and its stress tests 8 and 9 say why a heading\n * and a tag line are read this way rather than joined to the text below them.\n * A tag line is bound in every format that reads one, so a marker under\n * `<body>` anchors the element below it and never the whole document.\n */\nexport function isBoundLine(line: string, format: string): boolean {\n if (ADORNMENT.test(line)) return true;\n switch (format) {\n case \"markdown\":\n case \"mdx\":\n return ATX_HEADING.test(line) || TAG_LINE.test(line);\n case \"html\":\n case \"xml\":\n return TAG_LINE.test(line);\n case \"asciidoc\":\n return ASCIIDOC_TITLE.test(line);\n default:\n return false;\n }\n}\n\n/** Scan a body for statements. `from` maps body offsets/lines to file offsets/lines. */\nexport function parseStatements(\n body: string,\n format: string,\n from: { offset: number; line: number } = { offset: 0, line: 1 },\n): InlineStatement[] {\n const out: InlineStatement[] = [];\n const fileLine = (bodyOffset: number): number =>\n from.line + lineAt(body, bodyOffset) - 1;\n const code = codeRegions(body, format);\n\n for (const form of statementForms(format)) {\n let cursor = 0;\n for (;;) {\n const at = body.indexOf(form.open, cursor);\n if (at === -1) break;\n const codeEnd = codeEndAt(code, at);\n if (codeEnd !== undefined) {\n cursor = codeEnd;\n continue;\n }\n const closeAt = body.indexOf(form.close, at + form.open.length);\n if (closeAt === -1) break;\n const end = closeAt + form.close.length;\n cursor = end;\n const inner = body.slice(at + form.open.length, closeAt).trim();\n if (!isCite(inner)) continue;\n const payload = inner.slice(4).trim();\n\n // Anchor: the rest of the close delimiter's line when it carries text,\n // else the paragraph or fenced block that follows.\n const unit = anchoredLines(body, end, format);\n const anchorLine = unit === undefined ? undefined : from.line + unit.start - 1;\n\n const statement: InlineStatement = {\n line: fileLine(at),\n payload: payloadOf(payload),\n raw: inner,\n start: from.offset + at,\n end: from.offset + end,\n };\n if (anchorLine !== undefined) statement.anchorLine = anchorLine;\n out.push(statement);\n }\n }\n return out.sort((a, b) => a.start - b.start);\n}\n\n/** `\"\\r\\n\"` when the text's first line break is CRLF, else `\"\\n\"`. */\nexport function detectEol(text: string): \"\\n\" | \"\\r\\n\" {\n const nl = text.indexOf(\"\\n\");\n return nl > 0 && text.charCodeAt(nl - 1) === 13 ? \"\\r\\n\" : \"\\n\";\n}\n\n/**\n * The paragraph starting at or after `offset` (skipping blank lines), as\n * `{ start, end, line }` file offsets and the file line it starts on. A fence\n * opener ends the search with no paragraph. Given a `format`, a marker-only\n * line is skipped like a blank one before the paragraph, and ends it after,\n * and a bound line is a unit of one line: it ends a paragraph above it, and\n * stands alone when it is the first line found.\n */\nexport function paragraphAfter(\n content: string,\n offset: number,\n format?: string,\n): { start: number; end: number; line: number } | undefined {\n const marker = (text: string): boolean => format !== undefined && isMarkerLine(text, format);\n const bound = (text: string): boolean => format !== undefined && isBoundLine(text, format);\n let pos = offset;\n // The rest of the line `offset` sits in counts when it carries text; when\n // it is blank the paragraph can only start on a later line.\n const firstEnd = lineEnd(content, pos);\n if (lineText(content, pos, firstEnd).trim() === \"\") pos = firstEnd + 1;\n\n while (pos < content.length) {\n const end = lineEnd(content, pos);\n const text = lineText(content, pos, end);\n if (text.trim() === \"\" || marker(text)) {\n pos = end + 1;\n continue;\n }\n if (ANY_FENCE.test(text)) return undefined;\n const start = pos;\n let last = end;\n // A bound line is its own unit, so it never gathers the lines under it.\n let cursor = bound(text) ? content.length : end + 1;\n while (cursor < content.length) {\n const e = lineEnd(content, cursor);\n const t = lineText(content, cursor, e);\n if (t.trim() === \"\" || ANY_FENCE.test(t) || marker(t) || bound(t)) break;\n last = e;\n cursor = e + 1;\n }\n return { start, end: beforeCr(content, last), line: lineAt(content, start) };\n }\n return undefined;\n}\n\ninterface FencedBlock {\n start: number;\n end: number;\n line: number;\n text: string;\n}\n\n/**\n * The next fenced block whose opener matches `fence`, scanning whole lines\n * from `offset`, plus the offset just past its closing fence line so a caller\n * can continue.\n */\nfunction scanFence(\n content: string,\n offset: number,\n fence: RegExp,\n): (FencedBlock & { next: number }) | undefined {\n let pos = nextLineStart(content, offset);\n while (pos < content.length) {\n const end = lineEnd(content, pos);\n const text = lineText(content, pos, end);\n const opener = fence.exec(text)?.[1];\n if (opener === undefined) {\n pos = end + 1;\n continue;\n }\n const line = lineAt(content, pos);\n const start = Math.min(end + 1, content.length);\n let cursor = start;\n while (cursor < content.length) {\n const e = lineEnd(content, cursor);\n if (lineText(content, cursor, e).startsWith(opener)) {\n return {\n start,\n end: cursor,\n line,\n text: content.slice(start, cursor),\n next: e + 1,\n };\n }\n cursor = e + 1;\n }\n return undefined;\n }\n return undefined;\n}\n\n/**\n * The fenced code block starting at or after `offset` (``` or ~~~; asciidoc\n * `----`), as file offsets of its content lines and the file line of the\n * opening fence. `undefined` for formats with no locator (html, xml, rst).\n */\nexport function fencedBlockAfter(\n content: string,\n offset: number,\n format: string,\n): { start: number; end: number; line: number; text: string } | undefined {\n const fence = fenceFor(format);\n if (!fence) return undefined;\n const block = scanFence(content, offset, fence);\n if (!block) return undefined;\n const { start, end, line, text } = block;\n return { start, end, line, text };\n}\n\n/**\n * The fenced block at `offset` in any fence family, for callers that have no\n * format at hand (the claim search). Undefined when the line at `offset` is\n * not an opener.\n */\nexport function fencedBlockAt(\n content: string,\n offset: number,\n): { start: number; end: number; line: number; text: string } | undefined {\n const pos = nextLineStart(content, offset);\n if (!ANY_FENCE.test(lineText(content, pos, lineEnd(content, pos)))) {\n return undefined;\n }\n const block =\n scanFence(content, pos, MARKDOWN_FENCE) ?? scanFence(content, pos, ASCIIDOC_FENCE);\n if (!block || block.line !== lineAt(content, pos)) return undefined;\n const { start, end, line, text } = block;\n return { start, end, line, text };\n}\n\n/** Every fenced block in the body, for `--quote` without `--claim`. */\nexport function fencedBlocks(\n content: string,\n bodyOffset: number,\n format: string,\n): { start: number; end: number; line: number; text: string }[] {\n const fence = fenceFor(format);\n if (!fence) return [];\n const out: FencedBlock[] = [];\n let pos = bodyOffset;\n for (;;) {\n const block = scanFence(content, pos, fence);\n if (!block) return out;\n const { start, end, line, text } = block;\n out.push({ start, end, line, text });\n pos = block.next;\n }\n}\n\n/** The form `formatStatement` writes for a format, or a refusal naming the format. */\nfunction writtenForm(format: string): StatementForm {\n const form = statementForms(format)[0];\n if (!form) {\n throw new CiteError(`No marker syntax for format \"${format}\".`);\n }\n return form;\n}\n\n/** Render a marker in the format's first form, e.g. `<!-- cite fetch-timeout -->`. */\nexport function formatStatement(\n format: string,\n payload: { kind: \"ref\"; ids: readonly [string, ...string[]] },\n): string {\n const form = writtenForm(format);\n // `[comment]: # (`, `// (` and `.. (` hug their parentheses; the comment\n // forms take a space inside each delimiter.\n const pad = form.open.endsWith(\"(\") ? \"\" : \" \";\n return `${form.open}${pad}cite ${payload.ids.join(\" \")}${pad}${form.close}`;\n}\n\n/**\n * The one edit that replaces a marker's payload with `ids`, as a span of\n * `content` and the text to put in it. The marker keeps its own form rather\n * than being rendered afresh, so a page that spells its markers in a form\n * `formatStatement` would not choose keeps that form, along with its\n * indentation and inner spacing.\n *\n * Returned as an edit rather than applied, so a caller with several of them\n * can collect them all against one parse and apply them together. That is how\n * `remove` rewrites a page: nothing it reads a position from has been written\n * to yet.\n *\n * `content` must be the text the statement was parsed from; the payload is\n * found inside the span the statement recorded, and the answer is undefined\n * when it is not there. The marker must also open and close on one line, since\n * a payload spanning lines would be folded onto one here, changing the line\n * count and moving every claim below it. Both callers hold to that: `remove`\n * refuses a marker line it cannot read whole, and `add` joins only a marker\n * with no line break in it.\n */\nexport function respellEdit(\n content: string,\n statement: InlineStatement,\n ids: readonly [string, ...string[]],\n): TextEdit | undefined {\n const text = content.slice(statement.start, statement.end);\n // No open delimiter carries `cite`, so the first occurrence of the trimmed\n // inner text is the payload itself.\n const at = text.indexOf(statement.raw);\n if (at === -1) return undefined;\n return {\n start: statement.start + at,\n end: statement.start + at + statement.raw.length,\n text: `cite ${ids.join(\" \")}`,\n };\n}\n\n/**\n * `content` with the marker's payload replaced by `ids`. The one-edit form of\n * `respellEdit`, for a caller rewriting a single marker; `content` unchanged\n * when the payload is not where the statement says.\n */\nexport function respellStatement(\n content: string,\n statement: InlineStatement,\n ids: readonly [string, ...string[]],\n): string {\n const edit = respellEdit(content, statement, ids);\n if (edit === undefined) return content;\n return content.slice(0, edit.start) + edit.text + content.slice(edit.end);\n}\n\n/**\n * The first line at or after `offset` that is neither blank nor a marker on\n * its own, as a 1-based line of `text`.\n */\nfunction nextTextLine(text: string, offset: number, format: string): number | undefined {\n let pos = nextLineStart(text, offset);\n while (pos < text.length) {\n const end = lineEnd(text, pos);\n const line = lineText(text, pos, end);\n if (line.trim() !== \"\" && !isMarkerLine(line, format)) return lineAt(text, pos);\n pos = end + 1;\n }\n return undefined;\n}\n\n/**\n * The fenced block opening on `line`, as the lines it spans, fences included.\n * Undefined when that line is not an opener the format knows, or the block is\n * never closed.\n */\nexport function fenceSpanAt(\n text: string,\n line: number,\n format: string,\n): { start: number; end: number } | undefined {\n const fence = fenceFor(format);\n if (!fence) return undefined;\n const block = scanFence(text, offsetOfLine(text, line), fence);\n if (!block || block.line !== line) return undefined;\n return { start: line, end: lineAt(text, block.end) };\n}\n\n/**\n * The fenced block `line` sits inside rather than opens, as the lines it\n * spans, fences included. Undefined when the line opens a block, sits in no\n * block, or the format has no fence. The closing fence counts as inside, so a\n * caller that wants a content line asks whether its span ends before the\n * block does.\n */\nexport function fenceAround(\n text: string,\n bodyOffset: number,\n line: number,\n format: string,\n): { start: number; end: number } | undefined {\n const block = fencedBlocks(text, bodyOffset, format).find(\n (b) => line > b.line && line <= lineAt(text, b.end),\n );\n return block === undefined ? undefined : { start: block.line, end: lineAt(text, block.end) };\n}\n\n/** Whether `line` sits inside a fenced block rather than opening one. */\nexport function insideFence(\n text: string,\n bodyOffset: number,\n line: number,\n format: string,\n): boolean {\n return fenceAround(text, bodyOffset, line, format) !== undefined;\n}\n\n/**\n * The paragraph or fenced block that holds `line`, as the lines it spans,\n * fences included: what a marker written above it anchors. A paragraph runs\n * up to a blank line, a fence, a marker-only line, a bound line or the body's\n * start, and down as `paragraphAfter` reads it. Undefined for a blank line, a\n * marker-only line, or a line nothing can anchor.\n */\nexport function unitHolding(\n text: string,\n line: number,\n format: string,\n body: { offset: number; line: number },\n): { start: number; end: number; kind: \"paragraph\" | \"block\" } | undefined {\n const textOf = (n: number): string => lineText(text, offsetOfLine(text, n), lineEnd(text, offsetOfLine(text, n)));\n const own = textOf(line);\n if (own.trim() === \"\" || isMarkerLine(own, format)) return undefined;\n const block = fencedBlocks(text, body.offset, format).find(\n (b) => line >= b.line && line <= lineAt(text, b.end),\n );\n if (block !== undefined) {\n return { start: block.line, end: lineAt(text, block.end), kind: \"block\" };\n }\n if (ANY_FENCE.test(own)) return undefined;\n let start = line;\n // A bound line is its own unit, so the walk up never leaves it.\n while (start - 1 >= body.line && !isBoundLine(own, format)) {\n const above = textOf(start - 1);\n if (\n above.trim() === \"\" ||\n ANY_FENCE.test(above) ||\n isMarkerLine(above, format) ||\n isBoundLine(above, format)\n ) {\n break;\n }\n start--;\n }\n const paragraph = paragraphAfter(text, offsetOfLine(text, start), format);\n if (paragraph?.line !== start) return undefined;\n return { start, end: lineAt(text, paragraph.end), kind: \"paragraph\" };\n}\n\n/** A markdown list item's opening line: its indentation, its bullet or number, and the gap after. */\nconst LIST_ITEM = /^([ \\t]*)([-*+]|\\d{1,9}[.)])([ \\t]+|$)/;\n/** A thematic break, which a list item's pattern would otherwise read as `* * *`. */\nconst THEMATIC_BREAK = /^[ \\t]*([-*_])(?:[ \\t]*\\1){2,}[ \\t]*$/;\n\n/** The text of 1-based line `n` of `text`. */\nfunction textOfLine(text: string, n: number): string {\n const pos = offsetOfLine(text, n);\n return lineText(text, pos, lineEnd(text, pos));\n}\n\n/** The leading spaces and tabs of a line. */\nfunction indentOf(line: string): string {\n return /^[ \\t]*/.exec(line)?.[0] ?? \"\";\n}\n\n/** The column a run of spaces and tabs reaches, with tab stops every 4 columns. */\nfunction columnOf(indent: string): number {\n let column = 0;\n for (const ch of indent) column = ch === \"\\t\" ? column + 4 - (column % 4) : column + 1;\n return column;\n}\n\n/** The list item a markdown line opens, or undefined. */\nfunction listItemOf(line: string): { lead: string; bullet: string; gap: string } | undefined {\n if (THEMATIC_BREAK.test(line)) return undefined;\n const match = LIST_ITEM.exec(line);\n if (match === null) return undefined;\n const [, lead = \"\", bullet = \"\", gap = \"\"] = match;\n return { lead, bullet, gap };\n}\n\n/**\n * Whether the list item on `line` follows an earlier item of its list: the\n * nearest text above it, past blank and marker-only lines, is indented deeper\n * than the item, or sits in a paragraph holding an item at the same column.\n */\nfunction continuesList(\n text: string,\n line: number,\n format: string,\n bodyLine: number,\n column: number,\n): boolean {\n let n = line - 1;\n while (n >= bodyLine) {\n const above = textOfLine(text, n);\n if (above.trim() !== \"\" && !isMarkerLine(above, format)) break;\n n--;\n }\n if (n < bodyLine) return false;\n if (columnOf(indentOf(textOfLine(text, n))) > column) return true;\n for (; n >= bodyLine; n--) {\n const above = textOfLine(text, n);\n if (\n above.trim() === \"\" ||\n ANY_FENCE.test(above) ||\n isMarkerLine(above, format) ||\n isBoundLine(above, format)\n ) {\n break;\n }\n const item = listItemOf(above);\n if (item !== undefined && columnOf(item.lead) === column) return true;\n }\n return false;\n}\n\n/**\n * The indentation a marker written above `line` carries, so it stays in the\n * container the line is in. A paragraph's own leading spaces and tabs, copied\n * as they are. Above a markdown list item that follows an earlier item, the\n * column of the item's text, which keeps the marker inside the list: at the\n * item's own column it would end the list. asciidoc never indents one, since\n * a comment there starts at column 0.\n */\nexport function markerIndent(\n text: string,\n line: number,\n format: string,\n bodyLine: number,\n): string {\n if (format === \"asciidoc\") return \"\";\n const own = textOfLine(text, line);\n const indent = indentOf(own);\n if (format !== \"markdown\" && format !== \"mdx\") return indent;\n const item = listItemOf(own);\n if (item === undefined || !continuesList(text, line, format, bodyLine, columnOf(item.lead))) {\n return indent;\n }\n // A gap of five spaces or more is one space and indented code, so the text starts after one.\n const gap = item.gap === \"\" || columnOf(item.gap) > 4 ? \" \" : item.gap;\n return item.lead + \" \".repeat(item.bullet.length) + gap;\n}\n\n/**\n * The lines a marker anchors, as 1-based lines of `text`: the rest of its own\n * line when that carries text, else the paragraph or fenced block that\n * follows. Marker-only lines below it are skipped, so markers stacked above\n * one paragraph all anchor it, and none is part of the pin. With `quote`, the\n * next fenced block after it, wherever that is. Undefined when nothing\n * follows to anchor.\n */\nexport function anchoredLines(\n text: string,\n after: number,\n format: string,\n quote = false,\n): { start: number; end: number } | undefined {\n if (quote) {\n const block = fencedBlockAfter(text, after, format);\n return block === undefined ? undefined : { start: block.line, end: lineAt(text, block.end) };\n }\n const restEnd = lineEnd(text, after);\n if (text.slice(after, restEnd).trim() !== \"\") {\n const line = lineAt(text, after);\n return { start: line, end: line };\n }\n const paragraph = paragraphAfter(text, after, format);\n if (paragraph !== undefined) {\n return { start: paragraph.line, end: lineAt(text, paragraph.end) };\n }\n // A fence where a paragraph would be: the block is what the marker anchors.\n const line = nextTextLine(text, after, format);\n return line === undefined ? undefined : fenceSpanAt(text, line, format);\n}\n\n/**\n * The offset of every LF in a text, so a line number is a lookup rather than\n * a scan from the start. A page asks `lineAt` and `offsetOfLine` once per\n * citation, and a scan per question made a page of hundreds of citations\n * quadratic. Only the last few texts are kept, and the index is a pure\n * function of the text, so nothing carries from one run to the next.\n */\nconst LINE_INDEX_SLOTS = 4;\nconst lineIndexes: { text: string; newlines: number[] }[] = [];\n\nfunction newlinesOf(content: string): readonly number[] {\n const hit = lineIndexes.findIndex((entry) => entry.text === content);\n const found = hit === -1 ? undefined : lineIndexes[hit];\n if (found !== undefined) return found.newlines;\n const newlines: number[] = [];\n for (let at = content.indexOf(\"\\n\"); at !== -1; at = content.indexOf(\"\\n\", at + 1)) newlines.push(at);\n lineIndexes.unshift({ text: content, newlines });\n lineIndexes.length = Math.min(lineIndexes.length, LINE_INDEX_SLOTS);\n return newlines;\n}\n\n/** 1-based file line of a file offset, counting CRLF once. */\nexport function lineAt(content: string, offset: number): number {\n const stop = Math.min(offset, content.length);\n const newlines = newlinesOf(content);\n // The number of LFs before `stop`: a binary search for the first one at or past it.\n let low = 0;\n let high = newlines.length;\n while (low < high) {\n const mid = (low + high) >>> 1;\n if ((newlines[mid] ?? Infinity) < stop) low = mid + 1;\n else high = mid;\n }\n return low + 1;\n}\n\n/** File offset of the first character of a 1-based line (the length when past the end). */\nexport function offsetOfLine(content: string, line: number): number {\n if (line <= 1) return 0;\n const before = newlinesOf(content)[line - 2];\n return before === undefined ? content.length : before + 1;\n}\n"],"mappings":"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","names":["sliceLines","hashRange","line"]}