chaffjs 0.30.0 → 0.31.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 (530) hide show
  1. package/dist/announced-count.d.ts +2 -0
  2. package/dist/announced-count.d.ts.map +1 -1
  3. package/dist/announced-count.js +4 -3
  4. package/dist/announced-count.js.map +1 -1
  5. package/dist/contacts/addresses.d.ts +21 -0
  6. package/dist/contacts/addresses.d.ts.map +1 -0
  7. package/dist/contacts/addresses.js +144 -0
  8. package/dist/contacts/addresses.js.map +1 -0
  9. package/dist/contacts/emails.d.ts +11 -0
  10. package/dist/contacts/emails.d.ts.map +1 -0
  11. package/dist/contacts/emails.js +51 -0
  12. package/dist/contacts/emails.js.map +1 -0
  13. package/dist/contacts/variants.d.ts +14 -0
  14. package/dist/contacts/variants.d.ts.map +1 -0
  15. package/dist/contacts/variants.js +25 -0
  16. package/dist/contacts/variants.js.map +1 -0
  17. package/dist/derived/battery-runtime.d.ts +85 -0
  18. package/dist/derived/battery-runtime.d.ts.map +1 -0
  19. package/dist/derived/battery-runtime.js +217 -0
  20. package/dist/derived/battery-runtime.js.map +1 -0
  21. package/dist/derived/period-counts.d.ts +48 -0
  22. package/dist/derived/period-counts.d.ts.map +1 -0
  23. package/dist/derived/period-counts.js +128 -0
  24. package/dist/derived/period-counts.js.map +1 -0
  25. package/dist/derived/time-lengths.d.ts.map +1 -1
  26. package/dist/derived/time-lengths.js +12 -9
  27. package/dist/derived/time-lengths.js.map +1 -1
  28. package/dist/derived/use-interval.d.ts +33 -0
  29. package/dist/derived/use-interval.d.ts.map +1 -0
  30. package/dist/derived/use-interval.js +93 -0
  31. package/dist/derived/use-interval.js.map +1 -0
  32. package/dist/detectors/announced-count.d.ts.map +1 -1
  33. package/dist/detectors/announced-count.js +1 -0
  34. package/dist/detectors/announced-count.js.map +1 -1
  35. package/dist/detectors/capacity-runtime.d.ts +7 -0
  36. package/dist/detectors/capacity-runtime.d.ts.map +1 -0
  37. package/dist/detectors/capacity-runtime.js +98 -0
  38. package/dist/detectors/capacity-runtime.js.map +1 -0
  39. package/dist/detectors/contact-variant.d.ts +3 -0
  40. package/dist/detectors/contact-variant.d.ts.map +1 -0
  41. package/dist/detectors/contact-variant.js +21 -0
  42. package/dist/detectors/contact-variant.js.map +1 -0
  43. package/dist/detectors/count-over-capacity.d.ts +4 -0
  44. package/dist/detectors/count-over-capacity.d.ts.map +1 -0
  45. package/dist/detectors/count-over-capacity.js +26 -0
  46. package/dist/detectors/count-over-capacity.js.map +1 -0
  47. package/dist/detectors/date-outside-period.d.ts.map +1 -1
  48. package/dist/detectors/date-outside-period.js +2 -1
  49. package/dist/detectors/date-outside-period.js.map +1 -1
  50. package/dist/detectors/date-stamp-order.d.ts +3 -0
  51. package/dist/detectors/date-stamp-order.d.ts.map +1 -0
  52. package/dist/detectors/date-stamp-order.js +24 -0
  53. package/dist/detectors/date-stamp-order.js.map +1 -0
  54. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  55. package/dist/detectors/defined-term-form.js +57 -7
  56. package/dist/detectors/defined-term-form.js.map +1 -1
  57. package/dist/detectors/definition-use.d.ts +1 -1
  58. package/dist/detectors/definition-use.d.ts.map +1 -1
  59. package/dist/detectors/definition-use.js +4 -2
  60. package/dist/detectors/definition-use.js.map +1 -1
  61. package/dist/detectors/derived-numbers.d.ts +9 -1
  62. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  63. package/dist/detectors/derived-numbers.js +2 -2
  64. package/dist/detectors/derived-numbers.js.map +1 -1
  65. package/dist/detectors/due-date.d.ts.map +1 -1
  66. package/dist/detectors/due-date.js +6 -0
  67. package/dist/detectors/due-date.js.map +1 -1
  68. package/dist/detectors/en-shape.d.ts.map +1 -1
  69. package/dist/detectors/en-shape.js +7 -4
  70. package/dist/detectors/en-shape.js.map +1 -1
  71. package/dist/detectors/event-deadline.d.ts +3 -0
  72. package/dist/detectors/event-deadline.d.ts.map +1 -0
  73. package/dist/detectors/event-deadline.js +24 -0
  74. package/dist/detectors/event-deadline.js.map +1 -0
  75. package/dist/detectors/fact-consistency.d.ts +1 -1
  76. package/dist/detectors/fact-consistency.d.ts.map +1 -1
  77. package/dist/detectors/fact-consistency.js +52 -3
  78. package/dist/detectors/fact-consistency.js.map +1 -1
  79. package/dist/detectors/heading-case.d.ts +5 -2
  80. package/dist/detectors/heading-case.d.ts.map +1 -1
  81. package/dist/detectors/heading-case.js +12 -4
  82. package/dist/detectors/heading-case.js.map +1 -1
  83. package/dist/detectors/heading-echo.d.ts +5 -0
  84. package/dist/detectors/heading-echo.d.ts.map +1 -1
  85. package/dist/detectors/heading-echo.js +14 -1
  86. package/dist/detectors/heading-echo.js.map +1 -1
  87. package/dist/detectors/layout-code.d.ts +5 -0
  88. package/dist/detectors/layout-code.d.ts.map +1 -0
  89. package/dist/detectors/layout-code.js +124 -0
  90. package/dist/detectors/layout-code.js.map +1 -0
  91. package/dist/detectors/measured-facts.d.ts +8 -0
  92. package/dist/detectors/measured-facts.d.ts.map +1 -0
  93. package/dist/detectors/measured-facts.js +18 -0
  94. package/dist/detectors/measured-facts.js.map +1 -0
  95. package/dist/detectors/name-variant-model-codes.d.ts +11 -0
  96. package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
  97. package/dist/detectors/name-variant-model-codes.js +21 -0
  98. package/dist/detectors/name-variant-model-codes.js.map +1 -0
  99. package/dist/detectors/name-variant.d.ts.map +1 -1
  100. package/dist/detectors/name-variant.js +25 -4
  101. package/dist/detectors/name-variant.js.map +1 -1
  102. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  103. package/dist/detectors/orthographic-variant.js +62 -7
  104. package/dist/detectors/orthographic-variant.js.map +1 -1
  105. package/dist/detectors/orthography.d.ts.map +1 -1
  106. package/dist/detectors/orthography.js +12 -6
  107. package/dist/detectors/orthography.js.map +1 -1
  108. package/dist/detectors/quantity-range.d.ts +3 -0
  109. package/dist/detectors/quantity-range.d.ts.map +1 -0
  110. package/dist/detectors/quantity-range.js +35 -0
  111. package/dist/detectors/quantity-range.js.map +1 -0
  112. package/dist/detectors/range-band.d.ts +5 -0
  113. package/dist/detectors/range-band.d.ts.map +1 -0
  114. package/dist/detectors/range-band.js +80 -0
  115. package/dist/detectors/range-band.js.map +1 -0
  116. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
  117. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
  118. package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
  119. package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
  120. package/dist/detectors/registry/contact-variant.d.ts +3 -0
  121. package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
  122. package/dist/detectors/registry/contact-variant.js +3 -0
  123. package/dist/detectors/registry/contact-variant.js.map +1 -0
  124. package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
  125. package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
  126. package/dist/detectors/registry/count-over-capacity.js +3 -0
  127. package/dist/detectors/registry/count-over-capacity.js.map +1 -0
  128. package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
  129. package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
  130. package/dist/detectors/registry/date-stamp-order.js +3 -0
  131. package/dist/detectors/registry/date-stamp-order.js.map +1 -0
  132. package/dist/detectors/registry/event-deadline.d.ts +3 -0
  133. package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
  134. package/dist/detectors/registry/event-deadline.js +3 -0
  135. package/dist/detectors/registry/event-deadline.js.map +1 -0
  136. package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
  137. package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
  138. package/dist/detectors/registry/interval-count-mismatch.js +3 -0
  139. package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
  140. package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
  141. package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
  142. package/dist/detectors/registry/layout-code-mismatch.js +3 -0
  143. package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
  144. package/dist/detectors/registry/quantity-range.d.ts +3 -0
  145. package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
  146. package/dist/detectors/registry/quantity-range.js +3 -0
  147. package/dist/detectors/registry/quantity-range.js.map +1 -0
  148. package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
  149. package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
  150. package/dist/detectors/registry/range-band-mismatch.js +3 -0
  151. package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
  152. package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
  153. package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
  154. package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
  155. package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
  156. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
  157. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
  158. package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
  159. package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
  160. package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
  161. package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
  162. package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
  163. package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
  164. package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
  165. package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
  166. package/dist/detectors/registry/time-range-reversed.js +3 -0
  167. package/dist/detectors/registry/time-range-reversed.js.map +1 -0
  168. package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
  169. package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
  170. package/dist/detectors/registry/unlisted-item-used.js +3 -0
  171. package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
  172. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
  173. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
  174. package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
  175. package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
  176. package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
  177. package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
  178. package/dist/detectors/rent-multiple-mismatch.js +39 -0
  179. package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
  180. package/dist/detectors/scaled-quantity.d.ts +3 -0
  181. package/dist/detectors/scaled-quantity.d.ts.map +1 -0
  182. package/dist/detectors/scaled-quantity.js +37 -0
  183. package/dist/detectors/scaled-quantity.js.map +1 -0
  184. package/dist/detectors/signals.d.ts.map +1 -1
  185. package/dist/detectors/signals.js +3 -1
  186. package/dist/detectors/signals.js.map +1 -1
  187. package/dist/detectors/step-time-sum.d.ts +3 -0
  188. package/dist/detectors/step-time-sum.d.ts.map +1 -0
  189. package/dist/detectors/step-time-sum.js +46 -0
  190. package/dist/detectors/step-time-sum.js.map +1 -0
  191. package/dist/detectors/structure-tree.d.ts +2 -0
  192. package/dist/detectors/structure-tree.d.ts.map +1 -1
  193. package/dist/detectors/structure-tree.js +22 -5
  194. package/dist/detectors/structure-tree.js.map +1 -1
  195. package/dist/detectors/structure.js +1 -1
  196. package/dist/detectors/structure.js.map +1 -1
  197. package/dist/detectors/technical-docs.d.ts +5 -0
  198. package/dist/detectors/technical-docs.d.ts.map +1 -1
  199. package/dist/detectors/technical-docs.js +12 -0
  200. package/dist/detectors/technical-docs.js.map +1 -1
  201. package/dist/detectors/time-order.d.ts +1 -0
  202. package/dist/detectors/time-order.d.ts.map +1 -1
  203. package/dist/detectors/time-order.js +3 -1
  204. package/dist/detectors/time-order.js.map +1 -1
  205. package/dist/detectors/unit-consistency.d.ts +1 -1
  206. package/dist/detectors/unit-consistency.d.ts.map +1 -1
  207. package/dist/detectors/unit-consistency.js +46 -38
  208. package/dist/detectors/unit-consistency.js.map +1 -1
  209. package/dist/detectors/unlisted-items.d.ts +3 -0
  210. package/dist/detectors/unlisted-items.d.ts.map +1 -0
  211. package/dist/detectors/unlisted-items.js +57 -0
  212. package/dist/detectors/unlisted-items.js.map +1 -0
  213. package/dist/detectors/use-count-words.d.ts +8 -0
  214. package/dist/detectors/use-count-words.d.ts.map +1 -0
  215. package/dist/detectors/use-count-words.js +20 -0
  216. package/dist/detectors/use-count-words.js.map +1 -0
  217. package/dist/detectors/use-interval.d.ts +3 -0
  218. package/dist/detectors/use-interval.d.ts.map +1 -0
  219. package/dist/detectors/use-interval.js +39 -0
  220. package/dist/detectors/use-interval.js.map +1 -0
  221. package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
  222. package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
  223. package/dist/detectors/walk-time-distance-mismatch.js +38 -0
  224. package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
  225. package/dist/facts/approximate-tolerance.d.ts +7 -0
  226. package/dist/facts/approximate-tolerance.d.ts.map +1 -0
  227. package/dist/facts/approximate-tolerance.js +16 -0
  228. package/dist/facts/approximate-tolerance.js.map +1 -0
  229. package/dist/facts/item-amounts.d.ts +18 -0
  230. package/dist/facts/item-amounts.d.ts.map +1 -0
  231. package/dist/facts/item-amounts.js +67 -0
  232. package/dist/facts/item-amounts.js.map +1 -0
  233. package/dist/facts/labelled-facts.d.ts +4 -0
  234. package/dist/facts/labelled-facts.d.ts.map +1 -1
  235. package/dist/facts/labelled-facts.js +21 -6
  236. package/dist/facts/labelled-facts.js.map +1 -1
  237. package/dist/facts/measure-units.d.ts +5 -0
  238. package/dist/facts/measure-units.d.ts.map +1 -0
  239. package/dist/facts/measure-units.js +28 -0
  240. package/dist/facts/measure-units.js.map +1 -0
  241. package/dist/facts/measures.d.ts +23 -4
  242. package/dist/facts/measures.d.ts.map +1 -1
  243. package/dist/facts/measures.js +68 -19
  244. package/dist/facts/measures.js.map +1 -1
  245. package/dist/facts/retention-periods.d.ts +51 -0
  246. package/dist/facts/retention-periods.d.ts.map +1 -0
  247. package/dist/facts/retention-periods.js +148 -0
  248. package/dist/facts/retention-periods.js.map +1 -0
  249. package/dist/facts/unit-conflicts.d.ts +3 -2
  250. package/dist/facts/unit-conflicts.d.ts.map +1 -1
  251. package/dist/facts/unit-conflicts.js +15 -7
  252. package/dist/facts/unit-conflicts.js.map +1 -1
  253. package/dist/facts/unit-equivalents.d.ts +13 -0
  254. package/dist/facts/unit-equivalents.d.ts.map +1 -0
  255. package/dist/facts/unit-equivalents.js +56 -0
  256. package/dist/facts/unit-equivalents.js.map +1 -0
  257. package/dist/facts/unit-tolerance.d.ts +4 -0
  258. package/dist/facts/unit-tolerance.d.ts.map +1 -0
  259. package/dist/facts/unit-tolerance.js +13 -0
  260. package/dist/facts/unit-tolerance.js.map +1 -0
  261. package/dist/facts/unit-word.d.ts +8 -0
  262. package/dist/facts/unit-word.d.ts.map +1 -0
  263. package/dist/facts/unit-word.js +34 -0
  264. package/dist/facts/unit-word.js.map +1 -0
  265. package/dist/facts/written-precision.d.ts +10 -0
  266. package/dist/facts/written-precision.d.ts.map +1 -0
  267. package/dist/facts/written-precision.js +20 -0
  268. package/dist/facts/written-precision.js.map +1 -0
  269. package/dist/kana-spelling.d.ts +12 -0
  270. package/dist/kana-spelling.d.ts.map +1 -1
  271. package/dist/kana-spelling.js +23 -0
  272. package/dist/kana-spelling.js.map +1 -1
  273. package/dist/model-codes.d.ts +23 -0
  274. package/dist/model-codes.d.ts.map +1 -0
  275. package/dist/model-codes.js +81 -0
  276. package/dist/model-codes.js.map +1 -0
  277. package/dist/name-spelling-chars.d.ts +16 -0
  278. package/dist/name-spelling-chars.d.ts.map +1 -0
  279. package/dist/name-spelling-chars.js +14 -0
  280. package/dist/name-spelling-chars.js.map +1 -0
  281. package/dist/name-variants.d.ts +3 -2
  282. package/dist/name-variants.d.ts.map +1 -1
  283. package/dist/name-variants.js +4 -7
  284. package/dist/name-variants.js.map +1 -1
  285. package/dist/phone-numbers.d.ts.map +1 -1
  286. package/dist/phone-numbers.js +22 -5
  287. package/dist/phone-numbers.js.map +1 -1
  288. package/dist/place-names.d.ts +12 -5
  289. package/dist/place-names.d.ts.map +1 -1
  290. package/dist/place-names.js +34 -14
  291. package/dist/place-names.js.map +1 -1
  292. package/dist/plugin.d.ts +5 -0
  293. package/dist/plugin.d.ts.map +1 -1
  294. package/dist/preamble-paragraphs.d.ts +2 -1
  295. package/dist/preamble-paragraphs.d.ts.map +1 -1
  296. package/dist/preamble-paragraphs.js +16 -1
  297. package/dist/preamble-paragraphs.js.map +1 -1
  298. package/dist/profile/parse.d.ts.map +1 -1
  299. package/dist/profile/parse.js +11 -0
  300. package/dist/profile/parse.js.map +1 -1
  301. package/dist/spacing-code.d.ts +6 -0
  302. package/dist/spacing-code.d.ts.map +1 -0
  303. package/dist/spacing-code.js +16 -0
  304. package/dist/spacing-code.js.map +1 -0
  305. package/dist/structure/amount-range.d.ts +2 -2
  306. package/dist/structure/amount-range.d.ts.map +1 -1
  307. package/dist/structure/amount-range.js +12 -5
  308. package/dist/structure/amount-range.js.map +1 -1
  309. package/dist/structure/capacity-count.d.ts +59 -0
  310. package/dist/structure/capacity-count.d.ts.map +1 -0
  311. package/dist/structure/capacity-count.js +118 -0
  312. package/dist/structure/capacity-count.js.map +1 -0
  313. package/dist/structure/counted-amounts.d.ts +6 -0
  314. package/dist/structure/counted-amounts.d.ts.map +1 -1
  315. package/dist/structure/counted-amounts.js +4 -4
  316. package/dist/structure/counted-amounts.js.map +1 -1
  317. package/dist/structure/date-outside-period.d.ts +3 -1
  318. package/dist/structure/date-outside-period.d.ts.map +1 -1
  319. package/dist/structure/date-outside-period.js +8 -5
  320. package/dist/structure/date-outside-period.js.map +1 -1
  321. package/dist/structure/date-range.d.ts +1 -0
  322. package/dist/structure/date-range.d.ts.map +1 -1
  323. package/dist/structure/date-range.js +1 -1
  324. package/dist/structure/date-range.js.map +1 -1
  325. package/dist/structure/deadline-after-event.d.ts +23 -0
  326. package/dist/structure/deadline-after-event.d.ts.map +1 -0
  327. package/dist/structure/deadline-after-event.js +42 -0
  328. package/dist/structure/deadline-after-event.js.map +1 -0
  329. package/dist/structure/definition-use.d.ts +1 -1
  330. package/dist/structure/definition-use.d.ts.map +1 -1
  331. package/dist/structure/definition-use.js +55 -2
  332. package/dist/structure/definition-use.js.map +1 -1
  333. package/dist/structure/due-date.d.ts.map +1 -1
  334. package/dist/structure/due-date.js +5 -27
  335. package/dist/structure/due-date.js.map +1 -1
  336. package/dist/structure/labelled-date-order.d.ts +38 -0
  337. package/dist/structure/labelled-date-order.d.ts.map +1 -0
  338. package/dist/structure/labelled-date-order.js +41 -0
  339. package/dist/structure/labelled-date-order.js.map +1 -0
  340. package/dist/structure/layout-codes.d.ts +68 -0
  341. package/dist/structure/layout-codes.d.ts.map +1 -0
  342. package/dist/structure/layout-codes.js +161 -0
  343. package/dist/structure/layout-codes.js.map +1 -0
  344. package/dist/structure/measured-amounts.d.ts +17 -0
  345. package/dist/structure/measured-amounts.d.ts.map +1 -0
  346. package/dist/structure/measured-amounts.js +110 -0
  347. package/dist/structure/measured-amounts.js.map +1 -0
  348. package/dist/structure/quantity-range.d.ts +8 -0
  349. package/dist/structure/quantity-range.d.ts.map +1 -0
  350. package/dist/structure/quantity-range.js +36 -0
  351. package/dist/structure/quantity-range.js.map +1 -0
  352. package/dist/structure/range-bands.d.ts +75 -0
  353. package/dist/structure/range-bands.d.ts.map +1 -0
  354. package/dist/structure/range-bands.js +244 -0
  355. package/dist/structure/range-bands.js.map +1 -0
  356. package/dist/structure/rent-multiple.d.ts +76 -0
  357. package/dist/structure/rent-multiple.d.ts.map +1 -0
  358. package/dist/structure/rent-multiple.js +186 -0
  359. package/dist/structure/rent-multiple.js.map +1 -0
  360. package/dist/structure/scaled-quantities.d.ts +50 -0
  361. package/dist/structure/scaled-quantities.d.ts.map +1 -0
  362. package/dist/structure/scaled-quantities.js +153 -0
  363. package/dist/structure/scaled-quantities.js.map +1 -0
  364. package/dist/structure/stamp-order.d.ts +16 -0
  365. package/dist/structure/stamp-order.d.ts.map +1 -0
  366. package/dist/structure/stamp-order.js +78 -0
  367. package/dist/structure/stamp-order.js.map +1 -0
  368. package/dist/structure/step-times.d.ts +49 -0
  369. package/dist/structure/step-times.d.ts.map +1 -0
  370. package/dist/structure/step-times.js +128 -0
  371. package/dist/structure/step-times.js.map +1 -0
  372. package/dist/structure/term-deadline.d.ts +19 -0
  373. package/dist/structure/term-deadline.d.ts.map +1 -0
  374. package/dist/structure/term-deadline.js +28 -0
  375. package/dist/structure/term-deadline.js.map +1 -0
  376. package/dist/structure/time-range.d.ts +16 -0
  377. package/dist/structure/time-range.d.ts.map +1 -0
  378. package/dist/structure/time-range.js +57 -0
  379. package/dist/structure/time-range.js.map +1 -0
  380. package/dist/structure/total-line.d.ts +16 -0
  381. package/dist/structure/total-line.d.ts.map +1 -0
  382. package/dist/structure/total-line.js +44 -0
  383. package/dist/structure/total-line.js.map +1 -0
  384. package/dist/structure/total.d.ts +7 -1
  385. package/dist/structure/total.d.ts.map +1 -1
  386. package/dist/structure/total.js +28 -5
  387. package/dist/structure/total.js.map +1 -1
  388. package/dist/structure/unlisted-items.d.ts +70 -0
  389. package/dist/structure/unlisted-items.d.ts.map +1 -0
  390. package/dist/structure/unlisted-items.js +152 -0
  391. package/dist/structure/unlisted-items.js.map +1 -0
  392. package/dist/structure/walk-time.d.ts +87 -0
  393. package/dist/structure/walk-time.d.ts.map +1 -0
  394. package/dist/structure/walk-time.js +195 -0
  395. package/dist/structure/walk-time.js.map +1 -0
  396. package/package.json +3 -3
  397. package/profiles/listing.yaml +20 -0
  398. package/rules/announced-count-mismatch.yaml +2 -2
  399. package/rules/capacity-runtime-mismatch.yaml +66 -0
  400. package/rules/contact-variant.yaml +76 -0
  401. package/rules/count-over-capacity.yaml +55 -0
  402. package/rules/date-outside-period.yaml +4 -4
  403. package/rules/date-stamp-order.yaml +82 -0
  404. package/rules/deadline-after-event.yaml +67 -0
  405. package/rules/defined-name-repeated.yaml +2 -2
  406. package/rules/due-before-issue.yaml +11 -7
  407. package/rules/fact-conflict.yaml +9 -4
  408. package/rules/heading-echo.yaml +2 -2
  409. package/rules/interval-count-mismatch.yaml +54 -0
  410. package/rules/latin-spacing.yaml +2 -2
  411. package/rules/layout-code-mismatch.yaml +68 -0
  412. package/rules/name-variant.yaml +8 -2
  413. package/rules/no-em-dash.yaml +2 -2
  414. package/rules/preamble-length.yaml +2 -2
  415. package/rules/quantity-range-reversed.yaml +53 -0
  416. package/rules/range-band-mismatch.yaml +70 -0
  417. package/rules/rent-multiple-mismatch.yaml +61 -0
  418. package/rules/scaled-quantity-mismatch.yaml +69 -0
  419. package/rules/step-time-sum-mismatch.yaml +65 -0
  420. package/rules/time-range-reversed.yaml +48 -0
  421. package/rules/title-case-consistency.yaml +2 -2
  422. package/rules/total-mismatch.yaml +2 -2
  423. package/rules/unit-mismatch.yaml +12 -4
  424. package/rules/unlisted-item-used.yaml +90 -0
  425. package/rules/unused-definition.yaml +2 -2
  426. package/rules/use-before-definition.yaml +2 -2
  427. package/rules/walk-time-distance-mismatch.yaml +55 -0
  428. package/src/announced-count.ts +6 -3
  429. package/src/contacts/addresses.ts +179 -0
  430. package/src/contacts/emails.ts +63 -0
  431. package/src/contacts/variants.ts +33 -0
  432. package/src/derived/battery-runtime.ts +284 -0
  433. package/src/derived/period-counts.ts +178 -0
  434. package/src/derived/time-lengths.ts +12 -9
  435. package/src/derived/use-interval.ts +151 -0
  436. package/src/detectors/announced-count.ts +1 -0
  437. package/src/detectors/capacity-runtime.ts +128 -0
  438. package/src/detectors/contact-variant.ts +24 -0
  439. package/src/detectors/count-over-capacity.ts +37 -0
  440. package/src/detectors/date-outside-period.ts +2 -1
  441. package/src/detectors/date-stamp-order.ts +28 -0
  442. package/src/detectors/defined-term-form.ts +63 -7
  443. package/src/detectors/definition-use.ts +6 -2
  444. package/src/detectors/derived-numbers.ts +3 -3
  445. package/src/detectors/due-date.ts +10 -2
  446. package/src/detectors/en-shape.ts +11 -4
  447. package/src/detectors/event-deadline.ts +29 -0
  448. package/src/detectors/fact-consistency.ts +64 -3
  449. package/src/detectors/heading-case.ts +13 -4
  450. package/src/detectors/heading-echo.ts +17 -1
  451. package/src/detectors/layout-code.ts +174 -0
  452. package/src/detectors/measured-facts.ts +20 -0
  453. package/src/detectors/name-variant-model-codes.ts +27 -0
  454. package/src/detectors/name-variant.ts +32 -6
  455. package/src/detectors/orthographic-variant.ts +78 -9
  456. package/src/detectors/orthography.ts +16 -6
  457. package/src/detectors/quantity-range.ts +47 -0
  458. package/src/detectors/range-band.ts +125 -0
  459. package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
  460. package/src/detectors/registry/contact-variant.ts +4 -0
  461. package/src/detectors/registry/count-over-capacity.ts +4 -0
  462. package/src/detectors/registry/date-stamp-order.ts +4 -0
  463. package/src/detectors/registry/event-deadline.ts +4 -0
  464. package/src/detectors/registry/interval-count-mismatch.ts +4 -0
  465. package/src/detectors/registry/layout-code-mismatch.ts +4 -0
  466. package/src/detectors/registry/quantity-range.ts +4 -0
  467. package/src/detectors/registry/range-band-mismatch.ts +4 -0
  468. package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
  469. package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
  470. package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
  471. package/src/detectors/registry/time-range-reversed.ts +4 -0
  472. package/src/detectors/registry/unlisted-item-used.ts +4 -0
  473. package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
  474. package/src/detectors/rent-multiple-mismatch.ts +56 -0
  475. package/src/detectors/scaled-quantity.ts +49 -0
  476. package/src/detectors/signals.ts +3 -1
  477. package/src/detectors/step-time-sum.ts +55 -0
  478. package/src/detectors/structure-tree.ts +30 -5
  479. package/src/detectors/structure.ts +1 -1
  480. package/src/detectors/technical-docs.ts +12 -0
  481. package/src/detectors/time-order.ts +5 -1
  482. package/src/detectors/unit-consistency.ts +50 -38
  483. package/src/detectors/unlisted-items.ts +72 -0
  484. package/src/detectors/use-count-words.ts +32 -0
  485. package/src/detectors/use-interval.ts +51 -0
  486. package/src/detectors/walk-time-distance-mismatch.ts +45 -0
  487. package/src/facts/approximate-tolerance.ts +19 -0
  488. package/src/facts/item-amounts.ts +94 -0
  489. package/src/facts/labelled-facts.ts +29 -7
  490. package/src/facts/measure-units.ts +31 -0
  491. package/src/facts/measures.ts +113 -25
  492. package/src/facts/retention-periods.ts +216 -0
  493. package/src/facts/unit-conflicts.ts +22 -9
  494. package/src/facts/unit-equivalents.ts +70 -0
  495. package/src/facts/unit-tolerance.ts +17 -0
  496. package/src/facts/unit-word.ts +35 -0
  497. package/src/facts/written-precision.ts +23 -0
  498. package/src/kana-spelling.ts +25 -0
  499. package/src/model-codes.ts +104 -0
  500. package/src/name-spelling-chars.ts +34 -0
  501. package/src/name-variants.ts +8 -7
  502. package/src/phone-numbers.ts +26 -6
  503. package/src/place-names.ts +53 -14
  504. package/src/plugin.ts +2 -0
  505. package/src/preamble-paragraphs.ts +21 -2
  506. package/src/profile/parse.ts +10 -0
  507. package/src/spacing-code.ts +26 -0
  508. package/src/structure/amount-range.ts +16 -7
  509. package/src/structure/capacity-count.ts +196 -0
  510. package/src/structure/counted-amounts.ts +5 -4
  511. package/src/structure/date-outside-period.ts +11 -6
  512. package/src/structure/date-range.ts +1 -1
  513. package/src/structure/deadline-after-event.ts +67 -0
  514. package/src/structure/definition-use.ts +59 -2
  515. package/src/structure/due-date.ts +8 -32
  516. package/src/structure/labelled-date-order.ts +84 -0
  517. package/src/structure/layout-codes.ts +210 -0
  518. package/src/structure/measured-amounts.ts +138 -0
  519. package/src/structure/quantity-range.ts +54 -0
  520. package/src/structure/range-bands.ts +316 -0
  521. package/src/structure/rent-multiple.ts +277 -0
  522. package/src/structure/scaled-quantities.ts +225 -0
  523. package/src/structure/stamp-order.ts +100 -0
  524. package/src/structure/step-times.ts +169 -0
  525. package/src/structure/term-deadline.ts +35 -0
  526. package/src/structure/time-range.ts +64 -0
  527. package/src/structure/total-line.ts +63 -0
  528. package/src/structure/total.ts +41 -6
  529. package/src/structure/unlisted-items.ts +207 -0
  530. package/src/structure/walk-time.ts +285 -0
@@ -1,14 +1,16 @@
1
1
  import type { Detector, Finding, ProseDocument } from "../plugin.ts";
2
2
  import { cuedNamesIn, mentionsIn, nameVariants, suffixedNamesIn, withKnownNeighbours, type NameMention, type VariantChars } from "../name-variants.ts";
3
+ import type { SpellingInput } from "../name-spelling-chars.ts";
3
4
  import { nameCueAt, type NameCues } from "../name-cue.ts";
4
5
  import { quoteAt } from "./structure-tree.ts";
5
6
  import { companyMentionsIn, companyVariants, type CompanyForm, type IsProper } from "../company-names.ts";
6
7
  import { proseAndTablesOf } from "../table-text.ts";
7
8
  import { tableBodyCells } from "../facts/table-facts.ts";
8
9
  import { cellNamesIn, cellNameVariants, proseNamesOf } from "../table-names.ts";
9
- import { placeMentionsIn, placeVariants, type PlaceReader, type PlaceWord } from "../place-names.ts";
10
+ import { placeMentionsIn, placeVariants, type PlaceChars, type PlaceReader, type PlaceWord } from "../place-names.ts";
10
11
  import type { TableCell, Token } from "../plugin.ts";
11
12
  import { proseWithCells } from "../table-cells.ts";
13
+ import { modelCodeFindings } from "./name-variant-model-codes.ts";
12
14
  import { labelledSpans, orderNamesOf, quotedSpans, stemOf, titleCaseSpans, wordOrderVariants, type OrderWord } from "../name-word-order.ts";
13
15
 
14
16
  // 人の名前と読ませる敬称(様、さん)は語彙表 person-suffix、人を指す前置き(担当の)は person-lead、名前のすぐ後ろに来る語
@@ -33,6 +35,18 @@ const properOf = (doc: ProseDocument): IsProper => {
33
35
  const placeWordsOf = (doc: ProseDocument): PlaceWord[] =>
34
36
  (doc.lexicons["place-word"] ?? []).map((entry) => ({ pattern: entry.pattern, group: entry.group ?? entry.pattern }));
35
37
 
38
+ /**
39
+ * 場所の名前の字。方角や位置の字(東、上、新)は語彙表 place-direction、読みの同じ字(洲 と 州)は place-name-char と、字体の違う
40
+ * 同じ字の name-variant-char が組(group)ごとに言う。
41
+ */
42
+ const placeCharsOf = (doc: ProseDocument, chars: VariantChars): PlaceChars => ({
43
+ directions: new Set(patternsOf(doc, "place-direction")),
44
+ sameReading: new Map([
45
+ ...chars,
46
+ ...(doc.lexicons["place-name-char"] ?? []).flatMap((entry): [string, string][] => (entry.group === undefined ? [] : [[entry.pattern, entry.group]])),
47
+ ]),
48
+ });
49
+
36
50
  /** 名前の頭に立たない語の品詞(The、at、and)。 */
37
51
  const FUNCTION_POS: ReadonlySet<string> = new Set(["DET", "ADP", "PRON", "CCONJ", "SCONJ", "AUX", "PART"]);
38
52
 
@@ -87,8 +101,8 @@ const companyFindings = (doc: ProseDocument, prose: string, reported: readonly R
87
101
  .map(({ mention, usual, kind }) => ({ offset: mention.offset, name: mention.surface, usual, kind: kind === "spelling" ? kind : `company-${kind}` }));
88
102
 
89
103
  /** 場所の名前の書き分け。表の本体の升の中の名前も読む(見出しの行は読まない)。人や会社の名前の見方がすでに指した所と重なるものは除く。 */
90
- const placeFindings = (doc: ProseDocument, prose: string, reported: readonly Reported[]): Reported[] =>
91
- placeVariants(placeMentionsIn(proseWithCells(prose, cellsOf(doc)), placeWordsOf(doc), placeReaderOf(doc)))
104
+ const placeFindings = (doc: ProseDocument, prose: string, reported: readonly Reported[], chars: VariantChars): Reported[] =>
105
+ placeVariants(placeMentionsIn(proseWithCells(prose, cellsOf(doc)), placeWordsOf(doc), placeReaderOf(doc)), placeCharsOf(doc, chars))
92
106
  .filter(({ mention }) => !overlapsAny(reported, mention.offset, mention.surface.length))
93
107
  .map(({ mention, usual, kind }) => ({ offset: mention.offset, name: mention.surface, usual, kind }));
94
108
 
@@ -142,6 +156,16 @@ const nameMentionsOf = (doc: ProseDocument, prose: string, chars: VariantChars):
142
156
  return withKnownNeighbours(cued, prose).toSorted((left, right) => left.offset - right.offset);
143
157
  };
144
158
 
159
+ /** 一語の名前の中で同じ音を書く字(ヶ・ケ・が)は語彙表 name-spelling-char が組(group)ごとに言う。 */
160
+ const spellingCharsOf = (doc: ProseDocument): VariantChars =>
161
+ new Map((doc.lexicons["name-spelling-char"] ?? []).flatMap((entry): [string, string][] => (entry.group === undefined ? [] : [[entry.pattern, entry.group]])));
162
+
163
+ /** 名前の中の同じ音の字は、表の升の中の、解析器が固有名詞と読む語(物件名の升の 桜ケ丘)とも比べる。 */
164
+ const spellingInputOf = (doc: ProseDocument): SpellingInput => {
165
+ const suffixes = patternsOf(doc, "person-suffix");
166
+ return { chars: spellingCharsOf(doc), alsoWritten: cellsOf(doc).flatMap((cell) => mentionsIn(cell.tokens ?? [], doc.source, suffixes)) };
167
+ };
168
+
145
169
  /** 表の升に書いた名前の書き分け。名前の形をした升を、本文の名前と比べる。ほかの見方がすでに指した所と重なるものは除く。 */
146
170
  const tableFindings = (doc: ProseDocument, prose: string, mentions: readonly NameMention[], reported: readonly Reported[]): Reported[] =>
147
171
  cellNameVariants(cellNamesIn(cellsOf(doc)), proseNamesOf(mentions, prose))
@@ -153,7 +177,7 @@ export const nameVariant: Detector = (doc): Finding[] => {
153
177
  const prose = doc.prose ?? doc.source;
154
178
  const chars = variantCharsOf(doc);
155
179
  const mentions = nameMentionsOf(doc, prose, chars);
156
- const names = nameVariants(mentions, chars).map(({ mention, usual, kind }): Reported => ({
180
+ const names = nameVariants(mentions, chars, spellingInputOf(doc)).map(({ mention, usual, kind }): Reported => ({
157
181
  offset: mention.offset,
158
182
  name: mention.surface,
159
183
  usual,
@@ -161,8 +185,10 @@ export const nameVariant: Detector = (doc): Finding[] => {
161
185
  }));
162
186
  const namesAndCompanies = [...names, ...companyFindings(doc, prose, names)];
163
187
  const withTables = [...namesAndCompanies, ...tableFindings(doc, prose, mentions, namesAndCompanies)];
164
- const withPlaces = [...withTables, ...placeFindings(doc, prose, withTables)];
165
- return [...withPlaces, ...orderFindings(doc, prose, withPlaces)]
188
+ const withPlaces = [...withTables, ...placeFindings(doc, prose, withTables, chars)];
189
+ const withOrder = [...withPlaces, ...orderFindings(doc, prose, withPlaces)];
190
+ const codes = modelCodeFindings(doc, prose).filter((code) => !overlapsAny(withOrder, code.offset, code.name.length));
191
+ return [...withOrder, ...codes]
166
192
  .toSorted((left, right) => left.offset - right.offset)
167
193
  .map(({ offset, name, usual, kind }) => ({
168
194
  rule: "",
@@ -1,6 +1,6 @@
1
- import type { Detector, Finding, ProseDocument, Sentence, Token } from "../plugin.ts";
1
+ import type { Detector, Finding, ProseDocument, Sentence, TableCell, Token } from "../plugin.ts";
2
2
  import { groupBy, oddSpellings, type KeyedWord, type OddSpelling } from "../spelling-variants.ts";
3
- import { dropsOkurigana, kanjiSkeleton, katakanaKey, lemmaReading } from "../kana-spelling.ts";
3
+ import { dropsOkurigana, kanjiKeyAmong, katakanaKey, lemmaReading } from "../kana-spelling.ts";
4
4
  import { isKatakanaWord, stemOf } from "../long-vowel.ts";
5
5
  import { QUOTATION_MARKS, isWithinAny, quotedSpans } from "../quoted-span.ts";
6
6
  import { nameSpans, touchesAny } from "../team-names.ts";
@@ -12,7 +12,14 @@ import { isPartOfAddress } from "./address-word.ts";
12
12
  * A word found in the document: where it is, which sentence it is in, its key and spelling, how it is written when that differs, and
13
13
  * whether it is written onto the noun after it (取扱事業者).
14
14
  */
15
- type Placed = KeyedWord & { readonly sentence: Sentence; readonly offset: number; readonly shown?: string; readonly compoundHead?: boolean };
15
+ type Placed = KeyedWord & {
16
+ readonly sentence: Sentence;
17
+ readonly offset: number;
18
+ readonly shown?: string;
19
+ readonly compoundHead?: boolean;
20
+ /** Where a run of words ends (タイヤレバー). */
21
+ readonly end?: number;
22
+ };
16
23
 
17
24
  type IsOpen = (start: number, end: number) => boolean;
18
25
 
@@ -141,11 +148,12 @@ const readingWords = (sentence: Sentence, isOpen: IsOpen, skips: Skips): Placed[
141
148
  */
142
149
  const byKanji = (words: readonly Placed[]): Placed[] =>
143
150
  [...groupBy(words, (word) => word.key).values()].flatMap((group) => {
144
- const skeletons = [...new Set(group.map((word) => kanjiSkeleton(word.spelling)).filter((skeleton) => skeleton !== ""))];
145
151
  const spellings = [...new Set(group.map((word) => word.spelling))];
152
+ const skeletonOf = (spelling: string): string => kanjiKeyAmong(spelling, spellings);
153
+ const skeletons = [...new Set(spellings.map(skeletonOf).filter((skeleton) => skeleton !== ""))];
146
154
  const apart = spellings.some((spelling) => dropsOkurigana(spelling, spellings)) ? "|compound" : "";
147
155
  return group.flatMap((word): Placed[] => {
148
- const own = kanjiSkeleton(word.spelling);
156
+ const own = skeletonOf(word.spelling);
149
157
  if (own === "" && skeletons.length > 1) return [];
150
158
  const use = word.compoundHead === true ? apart : "";
151
159
  return [{ ...word, key: `${word.key}|${own === "" ? (skeletons[0] ?? "") : own}${use}` }];
@@ -161,6 +169,44 @@ const katakanaWords = (sentence: Sentence, isOpen: IsOpen): Placed[] =>
161
169
  return [...key].length < MIN_KATAKANA_KEY ? [] : [{ key: `kana|${key}`, spelling: stemOf(token.surface), sentence, offset: token.span.start }];
162
170
  });
163
171
 
172
+ const isKatakanaNoun = (token: Token): boolean => token.pos === "NOUN" && isKatakanaWord(token.surface);
173
+
174
+ /** Katakana nouns written onto each other (タイヤ and レバー in タイヤレバー), each run whole: one place may be cut into words another is not. */
175
+ const katakanaRuns = (tokens: readonly Token[]): Token[][] =>
176
+ tokens.reduce<Token[][]>((runs, token, at) => {
177
+ if (!isKatakanaNoun(token)) return runs;
178
+ const last = runs.at(-1);
179
+ if (last !== undefined && isTouching(tokens[at - 1], token) && last.at(-1) === tokens[at - 1]) last.push(token);
180
+ else runs.push([token]);
181
+ return runs;
182
+ }, []);
183
+
184
+ /**
185
+ * Katakana runs keyed like one katakana word, spelled as their words' stems joined: タイヤーレバー (one unknown word) meets
186
+ * タイヤレバー read as タイヤ and レバー, while the final ー of a word inside the run (サーバーリスト and サーバリスト) stays katakana-long-vowel's.
187
+ */
188
+ const katakanaRunWords = (sentence: Sentence, isOpen: IsOpen): Placed[] =>
189
+ katakanaRuns(sentence.tokens ?? []).flatMap((run): Placed[] => {
190
+ const [first, last] = [run[0], run.at(-1)];
191
+ if (first === undefined || last === undefined) return [];
192
+ const [start, end] = [first.span.start - sentence.span.start, last.span.end - sentence.span.start];
193
+ const key = katakanaKey(run.map((token) => token.surface).join(""));
194
+ if ([...key].length < MIN_KATAKANA_KEY || !isOpen(start, end)) return [];
195
+ return [
196
+ {
197
+ key: `kana|${key}`,
198
+ spelling: run.map((token) => stemOf(token.surface)).join(""),
199
+ shown: run.map((token) => token.surface).join(""),
200
+ sentence,
201
+ offset: first.span.start,
202
+ end: last.span.end,
203
+ },
204
+ ];
205
+ });
206
+
207
+ /** Whether a run holds a place another key already points at: the word inside it is said, not the run around it. */
208
+ const coversOffset = (run: Placed, offset: number): boolean => offset >= run.offset && offset < (run.end ?? run.offset + 1);
209
+
164
210
  const LATIN_WORD = /[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)*/gu;
165
211
  const LOWER = /\p{Ll}/u;
166
212
 
@@ -204,6 +250,23 @@ const findingOf = (words: readonly Placed[], odd: OddSpelling<Placed>): Finding
204
250
  values: { written: odd.word.shown ?? odd.word.spelling, other: shownUsual(words, odd), count: odd.count, of: odd.of, offset: odd.word.offset },
205
251
  });
206
252
 
253
+ /** A cell that is one Latin word in lower case (openapi, contentType): a field name or a value, spelled by the data and not by the writer. */
254
+ const DATA_KEY_CELL = /^\p{Ll}[A-Za-z0-9]*$/u;
255
+
256
+ type Place = { readonly sentence: Sentence; readonly isOpen: IsOpen; readonly readsLatin: boolean };
257
+
258
+ /** A link's brackets and where it goes (`[`, `](#contentType)`, `][ref]`). The prose hides them outside tables; a cell keeps the Markdown as written. */
259
+ const LINK_CHROME = /\]\([^)]*\)|\]\[[^\]]*\]|\[/gu;
260
+
261
+ const withoutLinkChrome = (text: string): string => text.replaceAll(LINK_CHROME, (chrome) => " ".repeat(chrome.length));
262
+
263
+ /** A body cell of a table read as a sentence of its own: the prose covers tables, so a cell's words (片栗粉 in a list of ingredients) are not in the sentences. Header rows stay out. */
264
+ const cellPlace = (cell: TableCell, names: readonly string[]): Place => {
265
+ const text = withoutLinkChrome(cell.text);
266
+ const sentence: Sentence = { span: { start: cell.start, end: cell.end }, text, tokens: cell.tokens ?? [] };
267
+ return { sentence, isOpen: isOpenIn(sentence, names), readsLatin: !DATA_KEY_CELL.test(text.trim()) };
268
+ };
269
+
207
270
  /**
208
271
  * One word written two ways in one document (出来る and できる, 引っ越し and 引越し, ウィンドウ and ウインドウ, e-mail and email),
209
272
  * found without a list of words: the document's own words are grouped by reading or by an evened-out spelling, and the way the
@@ -211,12 +274,18 @@ const findingOf = (words: readonly Placed[], odd: OddSpelling<Placed>): Finding
211
274
  */
212
275
  export const orthographicVariant: Detector = (doc, options): Finding[] => {
213
276
  const skips: Skips = { skip: new Set([...patternsOf(doc, DISTINCT), ...patternsOf(doc, FUKUSHI)]), idioms: new Set(patternsOf(doc, IDIOM)) };
214
- const placed = doc.sentences.map((sentence) => ({ sentence, isOpen: isOpenIn(sentence, doc.names ?? []) }));
277
+ const names = doc.names ?? [];
278
+ const placed: Place[] = [
279
+ ...doc.sentences.map((sentence) => ({ sentence, isOpen: isOpenIn(sentence, names), readsLatin: true })),
280
+ ...(doc.tableCells?.() ?? []).map((cell) => cellPlace(cell, names)),
281
+ ];
215
282
  const reading = byKanji(placed.flatMap(({ sentence, isOpen }) => readingWords(sentence, isOpen, skips)));
216
283
  const katakana = placed.flatMap(({ sentence, isOpen }) => katakanaWords(sentence, isOpen));
217
- const latin = placed.flatMap(({ sentence, isOpen }) => latinWords(sentence, isOpen, skips.skip));
284
+ const runs = placed.flatMap(({ sentence, isOpen }) => katakanaRunWords(sentence, isOpen));
285
+ const latin = placed.flatMap(({ sentence, isOpen, readsLatin }) => (readsLatin ? latinWords(sentence, isOpen, skips.skip) : []));
218
286
  const odd = [reading, katakana, latin].flatMap((words) => oddSpellings(words, options.limit));
219
- const all = [...reading, ...katakana, ...latin];
287
+ const oddRuns = oddSpellings(runs, options.limit).filter(({ word }) => !odd.some((other) => coversOffset(word, other.word.offset)));
288
+ const all = [...reading, ...katakana, ...runs, ...latin];
220
289
  // A word can be odd by its reading and by its katakana key at once; one place is said once.
221
- return [...new Map(odd.map((entry) => [entry.word.offset, entry])).values()].map((entry) => findingOf(all, entry));
290
+ return [...new Map([...odd, ...oddRuns].map((entry) => [entry.word.offset, entry])).values()].map((entry) => findingOf(all, entry));
222
291
  };
@@ -5,6 +5,7 @@ import { minorityReport } from "../spacing-minority.ts";
5
5
  import { isWithinAny, quotedSpans } from "../quoted-span.ts";
6
6
  import { digitRunAround, endsWithDivisionLabel, isNumberName, sequenceLabelStarts, type NameContext } from "../number-name.ts";
7
7
  import { isSpacedLocationPart, type FloorWords } from "../location-part.ts";
8
+ import { addressSpans, clockTimeSpans, isInsideSpan } from "../spacing-code.ts";
8
9
 
9
10
  /** チームが chaff.yaml の prefer に書いた「使わない書き方」。書いていなければ何も言わない。 */
10
11
  export const preferredTerm: Detector = (doc, options): Finding[] => {
@@ -70,18 +71,27 @@ const followsItemNumber = (text: string, boundary: Boundary, itemNumbers: Readon
70
71
  const calendarStarts = (sentence: Sentence, units: CalendarUnits): ReadonlySet<number> =>
71
72
  new Set(calendarRuns(sentence.text, sentence.tokens, sentence.span.start, units).map(({ run }) => run.start));
72
73
 
74
+ /** 英字の側がメールアドレスか URL の境目。後ろの空白は、アドレスの終わりを示す区切り。 */
75
+ const besideAddress = (addresses: readonly Span[], boundary: Boundary): boolean =>
76
+ isInsideSpan(addresses, boundary.offset - 1) || isInsideSpan(addresses, boundary.spaced ? boundary.offset + 1 : boundary.offset);
77
+
78
+ /** 文の中で一度だけ探すもの: 日付・時刻の数の頭の位置、コロンで書いた時刻、メールアドレス・URL。 */
79
+ type SentenceCodes = { readonly calendar: ReadonlySet<number>; readonly clockTimes: readonly Span[]; readonly addresses: readonly Span[] };
80
+
73
81
  /**
74
82
  * 番号・識別子として書かれた数(number-name.ts)、日付・時刻の数、「第1節」の後ろの境目、文頭の項目の番号(「一 JIS」)の後ろの境目は、空け方の好みではないので数えない。
75
- * 日付・時刻は前の境目(「は 9月」「午後3時」「令和 3 年」)も数えない。日付はまとめて一つの書き方で、数量の空け方の票にはしない。
83
+ * 日付・時刻は前の境目(「は 9月」「午後3時」「令和 3 年」)も数えない。日付はまとめて一つの書き方で、数量の空け方の票にはしない。コロンで書いた時刻(13:30)も同じ。
84
+ * メールアドレス・URL の前後の境目も数えない(spacing-code.ts)。
76
85
  * 「本社 5階」のように名前の後ろに空白で区切った階・部屋の番号の前の空白も、所在の組の区切りなので数えない(location-part.ts)。
77
86
  */
78
- const isCounted = (sentence: Sentence, boundary: Boundary, context: NumberContext, calendar: ReadonlySet<number>): boolean => {
87
+ const isCounted = (sentence: Sentence, boundary: Boundary, context: NumberContext, codes: SentenceCodes): boolean => {
79
88
  if (followsItemNumber(sentence.text, boundary, context.itemNumbers)) return false;
80
89
  if (boundary.kind !== "after-digit" && endsWithDivisionLabel(sentence.text.slice(0, boundary.offset), context.divisions)) return false;
81
- if (boundary.kind === "letter") return true;
90
+ if (boundary.kind === "letter") return !besideAddress(codes.addresses, boundary);
91
+ if (isInsideSpan(codes.clockTimes, digitBeside(boundary))) return false;
82
92
  const run = digitRunAround(sentence.text, digitBeside(boundary));
83
93
  if (run === undefined) return true;
84
- if (calendar.has(run.start)) return false;
94
+ if (codes.calendar.has(run.start)) return false;
85
95
  if (boundary.kind === "before-digit" && isSpacedLocationPart(sentence.text, run, sentence.tokens, sentence.span.start, context.floors)) return false;
86
96
  return !isNumberName(sentence.text, run, sentence.tokens, sentence.span.start, context);
87
97
  };
@@ -153,10 +163,10 @@ export const latinSpacing: Detector = (doc, options): Finding[] => {
153
163
  };
154
164
  const located: Located[] = doc.sentences.flatMap((sentence) => {
155
165
  const quoted = quotedSpans(sentence.text);
156
- const calendar = calendarStarts(sentence, context.calendar);
166
+ const codes = { calendar: calendarStarts(sentence, context.calendar), clockTimes: clockTimeSpans(sentence.text), addresses: addressSpans(sentence.text) };
157
167
  return latinBoundaries(sentence.text, doc.source.slice(sentence.span.start, sentence.span.end))
158
168
  .filter((boundary) => !isQuoted(quoted, boundary) && !isInsideLink(doc.links, sentence.span.start + boundary.offset))
159
- .filter((boundary) => isCounted(sentence, boundary, context, calendar))
169
+ .filter((boundary) => isCounted(sentence, boundary, context, codes))
160
170
  .map((boundary) => ({ sentence, ...boundary }));
161
171
  });
162
172
  return KINDS.flatMap((kind) =>
@@ -0,0 +1,47 @@
1
+ // quantity-range-reversed: a range of measured quantities whose upper end is below its lower end (5–2 kg, 30〜10 cm,
2
+ // 40〜-10℃), decided by structure/amount-range.ts as amount-range-reversed is. The quantities are read with the unit
3
+ // lexicons (unit-length, unit-mass, …); the joints are range-connector, range-opener, range-closer and quantity-range-word;
4
+ // quantity-change-word (its lead group: a word right before the range) and quantity-bound-label say what makes a pair a
5
+ // change and what labels a bound.
6
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
7
+ import { measuredValues } from "../facts/measures.ts";
8
+ import { measureUnitsOf } from "../facts/measure-units.ts";
9
+ import { reversedAmountRanges, type AmountRangeWords } from "../structure/amount-range.ts";
10
+ import { quantityRangeEnds, unledRanges } from "../structure/quantity-range.ts";
11
+ import { AMOUNT } from "./currency-notation.ts";
12
+ import { proseAndTablesOf } from "../table-text.ts";
13
+ import { quoteAround } from "./quote-around.ts";
14
+
15
+ /** A bare end may carry a minus sign (-10〜-40℃); a hyphen right after a digit or a letter is a range mark (5-10 kg). */
16
+ const SIGNED_NUMBER = `(?:[-−-](?=[0-90-9]))?${AMOUNT}`;
17
+
18
+ const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] =>
19
+ (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern);
20
+
21
+ const ungroupedOf = (doc: ProseDocument, lexicon: string): string[] =>
22
+ (doc.lexicons[lexicon] ?? []).filter((entry) => entry.group === undefined).map((entry) => entry.pattern);
23
+
24
+ const wordsOf = (doc: ProseDocument): AmountRangeWords => ({
25
+ connectors: [...patternsOf(doc, "range-connector"), ...patternsOf(doc, "quantity-range-word")],
26
+ openers: patternsOf(doc, "range-opener"),
27
+ closers: patternsOf(doc, "range-closer"),
28
+ changes: ungroupedOf(doc, "quantity-change-word"),
29
+ lowers: patternsOf(doc, "quantity-bound-label", "lower"),
30
+ uppers: patternsOf(doc, "quantity-bound-label", "upper"),
31
+ links: patternsOf(doc, "quantity-bound-label", "link"),
32
+ scales: [],
33
+ number: SIGNED_NUMBER,
34
+ });
35
+
36
+ export const quantityRange: Detector = (doc): Finding[] => {
37
+ const text = proseAndTablesOf(doc);
38
+ const reversed = reversedAmountRanges(text, quantityRangeEnds(text, measuredValues(text, measureUnitsOf(doc))), wordsOf(doc));
39
+ return unledRanges(text, reversed, patternsOf(doc, "quantity-change-word", "lead")).map((issue) => ({
40
+ rule: "quantity-range-reversed",
41
+ severity: "warning",
42
+ line: 0,
43
+ column: 0,
44
+ quote: quoteAround(text, issue.offset),
45
+ values: { ...issue.values, offset: issue.offset },
46
+ }));
47
+ };
@@ -0,0 +1,125 @@
1
+ // range-band-mismatch: neighbouring bands of one table column or one list that overlap or leave a gap (structure/range-bands.ts).
2
+ // The words that bound a band are range-band-word's, the units range-band-unit's, and the column headings that say what bare
3
+ // numbers count range-band-heading's.
4
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
5
+ import { cellsOf, tablesOf } from "../facts/table-facts.ts";
6
+ import { linesOf } from "../structure/lines.ts";
7
+ import {
8
+ bandSlips,
9
+ headingOf,
10
+ parseBand,
11
+ runKindOf,
12
+ type BandMarker,
13
+ type BandSlip,
14
+ type BandWords,
15
+ type PlacedBand,
16
+ type UnitKind,
17
+ } from "../structure/range-bands.ts";
18
+ import { quoteAt } from "./structure-tree.ts";
19
+
20
+ const MARKER_GROUPS: Readonly<Record<string, Pick<BandMarker, "side" | "kind">>> = {
21
+ "lower-inclusive": { side: "low", kind: "inclusive" },
22
+ "lower-exclusive": { side: "low", kind: "exclusive" },
23
+ "upper-inclusive": { side: "high", kind: "inclusive" },
24
+ "upper-exclusive": { side: "high", kind: "exclusive" },
25
+ };
26
+
27
+ const isUnitKind = (group: string | undefined): group is UnitKind => group === "integer" || group === "continuous";
28
+
29
+ export const bandWordsOf = (lexicons: ProseDocument["lexicons"]): BandWords => {
30
+ const words = lexicons["range-band-word"] ?? [];
31
+ const patterns = (group: string): string[] => words.filter((entry) => entry.group === group).map((entry) => entry.pattern);
32
+ return {
33
+ connectors: patterns("connector"),
34
+ openers: words.filter((entry) => entry.group === "connector" && entry.position === "before").map((entry) => entry.pattern),
35
+ leads: patterns("lead"),
36
+ markers: words.flatMap((entry) => {
37
+ const marker = MARKER_GROUPS[entry.group ?? ""];
38
+ return marker === undefined ? [] : [{ pattern: entry.pattern, ...marker, position: entry.position ?? "after" }];
39
+ }),
40
+ units: (lexicons["range-band-unit"] ?? []).flatMap((entry) =>
41
+ isUnitKind(entry.group) ? [{ pattern: entry.pattern, kind: entry.group, position: entry.position ?? "after" }] : [],
42
+ ),
43
+ headings: (lexicons["range-band-heading"] ?? []).flatMap((entry) => (isUnitKind(entry.group) ? [{ pattern: entry.pattern, kind: entry.group }] : [])),
44
+ };
45
+ };
46
+
47
+ type Slot = { readonly text: string; readonly start: number; readonly end: number };
48
+
49
+ /** Runs of consecutive slots that each state a band. A slot that is not one (合計, a blank) ends the run. */
50
+ const bandRuns = (slots: readonly (Slot | undefined)[], words: BandWords): PlacedBand[][] =>
51
+ slots
52
+ .reduce<PlacedBand[][]>(
53
+ (runs, slot) => {
54
+ const band = slot === undefined ? undefined : parseBand(slot.text, words);
55
+ if (slot === undefined || band === undefined) return [...runs, []];
56
+ const last = runs.at(-1) ?? [];
57
+ return [...runs.slice(0, -1), [...last, { band, start: slot.start, end: slot.end, text: slot.text.trim() }]];
58
+ },
59
+ [[]],
60
+ )
61
+ .filter((run) => run.length > 1);
62
+
63
+ const slipsOfRuns = (runs: readonly PlacedBand[][], heading: ReturnType<typeof headingOf>, words: BandWords): BandSlip[] =>
64
+ runs.flatMap((run) => {
65
+ const kind = runKindOf(
66
+ run.map((placed) => placed.band),
67
+ heading,
68
+ words,
69
+ );
70
+ return kind === undefined ? [] : bandSlips(run, kind);
71
+ });
72
+
73
+ const trimmedSlot = (text: string, start: number): Slot => {
74
+ const lead = text.length - text.trimStart().length;
75
+ return { text: text.trim(), start: start + lead, end: start + lead + text.trim().length };
76
+ };
77
+
78
+ const tableSlips = (source: string, words: BandWords): BandSlip[] =>
79
+ tablesOf(linesOf(source)).flatMap((table) => {
80
+ const headings = cellsOf(table.header);
81
+ const rows = table.rows.map(cellsOf);
82
+ return headings.flatMap((heading, column) =>
83
+ slipsOfRuns(
84
+ bandRuns(
85
+ rows.map((cells) => {
86
+ const cell = cells[column];
87
+ return cell === undefined ? undefined : trimmedSlot(cell.text, cell.start);
88
+ }),
89
+ words,
90
+ ),
91
+ headingOf(heading.text, words),
92
+ words,
93
+ ),
94
+ );
95
+ });
96
+
97
+ const LIST_MARKER = /^\s*(?:[-*+]|\d+[.)])\s+/u;
98
+ /** Where the band at the head of a list item ends: a colon, a comma, a bracket, or a dash set off by spaces. */
99
+ const ITEM_HEAD_END = /[::、,,((]|\s[-–—]\s|\t/u;
100
+
101
+ const listSlips = (doc: ProseDocument, words: BandWords): BandSlip[] =>
102
+ doc.lists.flatMap((list) => {
103
+ const heads = list.itemSpans.map((span): Slot | undefined => {
104
+ const item = doc.source.slice(span.start, span.end).split("\n")[0] ?? "";
105
+ const marker = LIST_MARKER.exec(item)?.[0];
106
+ if (marker === undefined) return undefined;
107
+ const body = item.slice(marker.length);
108
+ const end = ITEM_HEAD_END.exec(body)?.index ?? body.length;
109
+ return trimmedSlot(body.slice(0, end), span.start + marker.length);
110
+ });
111
+ return slipsOfRuns(bandRuns(heads, words), undefined, words);
112
+ });
113
+
114
+ export const rangeBand: Detector = (doc): Finding[] => {
115
+ const words = bandWordsOf(doc.lexicons);
116
+ return [...tableSlips(doc.source, words), ...listSlips(doc, words)].map((slip) => ({
117
+ rule: "range-band-mismatch",
118
+ severity: "warning",
119
+ line: 0,
120
+ column: 0,
121
+ quote: quoteAt(doc.source, slip.later.start),
122
+ ...(slip.kind === "gap" ? { variant: "gap" } : {}),
123
+ values: { earlier: slip.earlier.text, later: slip.later.text, offset: slip.later.start },
124
+ }));
125
+ };
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { capacityRuntime } from "../capacity-runtime.ts";
3
+
4
+ export const detector: Detector = capacityRuntime;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { contactVariant } from "../contact-variant.ts";
3
+
4
+ export const detector: Detector = contactVariant;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { countOverCapacity } from "../count-over-capacity.ts";
3
+
4
+ export const detector: Detector = countOverCapacity;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { dateStampOrder } from "../date-stamp-order.ts";
3
+
4
+ export const detector: Detector = dateStampOrder;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { eventDeadline } from "../event-deadline.ts";
3
+
4
+ export const detector: Detector = eventDeadline;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { intervalCountMismatch } from "../use-interval.ts";
3
+
4
+ export const detector: Detector = intervalCountMismatch;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { layoutCode } from "../layout-code.ts";
3
+
4
+ export const detector: Detector = layoutCode;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { quantityRange } from "../quantity-range.ts";
3
+
4
+ export const detector: Detector = quantityRange;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { rangeBand } from "../range-band.ts";
3
+
4
+ export const detector: Detector = rangeBand;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { rentMultipleMismatch } from "../rent-multiple-mismatch.ts";
3
+
4
+ export const detector: Detector = rentMultipleMismatch;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { scaledQuantityMismatch } from "../scaled-quantity.ts";
3
+
4
+ export const detector: Detector = scaledQuantityMismatch;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { stepTimeSumMismatch } from "../step-time-sum.ts";
3
+
4
+ export const detector: Detector = stepTimeSumMismatch;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { timeRangeReversed } from "../time-order.ts";
3
+
4
+ export const detector: Detector = timeRangeReversed;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { unlistedItemUsed } from "../unlisted-items.ts";
3
+
4
+ export const detector: Detector = unlistedItemUsed;
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../../plugin.ts";
2
+ import { walkTimeDistanceMismatch } from "../walk-time-distance-mismatch.ts";
3
+
4
+ export const detector: Detector = walkTimeDistanceMismatch;
@@ -0,0 +1,56 @@
1
+ // rent-multiple-mismatch: a deposit or key money given in months of rent beside an amount that is not that many months of
2
+ // the rent (structure/rent-multiple.ts). The amounts are currency-notation's, read with amount-multiplier's words of scale;
3
+ // the labels are rent-label and rent-multiple-label, the words rent-multiple-word, the counts number-word (digit), and a
4
+ // range of rent is joined by range-connector or amount-range-word.
5
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
6
+ import { rentMultipleMismatches, type RentAmount, type RentMultipleWords } from "../structure/rent-multiple.ts";
7
+ import { scaledAmountOf, scaleWordsOf } from "./amount-scale.ts";
8
+ import { amountsIn, formsOf } from "./currency-notation.ts";
9
+ import { proseAndTablesOf } from "../table-text.ts";
10
+ import { quoteAround } from "./quote-around.ts";
11
+
12
+ const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] =>
13
+ (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern);
14
+
15
+ const numberWordsOf = (doc: ProseDocument): RentMultipleWords["numberWords"] =>
16
+ (doc.lexicons["number-word"] ?? []).flatMap((entry) =>
17
+ entry.group === "digit" && entry.weight !== undefined ? [{ word: entry.pattern, value: entry.weight }] : [],
18
+ );
19
+
20
+ const wordsOf = (doc: ProseDocument): RentMultipleWords => ({
21
+ rents: patternsOf(doc, "rent-label", "rent"),
22
+ fees: patternsOf(doc, "rent-label", "fee"),
23
+ multiples: patternsOf(doc, "rent-multiple-label"),
24
+ months: patternsOf(doc, "rent-multiple-word", "month"),
25
+ links: patternsOf(doc, "rent-multiple-word", "link"),
26
+ periods: patternsOf(doc, "rent-multiple-word", "period"),
27
+ skips: patternsOf(doc, "rent-multiple-word", "skip"),
28
+ connectors: [...patternsOf(doc, "range-connector"), ...patternsOf(doc, "amount-range-word")],
29
+ numberWords: numberWordsOf(doc),
30
+ });
31
+
32
+ const amountsOf = (text: string, doc: ProseDocument): RentAmount[] => {
33
+ const scales = scaleWordsOf(doc);
34
+ return amountsIn(
35
+ text,
36
+ formsOf(doc),
37
+ scales.map((scale) => scale.word),
38
+ ).flatMap((amount) => {
39
+ const read = scaledAmountOf(text, amount, scales);
40
+ return read === undefined
41
+ ? []
42
+ : [{ offset: read.offset, end: amount.offset + amount.written.length, currency: read.currency, value: read.value, scale: read.unit }];
43
+ });
44
+ };
45
+
46
+ export const rentMultipleMismatch: Detector = (doc): Finding[] => {
47
+ const text = proseAndTablesOf(doc);
48
+ return rentMultipleMismatches(text, amountsOf(text, doc), wordsOf(doc)).map((issue) => ({
49
+ rule: "rent-multiple-mismatch",
50
+ severity: "warning",
51
+ line: 0,
52
+ column: 0,
53
+ quote: quoteAround(text, issue.offset),
54
+ values: { ...issue.values, offset: issue.offset },
55
+ }));
56
+ };
@@ -0,0 +1,49 @@
1
+ // One row of a servings table (4人分 / 8人分, Serves 4 / Serves 8) that does not scale by the columns' ratio while the other rows
2
+ // do. The column marks, the amounts left to the cook and the spellings of one unit come from the language packages.
3
+ import { scaleSlips, type ScaleWords } from "../structure/scaled-quantities.ts";
4
+ import type { MeasureUnit } from "../facts/measures.ts";
5
+ import { quoteAt } from "./structure-tree.ts";
6
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
7
+
8
+ const DIMENSIONS = ["unit-mass", "unit-volume"] as const;
9
+
10
+ const measuresOf = (doc: ProseDocument): MeasureUnit[] =>
11
+ DIMENSIONS.flatMap((dimension) =>
12
+ (doc.lexicons[dimension] ?? []).flatMap((entry) =>
13
+ entry.weight === undefined
14
+ ? []
15
+ : [{ pattern: entry.pattern, dimension, factors: [entry.weight], zero: 0, before: entry.position === "before", context: [] }],
16
+ ),
17
+ );
18
+
19
+ const wordsOf = (doc: ProseDocument): ScaleWords => ({
20
+ columns: (doc.lexicons["servings-column"] ?? []).map((entry) => ({ pattern: entry.pattern, position: entry.position })),
21
+ unscaled: (doc.lexicons["unscaled-amount"] ?? []).map((entry) => entry.pattern),
22
+ unitForms: (doc.lexicons["scaled-unit-form"] ?? []).map((entry) => ({ pattern: entry.pattern, group: entry.group ?? entry.pattern })),
23
+ measures: measuresOf(doc),
24
+ });
25
+
26
+ const DECIMALS = 1000;
27
+ const shown = (amount: number): string => String(Math.round(amount * DECIMALS) / DECIMALS);
28
+
29
+ export const scaledQuantityMismatch: Detector = (doc): Finding[] =>
30
+ scaleSlips(doc.source, wordsOf(doc)).map((slip) => {
31
+ const offset = slip.cell.start + (slip.cell.text.length - slip.cell.text.trimStart().length);
32
+ return {
33
+ rule: "scaled-quantity-mismatch",
34
+ severity: "warning",
35
+ line: 0,
36
+ column: 0,
37
+ quote: quoteAt(doc.source, offset),
38
+ values: {
39
+ item: slip.item.text.trim(),
40
+ written: slip.cell.text.trim(),
41
+ column: slip.heading,
42
+ other: slip.other.text.trim(),
43
+ otherColumn: slip.otherHeading,
44
+ expected: shown(slip.expected),
45
+ scaledRows: slip.scaledRows,
46
+ offset,
47
+ },
48
+ };
49
+ });