chaffjs 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (840) hide show
  1. package/dist/announced-count.d.ts +26 -0
  2. package/dist/announced-count.d.ts.map +1 -0
  3. package/dist/announced-count.js +104 -0
  4. package/dist/announced-count.js.map +1 -0
  5. package/dist/bare-url.d.ts +14 -0
  6. package/dist/bare-url.d.ts.map +1 -0
  7. package/dist/bare-url.js +28 -0
  8. package/dist/bare-url.js.map +1 -0
  9. package/dist/baseline.d.ts +5 -0
  10. package/dist/baseline.d.ts.map +1 -1
  11. package/dist/baseline.js +16 -1
  12. package/dist/baseline.js.map +1 -1
  13. package/dist/body-section.d.ts +7 -0
  14. package/dist/body-section.d.ts.map +1 -0
  15. package/dist/body-section.js +6 -0
  16. package/dist/body-section.js.map +1 -0
  17. package/dist/calendar-number.d.ts +2 -0
  18. package/dist/calendar-number.d.ts.map +1 -1
  19. package/dist/calendar-number.js +9 -2
  20. package/dist/calendar-number.js.map +1 -1
  21. package/dist/cli-args.d.ts +4 -0
  22. package/dist/cli-args.d.ts.map +1 -1
  23. package/dist/cli-args.js +5 -0
  24. package/dist/cli-args.js.map +1 -1
  25. package/dist/cli-text.d.ts.map +1 -1
  26. package/dist/cli-text.js +8 -4
  27. package/dist/cli-text.js.map +1 -1
  28. package/dist/cli.js +33 -30
  29. package/dist/cli.js.map +1 -1
  30. package/dist/commands/compare.d.ts +8 -0
  31. package/dist/commands/compare.d.ts.map +1 -0
  32. package/dist/commands/compare.js +72 -0
  33. package/dist/commands/compare.js.map +1 -0
  34. package/dist/commands/eval.d.ts.map +1 -1
  35. package/dist/commands/eval.js +17 -9
  36. package/dist/commands/eval.js.map +1 -1
  37. package/dist/commands/feedback.d.ts +5 -2
  38. package/dist/commands/feedback.d.ts.map +1 -1
  39. package/dist/commands/feedback.js +11 -5
  40. package/dist/commands/feedback.js.map +1 -1
  41. package/dist/commands/test.d.ts.map +1 -1
  42. package/dist/commands/test.js +2 -4
  43. package/dist/commands/test.js.map +1 -1
  44. package/dist/commands/tree.d.ts +13 -0
  45. package/dist/commands/tree.d.ts.map +1 -1
  46. package/dist/commands/tree.js +35 -5
  47. package/dist/commands/tree.js.map +1 -1
  48. package/dist/compare/atom.d.ts +31 -0
  49. package/dist/compare/atom.d.ts.map +1 -0
  50. package/dist/compare/atom.js +4 -0
  51. package/dist/compare/atom.js.map +1 -0
  52. package/dist/compare/clock-time.d.ts +8 -0
  53. package/dist/compare/clock-time.d.ts.map +1 -0
  54. package/dist/compare/clock-time.js +65 -0
  55. package/dist/compare/clock-time.js.map +1 -0
  56. package/dist/compare/extract.d.ts +15 -0
  57. package/dist/compare/extract.d.ts.map +1 -0
  58. package/dist/compare/extract.js +58 -0
  59. package/dist/compare/extract.js.map +1 -0
  60. package/dist/compare/fact-text.d.ts +23 -0
  61. package/dist/compare/fact-text.d.ts.map +1 -0
  62. package/dist/compare/fact-text.js +47 -0
  63. package/dist/compare/fact-text.js.map +1 -0
  64. package/dist/compare/markup-atoms.d.ts +11 -0
  65. package/dist/compare/markup-atoms.d.ts.map +1 -0
  66. package/dist/compare/markup-atoms.js +23 -0
  67. package/dist/compare/markup-atoms.js.map +1 -0
  68. package/dist/compare/match.d.ts +19 -0
  69. package/dist/compare/match.d.ts.map +1 -0
  70. package/dist/compare/match.js +84 -0
  71. package/dist/compare/match.js.map +1 -0
  72. package/dist/compare/outcome.d.ts +33 -0
  73. package/dist/compare/outcome.d.ts.map +1 -0
  74. package/dist/compare/outcome.js +40 -0
  75. package/dist/compare/outcome.js.map +1 -0
  76. package/dist/compare/proper-nouns.d.ts +19 -0
  77. package/dist/compare/proper-nouns.d.ts.map +1 -0
  78. package/dist/compare/proper-nouns.js +44 -0
  79. package/dist/compare/proper-nouns.js.map +1 -0
  80. package/dist/compare/render.d.ts +9 -0
  81. package/dist/compare/render.d.ts.map +1 -0
  82. package/dist/compare/render.js +64 -0
  83. package/dist/compare/render.js.map +1 -0
  84. package/dist/compare/spans.d.ts +9 -0
  85. package/dist/compare/spans.d.ts.map +1 -0
  86. package/dist/compare/spans.js +11 -0
  87. package/dist/compare/spans.js.map +1 -0
  88. package/dist/compare/text-atoms.d.ts +24 -0
  89. package/dist/compare/text-atoms.d.ts.map +1 -0
  90. package/dist/compare/text-atoms.js +67 -0
  91. package/dist/compare/text-atoms.js.map +1 -0
  92. package/dist/compare/text.d.ts +24 -0
  93. package/dist/compare/text.d.ts.map +1 -0
  94. package/dist/compare/text.js +90 -0
  95. package/dist/compare/text.js.map +1 -0
  96. package/dist/compare/tree-atoms.d.ts +20 -0
  97. package/dist/compare/tree-atoms.d.ts.map +1 -0
  98. package/dist/compare/tree-atoms.js +58 -0
  99. package/dist/compare/tree-atoms.js.map +1 -0
  100. package/dist/compare/written-names.d.ts +8 -0
  101. package/dist/compare/written-names.d.ts.map +1 -0
  102. package/dist/compare/written-names.js +24 -0
  103. package/dist/compare/written-names.js.map +1 -0
  104. package/dist/config/load.d.ts +15 -0
  105. package/dist/config/load.d.ts.map +1 -1
  106. package/dist/config/load.js +5 -0
  107. package/dist/config/load.js.map +1 -1
  108. package/dist/config/option-problems.d.ts +16 -0
  109. package/dist/config/option-problems.d.ts.map +1 -0
  110. package/dist/config/option-problems.js +58 -0
  111. package/dist/config/option-problems.js.map +1 -0
  112. package/dist/config/read.d.ts +4 -0
  113. package/dist/config/read.d.ts.map +1 -0
  114. package/dist/config/read.js +8 -0
  115. package/dist/config/read.js.map +1 -0
  116. package/dist/config/rule-problems.d.ts +2 -4
  117. package/dist/config/rule-problems.d.ts.map +1 -1
  118. package/dist/config/rule-problems.js +7 -1
  119. package/dist/config/rule-problems.js.map +1 -1
  120. package/dist/config/style.d.ts +16 -0
  121. package/dist/config/style.d.ts.map +1 -0
  122. package/dist/config/style.js +36 -0
  123. package/dist/config/style.js.map +1 -0
  124. package/dist/config/warnings.d.ts +6 -0
  125. package/dist/config/warnings.d.ts.map +1 -0
  126. package/dist/config/warnings.js +10 -0
  127. package/dist/config/warnings.js.map +1 -0
  128. package/dist/config/write.d.ts.map +1 -1
  129. package/dist/config/write.js +17 -4
  130. package/dist/config/write.js.map +1 -1
  131. package/dist/custom/bounded-match.d.ts +15 -0
  132. package/dist/custom/bounded-match.d.ts.map +1 -0
  133. package/dist/custom/bounded-match.js +43 -0
  134. package/dist/custom/bounded-match.js.map +1 -0
  135. package/dist/custom/load.d.ts +8 -0
  136. package/dist/custom/load.d.ts.map +1 -0
  137. package/dist/custom/load.js +11 -0
  138. package/dist/custom/load.js.map +1 -0
  139. package/dist/custom/parse.d.ts +70 -0
  140. package/dist/custom/parse.d.ts.map +1 -0
  141. package/dist/custom/parse.js +215 -0
  142. package/dist/custom/parse.js.map +1 -0
  143. package/dist/custom/problems.d.ts +5 -0
  144. package/dist/custom/problems.d.ts.map +1 -0
  145. package/dist/custom/problems.js +78 -0
  146. package/dist/custom/problems.js.map +1 -0
  147. package/dist/custom/regex-safety.d.ts +11 -0
  148. package/dist/custom/regex-safety.d.ts.map +1 -0
  149. package/dist/custom/regex-safety.js +110 -0
  150. package/dist/custom/regex-safety.js.map +1 -0
  151. package/dist/custom/token-pattern.d.ts +8 -0
  152. package/dist/custom/token-pattern.d.ts.map +1 -0
  153. package/dist/custom/token-pattern.js +75 -0
  154. package/dist/custom/token-pattern.js.map +1 -0
  155. package/dist/detect.d.ts.map +1 -1
  156. package/dist/detect.js +24 -2
  157. package/dist/detect.js.map +1 -1
  158. package/dist/detectors/acronym-context.d.ts +2 -0
  159. package/dist/detectors/acronym-context.d.ts.map +1 -1
  160. package/dist/detectors/acronym-context.js +12 -4
  161. package/dist/detectors/acronym-context.js.map +1 -1
  162. package/dist/detectors/ai-phrasing.d.ts +20 -0
  163. package/dist/detectors/ai-phrasing.d.ts.map +1 -0
  164. package/dist/detectors/ai-phrasing.js +112 -0
  165. package/dist/detectors/ai-phrasing.js.map +1 -0
  166. package/dist/detectors/announced-count.d.ts +4 -0
  167. package/dist/detectors/announced-count.d.ts.map +1 -0
  168. package/dist/detectors/announced-count.js +20 -0
  169. package/dist/detectors/announced-count.js.map +1 -0
  170. package/dist/detectors/broken-link.d.ts +17 -0
  171. package/dist/detectors/broken-link.d.ts.map +1 -0
  172. package/dist/detectors/broken-link.js +66 -0
  173. package/dist/detectors/broken-link.js.map +1 -0
  174. package/dist/detectors/cited-name.d.ts.map +1 -1
  175. package/dist/detectors/cited-name.js +4 -4
  176. package/dist/detectors/cited-name.js.map +1 -1
  177. package/dist/detectors/cited-title.d.ts +11 -0
  178. package/dist/detectors/cited-title.d.ts.map +1 -0
  179. package/dist/detectors/cited-title.js +75 -0
  180. package/dist/detectors/cited-title.js.map +1 -0
  181. package/dist/detectors/concrete-evidence.d.ts +1 -1
  182. package/dist/detectors/concrete-evidence.d.ts.map +1 -1
  183. package/dist/detectors/concrete-evidence.js +2 -1
  184. package/dist/detectors/concrete-evidence.js.map +1 -1
  185. package/dist/detectors/contrast-frame.d.ts +17 -0
  186. package/dist/detectors/contrast-frame.d.ts.map +1 -0
  187. package/dist/detectors/contrast-frame.js +32 -0
  188. package/dist/detectors/contrast-frame.js.map +1 -0
  189. package/dist/detectors/custom.d.ts +8 -0
  190. package/dist/detectors/custom.d.ts.map +1 -0
  191. package/dist/detectors/custom.js +71 -0
  192. package/dist/detectors/custom.js.map +1 -0
  193. package/dist/detectors/dangling-figure.d.ts +4 -0
  194. package/dist/detectors/dangling-figure.d.ts.map +1 -0
  195. package/dist/detectors/dangling-figure.js +18 -0
  196. package/dist/detectors/dangling-figure.js.map +1 -0
  197. package/dist/detectors/doubled-punctuation.d.ts +9 -0
  198. package/dist/detectors/doubled-punctuation.d.ts.map +1 -0
  199. package/dist/detectors/doubled-punctuation.js +33 -0
  200. package/dist/detectors/doubled-punctuation.js.map +1 -0
  201. package/dist/detectors/doubled-word.d.ts +5 -0
  202. package/dist/detectors/doubled-word.d.ts.map +1 -1
  203. package/dist/detectors/doubled-word.js +24 -11
  204. package/dist/detectors/doubled-word.js.map +1 -1
  205. package/dist/detectors/enumerated-runs.d.ts +1 -1
  206. package/dist/detectors/enumerated-runs.d.ts.map +1 -1
  207. package/dist/detectors/enumerated-runs.js +15 -4
  208. package/dist/detectors/enumerated-runs.js.map +1 -1
  209. package/dist/detectors/gram-furniture.d.ts +6 -0
  210. package/dist/detectors/gram-furniture.d.ts.map +1 -0
  211. package/dist/detectors/gram-furniture.js +21 -0
  212. package/dist/detectors/gram-furniture.js.map +1 -0
  213. package/dist/detectors/gram-place.d.ts +2 -0
  214. package/dist/detectors/gram-place.d.ts.map +1 -1
  215. package/dist/detectors/gram-place.js +9 -7
  216. package/dist/detectors/gram-place.js.map +1 -1
  217. package/dist/detectors/gram-windows.d.ts +9 -0
  218. package/dist/detectors/gram-windows.d.ts.map +1 -0
  219. package/dist/detectors/gram-windows.js +27 -0
  220. package/dist/detectors/gram-windows.js.map +1 -0
  221. package/dist/detectors/heading-case.d.ts +2 -0
  222. package/dist/detectors/heading-case.d.ts.map +1 -1
  223. package/dist/detectors/heading-case.js +29 -2
  224. package/dist/detectors/heading-case.js.map +1 -1
  225. package/dist/detectors/heading-code-words.d.ts +6 -0
  226. package/dist/detectors/heading-code-words.d.ts.map +1 -0
  227. package/dist/detectors/heading-code-words.js +29 -0
  228. package/dist/detectors/heading-code-words.js.map +1 -0
  229. package/dist/detectors/heading-echo.d.ts.map +1 -1
  230. package/dist/detectors/heading-echo.js +7 -4
  231. package/dist/detectors/heading-echo.js.map +1 -1
  232. package/dist/detectors/heading-level-skip.d.ts +12 -0
  233. package/dist/detectors/heading-level-skip.d.ts.map +1 -0
  234. package/dist/detectors/heading-level-skip.js +11 -0
  235. package/dist/detectors/heading-level-skip.js.map +1 -0
  236. package/dist/detectors/image-alt-text.d.ts +3 -0
  237. package/dist/detectors/image-alt-text.d.ts.map +1 -0
  238. package/dist/detectors/image-alt-text.js +9 -0
  239. package/dist/detectors/image-alt-text.js.map +1 -0
  240. package/dist/detectors/index.d.ts.map +1 -1
  241. package/dist/detectors/index.js +34 -1
  242. package/dist/detectors/index.js.map +1 -1
  243. package/dist/detectors/kutoten-consistency.d.ts +24 -0
  244. package/dist/detectors/kutoten-consistency.d.ts.map +1 -0
  245. package/dist/detectors/kutoten-consistency.js +77 -0
  246. package/dist/detectors/kutoten-consistency.js.map +1 -0
  247. package/dist/detectors/lead-in.d.ts +1 -0
  248. package/dist/detectors/lead-in.d.ts.map +1 -1
  249. package/dist/detectors/lead-in.js +2 -1
  250. package/dist/detectors/lead-in.js.map +1 -1
  251. package/dist/detectors/lexicon-match.d.ts +4 -0
  252. package/dist/detectors/lexicon-match.d.ts.map +1 -1
  253. package/dist/detectors/lexicon-match.js +7 -2
  254. package/dist/detectors/lexicon-match.js.map +1 -1
  255. package/dist/detectors/list-item.d.ts +45 -0
  256. package/dist/detectors/list-item.d.ts.map +1 -0
  257. package/dist/detectors/list-item.js +88 -0
  258. package/dist/detectors/list-item.js.map +1 -0
  259. package/dist/detectors/list-items.d.ts +31 -0
  260. package/dist/detectors/list-items.d.ts.map +1 -0
  261. package/dist/detectors/list-items.js +113 -0
  262. package/dist/detectors/list-items.js.map +1 -0
  263. package/dist/detectors/list-lead-in.d.ts +7 -0
  264. package/dist/detectors/list-lead-in.d.ts.map +1 -0
  265. package/dist/detectors/list-lead-in.js +28 -0
  266. package/dist/detectors/list-lead-in.js.map +1 -0
  267. package/dist/detectors/list-sentence.d.ts +65 -0
  268. package/dist/detectors/list-sentence.d.ts.map +1 -0
  269. package/dist/detectors/list-sentence.js +105 -0
  270. package/dist/detectors/list-sentence.js.map +1 -0
  271. package/dist/detectors/list-view.d.ts +34 -0
  272. package/dist/detectors/list-view.d.ts.map +1 -0
  273. package/dist/detectors/list-view.js +137 -0
  274. package/dist/detectors/list-view.js.map +1 -0
  275. package/dist/detectors/long-vowel.d.ts +7 -0
  276. package/dist/detectors/long-vowel.d.ts.map +1 -0
  277. package/dist/detectors/long-vowel.js +57 -0
  278. package/dist/detectors/long-vowel.js.map +1 -0
  279. package/dist/detectors/markup-finding.d.ts +6 -0
  280. package/dist/detectors/markup-finding.d.ts.map +1 -0
  281. package/dist/detectors/markup-finding.js +19 -0
  282. package/dist/detectors/markup-finding.js.map +1 -0
  283. package/dist/detectors/name-numeral.d.ts +1 -1
  284. package/dist/detectors/name-numeral.d.ts.map +1 -1
  285. package/dist/detectors/name-numeral.js +2 -7
  286. package/dist/detectors/name-numeral.js.map +1 -1
  287. package/dist/detectors/orthography.d.ts.map +1 -1
  288. package/dist/detectors/orthography.js +14 -10
  289. package/dist/detectors/orthography.js.map +1 -1
  290. package/dist/detectors/oxford-comma.d.ts +7 -1
  291. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  292. package/dist/detectors/oxford-comma.js +135 -229
  293. package/dist/detectors/oxford-comma.js.map +1 -1
  294. package/dist/detectors/place-run.d.ts.map +1 -1
  295. package/dist/detectors/place-run.js +10 -6
  296. package/dist/detectors/place-run.js.map +1 -1
  297. package/dist/detectors/placeholder-text.d.ts +9 -0
  298. package/dist/detectors/placeholder-text.d.ts.map +1 -0
  299. package/dist/detectors/placeholder-text.js +18 -0
  300. package/dist/detectors/placeholder-text.js.map +1 -0
  301. package/dist/detectors/quote-around.d.ts +3 -0
  302. package/dist/detectors/quote-around.d.ts.map +1 -0
  303. package/dist/detectors/quote-around.js +8 -0
  304. package/dist/detectors/quote-around.js.map +1 -0
  305. package/dist/detectors/register.d.ts +11 -3
  306. package/dist/detectors/register.d.ts.map +1 -1
  307. package/dist/detectors/register.js +28 -9
  308. package/dist/detectors/register.js.map +1 -1
  309. package/dist/detectors/roman-numeral.d.ts +2 -0
  310. package/dist/detectors/roman-numeral.d.ts.map +1 -1
  311. package/dist/detectors/roman-numeral.js +2 -0
  312. package/dist/detectors/roman-numeral.js.map +1 -1
  313. package/dist/detectors/sentence-ending.d.ts +17 -1
  314. package/dist/detectors/sentence-ending.d.ts.map +1 -1
  315. package/dist/detectors/sentence-ending.js +13 -7
  316. package/dist/detectors/sentence-ending.js.map +1 -1
  317. package/dist/detectors/sentence-length.d.ts.map +1 -1
  318. package/dist/detectors/sentence-length.js +13 -4
  319. package/dist/detectors/sentence-length.js.map +1 -1
  320. package/dist/detectors/signals.d.ts +8 -0
  321. package/dist/detectors/signals.d.ts.map +1 -1
  322. package/dist/detectors/signals.js +67 -46
  323. package/dist/detectors/signals.js.map +1 -1
  324. package/dist/detectors/structure-tree.d.ts +6 -0
  325. package/dist/detectors/structure-tree.d.ts.map +1 -1
  326. package/dist/detectors/structure-tree.js +37 -1
  327. package/dist/detectors/structure-tree.js.map +1 -1
  328. package/dist/detectors/structure.d.ts +1 -3
  329. package/dist/detectors/structure.d.ts.map +1 -1
  330. package/dist/detectors/structure.js +3 -4
  331. package/dist/detectors/structure.js.map +1 -1
  332. package/dist/detectors/superlative-name.d.ts.map +1 -1
  333. package/dist/detectors/superlative-name.js +8 -5
  334. package/dist/detectors/superlative-name.js.map +1 -1
  335. package/dist/detectors/superlative-scope.d.ts.map +1 -1
  336. package/dist/detectors/superlative-scope.js +9 -4
  337. package/dist/detectors/superlative-scope.js.map +1 -1
  338. package/dist/detectors/token-column.d.ts +46 -0
  339. package/dist/detectors/token-column.d.ts.map +1 -0
  340. package/dist/detectors/token-column.js +95 -0
  341. package/dist/detectors/token-column.js.map +1 -0
  342. package/dist/detectors/unbalanced-bracket.d.ts +22 -0
  343. package/dist/detectors/unbalanced-bracket.d.ts.map +1 -0
  344. package/dist/detectors/unbalanced-bracket.js +119 -0
  345. package/dist/detectors/unbalanced-bracket.js.map +1 -0
  346. package/dist/detectors/url-run-on.d.ts +9 -0
  347. package/dist/detectors/url-run-on.d.ts.map +1 -0
  348. package/dist/detectors/url-run-on.js +19 -0
  349. package/dist/detectors/url-run-on.js.map +1 -0
  350. package/dist/document-sections.d.ts +13 -0
  351. package/dist/document-sections.d.ts.map +1 -0
  352. package/dist/document-sections.js +32 -0
  353. package/dist/document-sections.js.map +1 -0
  354. package/dist/document.d.ts +11 -4
  355. package/dist/document.d.ts.map +1 -1
  356. package/dist/document.js +90 -98
  357. package/dist/document.js.map +1 -1
  358. package/dist/email-parts.d.ts +28 -0
  359. package/dist/email-parts.d.ts.map +1 -0
  360. package/dist/email-parts.js +230 -0
  361. package/dist/email-parts.js.map +1 -0
  362. package/dist/eval.d.ts +2 -1
  363. package/dist/eval.d.ts.map +1 -1
  364. package/dist/eval.js +16 -4
  365. package/dist/eval.js.map +1 -1
  366. package/dist/figure-references.d.ts +30 -0
  367. package/dist/figure-references.d.ts.map +1 -0
  368. package/dist/figure-references.js +76 -0
  369. package/dist/figure-references.js.map +1 -0
  370. package/dist/files.d.ts +2 -0
  371. package/dist/files.d.ts.map +1 -1
  372. package/dist/files.js +4 -0
  373. package/dist/files.js.map +1 -1
  374. package/dist/heading-label.d.ts +22 -0
  375. package/dist/heading-label.d.ts.map +1 -0
  376. package/dist/heading-label.js +50 -0
  377. package/dist/heading-label.js.map +1 -0
  378. package/dist/in-page-nav.d.ts +4 -1
  379. package/dist/in-page-nav.d.ts.map +1 -1
  380. package/dist/in-page-nav.js +53 -18
  381. package/dist/in-page-nav.js.map +1 -1
  382. package/dist/joined-view.d.ts.map +1 -1
  383. package/dist/joined-view.js +1 -0
  384. package/dist/joined-view.js.map +1 -1
  385. package/dist/levels.d.ts +5 -1
  386. package/dist/levels.d.ts.map +1 -1
  387. package/dist/levels.js +9 -0
  388. package/dist/levels.js.map +1 -1
  389. package/dist/line-paragraphs.d.ts +8 -0
  390. package/dist/line-paragraphs.d.ts.map +1 -1
  391. package/dist/line-paragraphs.js +33 -10
  392. package/dist/line-paragraphs.js.map +1 -1
  393. package/dist/long-vowel.d.ts +35 -0
  394. package/dist/long-vowel.d.ts.map +1 -0
  395. package/dist/long-vowel.js +71 -0
  396. package/dist/long-vowel.js.map +1 -0
  397. package/dist/markdown-node.d.ts +19 -0
  398. package/dist/markdown-node.d.ts.map +1 -0
  399. package/dist/markdown-node.js +6 -0
  400. package/dist/markdown-node.js.map +1 -0
  401. package/dist/markdown-read.d.ts +17 -0
  402. package/dist/markdown-read.d.ts.map +1 -0
  403. package/dist/markdown-read.js +37 -0
  404. package/dist/markdown-read.js.map +1 -0
  405. package/dist/markup.d.ts +22 -0
  406. package/dist/markup.d.ts.map +1 -0
  407. package/dist/markup.js +180 -0
  408. package/dist/markup.js.map +1 -0
  409. package/dist/mask.js +8 -5
  410. package/dist/mask.js.map +1 -1
  411. package/dist/number-name.d.ts +21 -5
  412. package/dist/number-name.d.ts.map +1 -1
  413. package/dist/number-name.js +119 -17
  414. package/dist/number-name.js.map +1 -1
  415. package/dist/orthography.d.ts +2 -0
  416. package/dist/orthography.d.ts.map +1 -1
  417. package/dist/orthography.js +9 -4
  418. package/dist/orthography.js.map +1 -1
  419. package/dist/page-furniture.d.ts +5 -2
  420. package/dist/page-furniture.d.ts.map +1 -1
  421. package/dist/page-furniture.js +8 -2
  422. package/dist/page-furniture.js.map +1 -1
  423. package/dist/plain-source.d.ts +1 -0
  424. package/dist/plain-source.d.ts.map +1 -1
  425. package/dist/plain-source.js +2 -1
  426. package/dist/plain-source.js.map +1 -1
  427. package/dist/plugin.d.ts +115 -0
  428. package/dist/plugin.d.ts.map +1 -1
  429. package/dist/profile/for-file.d.ts.map +1 -1
  430. package/dist/profile/for-file.js +3 -1
  431. package/dist/profile/for-file.js.map +1 -1
  432. package/dist/quoted-span.d.ts.map +1 -1
  433. package/dist/quoted-span.js +8 -3
  434. package/dist/quoted-span.js.map +1 -1
  435. package/dist/reasons.d.ts +3 -0
  436. package/dist/reasons.d.ts.map +1 -1
  437. package/dist/reasons.js +6 -0
  438. package/dist/reasons.js.map +1 -1
  439. package/dist/reference-lists.d.ts +3 -0
  440. package/dist/reference-lists.d.ts.map +1 -0
  441. package/dist/reference-lists.js +75 -0
  442. package/dist/reference-lists.js.map +1 -0
  443. package/dist/render/explain.d.ts +7 -1
  444. package/dist/render/explain.d.ts.map +1 -1
  445. package/dist/render/explain.js +36 -12
  446. package/dist/render/explain.js.map +1 -1
  447. package/dist/render/friendly.d.ts.map +1 -1
  448. package/dist/render/friendly.js +19 -9
  449. package/dist/render/friendly.js.map +1 -1
  450. package/dist/render/options.d.ts +7 -0
  451. package/dist/render/options.d.ts.map +1 -0
  452. package/dist/render/options.js +50 -0
  453. package/dist/render/options.js.map +1 -0
  454. package/dist/render/rules-json.d.ts +4 -1
  455. package/dist/render/rules-json.d.ts.map +1 -1
  456. package/dist/render/rules-json.js +102 -12
  457. package/dist/render/rules-json.js.map +1 -1
  458. package/dist/render/rules-table.d.ts +4 -0
  459. package/dist/render/rules-table.d.ts.map +1 -0
  460. package/dist/render/rules-table.js +70 -0
  461. package/dist/render/rules-table.js.map +1 -0
  462. package/dist/render/sarif.js +4 -4
  463. package/dist/render/sarif.js.map +1 -1
  464. package/dist/render/semantic.js +3 -3
  465. package/dist/render/semantic.js.map +1 -1
  466. package/dist/render/severity-name.d.ts +6 -0
  467. package/dist/render/severity-name.d.ts.map +1 -0
  468. package/dist/render/severity-name.js +7 -0
  469. package/dist/render/severity-name.js.map +1 -0
  470. package/dist/render/text.d.ts +7 -2
  471. package/dist/render/text.d.ts.map +1 -1
  472. package/dist/render/text.js +15 -4
  473. package/dist/render/text.js.map +1 -1
  474. package/dist/resolve-genre.d.ts +1 -1
  475. package/dist/resolve-genre.d.ts.map +1 -1
  476. package/dist/resolve-genre.js +4 -1
  477. package/dist/resolve-genre.js.map +1 -1
  478. package/dist/rule-genres.d.ts +24 -0
  479. package/dist/rule-genres.d.ts.map +1 -0
  480. package/dist/rule-genres.js +11 -0
  481. package/dist/rule-genres.js.map +1 -0
  482. package/dist/rule-guide.d.ts +54 -0
  483. package/dist/rule-guide.d.ts.map +1 -0
  484. package/dist/rule-guide.js +81 -0
  485. package/dist/rule-guide.js.map +1 -0
  486. package/dist/rule-load.d.ts.map +1 -1
  487. package/dist/rule-load.js +87 -33
  488. package/dist/rule-load.js.map +1 -1
  489. package/dist/rule-options.d.ts +50 -0
  490. package/dist/rule-options.d.ts.map +1 -0
  491. package/dist/rule-options.js +106 -0
  492. package/dist/rule-options.js.map +1 -0
  493. package/dist/run.d.ts +13 -0
  494. package/dist/run.d.ts.map +1 -1
  495. package/dist/run.js +54 -10
  496. package/dist/run.js.map +1 -1
  497. package/dist/sentence-shape.d.ts.map +1 -1
  498. package/dist/sentence-shape.js +4 -4
  499. package/dist/sentence-shape.js.map +1 -1
  500. package/dist/setting-problems.d.ts +2 -1
  501. package/dist/setting-problems.d.ts.map +1 -1
  502. package/dist/setting-problems.js +6 -1
  503. package/dist/setting-problems.js.map +1 -1
  504. package/dist/span-cut.d.ts +6 -0
  505. package/dist/span-cut.d.ts.map +1 -0
  506. package/dist/span-cut.js +22 -0
  507. package/dist/span-cut.js.map +1 -0
  508. package/dist/structure/aside-depth.d.ts +21 -0
  509. package/dist/structure/aside-depth.d.ts.map +1 -0
  510. package/dist/structure/aside-depth.js +65 -0
  511. package/dist/structure/aside-depth.js.map +1 -0
  512. package/dist/structure/build.d.ts +5 -1
  513. package/dist/structure/build.d.ts.map +1 -1
  514. package/dist/structure/build.js +29 -17
  515. package/dist/structure/build.js.map +1 -1
  516. package/dist/structure/closed-pairs.d.ts +8 -0
  517. package/dist/structure/closed-pairs.d.ts.map +1 -0
  518. package/dist/structure/closed-pairs.js +22 -0
  519. package/dist/structure/closed-pairs.js.map +1 -0
  520. package/dist/structure/date-range.d.ts +21 -0
  521. package/dist/structure/date-range.d.ts.map +1 -0
  522. package/dist/structure/date-range.js +45 -0
  523. package/dist/structure/date-range.js.map +1 -0
  524. package/dist/structure/known-mentions.d.ts +18 -0
  525. package/dist/structure/known-mentions.d.ts.map +1 -0
  526. package/dist/structure/known-mentions.js +78 -0
  527. package/dist/structure/known-mentions.js.map +1 -0
  528. package/dist/structure/numbering.d.ts +1 -0
  529. package/dist/structure/numbering.d.ts.map +1 -1
  530. package/dist/structure/numbering.js +11 -6
  531. package/dist/structure/numbering.js.map +1 -1
  532. package/dist/structure/of.d.ts +3 -1
  533. package/dist/structure/of.d.ts.map +1 -1
  534. package/dist/structure/of.js +5 -1
  535. package/dist/structure/of.js.map +1 -1
  536. package/dist/structure/percent-sum.d.ts +16 -0
  537. package/dist/structure/percent-sum.d.ts.map +1 -0
  538. package/dist/structure/percent-sum.js +104 -0
  539. package/dist/structure/percent-sum.js.map +1 -0
  540. package/dist/structure/quoted-number.d.ts +8 -0
  541. package/dist/structure/quoted-number.d.ts.map +1 -0
  542. package/dist/structure/quoted-number.js +34 -0
  543. package/dist/structure/quoted-number.js.map +1 -0
  544. package/dist/structure/relative-find.d.ts.map +1 -1
  545. package/dist/structure/relative-find.js +34 -46
  546. package/dist/structure/relative-find.js.map +1 -1
  547. package/dist/structure/relative-resolve.d.ts.map +1 -1
  548. package/dist/structure/relative-resolve.js +76 -30
  549. package/dist/structure/relative-resolve.js.map +1 -1
  550. package/dist/structure/runs.d.ts +2 -0
  551. package/dist/structure/runs.d.ts.map +1 -1
  552. package/dist/structure/runs.js +2 -2
  553. package/dist/structure/runs.js.map +1 -1
  554. package/dist/structure/sexp.d.ts +1 -1
  555. package/dist/structure/sexp.d.ts.map +1 -1
  556. package/dist/structure/sexp.js +18 -6
  557. package/dist/structure/sexp.js.map +1 -1
  558. package/dist/structure/total.d.ts +3 -0
  559. package/dist/structure/total.d.ts.map +1 -1
  560. package/dist/structure/total.js +2 -2
  561. package/dist/structure/total.js.map +1 -1
  562. package/dist/structure/universal.d.ts +14 -2
  563. package/dist/structure/universal.d.ts.map +1 -1
  564. package/dist/structure/universal.js +17 -2
  565. package/dist/structure/universal.js.map +1 -1
  566. package/dist/structure/wrapped-tail.d.ts +14 -7
  567. package/dist/structure/wrapped-tail.d.ts.map +1 -1
  568. package/dist/structure/wrapped-tail.js +33 -8
  569. package/dist/structure/wrapped-tail.js.map +1 -1
  570. package/dist/style-load.d.ts +4 -0
  571. package/dist/style-load.d.ts.map +1 -0
  572. package/dist/style-load.js +28 -0
  573. package/dist/style-load.js.map +1 -0
  574. package/dist/style-parse.d.ts +19 -0
  575. package/dist/style-parse.d.ts.map +1 -0
  576. package/dist/style-parse.js +60 -0
  577. package/dist/style-parse.js.map +1 -0
  578. package/dist/subheading-line.d.ts +6 -0
  579. package/dist/subheading-line.d.ts.map +1 -0
  580. package/dist/subheading-line.js +57 -0
  581. package/dist/subheading-line.js.map +1 -0
  582. package/dist/template-syntax.d.ts +17 -0
  583. package/dist/template-syntax.d.ts.map +1 -0
  584. package/dist/template-syntax.js +140 -0
  585. package/dist/template-syntax.js.map +1 -0
  586. package/dist/text-above.d.ts +19 -0
  587. package/dist/text-above.d.ts.map +1 -0
  588. package/dist/text-above.js +43 -0
  589. package/dist/text-above.js.map +1 -0
  590. package/dist/text-figures.d.ts +10 -0
  591. package/dist/text-figures.d.ts.map +1 -0
  592. package/dist/text-figures.js +131 -0
  593. package/dist/text-figures.js.map +1 -0
  594. package/dist/trailing-aside.d.ts +2 -0
  595. package/dist/trailing-aside.d.ts.map +1 -0
  596. package/dist/trailing-aside.js +53 -0
  597. package/dist/trailing-aside.js.map +1 -0
  598. package/dist/tree-walk.d.ts +18 -0
  599. package/dist/tree-walk.d.ts.map +1 -0
  600. package/dist/tree-walk.js +45 -0
  601. package/dist/tree-walk.js.map +1 -0
  602. package/genres.yaml +26 -0
  603. package/package.json +5 -3
  604. package/reference-headings.yaml +17 -0
  605. package/rules/adverb-overuse.yaml +19 -0
  606. package/rules/agentless-passive.yaml +19 -0
  607. package/rules/agreement-slip.yaml +14 -1
  608. package/rules/ai-generated-composite.yaml +102 -0
  609. package/rules/ai-tell.yaml +23 -0
  610. package/rules/announced-count-mismatch.yaml +57 -0
  611. package/rules/announcing-opener.yaml +51 -0
  612. package/rules/assistant-residue.yaml +52 -0
  613. package/rules/bold-density.yaml +31 -0
  614. package/rules/broken-link.yaml +65 -0
  615. package/rules/closing-cliche.yaml +33 -0
  616. package/rules/colon-lead-in.yaml +59 -0
  617. package/rules/concrete-evidence-density.yaml +41 -0
  618. package/rules/contraction-consistency.yaml +18 -0
  619. package/rules/contrast-framing.yaml +57 -0
  620. package/rules/cushion-phrase-density.yaml +25 -0
  621. package/rules/dangling-figure-reference.yaml +61 -0
  622. package/rules/dangling-reference.yaml +53 -1
  623. package/rules/date-order.yaml +33 -1
  624. package/rules/date-range-reversed.yaml +46 -0
  625. package/rules/date-weekday-mismatch.yaml +17 -1
  626. package/rules/double-keigo.yaml +16 -0
  627. package/rules/doubled-punctuation.yaml +45 -0
  628. package/rules/doubled-word.yaml +17 -1
  629. package/rules/duplicate-definition.yaml +59 -1
  630. package/rules/emoji-density.yaml +25 -0
  631. package/rules/empty-conclusion.yaml +28 -0
  632. package/rules/empty-intensifier.yaml +19 -0
  633. package/rules/excessive-hedging.yaml +19 -0
  634. package/rules/expletive-construction.yaml +19 -0
  635. package/rules/heading-echo.yaml +31 -0
  636. package/rules/heading-level-skip.yaml +78 -0
  637. package/rules/hiragana-fukushi.yaml +16 -0
  638. package/rules/image-alt-text.yaml +54 -0
  639. package/rules/internal-jargon.yaml +21 -0
  640. package/rules/katakana-long-vowel.yaml +98 -0
  641. package/rules/kutoten-consistency.yaml +53 -0
  642. package/rules/latin-spacing.yaml +23 -4
  643. package/rules/max-kanji-continuous.yaml +16 -0
  644. package/rules/max-paragraph-length.yaml +33 -0
  645. package/rules/max-sentence-length.yaml +32 -0
  646. package/rules/ngram-repetition.yaml +29 -2
  647. package/rules/no-doubled-joshi.yaml +16 -0
  648. package/rules/no-em-dash.yaml +25 -0
  649. package/rules/no-mixed-desumasu.yaml +27 -2
  650. package/rules/no-nakaguro-parallel.yaml +20 -0
  651. package/rules/numbering-gap.yaml +53 -1
  652. package/rules/oxford-comma-consistency.yaml +18 -0
  653. package/rules/padded-intro.yaml +19 -0
  654. package/rules/paragraph-length-variance.yaml +49 -0
  655. package/rules/percent-sum-mismatch.yaml +58 -0
  656. package/rules/preamble-length.yaml +56 -0
  657. package/rules/preferred-term.yaml +27 -0
  658. package/rules/proper-noun-density.yaml +26 -0
  659. package/rules/repeated-conjunction.yaml +47 -0
  660. package/rules/repeated-sentence-head.yaml +23 -0
  661. package/rules/required-sections.yaml +49 -0
  662. package/rules/risk-disclosure.yaml +22 -0
  663. package/rules/rule-of-three.yaml +97 -0
  664. package/rules/sasete-itadaku.yaml +18 -0
  665. package/rules/section-length-uniformity.yaml +89 -0
  666. package/rules/sentence-initial-conjunction-run.yaml +18 -0
  667. package/rules/sentence-rhythm.yaml +25 -0
  668. package/rules/stock-transition.yaml +55 -0
  669. package/rules/stray-space.yaml +18 -0
  670. package/rules/taigen-dome-in-prose.yaml +19 -0
  671. package/rules/title-case-consistency.yaml +42 -0
  672. package/rules/total-mismatch.yaml +37 -1
  673. package/rules/unbalanced-bracket.yaml +56 -0
  674. package/rules/undefined-acronym.yaml +22 -0
  675. package/rules/unfilled-placeholder.yaml +50 -0
  676. package/rules/unqualified-superlative.yaml +19 -0
  677. package/rules/unsourced-number.yaml +16 -0
  678. package/rules/url-run-on.yaml +45 -0
  679. package/skills/chaff/SKILL.md +70 -0
  680. package/src/announced-count.ts +148 -0
  681. package/src/bare-url.ts +35 -0
  682. package/src/baseline.ts +17 -1
  683. package/src/body-section.ts +7 -0
  684. package/src/calendar-number.ts +18 -4
  685. package/src/cli-args.ts +6 -0
  686. package/src/cli-text.ts +8 -4
  687. package/src/cli.ts +37 -37
  688. package/src/commands/compare.ts +80 -0
  689. package/src/commands/eval.ts +19 -8
  690. package/src/commands/feedback.ts +12 -6
  691. package/src/commands/test.ts +2 -4
  692. package/src/commands/tree.ts +41 -5
  693. package/src/compare/atom.ts +33 -0
  694. package/src/compare/clock-time.ts +86 -0
  695. package/src/compare/extract.ts +78 -0
  696. package/src/compare/fact-text.ts +68 -0
  697. package/src/compare/markup-atoms.ts +29 -0
  698. package/src/compare/match.ts +105 -0
  699. package/src/compare/outcome.ts +70 -0
  700. package/src/compare/proper-nouns.ts +65 -0
  701. package/src/compare/render.ts +102 -0
  702. package/src/compare/spans.ts +17 -0
  703. package/src/compare/text-atoms.ts +95 -0
  704. package/src/compare/text.ts +120 -0
  705. package/src/compare/tree-atoms.ts +80 -0
  706. package/src/compare/written-names.ts +26 -0
  707. package/src/config/load.ts +21 -0
  708. package/src/config/option-problems.ts +70 -0
  709. package/src/config/read.ts +8 -0
  710. package/src/config/rule-problems.ts +12 -2
  711. package/src/config/style.ts +42 -0
  712. package/src/config/warnings.ts +13 -0
  713. package/src/config/write.ts +18 -4
  714. package/src/custom/bounded-match.ts +58 -0
  715. package/src/custom/load.ts +15 -0
  716. package/src/custom/parse.ts +274 -0
  717. package/src/custom/problems.ts +89 -0
  718. package/src/custom/regex-safety.ts +118 -0
  719. package/src/custom/token-pattern.ts +84 -0
  720. package/src/detect.ts +28 -2
  721. package/src/detectors/acronym-context.ts +16 -4
  722. package/src/detectors/ai-phrasing.ts +137 -0
  723. package/src/detectors/announced-count.ts +25 -0
  724. package/src/detectors/broken-link.ts +90 -0
  725. package/src/detectors/cited-name.ts +6 -5
  726. package/src/detectors/cited-title.ts +96 -0
  727. package/src/detectors/concrete-evidence.ts +3 -2
  728. package/src/detectors/contrast-frame.ts +54 -0
  729. package/src/detectors/custom.ts +89 -0
  730. package/src/detectors/dangling-figure.ts +22 -0
  731. package/src/detectors/doubled-punctuation.ts +49 -0
  732. package/src/detectors/doubled-word.ts +19 -4
  733. package/src/detectors/enumerated-runs.ts +13 -4
  734. package/src/detectors/gram-furniture.ts +30 -0
  735. package/src/detectors/gram-place.ts +9 -5
  736. package/src/detectors/gram-windows.ts +31 -0
  737. package/src/detectors/heading-case.ts +30 -2
  738. package/src/detectors/heading-code-words.ts +35 -0
  739. package/src/detectors/heading-echo.ts +8 -4
  740. package/src/detectors/heading-level-skip.ts +19 -0
  741. package/src/detectors/image-alt-text.ts +11 -0
  742. package/src/detectors/index.ts +43 -1
  743. package/src/detectors/kutoten-consistency.ts +96 -0
  744. package/src/detectors/lead-in.ts +3 -1
  745. package/src/detectors/lexicon-match.ts +9 -2
  746. package/src/detectors/list-item.ts +122 -0
  747. package/src/detectors/list-items.ts +163 -0
  748. package/src/detectors/list-lead-in.ts +33 -0
  749. package/src/detectors/list-sentence.ts +157 -0
  750. package/src/detectors/list-view.ts +157 -0
  751. package/src/detectors/long-vowel.ts +66 -0
  752. package/src/detectors/markup-finding.ts +24 -0
  753. package/src/detectors/name-numeral.ts +4 -2
  754. package/src/detectors/orthography.ts +23 -17
  755. package/src/detectors/oxford-comma.ts +178 -283
  756. package/src/detectors/place-run.ts +7 -4
  757. package/src/detectors/placeholder-text.ts +27 -0
  758. package/src/detectors/quote-around.ts +9 -0
  759. package/src/detectors/register.ts +37 -10
  760. package/src/detectors/roman-numeral.ts +3 -0
  761. package/src/detectors/sentence-ending.ts +14 -7
  762. package/src/detectors/sentence-length.ts +16 -5
  763. package/src/detectors/signals.ts +80 -46
  764. package/src/detectors/structure-tree.ts +45 -1
  765. package/src/detectors/structure.ts +3 -4
  766. package/src/detectors/superlative-name.ts +7 -4
  767. package/src/detectors/superlative-scope.ts +8 -3
  768. package/src/detectors/token-column.ts +149 -0
  769. package/src/detectors/unbalanced-bracket.ts +153 -0
  770. package/src/detectors/url-run-on.ts +27 -0
  771. package/src/document-sections.ts +46 -0
  772. package/src/document.ts +106 -138
  773. package/src/email-parts.ts +269 -0
  774. package/src/eval.ts +14 -4
  775. package/src/figure-references.ts +112 -0
  776. package/src/files.ts +5 -0
  777. package/src/heading-label.ts +55 -0
  778. package/src/in-page-nav.ts +48 -19
  779. package/src/joined-view.ts +1 -0
  780. package/src/levels.ts +14 -1
  781. package/src/line-paragraphs.ts +52 -9
  782. package/src/long-vowel.ts +99 -0
  783. package/src/markdown-node.ts +19 -0
  784. package/src/markdown-read.ts +44 -0
  785. package/src/markup.ts +210 -0
  786. package/src/mask.ts +8 -8
  787. package/src/number-name.ts +151 -24
  788. package/src/orthography.ts +10 -3
  789. package/src/page-furniture.ts +11 -2
  790. package/src/plain-source.ts +2 -1
  791. package/src/plugin.ts +101 -1
  792. package/src/profile/for-file.ts +3 -1
  793. package/src/quoted-span.ts +9 -3
  794. package/src/reasons.ts +9 -0
  795. package/src/reference-lists.ts +91 -0
  796. package/src/render/explain.ts +47 -13
  797. package/src/render/friendly.ts +20 -9
  798. package/src/render/options.ts +58 -0
  799. package/src/render/rules-json.ts +130 -14
  800. package/src/render/rules-table.ts +91 -0
  801. package/src/render/sarif.ts +4 -4
  802. package/src/render/semantic.ts +3 -3
  803. package/src/render/severity-name.ts +10 -0
  804. package/src/render/text.ts +21 -7
  805. package/src/resolve-genre.ts +4 -1
  806. package/src/rule-genres.ts +25 -0
  807. package/src/rule-guide.ts +135 -0
  808. package/src/rule-load.ts +91 -34
  809. package/src/rule-options.ts +131 -0
  810. package/src/run.ts +74 -11
  811. package/src/sentence-shape.ts +4 -5
  812. package/src/setting-problems.ts +7 -1
  813. package/src/span-cut.ts +28 -0
  814. package/src/structure/aside-depth.ts +67 -0
  815. package/src/structure/build.ts +38 -19
  816. package/src/structure/closed-pairs.ts +20 -0
  817. package/src/structure/date-range.ts +69 -0
  818. package/src/structure/known-mentions.ts +96 -0
  819. package/src/structure/numbering.ts +9 -5
  820. package/src/structure/of.ts +11 -3
  821. package/src/structure/percent-sum.ts +135 -0
  822. package/src/structure/quoted-number.ts +36 -0
  823. package/src/structure/relative-find.ts +34 -47
  824. package/src/structure/relative-resolve.ts +77 -30
  825. package/src/structure/runs.ts +2 -2
  826. package/src/structure/sexp.ts +17 -5
  827. package/src/structure/total.ts +2 -2
  828. package/src/structure/universal.ts +24 -4
  829. package/src/structure/wrapped-tail.ts +38 -13
  830. package/src/style-load.ts +28 -0
  831. package/src/style-parse.ts +77 -0
  832. package/src/subheading-line.ts +68 -0
  833. package/src/template-syntax.ts +164 -0
  834. package/src/text-above.ts +49 -0
  835. package/src/text-figures.ts +147 -0
  836. package/src/trailing-aside.ts +50 -0
  837. package/src/tree-walk.ts +48 -0
  838. package/styles/bunkacho.yaml +25 -0
  839. package/styles/ieice.yaml +29 -0
  840. package/styles/jis-z8301-2011.yaml +25 -0
@@ -0,0 +1,105 @@
1
+ import type { Atom } from "./atom.ts";
2
+
3
+ /** The same fact written another way: 1,000円 and 1000円, 2026年4月1日 and 2026/4/1, a reworded heading. */
4
+ export type Reformed = { readonly before: Atom; readonly after: Atom };
5
+
6
+ export type Comparison = {
7
+ /** In the first document and not in the second. */
8
+ readonly dropped: readonly Atom[];
9
+ /** In the second document and not in the first. */
10
+ readonly added: readonly Atom[];
11
+ readonly reformed: readonly Reformed[];
12
+ };
13
+
14
+ const identity = (atom: Atom): string => `${atom.kind}\u0000${atom.key}`;
15
+
16
+ const groupBy = <T>(items: readonly T[], keyOf: (item: T) => string): Map<string, T[]> => {
17
+ const groups = new Map<string, T[]>();
18
+ items.forEach((item) => {
19
+ const key = keyOf(item);
20
+ const group = groups.get(key);
21
+ if (group === undefined) groups.set(key, [item]);
22
+ else group.push(item);
23
+ });
24
+ return groups;
25
+ };
26
+
27
+ /** Takes and removes the first item the queue for key holds. */
28
+ type Queues<T> = { readonly groups: Map<string, T[]>; readonly taken: Map<string, number> };
29
+
30
+ const queuesOf = <T>(items: readonly T[], keyOf: (item: T) => string): Queues<T> => ({ groups: groupBy(items, keyOf), taken: new Map() });
31
+
32
+ /** The next item not yet taken under key. A cursor, not shift(): shift moves every item left, which is quadratic over a long queue. */
33
+ const takeFrom = <T>(queues: Queues<T>, key: string): T | undefined => {
34
+ const next = queues.taken.get(key) ?? 0;
35
+ const item = queues.groups.get(key)?.[next];
36
+ if (item !== undefined) queues.taken.set(key, next + 1);
37
+ return item;
38
+ };
39
+
40
+ /** Atoms of one fact: written the same way they need no report; written differently they pair as reformed; the rest are surplus. */
41
+ const matchGroup = (before: readonly Atom[], after: readonly Atom[]): Comparison => {
42
+ const afterByText = queuesOf(after, (atom) => atom.text);
43
+ const sameText = new Set<Atom>();
44
+ const unmatchedBefore = before.filter((atom) => {
45
+ const twin = takeFrom(afterByText, atom.text);
46
+ if (twin !== undefined) sameText.add(twin);
47
+ return twin === undefined;
48
+ });
49
+ const unmatchedAfter = after.filter((atom) => !sameText.has(atom));
50
+ const pairs = Math.min(unmatchedBefore.length, unmatchedAfter.length);
51
+ return {
52
+ reformed: unmatchedBefore.slice(0, pairs).map((atom, index) => ({ before: atom, after: unmatchedAfter[index] ?? atom })),
53
+ dropped: unmatchedBefore.slice(pairs),
54
+ added: unmatchedAfter.slice(pairs),
55
+ };
56
+ };
57
+
58
+ const unitQueue = (value: string | undefined, unitless: boolean): string => `${value ?? ""}\u0000${unitless ? "bare" : "unit"}`;
59
+
60
+ /**
61
+ * A number one side wrote with a unit the language package read (30 GB) and the other wrote so the unit was not read
62
+ * (30GB) is one number in another form. Two different units (5 件, 5 人) stay a dropped and an added fact.
63
+ */
64
+ const pairUnits = (comparison: Comparison): Comparison => {
65
+ const addedNumbers = queuesOf(
66
+ comparison.added.filter((atom) => atom.kind === "number"),
67
+ (atom) => unitQueue(atom.value, atom.unitless === true),
68
+ );
69
+ const paired = new Set<Atom>();
70
+ const reformed = comparison.dropped.flatMap((atom): Reformed[] => {
71
+ // Two unitless numbers of one value share a key and were already paired; only the other side's kind can match.
72
+ const found = atom.kind === "number" ? takeFrom(addedNumbers, unitQueue(atom.value, atom.unitless !== true)) : undefined;
73
+ if (found === undefined) return [];
74
+ paired.add(atom).add(found);
75
+ return [{ before: atom, after: found }];
76
+ });
77
+ return {
78
+ dropped: comparison.dropped.filter((atom) => !paired.has(atom)),
79
+ added: comparison.added.filter((atom) => !paired.has(atom)),
80
+ reformed: [...comparison.reformed, ...reformed],
81
+ };
82
+ };
83
+
84
+ const byLine = (left: Atom, right: Atom): number => left.line - right.line;
85
+
86
+ /**
87
+ * The facts of two documents compared as multisets: where a fact is written does not matter, how many times it is
88
+ * stated does. Pure: the same atoms always give the same comparison.
89
+ */
90
+ export const compareAtoms = (before: readonly Atom[], after: readonly Atom[]): Comparison => {
91
+ const beforeGroups = groupBy(before, identity);
92
+ const afterGroups = groupBy(after, identity);
93
+ const keys = [...new Set([...beforeGroups.keys(), ...afterGroups.keys()])];
94
+ const groups = keys.map((key) => matchGroup(beforeGroups.get(key) ?? [], afterGroups.get(key) ?? []));
95
+ const paired = pairUnits({
96
+ dropped: groups.flatMap((group) => group.dropped),
97
+ added: groups.flatMap((group) => group.added),
98
+ reformed: groups.flatMap((group) => group.reformed),
99
+ });
100
+ return {
101
+ dropped: paired.dropped.toSorted(byLine),
102
+ added: paired.added.toSorted(byLine),
103
+ reformed: paired.reformed.toSorted((left, right) => byLine(left.before, right.before)),
104
+ };
105
+ };
@@ -0,0 +1,70 @@
1
+ import { type Atom, type AtomKind, type Extraction, type Unread } from "./atom.ts";
2
+ import { compareAtoms, type Reformed } from "./match.ts";
3
+ import { unwrittenNames } from "./written-names.ts";
4
+
5
+ /** One document as compared: where it is, how many facts of each kind it states, and what could not be read. */
6
+ export type Side = {
7
+ readonly path: string;
8
+ readonly counts: Readonly<Record<AtomKind, number>>;
9
+ readonly unread: readonly Unread[];
10
+ };
11
+
12
+ /** A dropped or added fact, and whether the command line allowed its kind to change. */
13
+ export type Change = Atom & { readonly allowed: boolean };
14
+
15
+ export type Outcome = {
16
+ readonly before: Side;
17
+ readonly after: Side;
18
+ readonly dropped: readonly Change[];
19
+ readonly added: readonly Change[];
20
+ readonly reformed: readonly Reformed[];
21
+ /** No fact dropped or added, other than of a kind allowed to be. */
22
+ readonly ok: boolean;
23
+ };
24
+
25
+ /** Kinds whose facts may be dropped or added on purpose (--allow-dropped, --allow-added). */
26
+ export type Allowed = { readonly dropped: ReadonlySet<AtomKind>; readonly added: ReadonlySet<AtomKind> };
27
+
28
+ const NOTHING_ALLOWED: Allowed = { dropped: new Set(), added: new Set() };
29
+
30
+ const NO_FACTS: Readonly<Record<AtomKind, number>> = {
31
+ number: 0,
32
+ date: 0,
33
+ time: 0,
34
+ url: 0,
35
+ code: 0,
36
+ name: 0,
37
+ quote: 0,
38
+ heading: 0,
39
+ reference: 0,
40
+ footnote: 0,
41
+ };
42
+
43
+ const countsOf = (atoms: readonly Atom[]): Record<AtomKind, number> => {
44
+ const counts = { ...NO_FACTS };
45
+ atoms.forEach((atom) => {
46
+ counts[atom.kind] += 1;
47
+ });
48
+ return counts;
49
+ };
50
+
51
+ const sideOf = (path: string, extraction: Extraction): Side => ({ path, counts: countsOf(extraction.atoms), unread: extraction.unread });
52
+
53
+ const marked = (atoms: readonly Atom[], allowed: ReadonlySet<AtomKind>): Change[] => atoms.map((atom) => ({ ...atom, allowed: allowed.has(atom.kind) }));
54
+
55
+ export type Compared = { readonly path: string; readonly extraction: Extraction };
56
+
57
+ /** What `chaff compare` reports: the comparison of two documents' facts, with the allowed kinds set aside. */
58
+ export const outcomeOf = (before: Compared, after: Compared, allowed: Allowed = NOTHING_ALLOWED): Outcome => {
59
+ const comparison = compareAtoms(before.extraction.atoms, after.extraction.atoms);
60
+ const dropped = marked(unwrittenNames(comparison.dropped, before.extraction.atoms, after.extraction.nameText), allowed.dropped);
61
+ const added = marked(unwrittenNames(comparison.added, after.extraction.atoms, before.extraction.nameText), allowed.added);
62
+ return {
63
+ before: sideOf(before.path, before.extraction),
64
+ after: sideOf(after.path, after.extraction),
65
+ dropped,
66
+ added,
67
+ reformed: comparison.reformed,
68
+ ok: [...dropped, ...added].every((change) => change.allowed),
69
+ };
70
+ };
@@ -0,0 +1,65 @@
1
+ import type { ProseDocument, Span, Token } from "../plugin.ts";
2
+ import type { Atom } from "./atom.ts";
3
+ import { overlapsAny, type SpanIndex } from "./spans.ts";
4
+
5
+ const PROPER_NOUN = "PROPN";
6
+
7
+ /** Two proper nouns with at most one space between them are one name: New York, 日本 銀行. */
8
+ const MAX_GAP = 1;
9
+
10
+ type Run = Span & { readonly words: number };
11
+
12
+ const joins = (run: Run | undefined, token: Token, text: string): run is Run =>
13
+ run !== undefined && token.span.start - run.end <= MAX_GAP && text.slice(run.end, token.span.start).trim() === "";
14
+
15
+ /** Adjacent proper nouns, each run one name. */
16
+ const runsOf = (tokens: readonly Token[], text: string): Run[] =>
17
+ tokens.reduce<Run[]>((runs, token) => {
18
+ if (token.pos !== PROPER_NOUN) return runs;
19
+ const last = runs.at(-1);
20
+ if (joins(last, token, text)) runs[runs.length - 1] = { start: last.start, end: token.span.end, words: last.words + 1 };
21
+ else runs.push({ start: token.span.start, end: token.span.end, words: 1 });
22
+ return runs;
23
+ }, []);
24
+
25
+ /** Words written in lower case. A capitalised word the same document also writes this way is a common word that began a sentence. */
26
+ const LOWER_CASE_WORD = /(?<![\p{L}\p{M}])\p{Ll}[\p{L}\p{M}]*/gu;
27
+
28
+ const lowerCaseWords = (source: string): ReadonlySet<string> => new Set(source.match(LOWER_CASE_WORD) ?? []);
29
+
30
+ const withLowerFirst = (word: string): string => `${word.charAt(0).toLowerCase()}${word.slice(1)}`;
31
+
32
+ /** A one-word name the document also writes in lower case ("Scammers" opening a sentence, "scammers" elsewhere). */
33
+ const isCommonWord = (run: Run, written: string, lowerCase: ReadonlySet<string>): boolean => {
34
+ const lowered = withLowerFirst(written);
35
+ return run.words === 1 && lowered !== written && lowerCase.has(lowered);
36
+ };
37
+
38
+ export type NounInput = {
39
+ readonly doc: ProseDocument;
40
+ /** Facts other readers took and what is not prose: a proper noun inside one is read as that, not again here. */
41
+ readonly taken: SpanIndex;
42
+ readonly lineOf: (offset: number) => number;
43
+ };
44
+
45
+ /** A name as one spelling: full-width letters and line breaks inside a name do not make another name. */
46
+ export const nameKey = (written: string): string => written.normalize("NFKC").replace(/\s+/gu, " ");
47
+
48
+ /** Whether the language package tagged parts of speech: without them, a document with sentences has no proper nouns to read. */
49
+ export const readsProperNouns = (doc: ProseDocument): boolean => doc.sentences.length === 0 || doc.sentences.some((sentence) => sentence.tokens !== undefined);
50
+
51
+ /**
52
+ * Proper nouns as the part-of-speech tagger reads them in sentences. Headings are left out: they are compared as
53
+ * headings, may be reworded, and a title-case heading makes every word look like a name.
54
+ */
55
+ export const properNouns = (input: NounInput): Atom[] => {
56
+ const { source } = input.doc;
57
+ const lowerCase = lowerCaseWords(source);
58
+ return input.doc.sentences.flatMap((sentence) =>
59
+ runsOf(sentence.tokens ?? [], source).flatMap((run): Atom[] => {
60
+ const text = source.slice(run.start, run.end);
61
+ if (overlapsAny(input.taken, run) || isCommonWord(run, text, lowerCase)) return [];
62
+ return [{ kind: "name", key: nameKey(text), text, line: input.lineOf(run.start) }];
63
+ }),
64
+ );
65
+ };
@@ -0,0 +1,102 @@
1
+ import { ATOM_KINDS, type Atom, type AtomKind } from "./atom.ts";
2
+ import type { Reformed } from "./match.ts";
3
+ import type { Change, Outcome, Side } from "./outcome.ts";
4
+ import type { CompareText } from "./text.ts";
5
+
6
+ /** How much of a fact is shown: a code block is shown by its first line. */
7
+ const SHOWN_WIDTH = 60;
8
+
9
+ /** One line of a fact as written: a code block or a wrapped quotation is shown on one line, cut at SHOWN_WIDTH. */
10
+ const shown = (text: string): string => {
11
+ const oneLine = text.replace(/\s+/gu, " ").trim();
12
+ return oneLine.length > SHOWN_WIDTH ? `${oneLine.slice(0, SHOWN_WIDTH)}…` : oneLine;
13
+ };
14
+
15
+ const totalOf = (side: Side): number => ATOM_KINDS.reduce((sum, kind) => sum + side.counts[kind], 0);
16
+
17
+ /** The count line. Every kind is listed, zeros too: "nothing dropped" reads as N facts checked, never as nothing looked at. */
18
+ const checkedLine = (outcome: Outcome, text: CompareText): string => {
19
+ const perKind = ATOM_KINDS.map((kind) => `${text.kinds[kind]} ${String(outcome.before.counts[kind])}→${String(outcome.after.counts[kind])}`);
20
+ return text.checked(totalOf(outcome.before), totalOf(outcome.after), perKind.join(text.separator));
21
+ };
22
+
23
+ const failing = (changes: readonly Change[]): number => changes.filter((change) => !change.allowed).length;
24
+
25
+ const verdict = (outcome: Outcome, text: CompareText): string => (outcome.ok ? text.clean : text.failed(failing(outcome.dropped), failing(outcome.added)));
26
+
27
+ const kindColumn = (kind: AtomKind, text: CompareText): string => `${text.kind[kind]}: `;
28
+
29
+ const changeLine = (change: Change, path: string, flag: string, text: CompareText): string =>
30
+ ` ${kindColumn(change.kind, text)}${shown(change.text)} (${path}:${String(change.line)})${change.allowed ? text.allowed(flag) : ""}`;
31
+
32
+ const reformedLine = (pair: Reformed, text: CompareText): string =>
33
+ ` ${kindColumn(pair.before.kind, text)}${shown(pair.before.text)} → ${shown(pair.after.text)} (${text.lines(pair.before.line, pair.after.line)})`;
34
+
35
+ const block = (heading: string, lines: readonly string[]): string[] => (lines.length === 0 ? [] : ["", heading, ...lines]);
36
+
37
+ const unreadBlock = (outcome: Outcome, text: CompareText): string[] => {
38
+ const sides = [outcome.before, outcome.after];
39
+ const lines = sides.flatMap((side) => side.unread.map((unread) => ` ${side.path}: ${text.unread(unread.kind, unread.reason)}`));
40
+ return block(text.unreadHeading, lines);
41
+ };
42
+
43
+ /** For a person: what was dropped and added, with the line in each file, then what only changed its form. */
44
+ export const renderFriendly = (outcome: Outcome, text: CompareText): string => {
45
+ const { before, after } = outcome;
46
+ return [
47
+ `${before.path} → ${after.path}`,
48
+ ...block(
49
+ text.droppedHeading(outcome.dropped.length, before.path, after.path),
50
+ outcome.dropped.map((change) => changeLine(change, before.path, "--allow-dropped", text)),
51
+ ),
52
+ ...block(
53
+ text.addedHeading(outcome.added.length, after.path),
54
+ outcome.added.map((change) => changeLine(change, after.path, "--allow-added", text)),
55
+ ),
56
+ ...block(
57
+ text.reformedHeading(outcome.reformed.length),
58
+ outcome.reformed.map((pair) => reformedLine(pair, text)),
59
+ ),
60
+ ...unreadBlock(outcome, text),
61
+ "",
62
+ checkedLine(outcome, text),
63
+ verdict(outcome, text),
64
+ ].join("\n");
65
+ };
66
+
67
+ const compactChange = (change: Change, path: string, word: string): string =>
68
+ `${path}:${String(change.line)}: ${word} ${change.kind} ${shown(change.text)}${change.allowed ? " (allowed)" : ""}`;
69
+
70
+ /** For an engineer and grep: one line per fact, the words dropped / added / reformed and the kind in English. */
71
+ export const renderCompact = (outcome: Outcome, text: CompareText): string => {
72
+ const { before, after } = outcome;
73
+ return [
74
+ ...outcome.dropped.map((change) => compactChange(change, before.path, "dropped")),
75
+ ...outcome.added.map((change) => compactChange(change, after.path, "added")),
76
+ ...outcome.reformed.map(
77
+ (pair) =>
78
+ `${before.path}:${String(pair.before.line)} → ${after.path}:${String(pair.after.line)}: reformed ${pair.before.kind} ${shown(pair.before.text)} → ${shown(pair.after.text)}`,
79
+ ),
80
+ ...before.unread.map((unread) => `${before.path}: unread ${unread.kind} (${unread.reason})`),
81
+ ...after.unread.map((unread) => `${after.path}: unread ${unread.kind} (${unread.reason})`),
82
+ checkedLine(outcome, text),
83
+ text.tally(failing(outcome.dropped), failing(outcome.added), outcome.reformed.length),
84
+ ].join("\n");
85
+ };
86
+
87
+ const atomJson = (atom: Atom): Readonly<Record<string, string | number>> => ({ kind: atom.kind, key: atom.key, text: atom.text, line: atom.line });
88
+
89
+ /** For an AI to act on: every dropped and added fact with its line, the reformed pairs, and the counts per kind. */
90
+ export const renderJson = (outcome: Outcome): string =>
91
+ JSON.stringify(
92
+ {
93
+ ok: outcome.ok,
94
+ before: outcome.before,
95
+ after: outcome.after,
96
+ dropped: outcome.dropped.map((change) => ({ ...atomJson(change), allowed: change.allowed })),
97
+ added: outcome.added.map((change) => ({ ...atomJson(change), allowed: change.allowed })),
98
+ reformed: outcome.reformed.map((pair) => ({ kind: pair.before.kind, key: pair.before.key, before: atomJson(pair.before), after: atomJson(pair.after) })),
99
+ },
100
+ null,
101
+ 2,
102
+ );
@@ -0,0 +1,17 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import { firstEndingAfter } from "../soft-break.ts";
3
+ import { mergeSpans } from "../span-merge.ts";
4
+
5
+ /** Spans made ready for lookups: sorted and merged, so each lookup is a binary search, not a scan of every span. */
6
+ export type SpanIndex = readonly Span[];
7
+
8
+ export const spanIndex = (spans: readonly Span[]): SpanIndex => mergeSpans(spans, false);
9
+
10
+ /** Whether span shares at least one character with a span in the index. */
11
+ export const overlapsAny = (index: SpanIndex, span: Span): boolean => {
12
+ const region = index[firstEndingAfter(index, span.start)];
13
+ return region !== undefined && region.start < span.end;
14
+ };
15
+
16
+ /** Whether the character at offset is inside a span in the index. */
17
+ export const coversOffset = (index: SpanIndex, offset: number): boolean => overlapsAny(index, { start: offset, end: offset + 1 });
@@ -0,0 +1,95 @@
1
+ import { QUOTATION_MARKS, quotedSpans } from "../quoted-span.ts";
2
+ import type { Span } from "../plugin.ts";
3
+ import type { Atom } from "./atom.ts";
4
+ import { coversOffset, overlapsAny, spanIndex, type SpanIndex } from "./spans.ts";
5
+
6
+ /** Where a reader looks, and how it says where it found something. */
7
+ export type TextInput = { readonly text: string; readonly source: string; readonly lineOf: (offset: number) => number };
8
+
9
+ /** A fact found in the text: where it is and its key. */
10
+ export type Found = Span & { readonly key: string };
11
+
12
+ export const atomsOf = (found: readonly Found[], kind: Atom["kind"], input: TextInput): Atom[] =>
13
+ found.map((fact) => ({ kind, key: fact.key, text: input.source.slice(fact.start, fact.end), line: input.lineOf(fact.start) }));
14
+
15
+ const DIGIT = "[0-90-9]";
16
+ const DATE_SEPARATOR = "[-/./.]";
17
+
18
+ /** 2026/4/1, 2026-04-01, 2026.4.1: a year of four digits first, so the order is never in doubt. */
19
+ const NUMERIC_DATE = new RegExp(`(?<![0-90-9])(${DIGIT}{4})${DATE_SEPARATOR}(${DIGIT}{1,2})${DATE_SEPARATOR}(${DIGIT}{1,2})(?![0-90-9])`, "gu");
20
+
21
+ const MONTHS = 12;
22
+ const LAST_DAY = 31;
23
+ const PADDED = 2;
24
+
25
+ const dateKey = (year: string, month: string, day: string): string | undefined => {
26
+ const [m, d] = [Number(month.normalize("NFKC")), Number(day.normalize("NFKC"))];
27
+ if (m < 1 || m > MONTHS || d < 1 || d > LAST_DAY) return undefined;
28
+ return [year.normalize("NFKC"), String(m).padStart(PADDED, "0"), String(d).padStart(PADDED, "0")].join("-");
29
+ };
30
+
31
+ /** Dates written in figures only, where the language's date reader did not already read a date. */
32
+ export const numericDates = (text: string, taken: SpanIndex): Found[] =>
33
+ [...text.matchAll(NUMERIC_DATE)].flatMap((match) => {
34
+ const key = dateKey(match[1] ?? "", match[2] ?? "", match[3] ?? "");
35
+ const span = { start: match.index, end: match.index + match[0].length };
36
+ return key === undefined || overlapsAny(taken, span) ? [] : [{ ...span, key }];
37
+ });
38
+
39
+ /** A footnote mark `[^1]`, or the footnote itself `[^1]:`. */
40
+ const FOOTNOTE = /\[\^[^\]\s]+\]:?/gu;
41
+
42
+ export const footnotes = (text: string): Found[] =>
43
+ [...text.matchAll(FOOTNOTE)].map((match) => ({ start: match.index, end: match.index + match[0].length, key: match[0] }));
44
+
45
+ /** 1,200 or 12.5 or 3: the digits, a thousands separator only between groups of three, and a decimal part. */
46
+ const NUMBER = /[0-90-9]+(?:[,,][0-90-9]{3})*(?:[..][0-90-9]+)?/gu;
47
+
48
+ const valueOf = (written: string): string => String(Number(written.normalize("NFKC").replace(/,/gu, "")));
49
+
50
+ /** Numbers no other reader took: written with no unit the language package knows, or with none at all. */
51
+ export const bareNumbers = (input: TextInput, taken: SpanIndex): Atom[] =>
52
+ [...input.text.matchAll(NUMBER)].flatMap((match): Atom[] => {
53
+ if (coversOffset(taken, match.index)) return [];
54
+ const value = valueOf(match[0]);
55
+ return [{ kind: "number", key: `${value} `, text: match[0], line: input.lineOf(match.index), unitless: true, value }];
56
+ });
57
+
58
+ /** A paragraph break: a quotation does not run over one, and reading paragraph by paragraph keeps each read short. */
59
+ const PARAGRAPH_BREAK = /\n[ \t]*\n/gu;
60
+
61
+ const paragraphsOf = (text: string): Span[] => {
62
+ const breaks = [...text.matchAll(PARAGRAPH_BREAK)].map((match) => match.index);
63
+ return [-1, ...breaks].map((from, index) => ({ start: from + 1, end: breaks[index] ?? text.length }));
64
+ };
65
+
66
+ /** What is quoted, as one spelling: line breaks and runs of spaces are how it was wrapped, not what it says. */
67
+ const quoteKey = (inner: string): string => inner.normalize("NFKC").replace(/\s+/gu, " ").trim();
68
+
69
+ /** Quoted strings: 「」『』, “” and "". The key is what is inside; the marks are how it is written. */
70
+ export const quotations = (input: TextInput): Atom[] =>
71
+ paragraphsOf(input.text).flatMap((paragraph) =>
72
+ quotedSpans(input.text.slice(paragraph.start, paragraph.end), QUOTATION_MARKS).flatMap((inner) => {
73
+ const key = quoteKey(input.text.slice(paragraph.start + inner.start, paragraph.start + inner.end));
74
+ const span = { start: paragraph.start + inner.start - 1, end: paragraph.start + inner.end + 1 };
75
+ return key === "" ? [] : atomsOf([{ ...span, key }], "quote", input);
76
+ }),
77
+ );
78
+
79
+ const occurrences = (text: string, name: string): Span[] => {
80
+ const found: Span[] = [];
81
+ for (let at = text.indexOf(name); at !== -1; at = text.indexOf(name, at + name.length)) found.push({ start: at, end: at + name.length });
82
+ return found;
83
+ };
84
+
85
+ /** The team's own names (chaff.yaml's `names:`), wherever they are written. Longer names first: "Acme Cloud" before "Acme". */
86
+ export const teamNames = (text: string, names: readonly string[]): Found[] => {
87
+ const longestFirst = [...new Set(names.filter((name) => name !== ""))].toSorted((left, right) => right.length - left.length);
88
+ return longestFirst
89
+ .reduce<Found[]>((taken, name) => {
90
+ const index = spanIndex(taken);
91
+ const fresh = occurrences(text, name).filter((span) => !overlapsAny(index, span));
92
+ return [...taken, ...fresh.map((span) => ({ ...span, key: name }))];
93
+ }, [])
94
+ .toSorted((left, right) => left.start - right.start);
95
+ };
@@ -0,0 +1,120 @@
1
+ import type { Texts } from "../ui.ts";
2
+ import type { AtomKind, UnreadReason } from "./atom.ts";
3
+ import { counted } from "../render/plural.ts";
4
+
5
+ export type CompareText = {
6
+ readonly usage: string;
7
+ readonly unknownKind: (kind: string, kinds: string) => string;
8
+ /** A kind counted (numbers 3→3) and a kind of one fact (number: 1,000円). */
9
+ readonly kinds: Readonly<Record<AtomKind, string>>;
10
+ readonly kind: Readonly<Record<AtomKind, string>>;
11
+ readonly unread: (kind: AtomKind, reason: UnreadReason) => string;
12
+ readonly unreadHeading: string;
13
+ readonly droppedHeading: (count: number, before: string, after: string) => string;
14
+ readonly addedHeading: (count: number, after: string) => string;
15
+ readonly reformedHeading: (count: number) => string;
16
+ readonly allowed: (flag: string) => string;
17
+ readonly lines: (before: number, after: number) => string;
18
+ readonly checked: (before: number, after: number, perKind: string) => string;
19
+ readonly separator: string;
20
+ readonly clean: string;
21
+ readonly failed: (dropped: number, added: number) => string;
22
+ /** The tally of the compact output. */
23
+ readonly tally: (dropped: number, added: number, reformed: number) => string;
24
+ };
25
+
26
+ const KINDS_JA: Readonly<Record<AtomKind, string>> = {
27
+ number: "数",
28
+ date: "日付",
29
+ time: "時刻",
30
+ url: "URL",
31
+ code: "コード",
32
+ name: "固有名詞",
33
+ quote: "引用",
34
+ heading: "見出し",
35
+ reference: "条項の参照",
36
+ footnote: "脚注",
37
+ };
38
+
39
+ const KINDS_EN: Readonly<Record<AtomKind, string>> = {
40
+ number: "numbers",
41
+ date: "dates",
42
+ time: "times",
43
+ url: "URLs",
44
+ code: "code",
45
+ name: "names",
46
+ quote: "quotations",
47
+ heading: "headings",
48
+ reference: "references",
49
+ footnote: "footnotes",
50
+ };
51
+
52
+ const KIND_EN: Readonly<Record<AtomKind, string>> = {
53
+ number: "number",
54
+ date: "date",
55
+ time: "time",
56
+ url: "URL",
57
+ code: "code",
58
+ name: "name",
59
+ quote: "quotation",
60
+ heading: "heading",
61
+ reference: "reference",
62
+ footnote: "footnote",
63
+ };
64
+
65
+ const UNREAD_JA: Readonly<Record<UnreadReason, (kind: AtomKind) => string>> = {
66
+ "no-structure": (kind) =>
67
+ kind === "number" ? "言語パッケージが単位を読まないので、数字だけを比べました" : "言語パッケージが文書の構造を読まないので、読んでいません",
68
+ "no-dates": () => "言語パッケージが日付を読まないので、数字だけで書いた日付(2026-04-01)だけを比べました",
69
+ "no-pos": () => "品詞を読む解析器が無いので、chaff.yaml の names: だけを比べました",
70
+ "plain-text": () => "Markdown ではないので、コードの記法がありません",
71
+ };
72
+
73
+ const UNREAD_EN: Readonly<Record<UnreadReason, (kind: AtomKind) => string>> = {
74
+ "no-structure": (kind) =>
75
+ kind === "number"
76
+ ? "the language package reads no units, so only the figures were compared"
77
+ : "the language package reads no document structure, so none were read",
78
+ "no-dates": () => "the language package reads no dates, so only dates written in figures (2026-04-01) were compared",
79
+ "no-pos": () => "there is no part-of-speech tagger, so only chaff.yaml's names: were compared",
80
+ "plain-text": () => "the document is not Markdown, so it has no code markup",
81
+ };
82
+
83
+ export const COMPARE_TEXT: Texts<CompareText> = {
84
+ ja: {
85
+ usage: "使い方: chaff compare <書き換える前> <書き換えた後> [--compact | --json] [--allow-dropped <種類>] [--allow-added <種類>] [--language ja|en|…]",
86
+ unknownKind: (kind, kinds) => `知らない種類です: ${kind}(使えるのは ${kinds})`,
87
+ kinds: KINDS_JA,
88
+ kind: KINDS_JA,
89
+ unread: (kind, reason) => `${KINDS_JA[kind]}: ${UNREAD_JA[reason](kind)}`,
90
+ unreadHeading: "読めなかったもの",
91
+ droppedHeading: (count, before, after) => `✗ 落ちた事実 ${String(count)} 件(${before} にあって ${after} に無い)`,
92
+ addedHeading: (count, after) => `✗ 足された事実 ${String(count)} 件(${after} にだけある)`,
93
+ reformedHeading: (count) => `i 書き方だけ変わった事実 ${String(count)} 件`,
94
+ allowed: (flag) => `(${flag} で許可)`,
95
+ lines: (before, after) => `${String(before)} 行目 → ${String(after)} 行目`,
96
+ checked: (before, after, perKind) => `照合した事実 ${String(before)} 件 → ${String(after)} 件: ${perKind}`,
97
+ separator: "、",
98
+ clean: "落ちた事実も足された事実もありません",
99
+ failed: (dropped, added) => `落ちた事実 ${String(dropped)} 件、足された事実 ${String(added)} 件`,
100
+ tally: (dropped, added, reformed) => `落ちた ${String(dropped)} 件、足された ${String(added)} 件、書き方だけ ${String(reformed)} 件`,
101
+ },
102
+ en: {
103
+ usage: "usage: chaff compare <before> <after> [--compact | --json] [--allow-dropped <kind>] [--allow-added <kind>] [--language ja|en|…]",
104
+ unknownKind: (kind, kinds) => `Unknown kind: ${kind} (the kinds are ${kinds})`,
105
+ kinds: KINDS_EN,
106
+ kind: KIND_EN,
107
+ unread: (kind, reason) => `${KINDS_EN[kind]}: ${UNREAD_EN[reason](kind)}`,
108
+ unreadHeading: "Not fully read",
109
+ droppedHeading: (count, before, after) => `✗ ${counted(count, "fact")} dropped (in ${before}, not in ${after})`,
110
+ addedHeading: (count, after) => `✗ ${counted(count, "fact")} added (only in ${after})`,
111
+ reformedHeading: (count) => `i ${counted(count, "fact")} written another way`,
112
+ allowed: (flag) => ` (allowed by ${flag})`,
113
+ lines: (before, after) => `line ${String(before)} → line ${String(after)}`,
114
+ checked: (before, after, perKind) => `Facts checked: ${String(before)} → ${String(after)}: ${perKind}`,
115
+ separator: ", ",
116
+ clean: "No fact dropped or added",
117
+ failed: (dropped, added) => `${counted(dropped, "fact")} dropped, ${counted(added, "fact")} added`,
118
+ tally: (dropped, added, reformed) => `${String(dropped)} dropped, ${String(added)} added, ${String(reformed)} written another way`,
119
+ },
120
+ };
@@ -0,0 +1,80 @@
1
+ import { preOrder } from "../tree-walk.ts";
2
+ import type { Span, StructureNode } from "../plugin.ts";
3
+ import type { Atom, AtomKind } from "./atom.ts";
4
+ import { coversOffset, overlapsAny, type SpanIndex } from "./spans.ts";
5
+
6
+ /** Facts the structure reader found, and where they are written, so no other reader reads the same digits again. */
7
+ export type TreeFacts = { readonly atoms: readonly Atom[]; readonly taken: readonly Span[] };
8
+
9
+ export type TreeInput = {
10
+ readonly tree: StructureNode;
11
+ readonly source: string;
12
+ /** Code and URLs: what the reader found there belongs to them. */
13
+ readonly blanked: SpanIndex;
14
+ /** Times of day: 3時30分 is one time, not the quantities 3 時 and 30 分. */
15
+ readonly times: SpanIndex;
16
+ readonly lineOf: (offset: number) => number;
17
+ };
18
+
19
+ /** Numbered units that are the document's own structure. Items (a list's 1. 2. 3.) are left out: prose may replace a list. */
20
+ const NUMBERED: ReadonlySet<string> = new Set(["chapter", "article"]);
21
+
22
+ const attr = (node: StructureNode, name: string): string => String(node.attrs[name] ?? "");
23
+
24
+ /** The unit as one spelling: GB and GB, % and % are one unit. */
25
+ const unitKey = (unit: string): string => unit.normalize("NFKC");
26
+
27
+ const quantityAtom = (node: StructureNode, text: string, line: number): Atom => {
28
+ const value = attr(node, "value");
29
+ return { kind: "number", key: `${value} ${unitKey(attr(node, "unit"))}`, text, line, value };
30
+ };
31
+
32
+ /** The same section in another wording: Section 4.2 and § 4.2 point at one place; Article 4.2 does not. */
33
+ const referenceKey = (node: StructureNode): string => {
34
+ const numbering = attr(node, "numbering") || attr(node, "unitWord");
35
+ const document = attr(node, "document");
36
+ return [numbering, attr(node, "target"), document === "" ? "" : `@${document}`].filter((part) => part !== "").join(" ");
37
+ };
38
+
39
+ const leafAtom = (node: StructureNode, kind: AtomKind, text: string, line: number): Atom => {
40
+ if (kind === "number") return quantityAtom(node, text, line);
41
+ return { kind, key: kind === "date" ? attr(node, "value") : referenceKey(node), text, line };
42
+ };
43
+
44
+ const LEAF_KINDS: Readonly<Partial<Record<string, AtomKind>>> = { quantity: "number", date: "date", reference: "reference" };
45
+
46
+ /** The label's place on its line (第3条 after "## "), or none when the line does not spell it. */
47
+ const labelSpan = (node: StructureNode, source: string): Span[] => {
48
+ const label = attr(node, "label");
49
+ const lineEnd = source.indexOf("\n", node.span.start);
50
+ const at = source.indexOf(label, node.span.start);
51
+ return label === "" || at === -1 || (lineEnd !== -1 && at > lineEnd) ? [] : [{ start: at, end: at + label.length }];
52
+ };
53
+
54
+ const numberedFacts = (node: StructureNode, input: TreeInput): TreeFacts => {
55
+ const heading = attr(node, "heading");
56
+ const text = [attr(node, "label"), heading].filter((part) => part !== "").join(" ");
57
+ return { atoms: [{ kind: "heading", key: `${node.kind} ${node.address}`, text, line: node.line }], taken: labelSpan(node, input.source) };
58
+ };
59
+
60
+ const nodeFacts = (node: StructureNode, input: TreeInput): TreeFacts => {
61
+ if (NUMBERED.has(node.kind)) return numberedFacts(node, input);
62
+ const kind = LEAF_KINDS[node.kind];
63
+ if (kind === undefined || coversOffset(input.blanked, node.span.start)) return { atoms: [], taken: [] };
64
+ if (kind === "number" && overlapsAny(input.times, node.span)) return { atoms: [], taken: [] };
65
+ const span = withUnitAfter(node, input.source);
66
+ const text = input.source.slice(span.start, span.end);
67
+ return { atoms: [leafAtom(node, kind, text, input.lineOf(span.start))], taken: [span] };
68
+ };
69
+
70
+ /** A quantity's span may stop before the unit written right after it (25 of 25%); the fact is shown with its unit. */
71
+ const withUnitAfter = (node: StructureNode, source: string): Span => {
72
+ const unit = attr(node, "unit");
73
+ return unit !== "" && source.startsWith(unit, node.span.end) ? { start: node.span.start, end: node.span.end + unit.length } : node.span;
74
+ };
75
+
76
+ /** Numbers with their unit, dates, references to articles and sections, and the numbered articles themselves. */
77
+ export const treeFacts = (input: TreeInput): TreeFacts => {
78
+ const facts = preOrder(input.tree).map((node) => nodeFacts(node, input));
79
+ return { atoms: facts.flatMap((fact) => fact.atoms), taken: facts.flatMap((fact) => fact.taken) };
80
+ };