chaffjs 0.16.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 (939) 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 +9 -2
  18. package/dist/calendar-number.d.ts.map +1 -1
  19. package/dist/calendar-number.js +18 -4
  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/closing-numbers.d.ts +20 -0
  31. package/dist/closing-numbers.d.ts.map +1 -0
  32. package/dist/closing-numbers.js +56 -0
  33. package/dist/closing-numbers.js.map +1 -0
  34. package/dist/commands/compare.d.ts +8 -0
  35. package/dist/commands/compare.d.ts.map +1 -0
  36. package/dist/commands/compare.js +72 -0
  37. package/dist/commands/compare.js.map +1 -0
  38. package/dist/commands/eval.d.ts.map +1 -1
  39. package/dist/commands/eval.js +17 -9
  40. package/dist/commands/eval.js.map +1 -1
  41. package/dist/commands/feedback.d.ts +5 -2
  42. package/dist/commands/feedback.d.ts.map +1 -1
  43. package/dist/commands/feedback.js +11 -5
  44. package/dist/commands/feedback.js.map +1 -1
  45. package/dist/commands/test.d.ts.map +1 -1
  46. package/dist/commands/test.js +13 -13
  47. package/dist/commands/test.js.map +1 -1
  48. package/dist/commands/tree.d.ts +13 -0
  49. package/dist/commands/tree.d.ts.map +1 -1
  50. package/dist/commands/tree.js +35 -5
  51. package/dist/commands/tree.js.map +1 -1
  52. package/dist/compare/atom.d.ts +31 -0
  53. package/dist/compare/atom.d.ts.map +1 -0
  54. package/dist/compare/atom.js +4 -0
  55. package/dist/compare/atom.js.map +1 -0
  56. package/dist/compare/clock-time.d.ts +8 -0
  57. package/dist/compare/clock-time.d.ts.map +1 -0
  58. package/dist/compare/clock-time.js +65 -0
  59. package/dist/compare/clock-time.js.map +1 -0
  60. package/dist/compare/extract.d.ts +15 -0
  61. package/dist/compare/extract.d.ts.map +1 -0
  62. package/dist/compare/extract.js +58 -0
  63. package/dist/compare/extract.js.map +1 -0
  64. package/dist/compare/fact-text.d.ts +23 -0
  65. package/dist/compare/fact-text.d.ts.map +1 -0
  66. package/dist/compare/fact-text.js +47 -0
  67. package/dist/compare/fact-text.js.map +1 -0
  68. package/dist/compare/markup-atoms.d.ts +11 -0
  69. package/dist/compare/markup-atoms.d.ts.map +1 -0
  70. package/dist/compare/markup-atoms.js +23 -0
  71. package/dist/compare/markup-atoms.js.map +1 -0
  72. package/dist/compare/match.d.ts +19 -0
  73. package/dist/compare/match.d.ts.map +1 -0
  74. package/dist/compare/match.js +84 -0
  75. package/dist/compare/match.js.map +1 -0
  76. package/dist/compare/outcome.d.ts +33 -0
  77. package/dist/compare/outcome.d.ts.map +1 -0
  78. package/dist/compare/outcome.js +40 -0
  79. package/dist/compare/outcome.js.map +1 -0
  80. package/dist/compare/proper-nouns.d.ts +19 -0
  81. package/dist/compare/proper-nouns.d.ts.map +1 -0
  82. package/dist/compare/proper-nouns.js +44 -0
  83. package/dist/compare/proper-nouns.js.map +1 -0
  84. package/dist/compare/render.d.ts +9 -0
  85. package/dist/compare/render.d.ts.map +1 -0
  86. package/dist/compare/render.js +64 -0
  87. package/dist/compare/render.js.map +1 -0
  88. package/dist/compare/spans.d.ts +9 -0
  89. package/dist/compare/spans.d.ts.map +1 -0
  90. package/dist/compare/spans.js +11 -0
  91. package/dist/compare/spans.js.map +1 -0
  92. package/dist/compare/text-atoms.d.ts +24 -0
  93. package/dist/compare/text-atoms.d.ts.map +1 -0
  94. package/dist/compare/text-atoms.js +67 -0
  95. package/dist/compare/text-atoms.js.map +1 -0
  96. package/dist/compare/text.d.ts +24 -0
  97. package/dist/compare/text.d.ts.map +1 -0
  98. package/dist/compare/text.js +90 -0
  99. package/dist/compare/text.js.map +1 -0
  100. package/dist/compare/tree-atoms.d.ts +20 -0
  101. package/dist/compare/tree-atoms.d.ts.map +1 -0
  102. package/dist/compare/tree-atoms.js +58 -0
  103. package/dist/compare/tree-atoms.js.map +1 -0
  104. package/dist/compare/written-names.d.ts +8 -0
  105. package/dist/compare/written-names.d.ts.map +1 -0
  106. package/dist/compare/written-names.js +24 -0
  107. package/dist/compare/written-names.js.map +1 -0
  108. package/dist/config/load.d.ts +15 -0
  109. package/dist/config/load.d.ts.map +1 -1
  110. package/dist/config/load.js +5 -0
  111. package/dist/config/load.js.map +1 -1
  112. package/dist/config/option-problems.d.ts +16 -0
  113. package/dist/config/option-problems.d.ts.map +1 -0
  114. package/dist/config/option-problems.js +58 -0
  115. package/dist/config/option-problems.js.map +1 -0
  116. package/dist/config/read.d.ts +4 -0
  117. package/dist/config/read.d.ts.map +1 -0
  118. package/dist/config/read.js +8 -0
  119. package/dist/config/read.js.map +1 -0
  120. package/dist/config/rule-problems.d.ts +2 -4
  121. package/dist/config/rule-problems.d.ts.map +1 -1
  122. package/dist/config/rule-problems.js +7 -1
  123. package/dist/config/rule-problems.js.map +1 -1
  124. package/dist/config/style.d.ts +16 -0
  125. package/dist/config/style.d.ts.map +1 -0
  126. package/dist/config/style.js +36 -0
  127. package/dist/config/style.js.map +1 -0
  128. package/dist/config/warnings.d.ts +6 -0
  129. package/dist/config/warnings.d.ts.map +1 -0
  130. package/dist/config/warnings.js +10 -0
  131. package/dist/config/warnings.js.map +1 -0
  132. package/dist/config/write.d.ts.map +1 -1
  133. package/dist/config/write.js +17 -4
  134. package/dist/config/write.js.map +1 -1
  135. package/dist/custom/bounded-match.d.ts +15 -0
  136. package/dist/custom/bounded-match.d.ts.map +1 -0
  137. package/dist/custom/bounded-match.js +43 -0
  138. package/dist/custom/bounded-match.js.map +1 -0
  139. package/dist/custom/load.d.ts +8 -0
  140. package/dist/custom/load.d.ts.map +1 -0
  141. package/dist/custom/load.js +11 -0
  142. package/dist/custom/load.js.map +1 -0
  143. package/dist/custom/parse.d.ts +70 -0
  144. package/dist/custom/parse.d.ts.map +1 -0
  145. package/dist/custom/parse.js +215 -0
  146. package/dist/custom/parse.js.map +1 -0
  147. package/dist/custom/problems.d.ts +5 -0
  148. package/dist/custom/problems.d.ts.map +1 -0
  149. package/dist/custom/problems.js +78 -0
  150. package/dist/custom/problems.js.map +1 -0
  151. package/dist/custom/regex-safety.d.ts +11 -0
  152. package/dist/custom/regex-safety.d.ts.map +1 -0
  153. package/dist/custom/regex-safety.js +110 -0
  154. package/dist/custom/regex-safety.js.map +1 -0
  155. package/dist/custom/token-pattern.d.ts +8 -0
  156. package/dist/custom/token-pattern.d.ts.map +1 -0
  157. package/dist/custom/token-pattern.js +75 -0
  158. package/dist/custom/token-pattern.js.map +1 -0
  159. package/dist/detect.d.ts.map +1 -1
  160. package/dist/detect.js +24 -2
  161. package/dist/detect.js.map +1 -1
  162. package/dist/detectors/acronym-context.d.ts +4 -0
  163. package/dist/detectors/acronym-context.d.ts.map +1 -1
  164. package/dist/detectors/acronym-context.js +25 -4
  165. package/dist/detectors/acronym-context.js.map +1 -1
  166. package/dist/detectors/acronym-expansion.d.ts +2 -0
  167. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  168. package/dist/detectors/acronym-expansion.js +104 -1
  169. package/dist/detectors/acronym-expansion.js.map +1 -1
  170. package/dist/detectors/agreement-slip.d.ts +32 -0
  171. package/dist/detectors/agreement-slip.d.ts.map +1 -0
  172. package/dist/detectors/agreement-slip.js +137 -0
  173. package/dist/detectors/agreement-slip.js.map +1 -0
  174. package/dist/detectors/ai-phrasing.d.ts +20 -0
  175. package/dist/detectors/ai-phrasing.d.ts.map +1 -0
  176. package/dist/detectors/ai-phrasing.js +112 -0
  177. package/dist/detectors/ai-phrasing.js.map +1 -0
  178. package/dist/detectors/announced-count.d.ts +4 -0
  179. package/dist/detectors/announced-count.d.ts.map +1 -0
  180. package/dist/detectors/announced-count.js +20 -0
  181. package/dist/detectors/announced-count.js.map +1 -0
  182. package/dist/detectors/broken-link.d.ts +17 -0
  183. package/dist/detectors/broken-link.d.ts.map +1 -0
  184. package/dist/detectors/broken-link.js +66 -0
  185. package/dist/detectors/broken-link.js.map +1 -0
  186. package/dist/detectors/cited-name.d.ts.map +1 -1
  187. package/dist/detectors/cited-name.js +4 -4
  188. package/dist/detectors/cited-name.js.map +1 -1
  189. package/dist/detectors/cited-title.d.ts +11 -0
  190. package/dist/detectors/cited-title.d.ts.map +1 -0
  191. package/dist/detectors/cited-title.js +75 -0
  192. package/dist/detectors/cited-title.js.map +1 -0
  193. package/dist/detectors/concrete-evidence.d.ts +1 -1
  194. package/dist/detectors/concrete-evidence.d.ts.map +1 -1
  195. package/dist/detectors/concrete-evidence.js +2 -1
  196. package/dist/detectors/concrete-evidence.js.map +1 -1
  197. package/dist/detectors/content-morphemes.d.ts +4 -0
  198. package/dist/detectors/content-morphemes.d.ts.map +1 -0
  199. package/dist/detectors/content-morphemes.js +12 -0
  200. package/dist/detectors/content-morphemes.js.map +1 -0
  201. package/dist/detectors/contrast-frame.d.ts +17 -0
  202. package/dist/detectors/contrast-frame.d.ts.map +1 -0
  203. package/dist/detectors/contrast-frame.js +32 -0
  204. package/dist/detectors/contrast-frame.js.map +1 -0
  205. package/dist/detectors/custom.d.ts +8 -0
  206. package/dist/detectors/custom.d.ts.map +1 -0
  207. package/dist/detectors/custom.js +71 -0
  208. package/dist/detectors/custom.js.map +1 -0
  209. package/dist/detectors/dangling-figure.d.ts +4 -0
  210. package/dist/detectors/dangling-figure.d.ts.map +1 -0
  211. package/dist/detectors/dangling-figure.js +18 -0
  212. package/dist/detectors/dangling-figure.js.map +1 -0
  213. package/dist/detectors/doubled-punctuation.d.ts +9 -0
  214. package/dist/detectors/doubled-punctuation.d.ts.map +1 -0
  215. package/dist/detectors/doubled-punctuation.js +33 -0
  216. package/dist/detectors/doubled-punctuation.js.map +1 -0
  217. package/dist/detectors/doubled-word.d.ts +5 -0
  218. package/dist/detectors/doubled-word.d.ts.map +1 -1
  219. package/dist/detectors/doubled-word.js +24 -11
  220. package/dist/detectors/doubled-word.js.map +1 -1
  221. package/dist/detectors/en-shape.d.ts +0 -1
  222. package/dist/detectors/en-shape.d.ts.map +1 -1
  223. package/dist/detectors/en-shape.js +0 -235
  224. package/dist/detectors/en-shape.js.map +1 -1
  225. package/dist/detectors/enumerated-runs.d.ts +1 -1
  226. package/dist/detectors/enumerated-runs.d.ts.map +1 -1
  227. package/dist/detectors/enumerated-runs.js +15 -4
  228. package/dist/detectors/enumerated-runs.js.map +1 -1
  229. package/dist/detectors/gram-furniture.d.ts +6 -0
  230. package/dist/detectors/gram-furniture.d.ts.map +1 -0
  231. package/dist/detectors/gram-furniture.js +21 -0
  232. package/dist/detectors/gram-furniture.js.map +1 -0
  233. package/dist/detectors/gram-place.d.ts +2 -0
  234. package/dist/detectors/gram-place.d.ts.map +1 -1
  235. package/dist/detectors/gram-place.js +9 -7
  236. package/dist/detectors/gram-place.js.map +1 -1
  237. package/dist/detectors/gram-windows.d.ts +9 -0
  238. package/dist/detectors/gram-windows.d.ts.map +1 -0
  239. package/dist/detectors/gram-windows.js +27 -0
  240. package/dist/detectors/gram-windows.js.map +1 -0
  241. package/dist/detectors/heading-case.d.ts +2 -0
  242. package/dist/detectors/heading-case.d.ts.map +1 -1
  243. package/dist/detectors/heading-case.js +29 -2
  244. package/dist/detectors/heading-case.js.map +1 -1
  245. package/dist/detectors/heading-code-words.d.ts +6 -0
  246. package/dist/detectors/heading-code-words.d.ts.map +1 -0
  247. package/dist/detectors/heading-code-words.js +29 -0
  248. package/dist/detectors/heading-code-words.js.map +1 -0
  249. package/dist/detectors/heading-echo.d.ts.map +1 -1
  250. package/dist/detectors/heading-echo.js +17 -20
  251. package/dist/detectors/heading-echo.js.map +1 -1
  252. package/dist/detectors/heading-level-skip.d.ts +12 -0
  253. package/dist/detectors/heading-level-skip.d.ts.map +1 -0
  254. package/dist/detectors/heading-level-skip.js +11 -0
  255. package/dist/detectors/heading-level-skip.js.map +1 -0
  256. package/dist/detectors/heading-overlap.d.ts +13 -0
  257. package/dist/detectors/heading-overlap.d.ts.map +1 -0
  258. package/dist/detectors/heading-overlap.js +52 -0
  259. package/dist/detectors/heading-overlap.js.map +1 -0
  260. package/dist/detectors/image-alt-text.d.ts +3 -0
  261. package/dist/detectors/image-alt-text.d.ts.map +1 -0
  262. package/dist/detectors/image-alt-text.js +9 -0
  263. package/dist/detectors/image-alt-text.js.map +1 -0
  264. package/dist/detectors/index.d.ts.map +1 -1
  265. package/dist/detectors/index.js +40 -2
  266. package/dist/detectors/index.js.map +1 -1
  267. package/dist/detectors/kutoten-consistency.d.ts +24 -0
  268. package/dist/detectors/kutoten-consistency.d.ts.map +1 -0
  269. package/dist/detectors/kutoten-consistency.js +77 -0
  270. package/dist/detectors/kutoten-consistency.js.map +1 -0
  271. package/dist/detectors/lead-in.d.ts +1 -0
  272. package/dist/detectors/lead-in.d.ts.map +1 -1
  273. package/dist/detectors/lead-in.js +2 -1
  274. package/dist/detectors/lead-in.js.map +1 -1
  275. package/dist/detectors/letter-joined-name.d.ts +13 -0
  276. package/dist/detectors/letter-joined-name.d.ts.map +1 -0
  277. package/dist/detectors/letter-joined-name.js +11 -0
  278. package/dist/detectors/letter-joined-name.js.map +1 -0
  279. package/dist/detectors/lexicon-match.d.ts +4 -0
  280. package/dist/detectors/lexicon-match.d.ts.map +1 -1
  281. package/dist/detectors/lexicon-match.js +7 -2
  282. package/dist/detectors/lexicon-match.js.map +1 -1
  283. package/dist/detectors/lexicon.d.ts.map +1 -1
  284. package/dist/detectors/lexicon.js +5 -2
  285. package/dist/detectors/lexicon.js.map +1 -1
  286. package/dist/detectors/list-item.d.ts +45 -0
  287. package/dist/detectors/list-item.d.ts.map +1 -0
  288. package/dist/detectors/list-item.js +88 -0
  289. package/dist/detectors/list-item.js.map +1 -0
  290. package/dist/detectors/list-items.d.ts +31 -0
  291. package/dist/detectors/list-items.d.ts.map +1 -0
  292. package/dist/detectors/list-items.js +113 -0
  293. package/dist/detectors/list-items.js.map +1 -0
  294. package/dist/detectors/list-lead-in.d.ts +7 -0
  295. package/dist/detectors/list-lead-in.d.ts.map +1 -0
  296. package/dist/detectors/list-lead-in.js +28 -0
  297. package/dist/detectors/list-lead-in.js.map +1 -0
  298. package/dist/detectors/list-sentence.d.ts +65 -0
  299. package/dist/detectors/list-sentence.d.ts.map +1 -0
  300. package/dist/detectors/list-sentence.js +105 -0
  301. package/dist/detectors/list-sentence.js.map +1 -0
  302. package/dist/detectors/list-view.d.ts +34 -0
  303. package/dist/detectors/list-view.d.ts.map +1 -0
  304. package/dist/detectors/list-view.js +137 -0
  305. package/dist/detectors/list-view.js.map +1 -0
  306. package/dist/detectors/long-vowel.d.ts +7 -0
  307. package/dist/detectors/long-vowel.d.ts.map +1 -0
  308. package/dist/detectors/long-vowel.js +57 -0
  309. package/dist/detectors/long-vowel.js.map +1 -0
  310. package/dist/detectors/markup-finding.d.ts +6 -0
  311. package/dist/detectors/markup-finding.d.ts.map +1 -0
  312. package/dist/detectors/markup-finding.js +19 -0
  313. package/dist/detectors/markup-finding.js.map +1 -0
  314. package/dist/detectors/name-numeral.d.ts +1 -1
  315. package/dist/detectors/name-numeral.d.ts.map +1 -1
  316. package/dist/detectors/name-numeral.js +2 -7
  317. package/dist/detectors/name-numeral.js.map +1 -1
  318. package/dist/detectors/orthography.d.ts.map +1 -1
  319. package/dist/detectors/orthography.js +16 -12
  320. package/dist/detectors/orthography.js.map +1 -1
  321. package/dist/detectors/oxford-comma.d.ts +9 -0
  322. package/dist/detectors/oxford-comma.d.ts.map +1 -0
  323. package/dist/detectors/oxford-comma.js +246 -0
  324. package/dist/detectors/oxford-comma.js.map +1 -0
  325. package/dist/detectors/place-run.d.ts.map +1 -1
  326. package/dist/detectors/place-run.js +10 -6
  327. package/dist/detectors/place-run.js.map +1 -1
  328. package/dist/detectors/placeholder-text.d.ts +9 -0
  329. package/dist/detectors/placeholder-text.d.ts.map +1 -0
  330. package/dist/detectors/placeholder-text.js +18 -0
  331. package/dist/detectors/placeholder-text.js.map +1 -0
  332. package/dist/detectors/quote-around.d.ts +3 -0
  333. package/dist/detectors/quote-around.d.ts.map +1 -0
  334. package/dist/detectors/quote-around.js +8 -0
  335. package/dist/detectors/quote-around.js.map +1 -0
  336. package/dist/detectors/register.d.ts +11 -3
  337. package/dist/detectors/register.d.ts.map +1 -1
  338. package/dist/detectors/register.js +28 -9
  339. package/dist/detectors/register.js.map +1 -1
  340. package/dist/detectors/roman-numeral.d.ts +2 -0
  341. package/dist/detectors/roman-numeral.d.ts.map +1 -1
  342. package/dist/detectors/roman-numeral.js +2 -0
  343. package/dist/detectors/roman-numeral.js.map +1 -1
  344. package/dist/detectors/sentence-ending.d.ts +17 -1
  345. package/dist/detectors/sentence-ending.d.ts.map +1 -1
  346. package/dist/detectors/sentence-ending.js +13 -7
  347. package/dist/detectors/sentence-ending.js.map +1 -1
  348. package/dist/detectors/sentence-length.d.ts.map +1 -1
  349. package/dist/detectors/sentence-length.js +13 -4
  350. package/dist/detectors/sentence-length.js.map +1 -1
  351. package/dist/detectors/signals.d.ts +8 -0
  352. package/dist/detectors/signals.d.ts.map +1 -1
  353. package/dist/detectors/signals.js +94 -54
  354. package/dist/detectors/signals.js.map +1 -1
  355. package/dist/detectors/stacked-hedge.d.ts +0 -7
  356. package/dist/detectors/stacked-hedge.d.ts.map +1 -1
  357. package/dist/detectors/stacked-hedge.js +6 -3
  358. package/dist/detectors/stacked-hedge.js.map +1 -1
  359. package/dist/detectors/stray-space.d.ts +7 -0
  360. package/dist/detectors/stray-space.d.ts.map +1 -0
  361. package/dist/detectors/stray-space.js +34 -0
  362. package/dist/detectors/stray-space.js.map +1 -0
  363. package/dist/detectors/structure-tree.d.ts +6 -0
  364. package/dist/detectors/structure-tree.d.ts.map +1 -1
  365. package/dist/detectors/structure-tree.js +37 -1
  366. package/dist/detectors/structure-tree.js.map +1 -1
  367. package/dist/detectors/structure.d.ts +4 -4
  368. package/dist/detectors/structure.d.ts.map +1 -1
  369. package/dist/detectors/structure.js +7 -6
  370. package/dist/detectors/structure.js.map +1 -1
  371. package/dist/detectors/superlative-name.d.ts.map +1 -1
  372. package/dist/detectors/superlative-name.js +8 -5
  373. package/dist/detectors/superlative-name.js.map +1 -1
  374. package/dist/detectors/superlative-scope.d.ts.map +1 -1
  375. package/dist/detectors/superlative-scope.js +9 -4
  376. package/dist/detectors/superlative-scope.js.map +1 -1
  377. package/dist/detectors/token-column.d.ts +46 -0
  378. package/dist/detectors/token-column.d.ts.map +1 -0
  379. package/dist/detectors/token-column.js +95 -0
  380. package/dist/detectors/token-column.js.map +1 -0
  381. package/dist/detectors/unbalanced-bracket.d.ts +22 -0
  382. package/dist/detectors/unbalanced-bracket.d.ts.map +1 -0
  383. package/dist/detectors/unbalanced-bracket.js +119 -0
  384. package/dist/detectors/unbalanced-bracket.js.map +1 -0
  385. package/dist/detectors/url-run-on.d.ts +9 -0
  386. package/dist/detectors/url-run-on.d.ts.map +1 -0
  387. package/dist/detectors/url-run-on.js +19 -0
  388. package/dist/detectors/url-run-on.js.map +1 -0
  389. package/dist/document-sections.d.ts +13 -0
  390. package/dist/document-sections.d.ts.map +1 -0
  391. package/dist/document-sections.js +32 -0
  392. package/dist/document-sections.js.map +1 -0
  393. package/dist/document.d.ts +11 -4
  394. package/dist/document.d.ts.map +1 -1
  395. package/dist/document.js +99 -95
  396. package/dist/document.js.map +1 -1
  397. package/dist/email-parts.d.ts +28 -0
  398. package/dist/email-parts.d.ts.map +1 -0
  399. package/dist/email-parts.js +230 -0
  400. package/dist/email-parts.js.map +1 -0
  401. package/dist/eval.d.ts +2 -1
  402. package/dist/eval.d.ts.map +1 -1
  403. package/dist/eval.js +16 -4
  404. package/dist/eval.js.map +1 -1
  405. package/dist/figure-references.d.ts +30 -0
  406. package/dist/figure-references.d.ts.map +1 -0
  407. package/dist/figure-references.js +76 -0
  408. package/dist/figure-references.js.map +1 -0
  409. package/dist/files.d.ts +2 -0
  410. package/dist/files.d.ts.map +1 -1
  411. package/dist/files.js +4 -0
  412. package/dist/files.js.map +1 -1
  413. package/dist/genre-parse.d.ts +5 -1
  414. package/dist/genre-parse.d.ts.map +1 -1
  415. package/dist/genre-parse.js +16 -3
  416. package/dist/genre-parse.js.map +1 -1
  417. package/dist/genre-suggest.d.ts.map +1 -1
  418. package/dist/genre-suggest.js +15 -1
  419. package/dist/genre-suggest.js.map +1 -1
  420. package/dist/heading-label.d.ts +22 -0
  421. package/dist/heading-label.d.ts.map +1 -0
  422. package/dist/heading-label.js +50 -0
  423. package/dist/heading-label.js.map +1 -0
  424. package/dist/in-page-nav.d.ts +4 -1
  425. package/dist/in-page-nav.d.ts.map +1 -1
  426. package/dist/in-page-nav.js +53 -18
  427. package/dist/in-page-nav.js.map +1 -1
  428. package/dist/joined-view.d.ts.map +1 -1
  429. package/dist/joined-view.js +1 -0
  430. package/dist/joined-view.js.map +1 -1
  431. package/dist/levels.d.ts +5 -1
  432. package/dist/levels.d.ts.map +1 -1
  433. package/dist/levels.js +9 -0
  434. package/dist/levels.js.map +1 -1
  435. package/dist/line-paragraphs.d.ts +8 -0
  436. package/dist/line-paragraphs.d.ts.map +1 -1
  437. package/dist/line-paragraphs.js +36 -11
  438. package/dist/line-paragraphs.js.map +1 -1
  439. package/dist/long-vowel.d.ts +35 -0
  440. package/dist/long-vowel.d.ts.map +1 -0
  441. package/dist/long-vowel.js +71 -0
  442. package/dist/long-vowel.js.map +1 -0
  443. package/dist/markdown-node.d.ts +19 -0
  444. package/dist/markdown-node.d.ts.map +1 -0
  445. package/dist/markdown-node.js +6 -0
  446. package/dist/markdown-node.js.map +1 -0
  447. package/dist/markdown-read.d.ts +17 -0
  448. package/dist/markdown-read.d.ts.map +1 -0
  449. package/dist/markdown-read.js +37 -0
  450. package/dist/markdown-read.js.map +1 -0
  451. package/dist/markup.d.ts +22 -0
  452. package/dist/markup.d.ts.map +1 -0
  453. package/dist/markup.js +180 -0
  454. package/dist/markup.js.map +1 -0
  455. package/dist/mask.d.ts.map +1 -1
  456. package/dist/mask.js +10 -16
  457. package/dist/mask.js.map +1 -1
  458. package/dist/number-name.d.ts +21 -5
  459. package/dist/number-name.d.ts.map +1 -1
  460. package/dist/number-name.js +120 -18
  461. package/dist/number-name.js.map +1 -1
  462. package/dist/opening-line.d.ts +3 -0
  463. package/dist/opening-line.d.ts.map +1 -0
  464. package/dist/opening-line.js +14 -0
  465. package/dist/opening-line.js.map +1 -0
  466. package/dist/orthography.d.ts +7 -1
  467. package/dist/orthography.d.ts.map +1 -1
  468. package/dist/orthography.js +16 -5
  469. package/dist/orthography.js.map +1 -1
  470. package/dist/page-furniture.d.ts +5 -2
  471. package/dist/page-furniture.d.ts.map +1 -1
  472. package/dist/page-furniture.js +8 -2
  473. package/dist/page-furniture.js.map +1 -1
  474. package/dist/paragraph-length.d.ts +7 -0
  475. package/dist/paragraph-length.d.ts.map +1 -0
  476. package/dist/paragraph-length.js +10 -0
  477. package/dist/paragraph-length.js.map +1 -0
  478. package/dist/plain-source.d.ts +1 -0
  479. package/dist/plain-source.d.ts.map +1 -1
  480. package/dist/plain-source.js +2 -1
  481. package/dist/plain-source.js.map +1 -1
  482. package/dist/plugin.d.ts +121 -0
  483. package/dist/plugin.d.ts.map +1 -1
  484. package/dist/profile/for-file.d.ts.map +1 -1
  485. package/dist/profile/for-file.js +3 -1
  486. package/dist/profile/for-file.js.map +1 -1
  487. package/dist/quoted-span.d.ts +0 -2
  488. package/dist/quoted-span.d.ts.map +1 -1
  489. package/dist/quoted-span.js +9 -4
  490. package/dist/quoted-span.js.map +1 -1
  491. package/dist/reasons.d.ts +3 -0
  492. package/dist/reasons.d.ts.map +1 -1
  493. package/dist/reasons.js +6 -0
  494. package/dist/reasons.js.map +1 -1
  495. package/dist/reference-lists.d.ts +3 -0
  496. package/dist/reference-lists.d.ts.map +1 -0
  497. package/dist/reference-lists.js +75 -0
  498. package/dist/reference-lists.js.map +1 -0
  499. package/dist/render/explain.d.ts +7 -1
  500. package/dist/render/explain.d.ts.map +1 -1
  501. package/dist/render/explain.js +36 -12
  502. package/dist/render/explain.js.map +1 -1
  503. package/dist/render/friendly.d.ts.map +1 -1
  504. package/dist/render/friendly.js +19 -9
  505. package/dist/render/friendly.js.map +1 -1
  506. package/dist/render/options.d.ts +7 -0
  507. package/dist/render/options.d.ts.map +1 -0
  508. package/dist/render/options.js +50 -0
  509. package/dist/render/options.js.map +1 -0
  510. package/dist/render/rules-json.d.ts +4 -1
  511. package/dist/render/rules-json.d.ts.map +1 -1
  512. package/dist/render/rules-json.js +102 -12
  513. package/dist/render/rules-json.js.map +1 -1
  514. package/dist/render/rules-table.d.ts +4 -0
  515. package/dist/render/rules-table.d.ts.map +1 -0
  516. package/dist/render/rules-table.js +70 -0
  517. package/dist/render/rules-table.js.map +1 -0
  518. package/dist/render/sarif.js +4 -4
  519. package/dist/render/sarif.js.map +1 -1
  520. package/dist/render/semantic.js +3 -3
  521. package/dist/render/semantic.js.map +1 -1
  522. package/dist/render/severity-name.d.ts +6 -0
  523. package/dist/render/severity-name.d.ts.map +1 -0
  524. package/dist/render/severity-name.js +7 -0
  525. package/dist/render/severity-name.js.map +1 -0
  526. package/dist/render/text.d.ts +7 -2
  527. package/dist/render/text.d.ts.map +1 -1
  528. package/dist/render/text.js +15 -4
  529. package/dist/render/text.js.map +1 -1
  530. package/dist/resolve-genre.d.ts +1 -1
  531. package/dist/resolve-genre.d.ts.map +1 -1
  532. package/dist/resolve-genre.js +4 -1
  533. package/dist/resolve-genre.js.map +1 -1
  534. package/dist/rule-genres.d.ts +24 -0
  535. package/dist/rule-genres.d.ts.map +1 -0
  536. package/dist/rule-genres.js +11 -0
  537. package/dist/rule-genres.js.map +1 -0
  538. package/dist/rule-guide.d.ts +54 -0
  539. package/dist/rule-guide.d.ts.map +1 -0
  540. package/dist/rule-guide.js +81 -0
  541. package/dist/rule-guide.js.map +1 -0
  542. package/dist/rule-load.d.ts.map +1 -1
  543. package/dist/rule-load.js +94 -32
  544. package/dist/rule-load.js.map +1 -1
  545. package/dist/rule-options.d.ts +50 -0
  546. package/dist/rule-options.d.ts.map +1 -0
  547. package/dist/rule-options.js +106 -0
  548. package/dist/rule-options.js.map +1 -0
  549. package/dist/run-semantic.d.ts +3 -1
  550. package/dist/run-semantic.d.ts.map +1 -1
  551. package/dist/run-semantic.js +8 -3
  552. package/dist/run-semantic.js.map +1 -1
  553. package/dist/run.d.ts +15 -0
  554. package/dist/run.d.ts.map +1 -1
  555. package/dist/run.js +57 -10
  556. package/dist/run.js.map +1 -1
  557. package/dist/semantic.d.ts +1 -1
  558. package/dist/semantic.d.ts.map +1 -1
  559. package/dist/semantic.js +10 -3
  560. package/dist/semantic.js.map +1 -1
  561. package/dist/sentence-shape.d.ts.map +1 -1
  562. package/dist/sentence-shape.js +4 -4
  563. package/dist/sentence-shape.js.map +1 -1
  564. package/dist/setting-problems.d.ts +2 -1
  565. package/dist/setting-problems.d.ts.map +1 -1
  566. package/dist/setting-problems.js +6 -1
  567. package/dist/setting-problems.js.map +1 -1
  568. package/dist/span-cut.d.ts +6 -0
  569. package/dist/span-cut.d.ts.map +1 -0
  570. package/dist/span-cut.js +22 -0
  571. package/dist/span-cut.js.map +1 -0
  572. package/dist/span-merge.d.ts +4 -0
  573. package/dist/span-merge.d.ts.map +1 -0
  574. package/dist/span-merge.js +12 -0
  575. package/dist/span-merge.js.map +1 -0
  576. package/dist/speaker-labels.d.ts +4 -0
  577. package/dist/speaker-labels.d.ts.map +1 -0
  578. package/dist/speaker-labels.js +106 -0
  579. package/dist/speaker-labels.js.map +1 -0
  580. package/dist/stray-space.d.ts +17 -0
  581. package/dist/stray-space.d.ts.map +1 -0
  582. package/dist/stray-space.js +66 -0
  583. package/dist/stray-space.js.map +1 -0
  584. package/dist/structure/aside-depth.d.ts +21 -0
  585. package/dist/structure/aside-depth.d.ts.map +1 -0
  586. package/dist/structure/aside-depth.js +65 -0
  587. package/dist/structure/aside-depth.js.map +1 -0
  588. package/dist/structure/build.d.ts +5 -1
  589. package/dist/structure/build.d.ts.map +1 -1
  590. package/dist/structure/build.js +29 -17
  591. package/dist/structure/build.js.map +1 -1
  592. package/dist/structure/closed-pairs.d.ts +8 -0
  593. package/dist/structure/closed-pairs.d.ts.map +1 -0
  594. package/dist/structure/closed-pairs.js +22 -0
  595. package/dist/structure/closed-pairs.js.map +1 -0
  596. package/dist/structure/date-range.d.ts +21 -0
  597. package/dist/structure/date-range.d.ts.map +1 -0
  598. package/dist/structure/date-range.js +45 -0
  599. package/dist/structure/date-range.js.map +1 -0
  600. package/dist/structure/known-mentions.d.ts +18 -0
  601. package/dist/structure/known-mentions.d.ts.map +1 -0
  602. package/dist/structure/known-mentions.js +78 -0
  603. package/dist/structure/known-mentions.js.map +1 -0
  604. package/dist/structure/numbering.d.ts +1 -0
  605. package/dist/structure/numbering.d.ts.map +1 -1
  606. package/dist/structure/numbering.js +11 -6
  607. package/dist/structure/numbering.js.map +1 -1
  608. package/dist/structure/of.d.ts +3 -1
  609. package/dist/structure/of.d.ts.map +1 -1
  610. package/dist/structure/of.js +5 -1
  611. package/dist/structure/of.js.map +1 -1
  612. package/dist/structure/percent-sum.d.ts +16 -0
  613. package/dist/structure/percent-sum.d.ts.map +1 -0
  614. package/dist/structure/percent-sum.js +104 -0
  615. package/dist/structure/percent-sum.js.map +1 -0
  616. package/dist/structure/quoted-number.d.ts +8 -0
  617. package/dist/structure/quoted-number.d.ts.map +1 -0
  618. package/dist/structure/quoted-number.js +34 -0
  619. package/dist/structure/quoted-number.js.map +1 -0
  620. package/dist/structure/relative-find.d.ts.map +1 -1
  621. package/dist/structure/relative-find.js +34 -46
  622. package/dist/structure/relative-find.js.map +1 -1
  623. package/dist/structure/relative-resolve.d.ts.map +1 -1
  624. package/dist/structure/relative-resolve.js +94 -42
  625. package/dist/structure/relative-resolve.js.map +1 -1
  626. package/dist/structure/runs.d.ts +2 -0
  627. package/dist/structure/runs.d.ts.map +1 -1
  628. package/dist/structure/runs.js +2 -2
  629. package/dist/structure/runs.js.map +1 -1
  630. package/dist/structure/sexp.d.ts +1 -1
  631. package/dist/structure/sexp.d.ts.map +1 -1
  632. package/dist/structure/sexp.js +18 -6
  633. package/dist/structure/sexp.js.map +1 -1
  634. package/dist/structure/total.d.ts +3 -0
  635. package/dist/structure/total.d.ts.map +1 -1
  636. package/dist/structure/total.js +2 -2
  637. package/dist/structure/total.js.map +1 -1
  638. package/dist/structure/universal.d.ts +14 -2
  639. package/dist/structure/universal.d.ts.map +1 -1
  640. package/dist/structure/universal.js +17 -2
  641. package/dist/structure/universal.js.map +1 -1
  642. package/dist/structure/wrapped-tail.d.ts +14 -7
  643. package/dist/structure/wrapped-tail.d.ts.map +1 -1
  644. package/dist/structure/wrapped-tail.js +33 -8
  645. package/dist/structure/wrapped-tail.js.map +1 -1
  646. package/dist/style-load.d.ts +4 -0
  647. package/dist/style-load.d.ts.map +1 -0
  648. package/dist/style-load.js +28 -0
  649. package/dist/style-load.js.map +1 -0
  650. package/dist/style-parse.d.ts +19 -0
  651. package/dist/style-parse.d.ts.map +1 -0
  652. package/dist/style-parse.js +60 -0
  653. package/dist/style-parse.js.map +1 -0
  654. package/dist/subheading-line.d.ts +6 -0
  655. package/dist/subheading-line.d.ts.map +1 -0
  656. package/dist/subheading-line.js +57 -0
  657. package/dist/subheading-line.js.map +1 -0
  658. package/dist/team-names.d.ts.map +1 -1
  659. package/dist/team-names.js +11 -14
  660. package/dist/team-names.js.map +1 -1
  661. package/dist/template-syntax.d.ts +17 -0
  662. package/dist/template-syntax.d.ts.map +1 -0
  663. package/dist/template-syntax.js +140 -0
  664. package/dist/template-syntax.js.map +1 -0
  665. package/dist/text-above.d.ts +19 -0
  666. package/dist/text-above.d.ts.map +1 -0
  667. package/dist/text-above.js +43 -0
  668. package/dist/text-above.js.map +1 -0
  669. package/dist/text-figures.d.ts +10 -0
  670. package/dist/text-figures.d.ts.map +1 -0
  671. package/dist/text-figures.js +131 -0
  672. package/dist/text-figures.js.map +1 -0
  673. package/dist/trailing-aside.d.ts +2 -0
  674. package/dist/trailing-aside.d.ts.map +1 -0
  675. package/dist/trailing-aside.js +53 -0
  676. package/dist/trailing-aside.js.map +1 -0
  677. package/dist/tree-walk.d.ts +18 -0
  678. package/dist/tree-walk.d.ts.map +1 -0
  679. package/dist/tree-walk.js +45 -0
  680. package/dist/tree-walk.js.map +1 -0
  681. package/genres.yaml +60 -2
  682. package/package.json +5 -3
  683. package/reference-headings.yaml +17 -0
  684. package/rules/adverb-overuse.yaml +19 -0
  685. package/rules/agentless-passive.yaml +19 -0
  686. package/rules/agreement-slip.yaml +49 -0
  687. package/rules/ai-generated-composite.yaml +102 -0
  688. package/rules/ai-tell.yaml +23 -0
  689. package/rules/announced-count-mismatch.yaml +57 -0
  690. package/rules/announcing-opener.yaml +51 -0
  691. package/rules/assistant-residue.yaml +52 -0
  692. package/rules/bold-density.yaml +31 -0
  693. package/rules/broken-link.yaml +65 -0
  694. package/rules/closing-cliche.yaml +33 -0
  695. package/rules/colon-lead-in.yaml +59 -0
  696. package/rules/concrete-evidence-density.yaml +41 -0
  697. package/rules/contraction-consistency.yaml +18 -0
  698. package/rules/contrast-framing.yaml +57 -0
  699. package/rules/cushion-phrase-density.yaml +25 -0
  700. package/rules/dangling-figure-reference.yaml +61 -0
  701. package/rules/dangling-reference.yaml +53 -1
  702. package/rules/date-order.yaml +33 -1
  703. package/rules/date-range-reversed.yaml +46 -0
  704. package/rules/date-weekday-mismatch.yaml +17 -1
  705. package/rules/double-keigo.yaml +16 -0
  706. package/rules/doubled-punctuation.yaml +45 -0
  707. package/rules/doubled-word.yaml +17 -1
  708. package/rules/duplicate-definition.yaml +59 -1
  709. package/rules/emoji-density.yaml +25 -0
  710. package/rules/empty-conclusion.yaml +29 -0
  711. package/rules/empty-intensifier.yaml +19 -0
  712. package/rules/excessive-hedging.yaml +19 -0
  713. package/rules/expletive-construction.yaml +19 -0
  714. package/rules/heading-echo.yaml +32 -0
  715. package/rules/heading-level-skip.yaml +78 -0
  716. package/rules/hiragana-fukushi.yaml +16 -0
  717. package/rules/image-alt-text.yaml +54 -0
  718. package/rules/internal-jargon.yaml +21 -0
  719. package/rules/katakana-long-vowel.yaml +98 -0
  720. package/rules/kutoten-consistency.yaml +53 -0
  721. package/rules/latin-spacing.yaml +23 -4
  722. package/rules/max-kanji-continuous.yaml +16 -0
  723. package/rules/max-paragraph-length.yaml +42 -2
  724. package/rules/max-sentence-length.yaml +36 -0
  725. package/rules/ngram-repetition.yaml +29 -2
  726. package/rules/no-doubled-joshi.yaml +16 -0
  727. package/rules/no-em-dash.yaml +25 -0
  728. package/rules/no-mixed-desumasu.yaml +27 -2
  729. package/rules/no-nakaguro-parallel.yaml +20 -0
  730. package/rules/numbering-gap.yaml +53 -1
  731. package/rules/oxford-comma-consistency.yaml +19 -1
  732. package/rules/padded-intro.yaml +19 -0
  733. package/rules/paragraph-length-variance.yaml +49 -0
  734. package/rules/percent-sum-mismatch.yaml +58 -0
  735. package/rules/preamble-length.yaml +56 -0
  736. package/rules/preferred-term.yaml +27 -0
  737. package/rules/proper-noun-density.yaml +26 -0
  738. package/rules/repeated-conjunction.yaml +47 -0
  739. package/rules/repeated-sentence-head.yaml +23 -0
  740. package/rules/required-sections.yaml +49 -0
  741. package/rules/risk-disclosure.yaml +22 -0
  742. package/rules/rule-of-three.yaml +97 -0
  743. package/rules/sasete-itadaku.yaml +18 -0
  744. package/rules/section-length-uniformity.yaml +89 -0
  745. package/rules/sentence-initial-conjunction-run.yaml +18 -0
  746. package/rules/sentence-rhythm.yaml +25 -0
  747. package/rules/stock-transition.yaml +55 -0
  748. package/rules/stray-space.yaml +56 -0
  749. package/rules/taigen-dome-in-prose.yaml +19 -0
  750. package/rules/title-case-consistency.yaml +42 -0
  751. package/rules/total-mismatch.yaml +37 -1
  752. package/rules/unbalanced-bracket.yaml +56 -0
  753. package/rules/undefined-acronym.yaml +23 -0
  754. package/rules/unfilled-placeholder.yaml +50 -0
  755. package/rules/unqualified-superlative.yaml +19 -0
  756. package/rules/unsourced-number.yaml +16 -0
  757. package/rules/url-run-on.yaml +45 -0
  758. package/skills/chaff/SKILL.md +70 -0
  759. package/src/announced-count.ts +148 -0
  760. package/src/bare-url.ts +35 -0
  761. package/src/baseline.ts +17 -1
  762. package/src/body-section.ts +7 -0
  763. package/src/calendar-number.ts +33 -10
  764. package/src/cli-args.ts +6 -0
  765. package/src/cli-text.ts +8 -4
  766. package/src/cli.ts +37 -37
  767. package/src/closing-numbers.ts +72 -0
  768. package/src/commands/compare.ts +80 -0
  769. package/src/commands/eval.ts +19 -8
  770. package/src/commands/feedback.ts +12 -6
  771. package/src/commands/test.ts +20 -17
  772. package/src/commands/tree.ts +41 -5
  773. package/src/compare/atom.ts +33 -0
  774. package/src/compare/clock-time.ts +86 -0
  775. package/src/compare/extract.ts +78 -0
  776. package/src/compare/fact-text.ts +68 -0
  777. package/src/compare/markup-atoms.ts +29 -0
  778. package/src/compare/match.ts +105 -0
  779. package/src/compare/outcome.ts +70 -0
  780. package/src/compare/proper-nouns.ts +65 -0
  781. package/src/compare/render.ts +102 -0
  782. package/src/compare/spans.ts +17 -0
  783. package/src/compare/text-atoms.ts +95 -0
  784. package/src/compare/text.ts +120 -0
  785. package/src/compare/tree-atoms.ts +80 -0
  786. package/src/compare/written-names.ts +26 -0
  787. package/src/config/load.ts +21 -0
  788. package/src/config/option-problems.ts +70 -0
  789. package/src/config/read.ts +8 -0
  790. package/src/config/rule-problems.ts +12 -2
  791. package/src/config/style.ts +42 -0
  792. package/src/config/warnings.ts +13 -0
  793. package/src/config/write.ts +18 -4
  794. package/src/custom/bounded-match.ts +58 -0
  795. package/src/custom/load.ts +15 -0
  796. package/src/custom/parse.ts +274 -0
  797. package/src/custom/problems.ts +89 -0
  798. package/src/custom/regex-safety.ts +118 -0
  799. package/src/custom/token-pattern.ts +84 -0
  800. package/src/detect.ts +28 -2
  801. package/src/detectors/acronym-context.ts +32 -4
  802. package/src/detectors/acronym-expansion.ts +132 -1
  803. package/src/detectors/agreement-slip.ts +191 -0
  804. package/src/detectors/ai-phrasing.ts +137 -0
  805. package/src/detectors/announced-count.ts +25 -0
  806. package/src/detectors/broken-link.ts +90 -0
  807. package/src/detectors/cited-name.ts +6 -5
  808. package/src/detectors/cited-title.ts +96 -0
  809. package/src/detectors/concrete-evidence.ts +3 -2
  810. package/src/detectors/content-morphemes.ts +19 -0
  811. package/src/detectors/contrast-frame.ts +54 -0
  812. package/src/detectors/custom.ts +89 -0
  813. package/src/detectors/dangling-figure.ts +22 -0
  814. package/src/detectors/doubled-punctuation.ts +49 -0
  815. package/src/detectors/doubled-word.ts +19 -4
  816. package/src/detectors/en-shape.ts +0 -269
  817. package/src/detectors/enumerated-runs.ts +13 -4
  818. package/src/detectors/gram-furniture.ts +30 -0
  819. package/src/detectors/gram-place.ts +9 -5
  820. package/src/detectors/gram-windows.ts +31 -0
  821. package/src/detectors/heading-case.ts +30 -2
  822. package/src/detectors/heading-code-words.ts +35 -0
  823. package/src/detectors/heading-echo.ts +18 -22
  824. package/src/detectors/heading-level-skip.ts +19 -0
  825. package/src/detectors/heading-overlap.ts +65 -0
  826. package/src/detectors/image-alt-text.ts +11 -0
  827. package/src/detectors/index.ts +49 -2
  828. package/src/detectors/kutoten-consistency.ts +96 -0
  829. package/src/detectors/lead-in.ts +3 -1
  830. package/src/detectors/letter-joined-name.ts +15 -0
  831. package/src/detectors/lexicon-match.ts +9 -2
  832. package/src/detectors/lexicon.ts +6 -2
  833. package/src/detectors/list-item.ts +122 -0
  834. package/src/detectors/list-items.ts +163 -0
  835. package/src/detectors/list-lead-in.ts +33 -0
  836. package/src/detectors/list-sentence.ts +157 -0
  837. package/src/detectors/list-view.ts +157 -0
  838. package/src/detectors/long-vowel.ts +66 -0
  839. package/src/detectors/markup-finding.ts +24 -0
  840. package/src/detectors/name-numeral.ts +4 -2
  841. package/src/detectors/orthography.ts +24 -18
  842. package/src/detectors/oxford-comma.ts +297 -0
  843. package/src/detectors/place-run.ts +7 -4
  844. package/src/detectors/placeholder-text.ts +27 -0
  845. package/src/detectors/quote-around.ts +9 -0
  846. package/src/detectors/register.ts +37 -10
  847. package/src/detectors/roman-numeral.ts +3 -0
  848. package/src/detectors/sentence-ending.ts +14 -7
  849. package/src/detectors/sentence-length.ts +16 -5
  850. package/src/detectors/signals.ts +110 -53
  851. package/src/detectors/stacked-hedge.ts +6 -3
  852. package/src/detectors/stray-space.ts +40 -0
  853. package/src/detectors/structure-tree.ts +45 -1
  854. package/src/detectors/structure.ts +13 -6
  855. package/src/detectors/superlative-name.ts +7 -4
  856. package/src/detectors/superlative-scope.ts +8 -3
  857. package/src/detectors/token-column.ts +149 -0
  858. package/src/detectors/unbalanced-bracket.ts +153 -0
  859. package/src/detectors/url-run-on.ts +27 -0
  860. package/src/document-sections.ts +46 -0
  861. package/src/document.ts +116 -127
  862. package/src/email-parts.ts +269 -0
  863. package/src/eval.ts +14 -4
  864. package/src/figure-references.ts +112 -0
  865. package/src/files.ts +5 -0
  866. package/src/genre-parse.ts +30 -5
  867. package/src/genre-suggest.ts +15 -2
  868. package/src/heading-label.ts +55 -0
  869. package/src/in-page-nav.ts +48 -19
  870. package/src/joined-view.ts +1 -0
  871. package/src/levels.ts +14 -1
  872. package/src/line-paragraphs.ts +56 -10
  873. package/src/long-vowel.ts +99 -0
  874. package/src/markdown-node.ts +19 -0
  875. package/src/markdown-read.ts +44 -0
  876. package/src/markup.ts +210 -0
  877. package/src/mask.ts +10 -19
  878. package/src/number-name.ts +152 -25
  879. package/src/opening-line.ts +17 -0
  880. package/src/orthography.ts +20 -5
  881. package/src/page-furniture.ts +11 -2
  882. package/src/paragraph-length.ts +9 -0
  883. package/src/plain-source.ts +2 -1
  884. package/src/plugin.ts +107 -1
  885. package/src/profile/for-file.ts +3 -1
  886. package/src/quoted-span.ts +10 -4
  887. package/src/reasons.ts +9 -0
  888. package/src/reference-lists.ts +91 -0
  889. package/src/render/explain.ts +47 -13
  890. package/src/render/friendly.ts +20 -9
  891. package/src/render/options.ts +58 -0
  892. package/src/render/rules-json.ts +130 -14
  893. package/src/render/rules-table.ts +91 -0
  894. package/src/render/sarif.ts +4 -4
  895. package/src/render/semantic.ts +3 -3
  896. package/src/render/severity-name.ts +10 -0
  897. package/src/render/text.ts +21 -7
  898. package/src/resolve-genre.ts +4 -1
  899. package/src/rule-genres.ts +25 -0
  900. package/src/rule-guide.ts +135 -0
  901. package/src/rule-load.ts +98 -33
  902. package/src/rule-options.ts +131 -0
  903. package/src/run-semantic.ts +28 -20
  904. package/src/run.ts +78 -11
  905. package/src/semantic.ts +11 -3
  906. package/src/sentence-shape.ts +4 -5
  907. package/src/setting-problems.ts +7 -1
  908. package/src/span-cut.ts +28 -0
  909. package/src/span-merge.ts +13 -0
  910. package/src/speaker-labels.ts +129 -0
  911. package/src/stray-space.ts +81 -0
  912. package/src/structure/aside-depth.ts +67 -0
  913. package/src/structure/build.ts +38 -19
  914. package/src/structure/closed-pairs.ts +20 -0
  915. package/src/structure/date-range.ts +69 -0
  916. package/src/structure/known-mentions.ts +96 -0
  917. package/src/structure/numbering.ts +9 -5
  918. package/src/structure/of.ts +11 -3
  919. package/src/structure/percent-sum.ts +135 -0
  920. package/src/structure/quoted-number.ts +36 -0
  921. package/src/structure/relative-find.ts +34 -47
  922. package/src/structure/relative-resolve.ts +104 -42
  923. package/src/structure/runs.ts +2 -2
  924. package/src/structure/sexp.ts +17 -5
  925. package/src/structure/total.ts +2 -2
  926. package/src/structure/universal.ts +24 -4
  927. package/src/structure/wrapped-tail.ts +38 -13
  928. package/src/style-load.ts +28 -0
  929. package/src/style-parse.ts +77 -0
  930. package/src/subheading-line.ts +68 -0
  931. package/src/team-names.ts +13 -13
  932. package/src/template-syntax.ts +164 -0
  933. package/src/text-above.ts +49 -0
  934. package/src/text-figures.ts +147 -0
  935. package/src/trailing-aside.ts +50 -0
  936. package/src/tree-walk.ts +48 -0
  937. package/styles/bunkacho.yaml +25 -0
  938. package/styles/ieice.yaml +29 -0
  939. package/styles/jis-z8301-2011.yaml +25 -0
@@ -1,5 +1,4 @@
1
- import { plainSource } from "../plain-source.ts";
2
- import { readFile } from "node:fs/promises";
1
+ import { readDocumentFile } from "../files.ts";
3
2
  import { loadAdapter } from "../adapter-load.ts";
4
3
  import { applyByPath } from "../config/by-path.ts";
5
4
  import { profileFor } from "../profile/for-file.ts";
@@ -8,6 +7,7 @@ import { guessLanguage } from "../detect.ts";
8
7
  import { isMarkdownPath } from "../structure/markdown-path.ts";
9
8
  import { buildStructure } from "../structure/of.ts";
10
9
  import { toSexp } from "../structure/sexp.ts";
10
+ import { foldPostOrder } from "../tree-walk.ts";
11
11
  import type { Config } from "../config/load.ts";
12
12
  import type { StructureNode } from "../plugin.ts";
13
13
  import type { Texts, UiLanguage } from "../ui.ts";
@@ -23,16 +23,19 @@ const TEXT: Texts<{
23
23
  readonly usage: string;
24
24
  readonly unreadable: (path: string, why: string) => string;
25
25
  readonly noStructure: (path: string, language: string) => string;
26
+ readonly tooDeepForJson: (path: string, depth: number) => string;
26
27
  }> = {
27
28
  ja: {
28
29
  usage: "使い方: chaff tree <file>... [--format sexp|json] [--language ja|en|…]",
29
30
  unreadable: (path, why) => `${path} を読めませんでした: ${why}`,
30
31
  noStructure: (path, language) => `${path}: 言語 ${language} のパッケージは文書の構造を読めません(structure がありません)`,
32
+ tooDeepForJson: (path, depth) => `${path}: 木が ${String(depth)} 段と深すぎて、字下げした JSON に書き出せません(--format sexp なら書けることがあります)`,
31
33
  },
32
34
  en: {
33
35
  usage: "usage: chaff tree <file>... [--format sexp|json] [--language ja|en|…]",
34
36
  unreadable: (path, why) => `Could not read ${path}: ${why}`,
35
37
  noStructure: (path, language) => `${path}: the ${language} package cannot read a document's structure (it has no structure)`,
38
+ tooDeepForJson: (path, depth) => `${path}: the tree is ${String(depth)} levels deep, too deep to write as indented JSON (--format sexp may still write it)`,
36
39
  },
37
40
  };
38
41
 
@@ -48,7 +51,7 @@ export const treeTargets = (argv: readonly string[]): string[] =>
48
51
 
49
52
  export const readSource = async (path: string, context: TreeContext): Promise<string | undefined> => {
50
53
  try {
51
- return plainSource(await readFile(path, "utf8"));
54
+ return await readDocumentFile(path);
52
55
  } catch (err) {
53
56
  console.error(treeText(context).unreadable(path, err instanceof Error ? err.message : String(err)));
54
57
  return undefined;
@@ -83,13 +86,46 @@ export const readTree = async (path: string, argv: readonly string[], context: T
83
86
  // 日本語は形態素で数量と日付を読む。解析器が無ければ単位の表で読むので、木は作れる。
84
87
  await adapter.prepare?.({ pos: true });
85
88
  const profile = profileFor(context.config, path, source, language, resolveGenre(path, source, context.config, context.flag(argv, "--genre")).genre);
86
- return { source, tree: buildStructure({ path, source, language, markdown: isMarkdownPath(path), profile }, adapter.structure) };
89
+ return { source, tree: buildStructure({ path, source, language, markdown: isMarkdownPath(path), profile, lexicons: adapter.lexicons }, adapter.structure) };
90
+ };
91
+
92
+ /**
93
+ * 字下げした JSON。書けないほど深い木(言語パッケージの番号が何千段も入れ子になる)なら undefined。
94
+ * JSON.stringify は 1 段ごとに呼び直すのでスタックが尽き、字下げの合計は深さの二乗で増えて文字列の上限を超える。どちらも RangeError になる。
95
+ */
96
+ export const treeJson = (tree: StructureNode): string | undefined => {
97
+ try {
98
+ return JSON.stringify(tree, null, 2);
99
+ } catch (err) {
100
+ if (err instanceof RangeError) return undefined;
101
+ throw err;
102
+ }
103
+ };
104
+
105
+ /** 根を 1 段目と数えた、いちばん深い葉までの段の数。 */
106
+ export const depthOf = (tree: StructureNode): number =>
107
+ foldPostOrder(
108
+ tree,
109
+ (node) => node.children,
110
+ (_node, depths: readonly number[]) => depths.reduce((deepest, depth) => Math.max(deepest, depth), 0) + 1,
111
+ );
112
+
113
+ /** 出力する文字列。JSON に書けないほど深い木なら、その深さ。 */
114
+ export const renderTree = (tree: StructureNode, format: string | undefined): { readonly text: string } | { readonly tooDeep: number } => {
115
+ if (format !== "json") return { text: toSexp(tree) };
116
+ const json = treeJson(tree);
117
+ return json === undefined ? { tooDeep: depthOf(tree) } : { text: json };
87
118
  };
88
119
 
89
120
  const printTree = async (path: string, argv: readonly string[], context: TreeContext): Promise<boolean> => {
90
121
  const read = await readTree(path, argv, context);
91
122
  if (read === undefined) return false;
92
- console.log(context.flag(argv, "--format") === "json" ? JSON.stringify(read.tree, null, 2) : toSexp(read.tree));
123
+ const rendered = renderTree(read.tree, context.flag(argv, "--format"));
124
+ if ("tooDeep" in rendered) {
125
+ console.error(treeText(context).tooDeepForJson(path, rendered.tooDeep));
126
+ return false;
127
+ }
128
+ console.log(rendered.text);
93
129
  return true;
94
130
  };
95
131
 
@@ -0,0 +1,33 @@
1
+ /** The kinds of fact `chaff compare` reads, in the order it reports them. */
2
+ export const ATOM_KINDS = ["number", "date", "time", "url", "code", "name", "quote", "heading", "reference", "footnote"] as const;
3
+
4
+ export type AtomKind = (typeof ATOM_KINDS)[number];
5
+
6
+ export const isAtomKind = (value: string): value is AtomKind => ATOM_KINDS.some((kind) => kind === value);
7
+
8
+ /**
9
+ * One fact as a document states it. Two atoms are the same fact when kind and key are equal; `text` is how it was
10
+ * written (1,000円 and 1000円 share a key). Position is only for showing where it is: comparing ignores it.
11
+ */
12
+ export type Atom = {
13
+ readonly kind: AtomKind;
14
+ readonly key: string;
15
+ readonly text: string;
16
+ readonly line: number;
17
+ /** A number written with no unit the reader knows. It may stand for the same number written with a unit. */
18
+ readonly unitless?: boolean;
19
+ /** For a number: its value alone, without the unit. */
20
+ readonly value?: string;
21
+ };
22
+
23
+ /** A kind this document could not be fully read for, and why. Zero facts of it then does not mean none were there. */
24
+ export type Unread = { readonly kind: AtomKind; readonly reason: UnreadReason };
25
+
26
+ export type UnreadReason = "no-structure" | "no-dates" | "no-pos" | "plain-text";
27
+
28
+ export type Extraction = {
29
+ readonly atoms: readonly Atom[];
30
+ readonly unread: readonly Unread[];
31
+ /** The prose spelled as name keys are, so the other document can ask whether a name is written here at all. */
32
+ readonly nameText: string;
33
+ };
@@ -0,0 +1,86 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import { overlapsAny, spanIndex } from "./spans.ts";
3
+
4
+ /** A time of day and where it is written. key is "HH:MM" on a 24-hour clock, with ":SS" when seconds are given. */
5
+ export type ClockTime = Span & { readonly key: string };
6
+
7
+ const DIGIT = "[0-90-9]";
8
+ const COLON = "[::]";
9
+
10
+ /** 10:30, 15:30:05, 3:30 p.m. */
11
+ const COLON_TIME = new RegExp(
12
+ `(?<![0-90-9])(${DIGIT}{1,2})${COLON}(${DIGIT}{2})(?:${COLON}(${DIGIT}{2}))?(?:\\s?([ap])\\.?m\\.?(?![a-z]))?(?![0-90-9])`,
13
+ "giu",
14
+ );
15
+
16
+ /** 3 p.m., 11am. */
17
+ const MERIDIEM_TIME = new RegExp(`(?<![0-90-9::])(${DIGIT}{1,2})\\s?([ap])\\.?m\\.?(?![a-z])`, "giu");
18
+
19
+ /** 午後3時30分, 10時半, 9時. */
20
+ const JAPANESE_TIME = new RegExp(`(午前|午後)?(${DIGIT}{1,2})時(?:(${DIGIT}{1,2})分|(半))?`, "gu");
21
+
22
+ const LAST_HOUR = 24;
23
+ const MINUTES_PER_HOUR = 60;
24
+ const HALF_HOUR = 30;
25
+ const NOON = 12;
26
+ const PADDED = 2;
27
+
28
+ type Reading = { readonly hour: number; readonly minute: number; readonly second?: number | undefined; readonly afternoon?: boolean | undefined };
29
+
30
+ const hourOf = (hour: number, afternoon: boolean | undefined): number => {
31
+ if (afternoon === undefined) return hour;
32
+ if (afternoon) return hour < NOON ? hour + NOON : hour;
33
+ return hour === NOON ? 0 : hour;
34
+ };
35
+
36
+ const padded = (value: number): string => String(value).padStart(PADDED, "0");
37
+
38
+ /** Whether the hour is one on its clock: up to 12 with a.m. or p.m., else up to 23, and 24 only as 24:00. */
39
+ const isHour = ({ hour, minute, second, afternoon }: Reading): boolean => {
40
+ if (afternoon !== undefined) return hour <= NOON;
41
+ return hour < LAST_HOUR || (hour === LAST_HOUR && minute === 0 && (second ?? 0) === 0);
42
+ };
43
+
44
+ /** The key, or undefined when the numbers are not a time of day (24:30, 13 p.m., 3:75). */
45
+ const keyOf = (reading: Reading): string | undefined => {
46
+ const { hour, minute, second } = reading;
47
+ if (!isHour(reading) || minute >= MINUTES_PER_HOUR || (second ?? 0) >= MINUTES_PER_HOUR) return undefined;
48
+ const parts = [hourOf(hour, reading.afternoon), minute, ...(second === undefined ? [] : [second])];
49
+ return parts.map(padded).join(":");
50
+ };
51
+
52
+ const numberOf = (digits: string | undefined): number | undefined => (digits === undefined ? undefined : Number(digits.normalize("NFKC")));
53
+
54
+ const meridiem = (letter: string | undefined): boolean | undefined => (letter === undefined ? undefined : letter.toLowerCase() === "p");
55
+
56
+ const japaneseMeridiem = (word: string | undefined): boolean | undefined => (word === undefined ? undefined : word === "午後");
57
+
58
+ type Reader = { readonly pattern: RegExp; readonly read: (match: RegExpMatchArray) => Reading };
59
+
60
+ const READERS: readonly Reader[] = [
61
+ {
62
+ pattern: JAPANESE_TIME,
63
+ read: (match) => ({
64
+ hour: numberOf(match[2]) ?? 0,
65
+ minute: match[4] === undefined ? (numberOf(match[3]) ?? 0) : HALF_HOUR,
66
+ afternoon: japaneseMeridiem(match[1]),
67
+ }),
68
+ },
69
+ {
70
+ pattern: COLON_TIME,
71
+ read: (match) => ({ hour: numberOf(match[1]) ?? 0, minute: numberOf(match[2]) ?? 0, second: numberOf(match[3]), afternoon: meridiem(match[4]) }),
72
+ },
73
+ { pattern: MERIDIEM_TIME, read: (match) => ({ hour: numberOf(match[1]) ?? 0, minute: 0, afternoon: meridiem(match[2]) }) },
74
+ ];
75
+
76
+ /** Times of day in text, in the order written. Where two readings overlap, the earlier reader in READERS wins. */
77
+ export const clockTimes = (text: string): ClockTime[] =>
78
+ READERS.reduce<ClockTime[]>((found, reader) => {
79
+ const taken = spanIndex(found);
80
+ const fresh = [...text.matchAll(reader.pattern)].flatMap((match) => {
81
+ const key = keyOf(reader.read(match));
82
+ const span = { start: match.index, end: match.index + match[0].length };
83
+ return key === undefined || overlapsAny(taken, span) ? [] : [{ ...span, key }];
84
+ });
85
+ return [...found, ...fresh];
86
+ }, []).toSorted((left, right) => left.start - right.start);
@@ -0,0 +1,78 @@
1
+ import { lineStarts, placeOf } from "../position.ts";
2
+ import type { MarkdownNode } from "../markdown-node.ts";
3
+ import type { ProseDocument, Span, StructurePatterns } from "../plugin.ts";
4
+ import type { Atom, Extraction, Unread } from "./atom.ts";
5
+ import { clockTimes } from "./clock-time.ts";
6
+ import { factTextOf, type FactText } from "./fact-text.ts";
7
+ import { codeAtoms, headingAtoms, urlAtoms } from "./markup-atoms.ts";
8
+ import { nameKey, properNouns, readsProperNouns } from "./proper-nouns.ts";
9
+ import { spanIndex } from "./spans.ts";
10
+ import { atomsOf, bareNumbers, footnotes, numericDates, quotations, teamNames, type TextInput } from "./text-atoms.ts";
11
+ import { treeFacts, type TreeFacts } from "./tree-atoms.ts";
12
+
13
+ export type ExtractInput = {
14
+ readonly doc: ProseDocument;
15
+ /** The Markdown tree, or undefined for a plain-text document. */
16
+ readonly root: MarkdownNode | undefined;
17
+ /** The language package's structure reader: numbers with units, dates and references come from it. */
18
+ readonly structure: StructurePatterns | undefined;
19
+ /** chaff.yaml's `names:`. */
20
+ readonly names: readonly string[];
21
+ };
22
+
23
+ const NO_TREE_FACTS: TreeFacts = { atoms: [], taken: [] };
24
+
25
+ const STRUCTURE_KINDS = ["number", "date", "reference"] as const;
26
+
27
+ /** The kinds this document could not be fully read for. Each says why, so "nothing dropped" is never "nothing read". */
28
+ const unreadOf = (input: ExtractInput): Unread[] => [
29
+ ...(input.structure === undefined ? STRUCTURE_KINDS.map((kind): Unread => ({ kind, reason: "no-structure" })) : []),
30
+ ...(input.structure !== undefined && input.structure.dates === undefined ? [{ kind: "date", reason: "no-dates" } as const] : []),
31
+ ...(readsProperNouns(input.doc) ? [] : [{ kind: "name", reason: "no-pos" } as const]),
32
+ ...(input.root === undefined ? [{ kind: "code", reason: "plain-text" } as const] : []),
33
+ ];
34
+
35
+ type Readers = { readonly facts: FactText; readonly text: TextInput; readonly lineOf: (offset: number) => number };
36
+
37
+ /** Facts read from the prose itself. Each reader skips what an earlier one took, so one figure is one fact. */
38
+ const proseFacts = (input: ExtractInput, readers: Readers, tree: TreeFacts, times: readonly Span[]): Atom[] => {
39
+ const { facts, text, lineOf } = readers;
40
+ const marks = footnotes(facts.codeless);
41
+ const dates = numericDates(facts.text, spanIndex([...tree.taken, ...times]));
42
+ const names = teamNames(facts.text, input.names);
43
+ const taken = [...tree.taken, ...times, ...marks, ...dates];
44
+ return [
45
+ ...atomsOf(dates, "date", text),
46
+ ...atomsOf(marks, "footnote", text),
47
+ ...atomsOf(names, "name", text),
48
+ ...bareNumbers(text, spanIndex([...taken, ...facts.listMarkers])),
49
+ ...quotations(text),
50
+ // "April" in a date and "Section" in a reference are read with them, not again as names.
51
+ ...properNouns({ doc: input.doc, taken: spanIndex([...taken, ...names, ...facts.blanked]), lineOf }),
52
+ ];
53
+ };
54
+
55
+ const structureFacts = (input: ExtractInput, facts: FactText, times: readonly Span[], lineOf: (offset: number) => number): TreeFacts => {
56
+ const tree = input.doc.structure;
57
+ return tree === undefined ? NO_TREE_FACTS : treeFacts({ tree, source: input.doc.source, blanked: facts.blanked, times: spanIndex(times), lineOf });
58
+ };
59
+
60
+ /** Every fact atom of one document, and the kinds it could not be fully read for. */
61
+ export const extractFacts = (input: ExtractInput): Extraction => {
62
+ const source = input.doc.source;
63
+ const starts = lineStarts(source);
64
+ const lineOf = (offset: number): number => placeOf(starts, offset).line;
65
+ const facts = factTextOf(source, input.root);
66
+ const text = { text: facts.text, source, lineOf };
67
+ const times = clockTimes(facts.text);
68
+ const tree = structureFacts(input, facts, times, lineOf);
69
+ const atoms = [
70
+ ...tree.atoms,
71
+ ...atomsOf(times, "time", text),
72
+ ...proseFacts(input, { facts, text, lineOf }, tree, times),
73
+ ...urlAtoms(input.doc.markup, facts.bareUrls, lineOf),
74
+ ...(input.root === undefined ? [] : codeAtoms(input.root, lineOf)),
75
+ ...headingAtoms(input.doc.markup, lineOf),
76
+ ];
77
+ return { atoms, unread: unreadOf(input), nameText: nameKey(facts.text) };
78
+ };
@@ -0,0 +1,68 @@
1
+ import { bareUrls, type BareUrl } from "../bare-url.ts";
2
+ import { linkChrome } from "../document.ts";
3
+ import { opaqueSpans } from "../markdown-read.ts";
4
+ import { spanOf, type MarkdownNode } from "../markdown-node.ts";
5
+ import { maskSpans } from "../mask.ts";
6
+ import { eachPreOrder } from "../tree-walk.ts";
7
+ import type { Span } from "../plugin.ts";
8
+ import { overlapsAny, spanIndex, type SpanIndex } from "./spans.ts";
9
+
10
+ /**
11
+ * The document as the facts in prose are read from it: the same length as the source, with what is not prose
12
+ * blanked. Code, HTML and front matter are compared as code or not at all; a link's target and a bare URL are
13
+ * compared as URLs. Blanking them keeps the digits of a URL from being read again as numbers.
14
+ */
15
+ export type FactText = {
16
+ /** The source with code and URLs blanked. Offsets are the source's. */
17
+ readonly text: string;
18
+ /** The source with only code blanked: footnote marks are link syntax, so they are read before links are blanked. */
19
+ readonly codeless: string;
20
+ /** Where the blanked parts are. A fact the structure reader found there is not a fact of the prose. */
21
+ readonly blanked: SpanIndex;
22
+ readonly bareUrls: readonly BareUrl[];
23
+ /** Ordered-list markers (`1.`): they number the list, they do not state a number. */
24
+ readonly listMarkers: readonly Span[];
25
+ };
26
+
27
+ /** Nodes whose whole span is not read as prose: a link reference definition's URL is read as a URL, an image is not text. */
28
+ const WHOLE = new Set(["definition", "image", "imageReference"]);
29
+ const LINKS = new Set(["link", "linkReference"]);
30
+
31
+ /** `1.` or `1)` at the start of an ordered list item. */
32
+ const ORDERED_MARKER = /^\s*\d{1,9}[.)]/u;
33
+
34
+ const listMarker = (node: MarkdownNode, source: string): Span[] => {
35
+ const span = spanOf(node);
36
+ if (node.type !== "listItem" || span === undefined) return [];
37
+ const marker = ORDERED_MARKER.exec(source.slice(span.start, span.end));
38
+ return marker === null ? [] : [{ start: span.start, end: span.start + marker[0].length }];
39
+ };
40
+
41
+ type MarkdownParts = { readonly chrome: Span[]; readonly linkSpans: Span[]; readonly listMarkers: Span[] };
42
+
43
+ const markdownParts = (root: MarkdownNode, source: string): MarkdownParts => {
44
+ const parts: MarkdownParts = { chrome: [], linkSpans: [], listMarkers: [] };
45
+ eachPreOrder(root, (node) => {
46
+ const span = spanOf(node);
47
+ if (span === undefined) return;
48
+ if (WHOLE.has(node.type)) parts.chrome.push(span);
49
+ if (LINKS.has(node.type)) {
50
+ parts.chrome.push(...linkChrome(node));
51
+ parts.linkSpans.push(span);
52
+ }
53
+ parts.listMarkers.push(...listMarker(node, source));
54
+ });
55
+ return parts;
56
+ };
57
+
58
+ /** root is the Markdown tree, or undefined for a plain-text document, where nothing is code and URLs are bare. */
59
+ export const factTextOf = (source: string, root: MarkdownNode | undefined): FactText => {
60
+ const code = root === undefined ? [] : opaqueSpans(root);
61
+ const codeless = maskSpans(source, code);
62
+ const parts = root === undefined ? { chrome: [], linkSpans: [], listMarkers: [] } : markdownParts(root, source);
63
+ const insideLinks = spanIndex(parts.linkSpans);
64
+ // An autolink's text is its URL: it is read once, as the link.
65
+ const bare = bareUrls(codeless).filter((url) => !overlapsAny(insideLinks, url));
66
+ const blanked = [...code, ...parts.chrome, ...bare];
67
+ return { text: maskSpans(source, blanked), codeless, blanked: spanIndex(blanked), bareUrls: bare, listMarkers: parts.listMarkers };
68
+ };
@@ -0,0 +1,29 @@
1
+ import { spanOf, type MarkdownNode } from "../markdown-node.ts";
2
+ import { eachPreOrder } from "../tree-walk.ts";
3
+ import type { Markup } from "../plugin.ts";
4
+ import type { BareUrl } from "../bare-url.ts";
5
+ import type { Atom } from "./atom.ts";
6
+
7
+ const CODE = new Set(["code", "inlineCode"]);
8
+
9
+ /** Inline code and code blocks, compared as written: the key is the code itself, without its backticks or fences. */
10
+ export const codeAtoms = (root: MarkdownNode, lineOf: (offset: number) => number): Atom[] => {
11
+ const found: Atom[] = [];
12
+ eachPreOrder(root, (node) => {
13
+ const span = spanOf(node);
14
+ if (!CODE.has(node.type) || span === undefined) return;
15
+ const code = node.value ?? "";
16
+ found.push({ kind: "code", key: code, text: code, line: lineOf(span.start) });
17
+ });
18
+ return found;
19
+ };
20
+
21
+ /** Where links point (a Markdown link, an autolink, a used reference definition) and URLs written bare. */
22
+ export const urlAtoms = (markup: Markup | undefined, bare: readonly BareUrl[], lineOf: (offset: number) => number): Atom[] => [
23
+ ...(markup?.links ?? []).map((link) => ({ kind: "url" as const, key: link.destination, text: link.destination, line: lineOf(link.start) })),
24
+ ...bare.map((url) => ({ kind: "url" as const, key: url.url, text: url.url, line: lineOf(url.start) })),
25
+ ];
26
+
27
+ /** Markdown headings as structure: the key is the level, so a reworded heading is the same heading in another form. */
28
+ export const headingAtoms = (markup: Markup | undefined, lineOf: (offset: number) => number): Atom[] =>
29
+ (markup?.headings ?? []).map((heading) => ({ kind: "heading", key: "#".repeat(heading.depth), text: heading.text, line: lineOf(heading.start) }));
@@ -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
+ };