chaffjs 0.29.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.
- package/dist/announced-count.d.ts +2 -0
- package/dist/announced-count.d.ts.map +1 -1
- package/dist/announced-count.js +4 -3
- package/dist/announced-count.js.map +1 -1
- package/dist/contacts/addresses.d.ts +21 -0
- package/dist/contacts/addresses.d.ts.map +1 -0
- package/dist/contacts/addresses.js +144 -0
- package/dist/contacts/addresses.js.map +1 -0
- package/dist/contacts/emails.d.ts +11 -0
- package/dist/contacts/emails.d.ts.map +1 -0
- package/dist/contacts/emails.js +51 -0
- package/dist/contacts/emails.js.map +1 -0
- package/dist/contacts/variants.d.ts +14 -0
- package/dist/contacts/variants.d.ts.map +1 -0
- package/dist/contacts/variants.js +25 -0
- package/dist/contacts/variants.js.map +1 -0
- package/dist/derived/battery-runtime.d.ts +85 -0
- package/dist/derived/battery-runtime.d.ts.map +1 -0
- package/dist/derived/battery-runtime.js +217 -0
- package/dist/derived/battery-runtime.js.map +1 -0
- package/dist/derived/period-counts.d.ts +48 -0
- package/dist/derived/period-counts.d.ts.map +1 -0
- package/dist/derived/period-counts.js +128 -0
- package/dist/derived/period-counts.js.map +1 -0
- package/dist/derived/session-hours.d.ts +26 -0
- package/dist/derived/session-hours.d.ts.map +1 -0
- package/dist/derived/session-hours.js +29 -0
- package/dist/derived/session-hours.js.map +1 -0
- package/dist/derived/time-lengths.d.ts +37 -0
- package/dist/derived/time-lengths.d.ts.map +1 -0
- package/dist/derived/time-lengths.js +171 -0
- package/dist/derived/time-lengths.js.map +1 -0
- package/dist/derived/unit-counts.d.ts +7 -0
- package/dist/derived/unit-counts.d.ts.map +1 -0
- package/dist/derived/unit-counts.js +13 -0
- package/dist/derived/unit-counts.js.map +1 -0
- package/dist/derived/use-interval.d.ts +33 -0
- package/dist/derived/use-interval.d.ts.map +1 -0
- package/dist/derived/use-interval.js +93 -0
- package/dist/derived/use-interval.js.map +1 -0
- package/dist/derived/working-hours.d.ts +36 -0
- package/dist/derived/working-hours.d.ts.map +1 -0
- package/dist/derived/working-hours.js +55 -0
- package/dist/derived/working-hours.js.map +1 -0
- package/dist/detectors/acronym-expansion.d.ts +1 -1
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +46 -2
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/announced-count.d.ts.map +1 -1
- package/dist/detectors/announced-count.js +1 -0
- package/dist/detectors/announced-count.js.map +1 -1
- package/dist/detectors/capacity-runtime.d.ts +7 -0
- package/dist/detectors/capacity-runtime.d.ts.map +1 -0
- package/dist/detectors/capacity-runtime.js +98 -0
- package/dist/detectors/capacity-runtime.js.map +1 -0
- package/dist/detectors/capitalised-term.d.ts +44 -0
- package/dist/detectors/capitalised-term.d.ts.map +1 -0
- package/dist/detectors/capitalised-term.js +101 -0
- package/dist/detectors/capitalised-term.js.map +1 -0
- package/dist/detectors/contact-variant.d.ts +3 -0
- package/dist/detectors/contact-variant.d.ts.map +1 -0
- package/dist/detectors/contact-variant.js +21 -0
- package/dist/detectors/contact-variant.js.map +1 -0
- package/dist/detectors/count-over-capacity.d.ts +4 -0
- package/dist/detectors/count-over-capacity.d.ts.map +1 -0
- package/dist/detectors/count-over-capacity.js +26 -0
- package/dist/detectors/count-over-capacity.js.map +1 -0
- package/dist/detectors/date-outside-period.d.ts.map +1 -1
- package/dist/detectors/date-outside-period.js +5 -1
- package/dist/detectors/date-outside-period.js.map +1 -1
- package/dist/detectors/date-stamp-order.d.ts +3 -0
- package/dist/detectors/date-stamp-order.d.ts.map +1 -0
- package/dist/detectors/date-stamp-order.js +24 -0
- package/dist/detectors/date-stamp-order.js.map +1 -0
- package/dist/detectors/defined-term-form.d.ts.map +1 -1
- package/dist/detectors/defined-term-form.js +57 -7
- package/dist/detectors/defined-term-form.js.map +1 -1
- package/dist/detectors/definition-use.d.ts +1 -1
- package/dist/detectors/definition-use.d.ts.map +1 -1
- package/dist/detectors/definition-use.js +4 -2
- package/dist/detectors/definition-use.js.map +1 -1
- package/dist/detectors/derived-numbers.d.ts +11 -1
- package/dist/detectors/derived-numbers.d.ts.map +1 -1
- package/dist/detectors/derived-numbers.js +74 -20
- package/dist/detectors/derived-numbers.js.map +1 -1
- package/dist/detectors/due-date.d.ts.map +1 -1
- package/dist/detectors/due-date.js +6 -0
- package/dist/detectors/due-date.js.map +1 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +7 -4
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/event-deadline.d.ts +3 -0
- package/dist/detectors/event-deadline.d.ts.map +1 -0
- package/dist/detectors/event-deadline.js +24 -0
- package/dist/detectors/event-deadline.js.map +1 -0
- package/dist/detectors/fact-consistency.d.ts +1 -1
- package/dist/detectors/fact-consistency.d.ts.map +1 -1
- package/dist/detectors/fact-consistency.js +52 -3
- package/dist/detectors/fact-consistency.js.map +1 -1
- package/dist/detectors/heading-case.d.ts +5 -2
- package/dist/detectors/heading-case.d.ts.map +1 -1
- package/dist/detectors/heading-case.js +12 -4
- package/dist/detectors/heading-case.js.map +1 -1
- package/dist/detectors/heading-echo.d.ts +5 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +14 -1
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/layout-code.d.ts +5 -0
- package/dist/detectors/layout-code.d.ts.map +1 -0
- package/dist/detectors/layout-code.js +124 -0
- package/dist/detectors/layout-code.js.map +1 -0
- package/dist/detectors/measured-facts.d.ts +8 -0
- package/dist/detectors/measured-facts.d.ts.map +1 -0
- package/dist/detectors/measured-facts.js +18 -0
- package/dist/detectors/measured-facts.js.map +1 -0
- package/dist/detectors/name-variant-model-codes.d.ts +11 -0
- package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
- package/dist/detectors/name-variant-model-codes.js +21 -0
- package/dist/detectors/name-variant-model-codes.js.map +1 -0
- package/dist/detectors/name-variant.d.ts.map +1 -1
- package/dist/detectors/name-variant.js +78 -10
- package/dist/detectors/name-variant.js.map +1 -1
- package/dist/detectors/orthographic-variant.d.ts.map +1 -1
- package/dist/detectors/orthographic-variant.js +62 -7
- package/dist/detectors/orthographic-variant.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +12 -6
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/party-order.d.ts +3 -0
- package/dist/detectors/party-order.d.ts.map +1 -0
- package/dist/detectors/party-order.js +46 -0
- package/dist/detectors/party-order.js.map +1 -0
- package/dist/detectors/quantity-range.d.ts +3 -0
- package/dist/detectors/quantity-range.d.ts.map +1 -0
- package/dist/detectors/quantity-range.js +35 -0
- package/dist/detectors/quantity-range.js.map +1 -0
- package/dist/detectors/range-band.d.ts +5 -0
- package/dist/detectors/range-band.d.ts.map +1 -0
- package/dist/detectors/range-band.js +80 -0
- package/dist/detectors/range-band.js.map +1 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
- package/dist/detectors/registry/connection-time-short.d.ts +3 -0
- package/dist/detectors/registry/connection-time-short.d.ts.map +1 -0
- package/dist/detectors/registry/connection-time-short.js +3 -0
- package/dist/detectors/registry/connection-time-short.js.map +1 -0
- package/dist/detectors/registry/contact-variant.d.ts +3 -0
- package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
- package/dist/detectors/registry/contact-variant.js +3 -0
- package/dist/detectors/registry/contact-variant.js.map +1 -0
- package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
- package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
- package/dist/detectors/registry/count-over-capacity.js +3 -0
- package/dist/detectors/registry/count-over-capacity.js.map +1 -0
- package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
- package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
- package/dist/detectors/registry/date-stamp-order.js +3 -0
- package/dist/detectors/registry/date-stamp-order.js.map +1 -0
- package/dist/detectors/registry/duration-product-mismatch.d.ts +3 -0
- package/dist/detectors/registry/duration-product-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/duration-product-mismatch.js +3 -0
- package/dist/detectors/registry/duration-product-mismatch.js.map +1 -0
- package/dist/detectors/registry/event-deadline.d.ts +3 -0
- package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
- package/dist/detectors/registry/event-deadline.js +3 -0
- package/dist/detectors/registry/event-deadline.js.map +1 -0
- package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
- package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/interval-count-mismatch.js +3 -0
- package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
- package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
- package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/layout-code-mismatch.js +3 -0
- package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
- package/dist/detectors/registry/party-order.d.ts +3 -0
- package/dist/detectors/registry/party-order.d.ts.map +1 -0
- package/dist/detectors/registry/party-order.js +3 -0
- package/dist/detectors/registry/party-order.js.map +1 -0
- package/dist/detectors/registry/quantity-range.d.ts +3 -0
- package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
- package/dist/detectors/registry/quantity-range.js +3 -0
- package/dist/detectors/registry/quantity-range.js.map +1 -0
- package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
- package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/range-band-mismatch.js +3 -0
- package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
- package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
- package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
- package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
- package/dist/detectors/registry/requirement-lists.d.ts +3 -0
- package/dist/detectors/registry/requirement-lists.d.ts.map +1 -0
- package/dist/detectors/registry/requirement-lists.js +3 -0
- package/dist/detectors/registry/requirement-lists.js.map +1 -0
- package/dist/detectors/registry/return-type.d.ts +3 -0
- package/dist/detectors/registry/return-type.d.ts.map +1 -0
- package/dist/detectors/registry/return-type.js +3 -0
- package/dist/detectors/registry/return-type.js.map +1 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
- package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
- package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
- package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
- package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
- package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
- package/dist/detectors/registry/time-range-reversed.js +3 -0
- package/dist/detectors/registry/time-range-reversed.js.map +1 -0
- package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
- package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
- package/dist/detectors/registry/unlisted-item-used.js +3 -0
- package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
- package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
- package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
- package/dist/detectors/rent-multiple-mismatch.js +39 -0
- package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
- package/dist/detectors/requirement-lists.d.ts +3 -0
- package/dist/detectors/requirement-lists.d.ts.map +1 -0
- package/dist/detectors/requirement-lists.js +46 -0
- package/dist/detectors/requirement-lists.js.map +1 -0
- package/dist/detectors/return-type.d.ts +3 -0
- package/dist/detectors/return-type.d.ts.map +1 -0
- package/dist/detectors/return-type.js +24 -0
- package/dist/detectors/return-type.js.map +1 -0
- package/dist/detectors/scaled-quantity.d.ts +3 -0
- package/dist/detectors/scaled-quantity.d.ts.map +1 -0
- package/dist/detectors/scaled-quantity.js +37 -0
- package/dist/detectors/scaled-quantity.js.map +1 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +3 -1
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/step-time-sum.d.ts +3 -0
- package/dist/detectors/step-time-sum.d.ts.map +1 -0
- package/dist/detectors/step-time-sum.js +46 -0
- package/dist/detectors/step-time-sum.js.map +1 -0
- package/dist/detectors/structure-tree.d.ts +2 -0
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +31 -6
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/structure.js +1 -1
- package/dist/detectors/structure.js.map +1 -1
- package/dist/detectors/technical-docs.d.ts +5 -0
- package/dist/detectors/technical-docs.d.ts.map +1 -1
- package/dist/detectors/technical-docs.js +12 -0
- package/dist/detectors/technical-docs.js.map +1 -1
- package/dist/detectors/time-order.d.ts +2 -0
- package/dist/detectors/time-order.d.ts.map +1 -1
- package/dist/detectors/time-order.js +33 -5
- package/dist/detectors/time-order.js.map +1 -1
- package/dist/detectors/undefined-term.d.ts.map +1 -1
- package/dist/detectors/undefined-term.js +19 -3
- package/dist/detectors/undefined-term.js.map +1 -1
- package/dist/detectors/unit-consistency.d.ts +1 -1
- package/dist/detectors/unit-consistency.d.ts.map +1 -1
- package/dist/detectors/unit-consistency.js +46 -38
- package/dist/detectors/unit-consistency.js.map +1 -1
- package/dist/detectors/unlisted-items.d.ts +3 -0
- package/dist/detectors/unlisted-items.d.ts.map +1 -0
- package/dist/detectors/unlisted-items.js +57 -0
- package/dist/detectors/unlisted-items.js.map +1 -0
- package/dist/detectors/use-count-words.d.ts +8 -0
- package/dist/detectors/use-count-words.d.ts.map +1 -0
- package/dist/detectors/use-count-words.js +20 -0
- package/dist/detectors/use-count-words.js.map +1 -0
- package/dist/detectors/use-interval.d.ts +3 -0
- package/dist/detectors/use-interval.d.ts.map +1 -0
- package/dist/detectors/use-interval.js +39 -0
- package/dist/detectors/use-interval.js.map +1 -0
- package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
- package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
- package/dist/detectors/walk-time-distance-mismatch.js +38 -0
- package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +4 -1
- package/dist/document.js.map +1 -1
- package/dist/facts/approximate-tolerance.d.ts +7 -0
- package/dist/facts/approximate-tolerance.d.ts.map +1 -0
- package/dist/facts/approximate-tolerance.js +16 -0
- package/dist/facts/approximate-tolerance.js.map +1 -0
- package/dist/facts/item-amounts.d.ts +18 -0
- package/dist/facts/item-amounts.d.ts.map +1 -0
- package/dist/facts/item-amounts.js +67 -0
- package/dist/facts/item-amounts.js.map +1 -0
- package/dist/facts/labelled-facts.d.ts +4 -0
- package/dist/facts/labelled-facts.d.ts.map +1 -1
- package/dist/facts/labelled-facts.js +21 -6
- package/dist/facts/labelled-facts.js.map +1 -1
- package/dist/facts/measure-units.d.ts +5 -0
- package/dist/facts/measure-units.d.ts.map +1 -0
- package/dist/facts/measure-units.js +28 -0
- package/dist/facts/measure-units.js.map +1 -0
- package/dist/facts/measures.d.ts +23 -4
- package/dist/facts/measures.d.ts.map +1 -1
- package/dist/facts/measures.js +68 -19
- package/dist/facts/measures.js.map +1 -1
- package/dist/facts/retention-periods.d.ts +51 -0
- package/dist/facts/retention-periods.d.ts.map +1 -0
- package/dist/facts/retention-periods.js +148 -0
- package/dist/facts/retention-periods.js.map +1 -0
- package/dist/facts/table-facts.d.ts +2 -0
- package/dist/facts/table-facts.d.ts.map +1 -1
- package/dist/facts/table-facts.js +2 -0
- package/dist/facts/table-facts.js.map +1 -1
- package/dist/facts/unit-conflicts.d.ts +3 -2
- package/dist/facts/unit-conflicts.d.ts.map +1 -1
- package/dist/facts/unit-conflicts.js +15 -7
- package/dist/facts/unit-conflicts.js.map +1 -1
- package/dist/facts/unit-equivalents.d.ts +13 -0
- package/dist/facts/unit-equivalents.d.ts.map +1 -0
- package/dist/facts/unit-equivalents.js +56 -0
- package/dist/facts/unit-equivalents.js.map +1 -0
- package/dist/facts/unit-tolerance.d.ts +4 -0
- package/dist/facts/unit-tolerance.d.ts.map +1 -0
- package/dist/facts/unit-tolerance.js +13 -0
- package/dist/facts/unit-tolerance.js.map +1 -0
- package/dist/facts/unit-word.d.ts +8 -0
- package/dist/facts/unit-word.d.ts.map +1 -0
- package/dist/facts/unit-word.js +34 -0
- package/dist/facts/unit-word.js.map +1 -0
- package/dist/facts/written-precision.d.ts +10 -0
- package/dist/facts/written-precision.d.ts.map +1 -0
- package/dist/facts/written-precision.js +20 -0
- package/dist/facts/written-precision.js.map +1 -0
- package/dist/kana-spelling.d.ts +12 -0
- package/dist/kana-spelling.d.ts.map +1 -1
- package/dist/kana-spelling.js +23 -0
- package/dist/kana-spelling.js.map +1 -1
- package/dist/model-codes.d.ts +23 -0
- package/dist/model-codes.d.ts.map +1 -0
- package/dist/model-codes.js +81 -0
- package/dist/model-codes.js.map +1 -0
- package/dist/name-spelling-chars.d.ts +16 -0
- package/dist/name-spelling-chars.d.ts.map +1 -0
- package/dist/name-spelling-chars.js +14 -0
- package/dist/name-spelling-chars.js.map +1 -0
- package/dist/name-variants.d.ts +8 -2
- package/dist/name-variants.d.ts.map +1 -1
- package/dist/name-variants.js +72 -7
- package/dist/name-variants.js.map +1 -1
- package/dist/name-word-order.d.ts +49 -0
- package/dist/name-word-order.d.ts.map +1 -0
- package/dist/name-word-order.js +114 -0
- package/dist/name-word-order.js.map +1 -0
- package/dist/phone-numbers.d.ts.map +1 -1
- package/dist/phone-numbers.js +22 -5
- package/dist/phone-numbers.js.map +1 -1
- package/dist/place-names.d.ts +12 -5
- package/dist/place-names.d.ts.map +1 -1
- package/dist/place-names.js +34 -14
- package/dist/place-names.js.map +1 -1
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/preamble-paragraphs.d.ts +2 -1
- package/dist/preamble-paragraphs.d.ts.map +1 -1
- package/dist/preamble-paragraphs.js +16 -1
- package/dist/preamble-paragraphs.js.map +1 -1
- package/dist/profile/parse.d.ts.map +1 -1
- package/dist/profile/parse.js +11 -0
- package/dist/profile/parse.js.map +1 -1
- package/dist/return-type.d.ts +33 -0
- package/dist/return-type.d.ts.map +1 -0
- package/dist/return-type.js +302 -0
- package/dist/return-type.js.map +1 -0
- package/dist/spacing-code.d.ts +6 -0
- package/dist/spacing-code.d.ts.map +1 -0
- package/dist/spacing-code.js +16 -0
- package/dist/spacing-code.js.map +1 -0
- package/dist/structure/amount-range.d.ts +2 -2
- package/dist/structure/amount-range.d.ts.map +1 -1
- package/dist/structure/amount-range.js +12 -5
- package/dist/structure/amount-range.js.map +1 -1
- package/dist/structure/capacity-count.d.ts +59 -0
- package/dist/structure/capacity-count.d.ts.map +1 -0
- package/dist/structure/capacity-count.js +118 -0
- package/dist/structure/capacity-count.js.map +1 -0
- package/dist/structure/connection-times.d.ts +56 -0
- package/dist/structure/connection-times.d.ts.map +1 -0
- package/dist/structure/connection-times.js +165 -0
- package/dist/structure/connection-times.js.map +1 -0
- package/dist/structure/counted-amounts.d.ts +6 -0
- package/dist/structure/counted-amounts.d.ts.map +1 -1
- package/dist/structure/counted-amounts.js +4 -4
- package/dist/structure/counted-amounts.js.map +1 -1
- package/dist/structure/date-order-tie.d.ts +13 -0
- package/dist/structure/date-order-tie.d.ts.map +1 -0
- package/dist/structure/date-order-tie.js +61 -0
- package/dist/structure/date-order-tie.js.map +1 -0
- package/dist/structure/date-order.d.ts +2 -2
- package/dist/structure/date-order.d.ts.map +1 -1
- package/dist/structure/date-order.js +26 -9
- package/dist/structure/date-order.js.map +1 -1
- package/dist/structure/date-outside-period.d.ts +8 -0
- package/dist/structure/date-outside-period.d.ts.map +1 -1
- package/dist/structure/date-outside-period.js +52 -3
- package/dist/structure/date-outside-period.js.map +1 -1
- package/dist/structure/date-range.d.ts +6 -2
- package/dist/structure/date-range.d.ts.map +1 -1
- package/dist/structure/date-range.js +37 -9
- package/dist/structure/date-range.js.map +1 -1
- package/dist/structure/deadline-after-event.d.ts +23 -0
- package/dist/structure/deadline-after-event.d.ts.map +1 -0
- package/dist/structure/deadline-after-event.js +42 -0
- package/dist/structure/deadline-after-event.js.map +1 -0
- package/dist/structure/definition-use.d.ts +1 -1
- package/dist/structure/definition-use.d.ts.map +1 -1
- package/dist/structure/definition-use.js +55 -2
- package/dist/structure/definition-use.js.map +1 -1
- package/dist/structure/due-date.d.ts.map +1 -1
- package/dist/structure/due-date.js +5 -27
- package/dist/structure/due-date.js.map +1 -1
- package/dist/structure/labelled-date-order.d.ts +38 -0
- package/dist/structure/labelled-date-order.d.ts.map +1 -0
- package/dist/structure/labelled-date-order.js +41 -0
- package/dist/structure/labelled-date-order.js.map +1 -0
- package/dist/structure/layout-codes.d.ts +68 -0
- package/dist/structure/layout-codes.d.ts.map +1 -0
- package/dist/structure/layout-codes.js +161 -0
- package/dist/structure/layout-codes.js.map +1 -0
- package/dist/structure/leg-times.d.ts +2 -0
- package/dist/structure/leg-times.d.ts.map +1 -1
- package/dist/structure/leg-times.js +13 -0
- package/dist/structure/leg-times.js.map +1 -1
- package/dist/structure/measured-amounts.d.ts +17 -0
- package/dist/structure/measured-amounts.d.ts.map +1 -0
- package/dist/structure/measured-amounts.js +110 -0
- package/dist/structure/measured-amounts.js.map +1 -0
- package/dist/structure/party-order.d.ts +33 -0
- package/dist/structure/party-order.d.ts.map +1 -0
- package/dist/structure/party-order.js +79 -0
- package/dist/structure/party-order.js.map +1 -0
- package/dist/structure/quantity-range.d.ts +8 -0
- package/dist/structure/quantity-range.d.ts.map +1 -0
- package/dist/structure/quantity-range.js +36 -0
- package/dist/structure/quantity-range.js.map +1 -0
- package/dist/structure/range-bands.d.ts +75 -0
- package/dist/structure/range-bands.d.ts.map +1 -0
- package/dist/structure/range-bands.js +244 -0
- package/dist/structure/range-bands.js.map +1 -0
- package/dist/structure/rent-multiple.d.ts +76 -0
- package/dist/structure/rent-multiple.d.ts.map +1 -0
- package/dist/structure/rent-multiple.js +186 -0
- package/dist/structure/rent-multiple.js.map +1 -0
- package/dist/structure/requirement-lists.d.ts +37 -0
- package/dist/structure/requirement-lists.d.ts.map +1 -0
- package/dist/structure/requirement-lists.js +89 -0
- package/dist/structure/requirement-lists.js.map +1 -0
- package/dist/structure/scaled-quantities.d.ts +50 -0
- package/dist/structure/scaled-quantities.d.ts.map +1 -0
- package/dist/structure/scaled-quantities.js +153 -0
- package/dist/structure/scaled-quantities.js.map +1 -0
- package/dist/structure/stamp-order.d.ts +16 -0
- package/dist/structure/stamp-order.d.ts.map +1 -0
- package/dist/structure/stamp-order.js +78 -0
- package/dist/structure/stamp-order.js.map +1 -0
- package/dist/structure/step-times.d.ts +49 -0
- package/dist/structure/step-times.d.ts.map +1 -0
- package/dist/structure/step-times.js +128 -0
- package/dist/structure/step-times.js.map +1 -0
- package/dist/structure/term-deadline.d.ts +19 -0
- package/dist/structure/term-deadline.d.ts.map +1 -0
- package/dist/structure/term-deadline.js +28 -0
- package/dist/structure/term-deadline.js.map +1 -0
- package/dist/structure/time-order.d.ts +21 -0
- package/dist/structure/time-order.d.ts.map +1 -1
- package/dist/structure/time-order.js +5 -6
- package/dist/structure/time-order.js.map +1 -1
- package/dist/structure/time-range.d.ts +16 -0
- package/dist/structure/time-range.d.ts.map +1 -0
- package/dist/structure/time-range.js +57 -0
- package/dist/structure/time-range.js.map +1 -0
- package/dist/structure/total-line.d.ts +16 -0
- package/dist/structure/total-line.d.ts.map +1 -0
- package/dist/structure/total-line.js +44 -0
- package/dist/structure/total-line.js.map +1 -0
- package/dist/structure/total.d.ts +7 -1
- package/dist/structure/total.d.ts.map +1 -1
- package/dist/structure/total.js +28 -5
- package/dist/structure/total.js.map +1 -1
- package/dist/structure/unlisted-items.d.ts +70 -0
- package/dist/structure/unlisted-items.d.ts.map +1 -0
- package/dist/structure/unlisted-items.js +152 -0
- package/dist/structure/unlisted-items.js.map +1 -0
- package/dist/structure/version-order.d.ts +2 -0
- package/dist/structure/version-order.d.ts.map +1 -1
- package/dist/structure/version-order.js +10 -0
- package/dist/structure/version-order.js.map +1 -1
- package/dist/structure/walk-time.d.ts +87 -0
- package/dist/structure/walk-time.d.ts.map +1 -0
- package/dist/structure/walk-time.js +195 -0
- package/dist/structure/walk-time.js.map +1 -0
- package/dist/surname-near.d.ts +6 -0
- package/dist/surname-near.d.ts.map +1 -0
- package/dist/surname-near.js +31 -0
- package/dist/surname-near.js.map +1 -0
- package/dist/table-cells.d.ts +13 -0
- package/dist/table-cells.d.ts.map +1 -0
- package/dist/table-cells.js +36 -0
- package/dist/table-cells.js.map +1 -0
- package/dist/table-names.d.ts +42 -0
- package/dist/table-names.d.ts.map +1 -0
- package/dist/table-names.js +94 -0
- package/dist/table-names.js.map +1 -0
- package/package.json +3 -3
- package/profiles/listing.yaml +20 -0
- package/rules/announced-count-mismatch.yaml +2 -2
- package/rules/capacity-runtime-mismatch.yaml +66 -0
- package/rules/connection-time-short.yaml +52 -0
- package/rules/contact-variant.yaml +76 -0
- package/rules/count-over-capacity.yaml +55 -0
- package/rules/date-outside-period.yaml +4 -4
- package/rules/date-stamp-order.yaml +82 -0
- package/rules/deadline-after-event.yaml +67 -0
- package/rules/defined-name-repeated.yaml +2 -2
- package/rules/due-before-issue.yaml +11 -7
- package/rules/duration-mismatch.yaml +7 -4
- package/rules/duration-product-mismatch.yaml +73 -0
- package/rules/fact-conflict.yaml +9 -4
- package/rules/heading-echo.yaml +2 -2
- package/rules/interval-count-mismatch.yaml +54 -0
- package/rules/latin-spacing.yaml +2 -2
- package/rules/layout-code-mismatch.yaml +68 -0
- package/rules/name-variant.yaml +14 -2
- package/rules/no-em-dash.yaml +2 -2
- package/rules/party-order.yaml +84 -0
- package/rules/preamble-length.yaml +2 -2
- package/rules/quantity-range-reversed.yaml +53 -0
- package/rules/range-band-mismatch.yaml +70 -0
- package/rules/rent-multiple-mismatch.yaml +61 -0
- package/rules/required-listed-as-preferred.yaml +102 -0
- package/rules/return-type-mismatch.yaml +79 -0
- package/rules/scaled-quantity-mismatch.yaml +69 -0
- package/rules/step-time-sum-mismatch.yaml +65 -0
- package/rules/time-range-reversed.yaml +48 -0
- package/rules/title-case-consistency.yaml +2 -2
- package/rules/total-mismatch.yaml +2 -2
- package/rules/undefined-term.yaml +21 -7
- package/rules/unit-mismatch.yaml +12 -4
- package/rules/unlisted-item-used.yaml +90 -0
- package/rules/unused-definition.yaml +2 -2
- package/rules/use-before-definition.yaml +2 -2
- package/rules/walk-time-distance-mismatch.yaml +55 -0
- package/src/announced-count.ts +6 -3
- package/src/contacts/addresses.ts +179 -0
- package/src/contacts/emails.ts +63 -0
- package/src/contacts/variants.ts +33 -0
- package/src/derived/battery-runtime.ts +284 -0
- package/src/derived/period-counts.ts +178 -0
- package/src/derived/session-hours.ts +62 -0
- package/src/derived/time-lengths.ts +229 -0
- package/src/derived/unit-counts.ts +18 -0
- package/src/derived/use-interval.ts +151 -0
- package/src/derived/working-hours.ts +97 -0
- package/src/detectors/acronym-expansion.ts +57 -2
- package/src/detectors/announced-count.ts +1 -0
- package/src/detectors/capacity-runtime.ts +128 -0
- package/src/detectors/capitalised-term.ts +147 -0
- package/src/detectors/contact-variant.ts +24 -0
- package/src/detectors/count-over-capacity.ts +37 -0
- package/src/detectors/date-outside-period.ts +5 -1
- package/src/detectors/date-stamp-order.ts +28 -0
- package/src/detectors/defined-term-form.ts +63 -7
- package/src/detectors/definition-use.ts +6 -2
- package/src/detectors/derived-numbers.ts +97 -24
- package/src/detectors/due-date.ts +10 -2
- package/src/detectors/en-shape.ts +11 -4
- package/src/detectors/event-deadline.ts +29 -0
- package/src/detectors/fact-consistency.ts +64 -3
- package/src/detectors/heading-case.ts +13 -4
- package/src/detectors/heading-echo.ts +17 -1
- package/src/detectors/layout-code.ts +174 -0
- package/src/detectors/measured-facts.ts +20 -0
- package/src/detectors/name-variant-model-codes.ts +27 -0
- package/src/detectors/name-variant.ts +99 -15
- package/src/detectors/orthographic-variant.ts +78 -9
- package/src/detectors/orthography.ts +16 -6
- package/src/detectors/party-order.ts +50 -0
- package/src/detectors/quantity-range.ts +47 -0
- package/src/detectors/range-band.ts +125 -0
- package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
- package/src/detectors/registry/connection-time-short.ts +4 -0
- package/src/detectors/registry/contact-variant.ts +4 -0
- package/src/detectors/registry/count-over-capacity.ts +4 -0
- package/src/detectors/registry/date-stamp-order.ts +4 -0
- package/src/detectors/registry/duration-product-mismatch.ts +4 -0
- package/src/detectors/registry/event-deadline.ts +4 -0
- package/src/detectors/registry/interval-count-mismatch.ts +4 -0
- package/src/detectors/registry/layout-code-mismatch.ts +4 -0
- package/src/detectors/registry/party-order.ts +4 -0
- package/src/detectors/registry/quantity-range.ts +4 -0
- package/src/detectors/registry/range-band-mismatch.ts +4 -0
- package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
- package/src/detectors/registry/requirement-lists.ts +4 -0
- package/src/detectors/registry/return-type.ts +4 -0
- package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
- package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
- package/src/detectors/registry/time-range-reversed.ts +4 -0
- package/src/detectors/registry/unlisted-item-used.ts +4 -0
- package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
- package/src/detectors/rent-multiple-mismatch.ts +56 -0
- package/src/detectors/requirement-lists.ts +65 -0
- package/src/detectors/return-type.ts +30 -0
- package/src/detectors/scaled-quantity.ts +49 -0
- package/src/detectors/signals.ts +3 -1
- package/src/detectors/step-time-sum.ts +55 -0
- package/src/detectors/structure-tree.ts +42 -6
- package/src/detectors/structure.ts +1 -1
- package/src/detectors/technical-docs.ts +12 -0
- package/src/detectors/time-order.ts +45 -6
- package/src/detectors/undefined-term.ts +20 -3
- package/src/detectors/unit-consistency.ts +50 -38
- package/src/detectors/unlisted-items.ts +72 -0
- package/src/detectors/use-count-words.ts +32 -0
- package/src/detectors/use-interval.ts +51 -0
- package/src/detectors/walk-time-distance-mismatch.ts +45 -0
- package/src/document.ts +4 -1
- package/src/facts/approximate-tolerance.ts +19 -0
- package/src/facts/item-amounts.ts +94 -0
- package/src/facts/labelled-facts.ts +29 -7
- package/src/facts/measure-units.ts +31 -0
- package/src/facts/measures.ts +113 -25
- package/src/facts/retention-periods.ts +216 -0
- package/src/facts/table-facts.ts +3 -0
- package/src/facts/unit-conflicts.ts +22 -9
- package/src/facts/unit-equivalents.ts +70 -0
- package/src/facts/unit-tolerance.ts +17 -0
- package/src/facts/unit-word.ts +35 -0
- package/src/facts/written-precision.ts +23 -0
- package/src/kana-spelling.ts +25 -0
- package/src/model-codes.ts +104 -0
- package/src/name-spelling-chars.ts +34 -0
- package/src/name-variants.ts +83 -7
- package/src/name-word-order.ts +149 -0
- package/src/phone-numbers.ts +26 -6
- package/src/place-names.ts +53 -14
- package/src/plugin.ts +7 -0
- package/src/preamble-paragraphs.ts +21 -2
- package/src/profile/parse.ts +10 -0
- package/src/return-type.ts +361 -0
- package/src/spacing-code.ts +26 -0
- package/src/structure/amount-range.ts +16 -7
- package/src/structure/capacity-count.ts +196 -0
- package/src/structure/connection-times.ts +226 -0
- package/src/structure/counted-amounts.ts +5 -4
- package/src/structure/date-order-tie.ts +86 -0
- package/src/structure/date-order.ts +41 -14
- package/src/structure/date-outside-period.ts +77 -7
- package/src/structure/date-range.ts +45 -9
- package/src/structure/deadline-after-event.ts +67 -0
- package/src/structure/definition-use.ts +59 -2
- package/src/structure/due-date.ts +8 -32
- package/src/structure/labelled-date-order.ts +84 -0
- package/src/structure/layout-codes.ts +210 -0
- package/src/structure/leg-times.ts +15 -0
- package/src/structure/measured-amounts.ts +138 -0
- package/src/structure/party-order.ts +115 -0
- package/src/structure/quantity-range.ts +54 -0
- package/src/structure/range-bands.ts +316 -0
- package/src/structure/rent-multiple.ts +277 -0
- package/src/structure/requirement-lists.ts +145 -0
- package/src/structure/scaled-quantities.ts +225 -0
- package/src/structure/stamp-order.ts +100 -0
- package/src/structure/step-times.ts +169 -0
- package/src/structure/term-deadline.ts +35 -0
- package/src/structure/time-order.ts +8 -7
- package/src/structure/time-range.ts +64 -0
- package/src/structure/total-line.ts +63 -0
- package/src/structure/total.ts +41 -6
- package/src/structure/unlisted-items.ts +207 -0
- package/src/structure/version-order.ts +10 -0
- package/src/structure/walk-time.ts +285 -0
- package/src/surname-near.ts +31 -0
- package/src/table-cells.ts +41 -0
- package/src/table-names.ts +113 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Postal addresses written two ways in one document: the same street with its block numbers swapped or one of them changed
|
|
2
|
+
// (丸の内二丁目4番1号 and 丸の内二丁目1番4号, 2-4-1 and 2-1-4 Marunouchi), or the same street and numbers under two postcodes.
|
|
3
|
+
// Only an address with a label shortly before it on its line is read (住所, 本店所在地, Address, head office); the labels come
|
|
4
|
+
// from the language's address-label lexicon, grouped by whose address they name, so a head office and a branch are never
|
|
5
|
+
// compared. Pure.
|
|
6
|
+
import type { LexiconEntry } from "../plugin.ts";
|
|
7
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
8
|
+
import { writingVariants, type Variant, type Writing } from "./variants.ts";
|
|
9
|
+
|
|
10
|
+
/** An address as written: the street it is on and the area written with it (東京都千代田区, Chiyoda-ku), its block numbers, its postcode when the line has one, and its label's group. */
|
|
11
|
+
export type AddressWriting = Writing & {
|
|
12
|
+
readonly street: string;
|
|
13
|
+
readonly area: string;
|
|
14
|
+
readonly numbers: readonly number[];
|
|
15
|
+
readonly postcode: string | undefined;
|
|
16
|
+
readonly label: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** A group that names no one office (住所, Address) and so may be compared with any. */
|
|
20
|
+
const ANY_OFFICE = "address";
|
|
21
|
+
const KANA_HAN = String.raw`\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}ー々ヶ`;
|
|
22
|
+
const BLOCK_JOIN = String.raw`\s?(?:丁目|番地|番|号|の|[-‐−-])\s?`;
|
|
23
|
+
/** A Japanese street (its town, ward, city) and its block numbers: 東京都千代田区丸の内二丁目4番1号, 梅田1-2-3. */
|
|
24
|
+
const JA_ADDRESS = new RegExp(String.raw`([${KANA_HAN}]{2,40}?)([0-90-9]{1,4}(?:${BLOCK_JOIN}[0-90-9]{1,4}){1,3})(?:\s?(?:号|番地|番))?`, "gu");
|
|
25
|
+
/** Block numbers, the capitalised street after them and the area after a comma: 2-4-1 Marunouchi, Chiyoda-ku; 120 Main Street, Springfield. */
|
|
26
|
+
const EN_BLOCK = String.raw`(?<![\w.\-/])(\d{1,5}(?:-\d{1,5}){0,3})`;
|
|
27
|
+
const EN_STREET = String.raw`([A-Z][a-z]+(?:\s[A-Z][a-z]+){0,3})`;
|
|
28
|
+
const EN_AREA = String.raw`(?:,\s?([A-Z][A-Za-z]*(?:-[a-z]+)?))?`;
|
|
29
|
+
const EN_ADDRESS = new RegExp(String.raw`${EN_BLOCK}\s+${EN_STREET}${EN_AREA}`, "gu");
|
|
30
|
+
/** 都道府県, 市, 区 and 郡: what comes after the last of them in a Japanese street is the town the numbers count in. */
|
|
31
|
+
const DIVISIONS = [..."都道府県市区郡"];
|
|
32
|
+
const CHOME = "丁目";
|
|
33
|
+
const KANJI_NUMERALS = "一二三四五六七八九十";
|
|
34
|
+
const KANJI_DIGITS = "一二三四五六七八九";
|
|
35
|
+
const JP_POSTCODE = /(?<![\d\-‐−-])〒?\s?(\d{3})[-‐−-](\d{4})(?![\d\-‐−-])/u;
|
|
36
|
+
const US_ZIP = /\b[A-Z]{2}\s(\d{5})(?:-\d{4})?\b/u;
|
|
37
|
+
const JP_POSTCODE_ONLY = /^\d{3}-\d{4}$/u;
|
|
38
|
+
const YEAR = /^(?:19|20)\d{2}$/u;
|
|
39
|
+
const FULL_WIDTH_DIGIT = /[0-9]/gu;
|
|
40
|
+
const NUMBER = /\d+/gu;
|
|
41
|
+
const LATIN_START = /^[A-Za-z]/u;
|
|
42
|
+
const LETTER = /\p{L}/u;
|
|
43
|
+
/** How far before an address its label may end: 「本店所在地(」, "head office at ". */
|
|
44
|
+
const MAX_LABEL_GAP = 20;
|
|
45
|
+
const MIN_STREET_LENGTH = 2;
|
|
46
|
+
|
|
47
|
+
/** 一 to 九十九 as a number; the 丁目 of a Japanese street. */
|
|
48
|
+
const kanjiNumber = (kanji: string): number => {
|
|
49
|
+
const digit = (char: string): number => KANJI_DIGITS.indexOf(char) + 1;
|
|
50
|
+
if (!kanji.includes("十")) return digit(kanji);
|
|
51
|
+
const [tens = "", ones = ""] = kanji.split("十");
|
|
52
|
+
return (tens === "" ? 1 : digit(tens)) * 10 + (ones === "" ? 0 : digit(ones));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const halfWidth = (text: string): string => text.replaceAll(FULL_WIDTH_DIGIT, (char) => String.fromCharCode(char.charCodeAt(0) - 0xfee0));
|
|
56
|
+
const numbersOf = (text: string): number[] => [...halfWidth(text).matchAll(NUMBER)].map((match) => Number(match[0]));
|
|
57
|
+
|
|
58
|
+
/** The 二丁目 a street ends with, or "" when it ends with none. */
|
|
59
|
+
const chomeOf = (street: string): string => {
|
|
60
|
+
if (!street.endsWith(CHOME)) return "";
|
|
61
|
+
const numeral = Array.from(street.slice(0, -CHOME.length))
|
|
62
|
+
.toReversed()
|
|
63
|
+
.findIndex((char) => !KANJI_NUMERALS.includes(char));
|
|
64
|
+
const length = numeral === -1 ? street.length - CHOME.length : numeral;
|
|
65
|
+
return length === 0 ? "" : street.slice(street.length - CHOME.length - length);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** The town of a Japanese street as written (丸の内二丁目), its name without the 丁目 and the 丁目 as a number; undefined when the street names no ward, city or prefecture. */
|
|
69
|
+
const jaStreet = (run: string): { area: string; written: string; town: string; chome: number[] } | undefined => {
|
|
70
|
+
const lastDivision = Math.max(...DIVISIONS.map((division) => run.lastIndexOf(division)));
|
|
71
|
+
if (lastDivision < 0) return undefined;
|
|
72
|
+
const written = run.slice(lastDivision + 1);
|
|
73
|
+
const chome = chomeOf(written);
|
|
74
|
+
const town = written.slice(0, written.length - chome.length);
|
|
75
|
+
const numbered = chome === "" ? [] : [kanjiNumber(chome.slice(0, -CHOME.length))];
|
|
76
|
+
return town.length < MIN_STREET_LENGTH ? undefined : { area: run.slice(0, lastDivision + 1), written, town, chome: numbered };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
type Place = { readonly offset: number; readonly written: string; readonly street: string; readonly area: string; readonly numbers: number[] };
|
|
80
|
+
|
|
81
|
+
const jaPlaces = (text: string): Place[] =>
|
|
82
|
+
[...text.matchAll(JA_ADDRESS)].flatMap((match) => {
|
|
83
|
+
const run = match[1] ?? "";
|
|
84
|
+
const street = jaStreet(run);
|
|
85
|
+
if (street === undefined) return [];
|
|
86
|
+
const townStart = run.length - street.written.length;
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
offset: match.index + townStart,
|
|
90
|
+
written: match[0].slice(townStart),
|
|
91
|
+
street: street.town,
|
|
92
|
+
area: street.area,
|
|
93
|
+
numbers: [...street.chome, ...numbersOf(match[2] ?? "")],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/** A lone number before capitalised words is an address only when a comma goes on to the area (120 Main Street, Springfield), not a count (365 Plan). */
|
|
99
|
+
const isAddressBlock = (block: string, area: string): boolean => block.includes("-") || area !== "";
|
|
100
|
+
|
|
101
|
+
const enPlaces = (text: string): Place[] =>
|
|
102
|
+
[...text.matchAll(EN_ADDRESS)].flatMap((match) => {
|
|
103
|
+
const [, block = "", street = "", area = ""] = match;
|
|
104
|
+
if (JP_POSTCODE_ONLY.test(block) || YEAR.test(block) || !isAddressBlock(block, area)) return [];
|
|
105
|
+
return [{ offset: match.index, written: `${block} ${street}`, street: street.toLowerCase(), area: area.toLowerCase(), numbers: numbersOf(block) }];
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/** Where a label last occurs before an address on its line; a Latin label only as a whole word (office, not officer). */
|
|
109
|
+
const lastPlace = (before: string, label: string): number => {
|
|
110
|
+
const lowered = label.toLowerCase();
|
|
111
|
+
const places = [...before.matchAll(new RegExp(escapeRegExp(lowered), "gu"))].map((match) => match.index);
|
|
112
|
+
const whole = LATIN_START.test(label) ? places.filter((at) => !LETTER.test(before[at - 1] ?? "") && !LETTER.test(before[at + lowered.length] ?? "")) : places;
|
|
113
|
+
return whole.at(-1) ?? -1;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** The group of the label ending nearest before an address on its line, the longest when two end together (本店所在地 over 所在地). */
|
|
117
|
+
const labelBefore = (before: string, labels: readonly LexiconEntry[]): string | undefined =>
|
|
118
|
+
labels
|
|
119
|
+
.map((entry) => {
|
|
120
|
+
const at = lastPlace(before, entry.pattern);
|
|
121
|
+
return { group: entry.group ?? ANY_OFFICE, at, end: at + entry.pattern.length };
|
|
122
|
+
})
|
|
123
|
+
.filter((hit) => hit.at >= 0 && before.length - hit.end <= MAX_LABEL_GAP)
|
|
124
|
+
.toSorted((a, b) => b.end - a.end || a.at - b.at)[0]?.group;
|
|
125
|
+
|
|
126
|
+
const postcodeOf = (line: string): string | undefined => {
|
|
127
|
+
const japanese = JP_POSTCODE.exec(line);
|
|
128
|
+
if (japanese !== null) return `${japanese[1] ?? ""}-${japanese[2] ?? ""}`;
|
|
129
|
+
return US_ZIP.exec(line)?.[1];
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** The labelled addresses of one line; its postcode is given to an address only when the line holds one address. */
|
|
133
|
+
const lineAddresses = (line: string, start: number, labels: readonly LexiconEntry[]): AddressWriting[] => {
|
|
134
|
+
const places = [...jaPlaces(line), ...enPlaces(line)];
|
|
135
|
+
const postcode = places.length === 1 ? postcodeOf(line) : undefined;
|
|
136
|
+
return places.flatMap((place) => {
|
|
137
|
+
const label = labelBefore(line.slice(0, place.offset).toLowerCase(), labels);
|
|
138
|
+
if (label === undefined) return [];
|
|
139
|
+
const key = `${place.street}\u0000${place.numbers.join("-")}`;
|
|
140
|
+
return [{ ...place, offset: start + place.offset, key, postcode, label }];
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/** The postal addresses of a text that have a label shortly before them on their line. */
|
|
145
|
+
export const labelledAddresses = (text: string, labels: readonly LexiconEntry[]): AddressWriting[] => {
|
|
146
|
+
const lines = text.split("\n");
|
|
147
|
+
const starts = lines.reduce<number[]>((acc, line, index) => [...acc, index === 0 ? 0 : (acc[index - 1] ?? 0) + (lines[index - 1] ?? "").length + 1], []);
|
|
148
|
+
return lines.flatMap((line, index) => lineAddresses(line, starts[index] ?? 0, labels));
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/** Two lists of block numbers that are one address mistyped: the same numbers in another order, or one of them changed. */
|
|
152
|
+
export const isBlockSlip = (a: readonly number[], b: readonly number[]): boolean => {
|
|
153
|
+
if (a.length !== b.length || a.join("-") === b.join("-")) return false;
|
|
154
|
+
const sorted = (numbers: readonly number[]): string => numbers.toSorted((x, y) => x - y).join("-");
|
|
155
|
+
const differing = a.filter((number, index) => number !== b[index]).length;
|
|
156
|
+
return sorted(a) === sorted(b) || differing === 1;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/** Whether two writings are on one street: the same town or street name, in the same area where both write one (千代田区 within 東京都千代田区). */
|
|
160
|
+
const sameStreet = (a: AddressWriting, b: AddressWriting): boolean => a.street === b.street && (a.area.endsWith(b.area) || b.area.endsWith(a.area));
|
|
161
|
+
|
|
162
|
+
const sameOffice = (a: AddressWriting, b: AddressWriting): boolean => a.label === b.label || a.label === ANY_OFFICE || b.label === ANY_OFFICE;
|
|
163
|
+
|
|
164
|
+
export type AddressVariant = { readonly variant: "address" | "postcode"; readonly found: Variant<AddressWriting> };
|
|
165
|
+
|
|
166
|
+
/** Every writing of an address that is another of the same street with its numbers mistyped, or under another postcode. */
|
|
167
|
+
export const addressVariants = (text: string, labels: readonly LexiconEntry[]): AddressVariant[] => {
|
|
168
|
+
const addresses = labelledAddresses(text, labels);
|
|
169
|
+
const numbered = writingVariants(addresses, (a, b) => sameStreet(a, b) && sameOffice(a, b) && isBlockSlip(a.numbers, b.numbers));
|
|
170
|
+
const withPostcode = addresses.flatMap((address) => (address.postcode === undefined ? [] : [{ ...address, key: `${address.key}\u0000${address.postcode}` }]));
|
|
171
|
+
const postcoded = writingVariants(
|
|
172
|
+
withPostcode,
|
|
173
|
+
(a, b) => sameStreet(a, b) && a.numbers.join("-") === b.numbers.join("-") && a.postcode !== b.postcode && sameOffice(a, b),
|
|
174
|
+
);
|
|
175
|
+
const tagged =
|
|
176
|
+
(variant: AddressVariant["variant"]) =>
|
|
177
|
+
(found: Variant<AddressWriting>): AddressVariant => ({ variant, found });
|
|
178
|
+
return [...numbered.map(tagged("address")), ...postcoded.map(tagged("postcode"))];
|
|
179
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Email addresses written two ways in one document (support@hibari-lab.example and suport@hibari-lab.example). Two addresses
|
|
2
|
+
// are one contact misspelt only when everything but one word of them is the same, so sales@ and support@ of one domain, or
|
|
3
|
+
// one mailbox at two domains that differ by more than a slip, stay apart. Pure.
|
|
4
|
+
import { writingVariants, type Variant, type Writing } from "./variants.ts";
|
|
5
|
+
|
|
6
|
+
export type EmailWriting = Writing;
|
|
7
|
+
|
|
8
|
+
const EMAIL = /(?<![\w.%+-])[a-z0-9][a-z0-9._%+-]*@[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+/giu;
|
|
9
|
+
const SEPARATOR = /[.@\-_+%]/u;
|
|
10
|
+
const SEPARATORS = /[.\-_]/gu;
|
|
11
|
+
const DIGIT = /\d/u;
|
|
12
|
+
/** A word shorter than this is a whole name (hr, pr, jp, sale, info), not one to be misspelt. */
|
|
13
|
+
const MIN_WORD_LENGTH = 5;
|
|
14
|
+
/** Longer than this, a word is an id rather than a name, and is not compared. */
|
|
15
|
+
const MAX_WORD_LENGTH = 64;
|
|
16
|
+
/** From this length a word may differ by two edits and still be a slip; a shorter one by one. */
|
|
17
|
+
const TWO_EDIT_LENGTH = 6;
|
|
18
|
+
|
|
19
|
+
/** The email addresses of a text, lower-cased into their key. */
|
|
20
|
+
export const emailWritings = (text: string): EmailWriting[] =>
|
|
21
|
+
[...text.matchAll(EMAIL)].map((match) => ({ offset: match.index, written: match[0], key: match[0].toLowerCase() }));
|
|
22
|
+
|
|
23
|
+
/** Edits (insert, delete, replace, swap of neighbours) between two short strings: the optimal string alignment distance. */
|
|
24
|
+
export const editDistance = (a: string, b: string): number => {
|
|
25
|
+
const rows = [...a].reduce<number[][]>(
|
|
26
|
+
(done, charA, i) => {
|
|
27
|
+
const previous = done[i] ?? [];
|
|
28
|
+
const row = [...b].reduce<number[]>(
|
|
29
|
+
(cells, charB, j) => {
|
|
30
|
+
const cost = charA === charB ? 0 : 1;
|
|
31
|
+
const swapped = i > 0 && j > 0 && charA === b[j - 1] && a[i - 1] === charB ? (done[i - 1]?.[j - 1] ?? Infinity) + 1 : Infinity;
|
|
32
|
+
return [...cells, Math.min((previous[j + 1] ?? Infinity) + 1, (cells[j] ?? Infinity) + 1, (previous[j] ?? Infinity) + cost, swapped)];
|
|
33
|
+
},
|
|
34
|
+
[i + 1],
|
|
35
|
+
);
|
|
36
|
+
return [...done, row];
|
|
37
|
+
},
|
|
38
|
+
[[...Array(b.length + 1).keys()]],
|
|
39
|
+
);
|
|
40
|
+
return rows.at(-1)?.at(-1) ?? 0;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const separatorsOf = (address: string): string => [...address].filter((char) => SEPARATOR.test(char)).join("");
|
|
44
|
+
|
|
45
|
+
/** One word of the two differs, by a slip: both long enough, no digits (support1@ and support2@ are two mailboxes). */
|
|
46
|
+
const oneWordSlip = (a: string, b: string): boolean => {
|
|
47
|
+
if (separatorsOf(a) !== separatorsOf(b)) return false;
|
|
48
|
+
const wordsA = a.split(SEPARATOR);
|
|
49
|
+
const wordsB = b.split(SEPARATOR);
|
|
50
|
+
const differing = wordsA.flatMap((word, index): [string, string][] => (word === wordsB[index] ? [] : [[word, wordsB[index] ?? ""]]));
|
|
51
|
+
const [pair] = differing;
|
|
52
|
+
if (differing.length !== 1 || pair === undefined) return false;
|
|
53
|
+
const [wordA, wordB] = pair;
|
|
54
|
+
const shorter = Math.min(wordA.length, wordB.length);
|
|
55
|
+
if (shorter < MIN_WORD_LENGTH || Math.max(wordA.length, wordB.length) > MAX_WORD_LENGTH || DIGIT.test(wordA) || DIGIT.test(wordB)) return false;
|
|
56
|
+
return editDistance(wordA, wordB) <= (shorter >= TWO_EDIT_LENGTH ? 2 : 1);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Whether two addresses are one written two ways: a dot or hyphen dropped or added, or one word misspelt. */
|
|
60
|
+
export const isEmailSlip = (a: string, b: string): boolean => a !== b && (a.replaceAll(SEPARATORS, "") === b.replaceAll(SEPARATORS, "") || oneWordSlip(a, b));
|
|
61
|
+
|
|
62
|
+
/** Every writing of an address that is another address of the document written with a slip, with that other address. */
|
|
63
|
+
export const emailVariants = (text: string): Variant<EmailWriting>[] => writingVariants(emailWritings(text), (a, b) => isEmailSlip(a.key, b.key));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// One contact written two ways: of two related writings, the one written fewer times, or the one first written later when
|
|
2
|
+
// both are written as often, is reported with the other. Pure: what a writing is and which writings are related come from
|
|
3
|
+
// the caller (an email address, a postal address).
|
|
4
|
+
|
|
5
|
+
/** A contact as written and where. key is what makes two writings the same contact. */
|
|
6
|
+
export type Writing = { readonly offset: number; readonly written: string; readonly key: string };
|
|
7
|
+
|
|
8
|
+
/** A writing that is a variant of another contact's writing, and that other writing. */
|
|
9
|
+
export type Variant<T extends Writing> = { readonly writing: T; readonly other: T };
|
|
10
|
+
|
|
11
|
+
const byKey = <T extends Writing>(writings: readonly T[]): Map<string, T[]> =>
|
|
12
|
+
writings.reduce((groups, writing) => groups.set(writing.key, [...(groups.get(writing.key) ?? []), writing]), new Map<string, T[]>());
|
|
13
|
+
|
|
14
|
+
/** Every writing of the minority side of each related pair, with the first writing of the other side, in document order. */
|
|
15
|
+
export const writingVariants = <T extends Writing>(writings: readonly T[], related: (a: T, b: T) => boolean): Variant<T>[] => {
|
|
16
|
+
const groups = byKey(writings);
|
|
17
|
+
const timesOf = (writing: T): number => groups.get(writing.key)?.length ?? 0;
|
|
18
|
+
const firsts = [...groups.values()].flatMap((group) => group.slice(0, 1));
|
|
19
|
+
const minorityOf = (a: T, b: T): T => {
|
|
20
|
+
if (timesOf(a) !== timesOf(b)) return timesOf(a) < timesOf(b) ? a : b;
|
|
21
|
+
return a.offset > b.offset ? a : b;
|
|
22
|
+
};
|
|
23
|
+
return firsts
|
|
24
|
+
.flatMap((first, index) =>
|
|
25
|
+
firsts.slice(index + 1).flatMap((second) => {
|
|
26
|
+
if (!related(first, second)) return [];
|
|
27
|
+
const minority = minorityOf(first, second);
|
|
28
|
+
const other = minority === first ? second : first;
|
|
29
|
+
return (groups.get(minority.key) ?? []).map((writing) => ({ writing, other }));
|
|
30
|
+
}),
|
|
31
|
+
)
|
|
32
|
+
.toSorted((a, b) => a.writing.offset - b.writing.offset);
|
|
33
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// capacity-runtime-mismatch: a battery capacity divided by the stated consumption is the longest the device can run; a stated
|
|
2
|
+
// runtime above it by more than its written precision cannot be right (5000 mAh at 500 mA is 10 h, not 15 h). The item names
|
|
3
|
+
// come from battery-label, the units from battery-unit and unit-time, the rough marks from approximate-marker. Pure.
|
|
4
|
+
|
|
5
|
+
export type Family = "charge" | "current" | "energy" | "power";
|
|
6
|
+
export type Role = "capacity" | "consumption" | "runtime";
|
|
7
|
+
|
|
8
|
+
export type ScaledUnit = { readonly pattern: string; readonly weight: number };
|
|
9
|
+
export type ElectricUnit = ScaledUnit & { readonly family: Family };
|
|
10
|
+
export type Marker = { readonly pattern: string; readonly position: "before" | "after" };
|
|
11
|
+
|
|
12
|
+
export type BatteryWords = {
|
|
13
|
+
readonly electric: readonly ElectricUnit[];
|
|
14
|
+
/** Units of time, weight in seconds. */
|
|
15
|
+
readonly time: readonly ScaledUnit[];
|
|
16
|
+
readonly approximate: readonly Marker[];
|
|
17
|
+
/** Marks that bound a value from one side (以上, 以内, at least, under): the value is not compared at all. */
|
|
18
|
+
readonly bounds: readonly Marker[];
|
|
19
|
+
readonly connectors: readonly string[];
|
|
20
|
+
readonly labels: readonly { readonly pattern: string; readonly group: string }[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** A value as written: the low and high end of a range (the same for one number), in mAh, mA, Wh, W or seconds. */
|
|
24
|
+
export type Reading = {
|
|
25
|
+
readonly low: number;
|
|
26
|
+
readonly high: number;
|
|
27
|
+
readonly family: Family | "time";
|
|
28
|
+
/** One step of the last written digit, in the reading's unit (15時間 → 3600 s, 10.5 h → 360 s). */
|
|
29
|
+
readonly step: number;
|
|
30
|
+
readonly approximate: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type LabelReading = { readonly role: Role; readonly standby: boolean; readonly peak: boolean; readonly condition: string };
|
|
34
|
+
|
|
35
|
+
export type Entry = LabelReading & { readonly reading: Reading; readonly start: number; readonly text: string };
|
|
36
|
+
|
|
37
|
+
export type CapacityRuntimeMismatch = {
|
|
38
|
+
readonly capacity: Entry;
|
|
39
|
+
readonly consumption: Entry;
|
|
40
|
+
readonly runtime: Entry;
|
|
41
|
+
readonly idealHours: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const SECONDS_PER_HOUR = 3600;
|
|
45
|
+
const APPROXIMATE_FACTOR = 2;
|
|
46
|
+
const EPSILON = 1e-9;
|
|
47
|
+
const NUMBER = /^(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?/u;
|
|
48
|
+
const LETTER = /\p{L}/u;
|
|
49
|
+
const SPACES = /^\s*/u;
|
|
50
|
+
|
|
51
|
+
const normal = (text: string): string => text.normalize("NFKC").trim();
|
|
52
|
+
const folded = (text: string): string => normal(text).toLowerCase();
|
|
53
|
+
const conditionKey = (text: string): string => folded(text).replace(/\s+/gu, "");
|
|
54
|
+
|
|
55
|
+
/** The text without a note in brackets at its end, and the note (連続再生時間(音量50%)). */
|
|
56
|
+
export const splitCondition = (text: string): { readonly bare: string; readonly condition: string } => {
|
|
57
|
+
const plain = normal(text);
|
|
58
|
+
const open = plain.lastIndexOf("(");
|
|
59
|
+
if (!plain.endsWith(")") || open <= 0) return { bare: plain, condition: "" };
|
|
60
|
+
return { bare: plain.slice(0, open).trim(), condition: conditionKey(plain.slice(open + 1, -1)) };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const wordsOf = (words: BatteryWords, group: string): string[] => words.labels.filter((entry) => entry.group === group).map((entry) => folded(entry.pattern));
|
|
64
|
+
|
|
65
|
+
const holds = (text: string, patterns: readonly string[]): boolean => patterns.some((pattern) => text.includes(pattern));
|
|
66
|
+
|
|
67
|
+
const WORD_CHAR = /[\p{L}\p{N}]/u;
|
|
68
|
+
|
|
69
|
+
/** The text without the marks at its ends (Max. → max). */
|
|
70
|
+
const withoutEdgeMarks = (text: string): string => {
|
|
71
|
+
const chars = [...text];
|
|
72
|
+
const first = chars.findIndex((char) => WORD_CHAR.test(char));
|
|
73
|
+
const last = chars.findLastIndex((char) => WORD_CHAR.test(char));
|
|
74
|
+
return first === -1 ? "" : chars.slice(first, last + 1).join("");
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Whether a condition or a heading is only one of the words, with a word of the state after it (待機時, Max., idle mode). */
|
|
78
|
+
const isOnly = (text: string, patterns: readonly string[], words: BatteryWords): boolean => {
|
|
79
|
+
const bare = withoutEdgeMarks(folded(text));
|
|
80
|
+
const suffixes = ["", ...wordsOf(words, "state")];
|
|
81
|
+
return patterns.some((pattern) => suffixes.some((suffix) => [pattern + suffix, `${pattern} ${suffix}`].includes(bare)));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ROLES: readonly Role[] = ["capacity", "consumption", "runtime"];
|
|
85
|
+
|
|
86
|
+
/** What an item name says: which value, under which condition, whether idle or at the maximum. */
|
|
87
|
+
export const labelOf = (label: string, words: BatteryWords): LabelReading | undefined => {
|
|
88
|
+
const { bare, condition } = splitCondition(label);
|
|
89
|
+
const head = folded(bare);
|
|
90
|
+
const roles = ROLES.filter((role) => holds(head, wordsOf(words, role)));
|
|
91
|
+
const [role] = roles;
|
|
92
|
+
if (role === undefined || roles.length > 1) return undefined;
|
|
93
|
+
const standby = wordsOf(words, "standby");
|
|
94
|
+
return {
|
|
95
|
+
role,
|
|
96
|
+
standby: holds(head, standby) || (role === "consumption" && isOnly(condition, standby, words)),
|
|
97
|
+
peak: holds(head, wordsOf(words, "peak")) || isOnly(condition, wordsOf(words, "peak"), words),
|
|
98
|
+
condition,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** Whether a column heading is only a word of the maximum (最大, Max.). */
|
|
103
|
+
export const isPeakHeading = (heading: string, words: BatteryWords): boolean => isOnly(heading, wordsOf(words, "peak"), words);
|
|
104
|
+
|
|
105
|
+
type Stripped = { readonly core: string; readonly marks: readonly string[] };
|
|
106
|
+
|
|
107
|
+
const markAt = (text: string, marker: Marker): boolean => {
|
|
108
|
+
const lower = text.toLowerCase();
|
|
109
|
+
const pattern = folded(marker.pattern);
|
|
110
|
+
return marker.position === "before" ? lower.startsWith(pattern) : lower.endsWith(pattern);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const stripOnce = (text: string, markers: readonly Marker[]): Stripped | undefined => {
|
|
114
|
+
const marker = markers.find((candidate) => markAt(text, candidate));
|
|
115
|
+
if (marker === undefined) return undefined;
|
|
116
|
+
const length = folded(marker.pattern).length;
|
|
117
|
+
const core = marker.position === "before" ? text.slice(length) : text.slice(0, text.length - length);
|
|
118
|
+
return { core: core.trim(), marks: [folded(marker.pattern)] };
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const MAX_MARKS = 3;
|
|
122
|
+
|
|
123
|
+
/** The value without the marks around it (約, 最大, 以上, about, up to), and the marks taken off. */
|
|
124
|
+
export const stripMarks = (text: string, markers: readonly Marker[]): Stripped =>
|
|
125
|
+
Array.from({ length: MAX_MARKS }).reduce<Stripped>(
|
|
126
|
+
(stripped) => {
|
|
127
|
+
const next = stripOnce(stripped.core, markers);
|
|
128
|
+
return next === undefined ? stripped : { core: next.core, marks: [...stripped.marks, ...next.marks] };
|
|
129
|
+
},
|
|
130
|
+
{ core: normal(text), marks: [] },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
type Scanned = { readonly value: number; readonly decimals: number; readonly unit: ScaledUnit | undefined; readonly end: number };
|
|
134
|
+
|
|
135
|
+
const byLength = (units: readonly ScaledUnit[]): ScaledUnit[] => [...units].sort((left, right) => right.pattern.length - left.pattern.length);
|
|
136
|
+
|
|
137
|
+
/** The unit written at `at`, not running on into a word. A unit in lower case (hours) is read in any case; mA and W as written. */
|
|
138
|
+
const unitAt = (text: string, at: number, units: readonly ScaledUnit[]): ScaledUnit | undefined =>
|
|
139
|
+
byLength(units).find((unit) => {
|
|
140
|
+
const pattern = normal(unit.pattern);
|
|
141
|
+
const written = text.slice(at, at + pattern.length);
|
|
142
|
+
const caseless = pattern === pattern.toLowerCase();
|
|
143
|
+
return (caseless ? written.toLowerCase() === pattern : written === pattern) && !LETTER.test(text[at + pattern.length] ?? "");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const spacesAt = (text: string, at: number): number => at + (SPACES.exec(text.slice(at))?.[0].length ?? 0);
|
|
147
|
+
|
|
148
|
+
/** A number at `at` and the unit right after it (spaces between allowed). */
|
|
149
|
+
const scanAt = (text: string, at: number, units: readonly ScaledUnit[]): Scanned | undefined => {
|
|
150
|
+
const number = NUMBER.exec(text.slice(at))?.[0];
|
|
151
|
+
if (number === undefined) return undefined;
|
|
152
|
+
const unitStart = spacesAt(text, at + number.length);
|
|
153
|
+
const unit = unitAt(text, unitStart, units);
|
|
154
|
+
const end = unit === undefined ? at + number.length : unitStart + normal(unit.pattern).length;
|
|
155
|
+
return { value: Number(number.replaceAll(",", "")), decimals: number.split(".")[1]?.length ?? 0, unit, end };
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** Every number and unit from `at` to the end; a number without a unit only last. Undefined when anything else is written. */
|
|
159
|
+
const scanAll = (text: string, at: number, units: readonly ScaledUnit[]): Scanned[] | undefined => {
|
|
160
|
+
const from = spacesAt(text, at);
|
|
161
|
+
if (from >= text.length) return [];
|
|
162
|
+
const scanned = scanAt(text, from, units);
|
|
163
|
+
if (scanned === undefined) return undefined;
|
|
164
|
+
if (scanned.unit === undefined) return spacesAt(text, scanned.end) >= text.length ? [scanned] : undefined;
|
|
165
|
+
const rest = scanAll(text, scanned.end, units);
|
|
166
|
+
return rest === undefined ? undefined : [scanned, ...rest];
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const DECIMAL_BASE = 10;
|
|
170
|
+
|
|
171
|
+
type Side = { readonly value: number; readonly step: number; readonly unit: ScaledUnit | undefined };
|
|
172
|
+
|
|
173
|
+
/** One side of a range: a number and unit, or for time a run of them (10時間30分, 1 h 30 min), or a bare number. */
|
|
174
|
+
const sideOf = (text: string, units: readonly ScaledUnit[], compound: boolean): Side | undefined => {
|
|
175
|
+
const parts = scanAll(text, 0, units);
|
|
176
|
+
const last = parts?.at(-1);
|
|
177
|
+
if (parts === undefined || last === undefined || (parts.length > 1 && (!compound || last.unit === undefined))) return undefined;
|
|
178
|
+
const step = DECIMAL_BASE ** -last.decimals;
|
|
179
|
+
if (last.unit === undefined) return { value: last.value, step, unit: undefined };
|
|
180
|
+
const value = parts.reduce((sum, part) => sum + part.value * (part.unit?.weight ?? 0), 0);
|
|
181
|
+
return { value, step: step * last.unit.weight, unit: last.unit };
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const splitRange = (text: string, connectors: readonly string[]): readonly string[] => {
|
|
185
|
+
const connector = connectors.map(normal).find((candidate) => text.indexOf(candidate) > 0);
|
|
186
|
+
if (connector === undefined) return [text];
|
|
187
|
+
const at = text.indexOf(connector);
|
|
188
|
+
return [text.slice(0, at).trim(), text.slice(at + connector.length).trim()];
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** A side written without a unit (8〜10時間) takes the unit of the other side. */
|
|
192
|
+
const withUnit = (side: Side, other: Side): Side | undefined => {
|
|
193
|
+
if (side.unit !== undefined) return side;
|
|
194
|
+
if (other.unit === undefined) return undefined;
|
|
195
|
+
return { value: side.value * other.unit.weight, step: side.step * other.unit.weight, unit: other.unit };
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const rangeOf = (core: string, units: readonly ScaledUnit[], connectors: readonly string[], compound: boolean): [Side, Side] | undefined => {
|
|
199
|
+
const parts = splitRange(core, connectors);
|
|
200
|
+
const sides = parts.map((part) => sideOf(part, units, compound));
|
|
201
|
+
const [first, second] = sides;
|
|
202
|
+
if (sides.some((side) => side === undefined) || first === undefined) return undefined;
|
|
203
|
+
if (second === undefined) return first.unit === undefined ? undefined : [first, first];
|
|
204
|
+
const low = withUnit(first, second);
|
|
205
|
+
return low === undefined || second.unit === undefined ? undefined : [low, second];
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const familyOf = (unit: ScaledUnit | undefined, units: readonly ElectricUnit[]): Family | undefined => units.find((candidate) => candidate === unit)?.family;
|
|
209
|
+
|
|
210
|
+
const ROLE_FAMILIES: Readonly<Record<Role, readonly (Family | "time")[]>> = {
|
|
211
|
+
capacity: ["charge", "energy"],
|
|
212
|
+
consumption: ["current", "power"],
|
|
213
|
+
runtime: ["time"],
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export type ValueReading = { readonly reading: Reading; readonly peak: boolean };
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A value cell or the value after a label's colon. A runtime is a time (15時間, 8〜10 hours, 1 h 30 min); a capacity or a
|
|
220
|
+
* consumption one number with an electric unit, or a range of them. A note in brackets at the end is not part of it.
|
|
221
|
+
* Undefined when the value is anything else, or is bounded from one side (10時間以上, under 500 mA).
|
|
222
|
+
*/
|
|
223
|
+
export const valueOf = (text: string, role: Role, words: BatteryWords): ValueReading | undefined => {
|
|
224
|
+
const { bare } = splitCondition(text);
|
|
225
|
+
const { core, marks } = stripMarks(bare, words.approximate);
|
|
226
|
+
if (words.bounds.some((marker) => markAt(bare, marker) || markAt(core, marker))) return undefined;
|
|
227
|
+
const units: readonly ScaledUnit[] = role === "runtime" ? words.time : words.electric;
|
|
228
|
+
const range = rangeOf(core, units, words.connectors, role === "runtime");
|
|
229
|
+
if (range === undefined) return undefined;
|
|
230
|
+
const [low, high] = range;
|
|
231
|
+
const family = role === "runtime" ? "time" : familyOf(high.unit, words.electric);
|
|
232
|
+
if (family === undefined || !ROLE_FAMILIES[role].includes(family) || (role !== "runtime" && familyOf(low.unit, words.electric) !== family)) return undefined;
|
|
233
|
+
const reading: Reading = { low: low.value, high: high.value, family, step: low.step, approximate: marks.length > 0 };
|
|
234
|
+
return { reading, peak: marks.some((mark) => wordsOf(words, "peak").includes(mark)) };
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const DIVIDES: Readonly<Record<string, Family>> = { current: "charge", power: "energy" };
|
|
238
|
+
|
|
239
|
+
/** The consumption a runtime is read against: the one under the same condition, or the only one. */
|
|
240
|
+
const partnerOf = (runtime: Entry, consumptions: readonly Entry[]): Entry | undefined => {
|
|
241
|
+
const kind = consumptions.filter((entry) => entry.standby === runtime.standby);
|
|
242
|
+
const same = kind.filter((entry) => entry.condition === runtime.condition);
|
|
243
|
+
if (same.length === 1) return same[0];
|
|
244
|
+
const [only] = kind;
|
|
245
|
+
return same.length === 0 && kind.length === 1 && only !== undefined && (only.condition === "" || runtime.condition === "") ? only : undefined;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/** The one capacity a consumption divides: of the matching kind, and only one value of it. */
|
|
249
|
+
const capacityFor = (consumption: Entry, capacities: readonly Entry[]): Entry | undefined => {
|
|
250
|
+
const kind = capacities.filter((entry) => entry.reading.family === DIVIDES[consumption.reading.family]);
|
|
251
|
+
const values = new Set(kind.map((entry) => entry.reading.high));
|
|
252
|
+
return values.size === 1 ? kind[0] : undefined;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
type Pair = readonly [runtime: Entry, consumption: Entry];
|
|
256
|
+
|
|
257
|
+
const pairsOf = (entries: readonly Entry[]): Pair[] => {
|
|
258
|
+
const consumptions = entries.filter((entry) => entry.role === "consumption" && !entry.peak);
|
|
259
|
+
const pairs = entries
|
|
260
|
+
.filter((entry) => entry.role === "runtime")
|
|
261
|
+
.flatMap((runtime): Pair[] => {
|
|
262
|
+
const partner = partnerOf(runtime, consumptions);
|
|
263
|
+
return partner === undefined ? [] : [[runtime, partner]];
|
|
264
|
+
});
|
|
265
|
+
return pairs.filter(([, consumption]) => pairs.filter(([, other]) => other === consumption).length === 1);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Runtimes stated above what the capacity divided by the consumption allows. Entries are one device's: one key-value table and
|
|
270
|
+
* the labelled lines of a section, or one column of a wider table. The margin is one step of the runtime's last written digit
|
|
271
|
+
* (15時間 → 1 h), twice that when any of the three is marked rough (約, about). A runtime at or below the ideal is normal.
|
|
272
|
+
*/
|
|
273
|
+
export const capacityRuntimeMismatches = (entries: readonly Entry[]): CapacityRuntimeMismatch[] => {
|
|
274
|
+
const capacities = entries.filter((entry) => entry.role === "capacity");
|
|
275
|
+
return pairsOf(entries).flatMap(([runtime, consumption]) => {
|
|
276
|
+
const capacity = capacityFor(consumption, capacities);
|
|
277
|
+
if (capacity === undefined || consumption.reading.low <= 0) return [];
|
|
278
|
+
const idealHours = capacity.reading.high / consumption.reading.low;
|
|
279
|
+
const rough = [runtime, consumption, capacity].some((entry) => entry.reading.approximate);
|
|
280
|
+
const margin = (runtime.reading.step / SECONDS_PER_HOUR) * (rough ? APPROXIMATE_FACTOR : 1);
|
|
281
|
+
const statedHours = runtime.reading.low / SECONDS_PER_HOUR;
|
|
282
|
+
return statedHours > idealHours + margin + EPSILON ? [{ capacity, consumption, runtime, idealHours }] : [];
|
|
283
|
+
});
|
|
284
|
+
};
|