@mailwoman/corpus 8.0.0 → 8.2.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/out/src/adapter.d.ts +15 -5
- package/out/src/adapter.d.ts.map +1 -1
- package/out/src/adapter.js +11 -9
- package/out/src/adapter.js.map +1 -1
- package/out/src/adapters/ban/adapter.d.ts +7 -0
- package/out/src/adapters/ban/adapter.d.ts.map +1 -1
- package/out/src/adapters/ban/adapter.js +9 -2
- package/out/src/adapters/ban/adapter.js.map +1 -1
- package/out/src/adapters/ban/street-decompose.d.ts.map +1 -1
- package/out/src/adapters/ban/street-decompose.js +5 -1
- package/out/src/adapters/ban/street-decompose.js.map +1 -1
- package/out/src/adapters/fcc-bdc/adapter.d.ts +14 -1
- package/out/src/adapters/fcc-bdc/adapter.d.ts.map +1 -1
- package/out/src/adapters/fcc-bdc/adapter.js +15 -2
- package/out/src/adapters/fcc-bdc/adapter.js.map +1 -1
- package/out/src/adapters/geonames/adapter.d.ts +11 -0
- package/out/src/adapters/geonames/adapter.d.ts.map +1 -1
- package/out/src/adapters/geonames/adapter.js +24 -5
- package/out/src/adapters/geonames/adapter.js.map +1 -1
- package/out/src/adapters/geonames-postal/adapter.d.ts +11 -0
- package/out/src/adapters/geonames-postal/adapter.d.ts.map +1 -1
- package/out/src/adapters/geonames-postal/adapter.js +14 -1
- package/out/src/adapters/geonames-postal/adapter.js.map +1 -1
- package/out/src/adapters/gnaf/adapter.d.ts +10 -1
- package/out/src/adapters/gnaf/adapter.d.ts.map +1 -1
- package/out/src/adapters/gnaf/adapter.js +11 -2
- package/out/src/adapters/gnaf/adapter.js.map +1 -1
- package/out/src/adapters/gnaf/assemble.d.ts +18 -6
- package/out/src/adapters/gnaf/assemble.d.ts.map +1 -1
- package/out/src/adapters/gnaf/assemble.js +16 -6
- package/out/src/adapters/gnaf/assemble.js.map +1 -1
- package/out/src/adapters/index.d.ts.map +1 -1
- package/out/src/adapters/index.js.map +1 -1
- package/out/src/adapters/openaddresses/adapter.d.ts +11 -0
- package/out/src/adapters/openaddresses/adapter.d.ts.map +1 -1
- package/out/src/adapters/openaddresses/adapter.js +18 -3
- package/out/src/adapters/openaddresses/adapter.js.map +1 -1
- package/out/src/adapters/overture/adapter.d.ts +11 -0
- package/out/src/adapters/overture/adapter.d.ts.map +1 -1
- package/out/src/adapters/overture/adapter.js +12 -1
- package/out/src/adapters/overture/adapter.js.map +1 -1
- package/out/src/adapters/state-hi-schools/adapter.d.ts +11 -0
- package/out/src/adapters/state-hi-schools/adapter.d.ts.map +1 -1
- package/out/src/adapters/state-hi-schools/adapter.js +11 -0
- package/out/src/adapters/state-hi-schools/adapter.js.map +1 -1
- package/out/src/adapters/state-ia-contractors/adapter.d.ts +11 -0
- package/out/src/adapters/state-ia-contractors/adapter.d.ts.map +1 -1
- package/out/src/adapters/state-ia-contractors/adapter.js +11 -0
- package/out/src/adapters/state-ia-contractors/adapter.js.map +1 -1
- package/out/src/adapters/state-ny-notaries/adapter.d.ts +11 -0
- package/out/src/adapters/state-ny-notaries/adapter.d.ts.map +1 -1
- package/out/src/adapters/state-ny-notaries/adapter.js +11 -0
- package/out/src/adapters/state-ny-notaries/adapter.js.map +1 -1
- package/out/src/adapters/state-tx-notaries/adapter.d.ts +11 -0
- package/out/src/adapters/state-tx-notaries/adapter.d.ts.map +1 -1
- package/out/src/adapters/state-tx-notaries/adapter.js +15 -2
- package/out/src/adapters/state-tx-notaries/adapter.js.map +1 -1
- package/out/src/adapters/synth-po-box/adapter.d.ts +11 -0
- package/out/src/adapters/synth-po-box/adapter.d.ts.map +1 -1
- package/out/src/adapters/synth-po-box/adapter.js +13 -2
- package/out/src/adapters/synth-po-box/adapter.js.map +1 -1
- package/out/src/adapters/tiger/adapter.d.ts +14 -1
- package/out/src/adapters/tiger/adapter.d.ts.map +1 -1
- package/out/src/adapters/tiger/adapter.js +19 -4
- package/out/src/adapters/tiger/adapter.js.map +1 -1
- package/out/src/adapters/tiger/street-decompose.d.ts.map +1 -1
- package/out/src/adapters/tiger/street-decompose.js.map +1 -1
- package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-hrsa-fqhc/adapter.js +12 -1
- package/out/src/adapters/usgov-hrsa-fqhc/adapter.js.map +1 -1
- package/out/src/adapters/usgov-imls-pls/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-imls-pls/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-imls-pls/adapter.js +11 -0
- package/out/src/adapters/usgov-imls-pls/adapter.js.map +1 -1
- package/out/src/adapters/usgov-irs-bmf/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-irs-bmf/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-irs-bmf/adapter.js +17 -2
- package/out/src/adapters/usgov-irs-bmf/adapter.js.map +1 -1
- package/out/src/adapters/usgov-nad/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-nad/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-nad/adapter.js +12 -1
- package/out/src/adapters/usgov-nad/adapter.js.map +1 -1
- package/out/src/adapters/usgov-nppes/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-nppes/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-nppes/adapter.js +11 -0
- package/out/src/adapters/usgov-nppes/adapter.js.map +1 -1
- package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts +11 -0
- package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts.map +1 -1
- package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js +15 -2
- package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js.map +1 -1
- package/out/src/adapters/wof-admin-jp/adapter.d.ts +7 -0
- package/out/src/adapters/wof-admin-jp/adapter.d.ts.map +1 -1
- package/out/src/adapters/wof-admin-jp/adapter.js +18 -3
- package/out/src/adapters/wof-admin-jp/adapter.js.map +1 -1
- package/out/src/adapters/wof-admin-json/adapter.d.ts +13 -3
- package/out/src/adapters/wof-admin-json/adapter.d.ts.map +1 -1
- package/out/src/adapters/wof-admin-json/adapter.js +15 -5
- package/out/src/adapters/wof-admin-json/adapter.js.map +1 -1
- package/out/src/adapters/wof-postalcode-json/adapter.d.ts +7 -0
- package/out/src/adapters/wof-postalcode-json/adapter.d.ts.map +1 -1
- package/out/src/adapters/wof-postalcode-json/adapter.js +9 -2
- package/out/src/adapters/wof-postalcode-json/adapter.js.map +1 -1
- package/out/src/align.d.ts +12 -4
- package/out/src/align.d.ts.map +1 -1
- package/out/src/align.js +9 -5
- package/out/src/align.js.map +1 -1
- package/out/src/build.d.ts +24 -8
- package/out/src/build.d.ts.map +1 -1
- package/out/src/build.js +3 -3
- package/out/src/build.js.map +1 -1
- package/out/src/codex/us-fips-state.d.ts +6 -2
- package/out/src/codex/us-fips-state.d.ts.map +1 -1
- package/out/src/codex/us-fips-state.js +3 -1
- package/out/src/codex/us-fips-state.js.map +1 -1
- package/out/src/golden.d.ts +21 -7
- package/out/src/golden.d.ts.map +1 -1
- package/out/src/golden.js +18 -10
- package/out/src/golden.js.map +1 -1
- package/out/src/license.d.ts +3 -1
- package/out/src/license.d.ts.map +1 -1
- package/out/src/license.js +4 -2
- package/out/src/license.js.map +1 -1
- package/out/src/parquet-wrapper/reader.d.ts +4 -2
- package/out/src/parquet-wrapper/reader.d.ts.map +1 -1
- package/out/src/parquet-wrapper/reader.js +5 -4
- package/out/src/parquet-wrapper/reader.js.map +1 -1
- package/out/src/parquet-wrapper/schema.d.ts +14 -10
- package/out/src/parquet-wrapper/schema.d.ts.map +1 -1
- package/out/src/parquet-wrapper/schema.js +6 -2
- package/out/src/parquet-wrapper/schema.js.map +1 -1
- package/out/src/parquet-wrapper/writer.d.ts +12 -4
- package/out/src/parquet-wrapper/writer.d.ts.map +1 -1
- package/out/src/parquet-wrapper/writer.js +12 -4
- package/out/src/parquet-wrapper/writer.js.map +1 -1
- package/out/src/parquet.d.ts +21 -7
- package/out/src/parquet.d.ts.map +1 -1
- package/out/src/parquet.js +15 -4
- package/out/src/parquet.js.map +1 -1
- package/out/src/runner.d.ts +39 -13
- package/out/src/runner.d.ts.map +1 -1
- package/out/src/runner.js +7 -3
- package/out/src/runner.js.map +1 -1
- package/out/src/shard-recipes/anchor-absorption.d.ts +4 -0
- package/out/src/shard-recipes/anchor-absorption.d.ts.map +1 -1
- package/out/src/shard-recipes/anchor-absorption.js +14 -5
- package/out/src/shard-recipes/anchor-absorption.js.map +1 -1
- package/out/src/shard-recipes/boundary-stress.d.ts +4 -0
- package/out/src/shard-recipes/boundary-stress.d.ts.map +1 -1
- package/out/src/shard-recipes/boundary-stress.js +13 -7
- package/out/src/shard-recipes/boundary-stress.js.map +1 -1
- package/out/src/shard-recipes/country-balanced.d.ts +4 -0
- package/out/src/shard-recipes/country-balanced.d.ts.map +1 -1
- package/out/src/shard-recipes/country-balanced.js +39 -11
- package/out/src/shard-recipes/country-balanced.js.map +1 -1
- package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts +4 -0
- package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts.map +1 -1
- package/out/src/shard-recipes/cz-pcfirst-preposition.js +4 -0
- package/out/src/shard-recipes/cz-pcfirst-preposition.js.map +1 -1
- package/out/src/shard-recipes/fr-admin-split.d.ts +4 -0
- package/out/src/shard-recipes/fr-admin-split.d.ts.map +1 -1
- package/out/src/shard-recipes/fr-admin-split.js +14 -3
- package/out/src/shard-recipes/fr-admin-split.js.map +1 -1
- package/out/src/shard-recipes/fr-bare-street.d.ts +4 -0
- package/out/src/shard-recipes/fr-bare-street.d.ts.map +1 -1
- package/out/src/shard-recipes/fr-bare-street.js +4 -0
- package/out/src/shard-recipes/fr-bare-street.js.map +1 -1
- package/out/src/shard-recipes/fr-fragment.d.ts +4 -0
- package/out/src/shard-recipes/fr-fragment.d.ts.map +1 -1
- package/out/src/shard-recipes/fr-fragment.js +25 -8
- package/out/src/shard-recipes/fr-fragment.js.map +1 -1
- package/out/src/shard-recipes/fr-lieudit.d.ts +4 -0
- package/out/src/shard-recipes/fr-lieudit.d.ts.map +1 -1
- package/out/src/shard-recipes/fr-lieudit.js +21 -8
- package/out/src/shard-recipes/fr-lieudit.js.map +1 -1
- package/out/src/shard-recipes/fr-order.d.ts +4 -0
- package/out/src/shard-recipes/fr-order.d.ts.map +1 -1
- package/out/src/shard-recipes/fr-order.js +27 -8
- package/out/src/shard-recipes/fr-order.js.map +1 -1
- package/out/src/shard-recipes/german.d.ts +4 -0
- package/out/src/shard-recipes/german.d.ts.map +1 -1
- package/out/src/shard-recipes/german.js +16 -6
- package/out/src/shard-recipes/german.js.map +1 -1
- package/out/src/shard-recipes/house-venue.d.ts +4 -0
- package/out/src/shard-recipes/house-venue.d.ts.map +1 -1
- package/out/src/shard-recipes/house-venue.js +4 -0
- package/out/src/shard-recipes/house-venue.js.map +1 -1
- package/out/src/shard-recipes/index.d.ts +9 -3
- package/out/src/shard-recipes/index.d.ts.map +1 -1
- package/out/src/shard-recipes/index.js +12 -4
- package/out/src/shard-recipes/index.js.map +1 -1
- package/out/src/shard-recipes/intersection.d.ts +4 -0
- package/out/src/shard-recipes/intersection.d.ts.map +1 -1
- package/out/src/shard-recipes/intersection.js +27 -13
- package/out/src/shard-recipes/intersection.js.map +1 -1
- package/out/src/shard-recipes/locale.d.ts +4 -0
- package/out/src/shard-recipes/locale.d.ts.map +1 -1
- package/out/src/shard-recipes/locale.js +9 -4
- package/out/src/shard-recipes/locale.js.map +1 -1
- package/out/src/shard-recipes/nl-postcode.d.ts +4 -0
- package/out/src/shard-recipes/nl-postcode.d.ts.map +1 -1
- package/out/src/shard-recipes/nl-postcode.js +8 -2
- package/out/src/shard-recipes/nl-postcode.js.map +1 -1
- package/out/src/shard-recipes/no-fragment.d.ts +4 -0
- package/out/src/shard-recipes/no-fragment.d.ts.map +1 -1
- package/out/src/shard-recipes/no-fragment.js +8 -2
- package/out/src/shard-recipes/no-fragment.js.map +1 -1
- package/out/src/shard-recipes/no-street-led.d.ts +4 -0
- package/out/src/shard-recipes/no-street-led.d.ts.map +1 -1
- package/out/src/shard-recipes/no-street-led.js +5 -1
- package/out/src/shard-recipes/no-street-led.js.map +1 -1
- package/out/src/shard-recipes/no-street.d.ts +4 -0
- package/out/src/shard-recipes/no-street.d.ts.map +1 -1
- package/out/src/shard-recipes/no-street.js +4 -0
- package/out/src/shard-recipes/no-street.js.map +1 -1
- package/out/src/shard-recipes/po-box-cedex.d.ts +4 -0
- package/out/src/shard-recipes/po-box-cedex.d.ts.map +1 -1
- package/out/src/shard-recipes/po-box-cedex.js +99 -43
- package/out/src/shard-recipes/po-box-cedex.js.map +1 -1
- package/out/src/shard-recipes/po-box.d.ts +4 -0
- package/out/src/shard-recipes/po-box.d.ts.map +1 -1
- package/out/src/shard-recipes/po-box.js +4 -0
- package/out/src/shard-recipes/po-box.js.map +1 -1
- package/out/src/shard-recipes/scaffold.d.ts +48 -16
- package/out/src/shard-recipes/scaffold.d.ts.map +1 -1
- package/out/src/shard-recipes/scaffold.js +10 -6
- package/out/src/shard-recipes/scaffold.js.map +1 -1
- package/out/src/shard-recipes/si-bare-village.d.ts +4 -0
- package/out/src/shard-recipes/si-bare-village.d.ts.map +1 -1
- package/out/src/shard-recipes/si-bare-village.js +4 -0
- package/out/src/shard-recipes/si-bare-village.js.map +1 -1
- package/out/src/shard-recipes/street-affix.d.ts +4 -0
- package/out/src/shard-recipes/street-affix.d.ts.map +1 -1
- package/out/src/shard-recipes/street-affix.js +29 -10
- package/out/src/shard-recipes/street-affix.js.map +1 -1
- package/out/src/shard-recipes/street-bare.d.ts +4 -0
- package/out/src/shard-recipes/street-bare.d.ts.map +1 -1
- package/out/src/shard-recipes/street-bare.js +4 -0
- package/out/src/shard-recipes/street-bare.js.map +1 -1
- package/out/src/shard-recipes/street.d.ts +4 -0
- package/out/src/shard-recipes/street.d.ts.map +1 -1
- package/out/src/shard-recipes/street.js +4 -0
- package/out/src/shard-recipes/street.js.map +1 -1
- package/out/src/shard-recipes/unit.d.ts +4 -0
- package/out/src/shard-recipes/unit.d.ts.map +1 -1
- package/out/src/shard-recipes/unit.js +45 -14
- package/out/src/shard-recipes/unit.js.map +1 -1
- package/out/src/split.d.ts +18 -6
- package/out/src/split.d.ts.map +1 -1
- package/out/src/split.js +4 -2
- package/out/src/split.js.map +1 -1
- package/out/src/synthesize-anchor-absorption.d.ts +7 -1
- package/out/src/synthesize-anchor-absorption.d.ts.map +1 -1
- package/out/src/synthesize-anchor-absorption.js +28 -14
- package/out/src/synthesize-anchor-absorption.js.map +1 -1
- package/out/src/synthesize-boundary-stress.d.ts +3 -1
- package/out/src/synthesize-boundary-stress.d.ts.map +1 -1
- package/out/src/synthesize-boundary-stress.js +35 -28
- package/out/src/synthesize-boundary-stress.js.map +1 -1
- package/out/src/synthesize-german.d.ts +15 -5
- package/out/src/synthesize-german.d.ts.map +1 -1
- package/out/src/synthesize-german.js +9 -3
- package/out/src/synthesize-german.js.map +1 -1
- package/out/src/synthesize-house-venue.d.ts.map +1 -1
- package/out/src/synthesize-house-venue.js +15 -15
- package/out/src/synthesize-house-venue.js.map +1 -1
- package/out/src/synthesize-intersection.d.ts +9 -3
- package/out/src/synthesize-intersection.d.ts.map +1 -1
- package/out/src/synthesize-intersection.js +25 -6
- package/out/src/synthesize-intersection.js.map +1 -1
- package/out/src/synthesize-no-street.d.ts +3 -1
- package/out/src/synthesize-no-street.d.ts.map +1 -1
- package/out/src/synthesize-no-street.js +5 -6
- package/out/src/synthesize-no-street.js.map +1 -1
- package/out/src/synthesize-po-box.d.ts +15 -5
- package/out/src/synthesize-po-box.d.ts.map +1 -1
- package/out/src/synthesize-po-box.js +21 -7
- package/out/src/synthesize-po-box.js.map +1 -1
- package/out/src/synthesize-street.d.ts +3 -1
- package/out/src/synthesize-street.d.ts.map +1 -1
- package/out/src/synthesize-street.js +6 -1
- package/out/src/synthesize-street.js.map +1 -1
- package/out/src/synthesize.d.ts +39 -13
- package/out/src/synthesize.d.ts.map +1 -1
- package/out/src/synthesize.js +66 -36
- package/out/src/synthesize.js.map +1 -1
- package/out/src/tokenize.d.ts +15 -5
- package/out/src/tokenize.d.ts.map +1 -1
- package/out/src/tokenize.js.map +1 -1
- package/out/src/tools/align-shard.d.ts.map +1 -1
- package/out/src/tools/align-shard.js +3 -1
- package/out/src/tools/align-shard.js.map +1 -1
- package/out/src/tools/audit.d.ts.map +1 -1
- package/out/src/tools/audit.js +20 -8
- package/out/src/tools/audit.js.map +1 -1
- package/out/src/tools/corpus-stats.d.ts.map +1 -1
- package/out/src/tools/corpus-stats.js.map +1 -1
- package/out/src/tools/fetch/ban.d.ts.map +1 -1
- package/out/src/tools/fetch/ban.js +17 -10
- package/out/src/tools/fetch/ban.js.map +1 -1
- package/out/src/tools/fetch/download.d.ts +30 -10
- package/out/src/tools/fetch/download.d.ts.map +1 -1
- package/out/src/tools/fetch/download.js +26 -6
- package/out/src/tools/fetch/download.js.map +1 -1
- package/out/src/tools/fetch/hrsa.d.ts.map +1 -1
- package/out/src/tools/fetch/hrsa.js.map +1 -1
- package/out/src/tools/fetch/imls-pls.d.ts.map +1 -1
- package/out/src/tools/fetch/imls-pls.js +18 -18
- package/out/src/tools/fetch/imls-pls.js.map +1 -1
- package/out/src/tools/fetch/index.d.ts +3 -1
- package/out/src/tools/fetch/index.d.ts.map +1 -1
- package/out/src/tools/fetch/index.js +3 -1
- package/out/src/tools/fetch/index.js.map +1 -1
- package/out/src/tools/fetch/nad.d.ts +21 -7
- package/out/src/tools/fetch/nad.d.ts.map +1 -1
- package/out/src/tools/fetch/nad.js +5 -5
- package/out/src/tools/fetch/nad.js.map +1 -1
- package/out/src/tools/fetch/nppes.d.ts.map +1 -1
- package/out/src/tools/fetch/nppes.js +9 -15
- package/out/src/tools/fetch/nppes.js.map +1 -1
- package/out/src/tools/fetch/openaddresses.d.ts +3 -1
- package/out/src/tools/fetch/openaddresses.d.ts.map +1 -1
- package/out/src/tools/fetch/openaddresses.js +32 -23
- package/out/src/tools/fetch/openaddresses.js.map +1 -1
- package/out/src/tools/fetch/ppd.d.ts +3 -1
- package/out/src/tools/fetch/ppd.d.ts.map +1 -1
- package/out/src/tools/fetch/ppd.js +14 -3
- package/out/src/tools/fetch/ppd.js.map +1 -1
- package/out/src/tools/fetch/state-hi-schools.d.ts.map +1 -1
- package/out/src/tools/fetch/state-hi-schools.js +13 -6
- package/out/src/tools/fetch/state-hi-schools.js.map +1 -1
- package/out/src/tools/fetch/state-sources.d.ts.map +1 -1
- package/out/src/tools/fetch/state-sources.js +8 -3
- package/out/src/tools/fetch/state-sources.js.map +1 -1
- package/out/src/tools/fetch/tiger-full.d.ts +9 -3
- package/out/src/tools/fetch/tiger-full.d.ts.map +1 -1
- package/out/src/tools/fetch/tiger-full.js +35 -20
- package/out/src/tools/fetch/tiger-full.js.map +1 -1
- package/out/src/tools/gb-title-case.d.ts.map +1 -1
- package/out/src/tools/gb-title-case.js.map +1 -1
- package/out/src/tools/golden-expand.d.ts +24 -8
- package/out/src/tools/golden-expand.d.ts.map +1 -1
- package/out/src/tools/golden-expand.js +20 -12
- package/out/src/tools/golden-expand.js.map +1 -1
- package/out/src/tools/golden-promote.d.ts +18 -6
- package/out/src/tools/golden-promote.d.ts.map +1 -1
- package/out/src/tools/golden-promote.js +22 -7
- package/out/src/tools/golden-promote.js.map +1 -1
- package/out/src/tools/ingest-csv.d.ts +3 -1
- package/out/src/tools/ingest-csv.d.ts.map +1 -1
- package/out/src/tools/ingest-csv.js +17 -19
- package/out/src/tools/ingest-csv.js.map +1 -1
- package/out/src/tools/jsonl-to-parquet.d.ts +18 -6
- package/out/src/tools/jsonl-to-parquet.d.ts.map +1 -1
- package/out/src/tools/jsonl-to-parquet.js +11 -5
- package/out/src/tools/jsonl-to-parquet.js.map +1 -1
- package/out/src/tools/lint-shard-vocab.d.ts +36 -12
- package/out/src/tools/lint-shard-vocab.d.ts.map +1 -1
- package/out/src/tools/lint-shard-vocab.js +25 -11
- package/out/src/tools/lint-shard-vocab.js.map +1 -1
- package/out/src/tools/lint-shard.d.ts +30 -10
- package/out/src/tools/lint-shard.d.ts.map +1 -1
- package/out/src/tools/lint-shard.js +33 -20
- package/out/src/tools/lint-shard.js.map +1 -1
- package/out/src/tools/overlay-manifest.d.ts.map +1 -1
- package/out/src/tools/overlay-manifest.js +6 -4
- package/out/src/tools/overlay-manifest.js.map +1 -1
- package/out/src/tools/shard-kryptonite.d.ts +6 -2
- package/out/src/tools/shard-kryptonite.d.ts.map +1 -1
- package/out/src/tools/shard-kryptonite.js +1 -1
- package/out/src/tools/shard-kryptonite.js.map +1 -1
- package/out/src/tools/shard-translit.d.ts +9 -3
- package/out/src/tools/shard-translit.d.ts.map +1 -1
- package/out/src/tools/shard-translit.js +2 -2
- package/out/src/tools/shard-translit.js.map +1 -1
- package/out/src/types.d.ts +51 -17
- package/out/src/types.d.ts.map +1 -1
- package/out/src/wof-json.d.ts +9 -3
- package/out/src/wof-json.d.ts.map +1 -1
- package/out/src/wof-json.js +6 -4
- package/out/src/wof-json.js.map +1 -1
- package/package.json +7 -6
- package/src/adapter.ts +24 -13
- package/src/adapters/ban/adapter.ts +12 -2
- package/src/adapters/ban/street-decompose.ts +7 -1
- package/src/adapters/fcc-bdc/adapter.ts +19 -3
- package/src/adapters/geonames/adapter.ts +28 -5
- package/src/adapters/geonames-postal/adapter.ts +17 -1
- package/src/adapters/gnaf/adapter.ts +15 -2
- package/src/adapters/gnaf/assemble.ts +40 -10
- package/src/adapters/index.ts +13 -0
- package/src/adapters/openaddresses/adapter.ts +22 -3
- package/src/adapters/overture/adapter.ts +18 -2
- package/src/adapters/state-hi-schools/adapter.ts +15 -0
- package/src/adapters/state-ia-contractors/adapter.ts +15 -0
- package/src/adapters/state-ny-notaries/adapter.ts +15 -0
- package/src/adapters/state-tx-notaries/adapter.ts +18 -2
- package/src/adapters/synth-po-box/adapter.ts +19 -2
- package/src/adapters/tiger/adapter.ts +23 -4
- package/src/adapters/tiger/street-decompose.ts +1 -0
- package/src/adapters/usgov-hrsa-fqhc/adapter.ts +18 -2
- package/src/adapters/usgov-imls-pls/adapter.ts +15 -0
- package/src/adapters/usgov-irs-bmf/adapter.ts +19 -2
- package/src/adapters/usgov-nad/adapter.ts +14 -1
- package/src/adapters/usgov-nppes/adapter.ts +13 -0
- package/src/adapters/usgov-samhsa-treatment-locator/adapter.ts +21 -3
- package/src/adapters/wof-admin-jp/adapter.ts +20 -3
- package/src/adapters/wof-admin-json/adapter.ts +22 -7
- package/src/adapters/wof-postalcode-json/adapter.ts +10 -2
- package/src/align.ts +21 -8
- package/src/build.ts +48 -11
- package/src/codex/us-fips-state.ts +6 -2
- package/src/golden.ts +28 -13
- package/src/license.ts +4 -2
- package/src/parquet-wrapper/reader.ts +6 -4
- package/src/parquet-wrapper/schema.ts +14 -10
- package/src/parquet-wrapper/writer.ts +12 -4
- package/src/parquet.ts +39 -8
- package/src/runner.ts +49 -15
- package/src/shard-recipes/anchor-absorption.ts +21 -5
- package/src/shard-recipes/boundary-stress.ts +20 -7
- package/src/shard-recipes/country-balanced.ts +78 -13
- package/src/shard-recipes/cz-pcfirst-preposition.ts +9 -0
- package/src/shard-recipes/fr-admin-split.ts +30 -5
- package/src/shard-recipes/fr-bare-street.ts +10 -0
- package/src/shard-recipes/fr-fragment.ts +33 -8
- package/src/shard-recipes/fr-lieudit.ts +35 -9
- package/src/shard-recipes/fr-order.ts +47 -9
- package/src/shard-recipes/german.ts +32 -6
- package/src/shard-recipes/house-venue.ts +7 -0
- package/src/shard-recipes/index.ts +12 -4
- package/src/shard-recipes/intersection.ts +63 -14
- package/src/shard-recipes/locale.ts +37 -4
- package/src/shard-recipes/nl-postcode.ts +12 -2
- package/src/shard-recipes/no-fragment.ts +12 -2
- package/src/shard-recipes/no-street-led.ts +11 -1
- package/src/shard-recipes/no-street.ts +7 -0
- package/src/shard-recipes/po-box-cedex.ts +145 -45
- package/src/shard-recipes/po-box.ts +7 -0
- package/src/shard-recipes/scaffold.ts +52 -20
- package/src/shard-recipes/si-bare-village.ts +9 -0
- package/src/shard-recipes/street-affix.ts +88 -13
- package/src/shard-recipes/street-bare.ts +10 -0
- package/src/shard-recipes/street.ts +7 -0
- package/src/shard-recipes/unit.ts +79 -16
- package/src/split.ts +28 -7
- package/src/synthesize-anchor-absorption.ts +41 -14
- package/src/synthesize-boundary-stress.ts +57 -30
- package/src/synthesize-german.ts +30 -7
- package/src/synthesize-house-venue.ts +22 -16
- package/src/synthesize-intersection.ts +34 -7
- package/src/synthesize-no-street.ts +18 -10
- package/src/synthesize-po-box.ts +42 -10
- package/src/synthesize-street.ts +10 -2
- package/src/synthesize.ts +81 -38
- package/src/tokenize.ts +15 -5
- package/src/tools/align-shard.ts +6 -1
- package/src/tools/audit.ts +57 -11
- package/src/tools/corpus-stats.ts +9 -0
- package/src/tools/fetch/ban.ts +32 -10
- package/src/tools/fetch/download.ts +49 -12
- package/src/tools/fetch/hrsa.ts +3 -0
- package/src/tools/fetch/imls-pls.ts +28 -18
- package/src/tools/fetch/index.ts +3 -1
- package/src/tools/fetch/nad.ts +39 -12
- package/src/tools/fetch/nppes.ts +17 -15
- package/src/tools/fetch/openaddresses.ts +52 -24
- package/src/tools/fetch/ppd.ts +22 -3
- package/src/tools/fetch/state-hi-schools.ts +17 -6
- package/src/tools/fetch/state-sources.ts +16 -3
- package/src/tools/fetch/tiger-full.ts +66 -23
- package/src/tools/gb-title-case.ts +1 -0
- package/src/tools/golden-expand.ts +64 -20
- package/src/tools/golden-promote.ts +53 -13
- package/src/tools/ingest-csv.ts +41 -26
- package/src/tools/jsonl-to-parquet.ts +30 -10
- package/src/tools/lint-shard-vocab.ts +75 -24
- package/src/tools/lint-shard.ts +75 -31
- package/src/tools/overlay-manifest.ts +7 -4
- package/src/tools/shard-kryptonite.ts +11 -3
- package/src/tools/shard-translit.ts +20 -5
- package/src/types.ts +51 -17
- package/src/wof-json.ts +18 -7
package/src/tools/audit.ts
CHANGED
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
19
19
|
import { basename, join } from "node:path"
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Share of a shard one source may hold before the mix is flagged as dominated by it.
|
|
23
|
+
*/
|
|
24
|
+
const DOMINANT_SOURCE_SHARE = 0.4
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How far the leading source may outweigh the runner-up before the mix is flagged. Catches the case where no single
|
|
28
|
+
* source clears {@link DOMINANT_SOURCE_SHARE} but the distribution is still lopsided.
|
|
29
|
+
*/
|
|
30
|
+
const MAX_TOP_TO_RUNNER_UP_RATIO = 1.5
|
|
31
|
+
|
|
21
32
|
export interface AuditOpts {
|
|
22
33
|
corpusDir: string
|
|
23
34
|
configPath?: string
|
|
@@ -30,11 +41,17 @@ export interface AuditOpts {
|
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
interface ShardStats {
|
|
33
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Shards per source per split
|
|
46
|
+
*/
|
|
34
47
|
bySplit: Record<string, Record<string, number>>
|
|
35
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Total shards counted (may be less than file count if sampleShardCount caps reads)
|
|
50
|
+
*/
|
|
36
51
|
totalShards: number
|
|
37
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Total shards on disk (file count) — equals totalShards unless capped
|
|
54
|
+
*/
|
|
38
55
|
totalFiles: number
|
|
39
56
|
}
|
|
40
57
|
|
|
@@ -61,6 +78,7 @@ function parseConfig(configPath: string): ParsedConfig | null {
|
|
|
61
78
|
if (sourceWeightsMatch) {
|
|
62
79
|
inBlock = true
|
|
63
80
|
blockIndent = sourceWeightsMatch[1]!.length
|
|
81
|
+
|
|
64
82
|
continue
|
|
65
83
|
}
|
|
66
84
|
|
|
@@ -73,12 +91,14 @@ function parseConfig(configPath: string): ParsedConfig | null {
|
|
|
73
91
|
|
|
74
92
|
if (indent <= blockIndent) {
|
|
75
93
|
inBlock = false
|
|
94
|
+
|
|
76
95
|
continue
|
|
77
96
|
}
|
|
97
|
+
|
|
78
98
|
const m = raw.match(/^[\t ]+([\w-]+):\s*([\d.]+)/)
|
|
79
99
|
|
|
80
100
|
if (m) {
|
|
81
|
-
weights[m[1]!] = parseFloat(m[2]!)
|
|
101
|
+
weights[m[1]!] = Number.parseFloat(m[2]!)
|
|
82
102
|
}
|
|
83
103
|
}
|
|
84
104
|
|
|
@@ -96,9 +116,11 @@ function scanShards(corpusDir: string, sampleCount: number): ShardStats {
|
|
|
96
116
|
const splitDir = join(corpusDir, split)
|
|
97
117
|
|
|
98
118
|
if (!existsSync(splitDir)) continue
|
|
119
|
+
|
|
99
120
|
const files = readdirSync(splitDir)
|
|
100
121
|
.filter((f) => f.endsWith(".parquet"))
|
|
101
|
-
.
|
|
122
|
+
.toSorted()
|
|
123
|
+
|
|
102
124
|
stats.totalFiles += files.length
|
|
103
125
|
const sampleEvery = Math.max(1, Math.floor(files.length / sampleCount))
|
|
104
126
|
const sampled = files.filter((_, i) => i % sampleEvery === 0).slice(0, sampleCount)
|
|
@@ -112,12 +134,14 @@ function scanShards(corpusDir: string, sampleCount: number): ShardStats {
|
|
|
112
134
|
const inferred = inferSourceFromFilename(f)
|
|
113
135
|
splitMap[inferred] = (splitMap[inferred] ?? 0) + 1
|
|
114
136
|
}
|
|
137
|
+
|
|
115
138
|
// Scale to estimated full-shard counts.
|
|
116
139
|
const scale = files.length / Math.max(sampled.length, 1)
|
|
117
140
|
|
|
118
141
|
for (const k of Object.keys(splitMap)) {
|
|
119
142
|
splitMap[k] = Math.round(splitMap[k]! * scale)
|
|
120
143
|
}
|
|
144
|
+
|
|
121
145
|
stats.bySplit[split] = splitMap
|
|
122
146
|
stats.totalShards += files.length
|
|
123
147
|
}
|
|
@@ -165,10 +189,12 @@ const KNOWN_SOURCE_PREFIXES: ReadonlyArray<string> = [
|
|
|
165
189
|
"deepseek-translit-armn",
|
|
166
190
|
]
|
|
167
191
|
|
|
168
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* Extract the source-name prefix from a `first_source_id` value.
|
|
194
|
+
*/
|
|
169
195
|
function sourceFromID(sourceID: string, knownPrefixes: readonly string[]): string {
|
|
170
196
|
// Sort longest-first so usgov-nad beats usgov, wof-admin beats wof.
|
|
171
|
-
const sorted = [...knownPrefixes].
|
|
197
|
+
const sorted = [...knownPrefixes].toSorted((a, b) => b.length - a.length)
|
|
172
198
|
|
|
173
199
|
for (const prefix of sorted) {
|
|
174
200
|
if (sourceID.startsWith(prefix + "-") || sourceID === prefix) return prefix
|
|
@@ -190,6 +216,7 @@ function manifestScan(corpusDir: string, knownPrefixes: readonly string[]): Shar
|
|
|
190
216
|
const manifestPath = join(corpusDir, "MANIFEST.json")
|
|
191
217
|
|
|
192
218
|
if (!existsSync(manifestPath)) return null
|
|
219
|
+
|
|
193
220
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as {
|
|
194
221
|
shards?: Array<{ split: string; source?: string | null; first_source_id?: string | null }>
|
|
195
222
|
}
|
|
@@ -203,6 +230,7 @@ function manifestScan(corpusDir: string, knownPrefixes: readonly string[]): Shar
|
|
|
203
230
|
bySplit[split] ??= {}
|
|
204
231
|
bySplit[split][src] = (bySplit[split][src] ?? 0) + 1
|
|
205
232
|
}
|
|
233
|
+
|
|
206
234
|
const total = Object.values(bySplit).reduce((sum, m) => sum + Object.values(m).reduce((a, b) => a + b, 0), 0)
|
|
207
235
|
|
|
208
236
|
return { bySplit, totalShards: total, totalFiles: total }
|
|
@@ -231,12 +259,14 @@ function buildAuditRows(stats: Record<string, number>, weights: Record<string, n
|
|
|
231
259
|
const effective = weight !== undefined ? shards * weight : 0
|
|
232
260
|
sampleWeights.push([src, effective])
|
|
233
261
|
}
|
|
262
|
+
|
|
234
263
|
const totalSampleWeight = sampleWeights.reduce((a, [, w]) => a + w, 0)
|
|
235
264
|
|
|
236
265
|
for (const src of allSources) {
|
|
237
266
|
const shards = stats[src] ?? 0
|
|
238
267
|
const weight = weights[src] ?? "—"
|
|
239
268
|
const effective = typeof weight === "number" ? (shards * weight) / Math.max(totalSampleWeight, 1) : "—"
|
|
269
|
+
|
|
240
270
|
rows.push({
|
|
241
271
|
source: src,
|
|
242
272
|
shards,
|
|
@@ -245,6 +275,7 @@ function buildAuditRows(stats: Record<string, number>, weights: Record<string, n
|
|
|
245
275
|
effectiveSamplePct: typeof effective === "number" ? effective : "—",
|
|
246
276
|
})
|
|
247
277
|
}
|
|
278
|
+
|
|
248
279
|
// Flag the dominator: empirically calibrated against the v0.3.0 → v0.4.0 retrospective.
|
|
249
280
|
// v0.3.0 had usgov-nad at 52% effective sample (1.9× ban); the resulting label-space dilution
|
|
250
281
|
// was responsible for the coarse-F1 regression. So flag a source as "concentration warning"
|
|
@@ -252,16 +283,21 @@ function buildAuditRows(stats: Record<string, number>, weights: Record<string, n
|
|
|
252
283
|
const numeric = rows.filter((r) => typeof r.effectiveSamplePct === "number") as Array<
|
|
253
284
|
AuditRow & { effectiveSamplePct: number }
|
|
254
285
|
>
|
|
286
|
+
|
|
255
287
|
numeric.sort((a, b) => b.effectiveSamplePct - a.effectiveSamplePct)
|
|
256
288
|
|
|
257
|
-
if (numeric.length
|
|
289
|
+
if (numeric.length) {
|
|
258
290
|
const top = numeric[0]!
|
|
259
291
|
const next = numeric[1]?.effectiveSamplePct ?? 0
|
|
260
292
|
|
|
261
|
-
if (
|
|
293
|
+
if (
|
|
294
|
+
top.effectiveSamplePct > DOMINANT_SOURCE_SHARE ||
|
|
295
|
+
(next > 0 && top.effectiveSamplePct / next > MAX_TOP_TO_RUNNER_UP_RATIO)
|
|
296
|
+
) {
|
|
262
297
|
top.overweightFactor = next > 0 ? top.effectiveSamplePct / next : Infinity
|
|
263
298
|
}
|
|
264
299
|
}
|
|
300
|
+
|
|
265
301
|
rows.sort((a, b) => b.shards - a.shards)
|
|
266
302
|
|
|
267
303
|
return rows
|
|
@@ -279,19 +315,24 @@ function printReport(corpusDir: string, configPath: string | undefined, stats: S
|
|
|
279
315
|
if (configPath) {
|
|
280
316
|
console.log(`Config: ${configPath}`)
|
|
281
317
|
}
|
|
318
|
+
|
|
282
319
|
console.log(
|
|
283
320
|
`Total shards: ${stats.totalShards}${stats.totalFiles !== stats.totalShards ? ` (${stats.totalFiles} files on disk)` : ""}`
|
|
284
321
|
)
|
|
285
322
|
console.log("")
|
|
323
|
+
|
|
286
324
|
const trainStats = stats.bySplit["train"]
|
|
287
325
|
|
|
288
326
|
if (trainStats) {
|
|
289
327
|
const total = Object.values(trainStats).reduce((a, b) => a + b, 0)
|
|
328
|
+
|
|
290
329
|
console.log(`Train split: ${total} shards`)
|
|
291
330
|
console.log("")
|
|
331
|
+
|
|
292
332
|
const headers = ["source", "shards", "shard %", "weight", "eff. sample %"]
|
|
293
333
|
const widths = [22, 8, 10, 8, 14]
|
|
294
334
|
const fmtRow = (cells: string[]) => cells.map((c, i) => c.padEnd(widths[i]!)).join(" ")
|
|
335
|
+
|
|
295
336
|
console.log(fmtRow(headers))
|
|
296
337
|
console.log(fmtRow(widths.map((w) => "─".repeat(w))))
|
|
297
338
|
|
|
@@ -306,12 +347,15 @@ function printReport(corpusDir: string, configPath: string | undefined, stats: S
|
|
|
306
347
|
])
|
|
307
348
|
)
|
|
308
349
|
}
|
|
350
|
+
|
|
309
351
|
console.log("")
|
|
352
|
+
|
|
310
353
|
const dominator = rows.find((r) => r.overweightFactor !== undefined)
|
|
311
354
|
|
|
312
355
|
if (dominator) {
|
|
313
356
|
const factor = dominator.overweightFactor
|
|
314
357
|
const factorStr = factor === Infinity ? "∞" : factor?.toFixed(1)
|
|
358
|
+
|
|
315
359
|
console.error(
|
|
316
360
|
`⚠ Concentration: ${dominator.source} would sample ${formatPct(dominator.effectiveSamplePct)} ` +
|
|
317
361
|
`of training rows (${factorStr}× the next-highest). ` +
|
|
@@ -321,17 +365,19 @@ function printReport(corpusDir: string, configPath: string | undefined, stats: S
|
|
|
321
365
|
} else {
|
|
322
366
|
console.log("✓ No single-source concentration (top source < 40% effective sample AND < 1.5× next).")
|
|
323
367
|
}
|
|
368
|
+
|
|
324
369
|
const missingWeights = rows.filter((r) => r.weight === "—" && r.shards > 0)
|
|
325
370
|
|
|
326
|
-
if (missingWeights.length
|
|
371
|
+
if (missingWeights.length && configPath) {
|
|
327
372
|
console.error(
|
|
328
373
|
`⚠ Sources present in corpus but absent from config.source_weights ` +
|
|
329
374
|
`(loader will skip them): ${missingWeights.map((r) => r.source).join(", ")}`
|
|
330
375
|
)
|
|
331
376
|
}
|
|
377
|
+
|
|
332
378
|
const orphanWeights = rows.filter((r) => typeof r.weight === "number" && r.shards === 0)
|
|
333
379
|
|
|
334
|
-
if (orphanWeights.length
|
|
380
|
+
if (orphanWeights.length) {
|
|
335
381
|
console.error(
|
|
336
382
|
`⚠ Sources weighted in config but no shards found in corpus ` +
|
|
337
383
|
`(no-op weights): ${orphanWeights.map((r) => r.source).join(", ")}`
|
|
@@ -82,6 +82,7 @@ n = min(len(tokens_col), ${limit ?? "len(tokens_col)"})
|
|
|
82
82
|
for i in range(n):
|
|
83
83
|
sys.stdout.write(json.dumps({"tokens": tokens_col[i], "labels": labels_col[i]}) + "\\n")
|
|
84
84
|
`
|
|
85
|
+
|
|
85
86
|
const buf = execSync(`python3`, { input: py, maxBuffer: 1024 * 1024 * 1024 })
|
|
86
87
|
const rows: Array<{ tokens: string[]; labels: string[] }> = []
|
|
87
88
|
|
|
@@ -95,6 +96,7 @@ for i in range(n):
|
|
|
95
96
|
|
|
96
97
|
export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
97
98
|
const shardPaths = discoverShards(args.shardsArg)
|
|
99
|
+
|
|
98
100
|
console.error(`Discovered ${shardPaths.length} parquet shard(s)`)
|
|
99
101
|
|
|
100
102
|
const tokenStats = new Map<string, Map<string, number>>()
|
|
@@ -103,6 +105,7 @@ export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
|
103
105
|
|
|
104
106
|
for (const path of shardPaths) {
|
|
105
107
|
console.error(`Reading ${path}...`)
|
|
108
|
+
|
|
106
109
|
const rows = streamShardRows(path, args.limitPerShard)
|
|
107
110
|
totalRows += rows.length
|
|
108
111
|
|
|
@@ -121,6 +124,7 @@ export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
|
121
124
|
labelMap = new Map()
|
|
122
125
|
tokenStats.set(tk, labelMap)
|
|
123
126
|
}
|
|
127
|
+
|
|
124
128
|
labelMap.set(lb, (labelMap.get(lb) ?? 0) + 1)
|
|
125
129
|
|
|
126
130
|
if (i + 1 < tokens.length) {
|
|
@@ -132,10 +136,12 @@ export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
|
132
136
|
bMap = new Map()
|
|
133
137
|
bigramStats.set(bigramKey, bMap)
|
|
134
138
|
}
|
|
139
|
+
|
|
135
140
|
bMap.set(bigramLabel, (bMap.get(bigramLabel) ?? 0) + 1)
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
143
|
}
|
|
144
|
+
|
|
139
145
|
console.error(
|
|
140
146
|
` ${rows.length} rows; running totals: ${tokenStats.size} unique tokens, ${bigramStats.size} unique bigrams`
|
|
141
147
|
)
|
|
@@ -155,9 +161,11 @@ export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
|
155
161
|
|
|
156
162
|
if (total < MIN_BIGRAM_COUNT) {
|
|
157
163
|
bigramStats.delete(k)
|
|
164
|
+
|
|
158
165
|
prunedBigrams++
|
|
159
166
|
}
|
|
160
167
|
}
|
|
168
|
+
|
|
161
169
|
console.error(`Pruned ${prunedBigrams} singleton bigrams; ${bigramStats.size} remain`)
|
|
162
170
|
|
|
163
171
|
const out = {
|
|
@@ -177,6 +185,7 @@ export function buildCorpusStats(args: CorpusStatsOptions): void {
|
|
|
177
185
|
|
|
178
186
|
writeFileSync(args.outputPath, JSON.stringify(out))
|
|
179
187
|
const sizeMB = (Buffer.byteLength(JSON.stringify(out)) / 1024 / 1024).toFixed(1)
|
|
188
|
+
|
|
180
189
|
console.error(
|
|
181
190
|
`Wrote ${args.outputPath} (${sizeMB} MB) — ${totalRows} rows, ${tokenStats.size} tokens, ${bigramStats.size} bigrams`
|
|
182
191
|
)
|
package/src/tools/fetch/ban.ts
CHANGED
|
@@ -29,6 +29,12 @@ import { sha256File } from "@mailwoman/core/utils"
|
|
|
29
29
|
import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
30
30
|
import { downloadToFile, loadManifestEntries, writeManifest } from "./download.ts"
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Bytes per KiB — the divisor for human-readable sizes, and the floor below which a "download" is an error page rather
|
|
34
|
+
* than data.
|
|
35
|
+
*/
|
|
36
|
+
const BYTES_PER_KIB = 1024
|
|
37
|
+
|
|
32
38
|
const BASE_URL = "https://adresse.data.gouv.fr/data/ban/adresses/latest/csv"
|
|
33
39
|
|
|
34
40
|
/**
|
|
@@ -150,15 +156,18 @@ export interface BanManifestEntry {
|
|
|
150
156
|
|
|
151
157
|
export type FetchBanOptions = BaseFetchOptions
|
|
152
158
|
|
|
153
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Mimic `numfmt --to=iec` for a friendly byte-size log line.
|
|
161
|
+
*/
|
|
154
162
|
function iec(bytes: number): string {
|
|
155
|
-
if (bytes <
|
|
163
|
+
if (bytes < BYTES_PER_KIB) return String(bytes)
|
|
156
164
|
const units = ["K", "M", "G", "T", "P"]
|
|
157
165
|
let value = bytes / 1024
|
|
158
166
|
let i = 0
|
|
159
167
|
|
|
160
|
-
while (value >=
|
|
168
|
+
while (value >= BYTES_PER_KIB && i < units.length - 1) {
|
|
161
169
|
value /= 1024
|
|
170
|
+
|
|
162
171
|
i++
|
|
163
172
|
}
|
|
164
173
|
|
|
@@ -195,7 +204,9 @@ export async function fetchBan(options: FetchBanOptions, report?: (line: string)
|
|
|
195
204
|
|
|
196
205
|
if (recordedSha && existingSha === recordedSha) {
|
|
197
206
|
report?.(` → already present + sha matches — skipping`)
|
|
207
|
+
|
|
198
208
|
skipped++
|
|
209
|
+
|
|
199
210
|
continue
|
|
200
211
|
}
|
|
201
212
|
|
|
@@ -212,40 +223,49 @@ export async function fetchBan(options: FetchBanOptions, report?: (line: string)
|
|
|
212
223
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
213
224
|
report,
|
|
214
225
|
})
|
|
215
|
-
} catch (
|
|
216
|
-
report?.(` ✗ download failed: ${url} (${(
|
|
226
|
+
} catch (error) {
|
|
227
|
+
report?.(` ✗ download failed: ${url} (${(error as Error).message})`)
|
|
228
|
+
|
|
217
229
|
failed++
|
|
218
230
|
failedCodes.push(code)
|
|
231
|
+
|
|
219
232
|
continue
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
// Guard against truncated 404/error pages.
|
|
223
236
|
const gzSize = statSync(gzFile).size
|
|
224
237
|
|
|
225
|
-
if (gzSize <
|
|
238
|
+
if (gzSize < BYTES_PER_KIB) {
|
|
226
239
|
report?.(` ✗ response too small (${gzSize} bytes) — probable 404 / error page`)
|
|
227
240
|
await unlink(gzFile)
|
|
241
|
+
|
|
228
242
|
failed++
|
|
229
243
|
failedCodes.push(code)
|
|
244
|
+
|
|
230
245
|
continue
|
|
231
246
|
}
|
|
232
247
|
|
|
233
248
|
// Decompress in-place; delete the .gz.
|
|
234
249
|
try {
|
|
235
250
|
await writeFile(csvFile, gunzipSync(await readFile(gzFile)))
|
|
236
|
-
} catch (
|
|
237
|
-
report?.(` ✗ decompress failed: ${(
|
|
251
|
+
} catch (error) {
|
|
252
|
+
report?.(` ✗ decompress failed: ${(error as Error).message}`)
|
|
238
253
|
await unlink(gzFile)
|
|
254
|
+
|
|
239
255
|
failed++
|
|
240
256
|
failedCodes.push(code)
|
|
257
|
+
|
|
241
258
|
continue
|
|
242
259
|
}
|
|
260
|
+
|
|
243
261
|
await unlink(gzFile)
|
|
244
262
|
|
|
245
263
|
if (!existsSync(csvFile)) {
|
|
246
264
|
report?.(` ✗ decompressed file not found at ${csvFile}`)
|
|
265
|
+
|
|
247
266
|
failed++
|
|
248
267
|
failedCodes.push(code)
|
|
268
|
+
|
|
249
269
|
continue
|
|
250
270
|
}
|
|
251
271
|
|
|
@@ -262,6 +282,7 @@ export async function fetchBan(options: FetchBanOptions, report?: (line: string)
|
|
|
262
282
|
})
|
|
263
283
|
|
|
264
284
|
report?.(` ✓ ${iec(bytes)} sha256=${sha}`)
|
|
285
|
+
|
|
265
286
|
fetched++
|
|
266
287
|
|
|
267
288
|
// Be a polite citizen — short pause between requests.
|
|
@@ -269,9 +290,10 @@ export async function fetchBan(options: FetchBanOptions, report?: (line: string)
|
|
|
269
290
|
}
|
|
270
291
|
|
|
271
292
|
// Write the consolidated MANIFEST.json (entries sorted by dept_code, codepoint order).
|
|
272
|
-
const sorted = [...entries.values()].
|
|
293
|
+
const sorted = [...entries.values()].toSorted((a, b) =>
|
|
273
294
|
a.dept_code < b.dept_code ? -1 : a.dept_code > b.dept_code ? 1 : 0
|
|
274
295
|
)
|
|
296
|
+
|
|
275
297
|
await writeManifest(manifestPath, sorted)
|
|
276
298
|
report?.(`Wrote ${manifestPath} with ${sorted.length} entries.`)
|
|
277
299
|
|
|
@@ -280,7 +302,7 @@ export async function fetchBan(options: FetchBanOptions, report?: (line: string)
|
|
|
280
302
|
report?.(`skipped: ${skipped} (already present + sha matched)`)
|
|
281
303
|
report?.(`failed: ${failed}`)
|
|
282
304
|
|
|
283
|
-
if (failedCodes.length
|
|
305
|
+
if (failedCodes.length) {
|
|
284
306
|
report?.(`failed codes: ${failedCodes.join(" ")}`)
|
|
285
307
|
}
|
|
286
308
|
|
|
@@ -12,13 +12,34 @@ import { existsSync } from "node:fs"
|
|
|
12
12
|
import { readFile, writeFile } from "node:fs/promises"
|
|
13
13
|
import { setTimeout as sleep } from "node:timers/promises"
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Rate limited — retryable, the server is asking us to back off.
|
|
17
|
+
*/
|
|
18
|
+
const HTTP_TOO_MANY_REQUESTS = 429
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Lowest 5xx status. Server-side failures are retryable; 4xx are not.
|
|
22
|
+
*/
|
|
23
|
+
const HTTP_SERVER_ERROR_MIN = 500
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Highest 5xx status.
|
|
27
|
+
*/
|
|
28
|
+
const HTTP_SERVER_ERROR_MAX = 599
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The option base every `mailwoman corpus fetch <source>` module extends.
|
|
32
|
+
*/
|
|
16
33
|
export interface BaseFetchOptions {
|
|
17
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Destination root for downloaded source data. Each source writes its own subdirectory.
|
|
36
|
+
*/
|
|
18
37
|
outRoot: string
|
|
19
38
|
}
|
|
20
39
|
|
|
21
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* The per-run result every fetch module returns; the command maps `failed > 0` to exit code 1.
|
|
42
|
+
*/
|
|
22
43
|
export interface FetchSummary {
|
|
23
44
|
fetched: number
|
|
24
45
|
skipped: number
|
|
@@ -26,19 +47,27 @@ export interface FetchSummary {
|
|
|
26
47
|
failedCodes: string[]
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* A status worth retrying: rate limiting or a server-side failure.
|
|
52
|
+
*/
|
|
30
53
|
export function isTransientStatus(status: number): boolean {
|
|
31
|
-
return status ===
|
|
54
|
+
return status === HTTP_TOO_MANY_REQUESTS || (status >= HTTP_SERVER_ERROR_MIN && status <= HTTP_SERVER_ERROR_MAX)
|
|
32
55
|
}
|
|
33
56
|
|
|
34
57
|
export interface DownloadOptions {
|
|
35
58
|
url: string
|
|
36
59
|
dest: string
|
|
37
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Per-attempt timeout. Default 10 minutes — these are multi-GB government dumps.
|
|
62
|
+
*/
|
|
38
63
|
timeoutMs?: number
|
|
39
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Extra attempts after the first, taken only on transient statuses or network errors. Default 0.
|
|
66
|
+
*/
|
|
40
67
|
retries?: number
|
|
41
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Delay between attempts. Default 5s.
|
|
70
|
+
*/
|
|
42
71
|
retryDelayMs?: number
|
|
43
72
|
headers?: Record<string, string>
|
|
44
73
|
report?: (line: string) => void
|
|
@@ -49,7 +78,7 @@ export interface DownloadOptions {
|
|
|
49
78
|
* or once retries are exhausted. Returns the byte count written.
|
|
50
79
|
*/
|
|
51
80
|
export async function downloadToFile(options: DownloadOptions): Promise<{ bytes: number }> {
|
|
52
|
-
const { url, dest, timeoutMs = 600_000, retries = 0, retryDelayMs =
|
|
81
|
+
const { url, dest, timeoutMs = 600_000, retries = 0, retryDelayMs = 5000, headers, report } = options
|
|
53
82
|
let lastError: unknown
|
|
54
83
|
|
|
55
84
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
@@ -65,6 +94,7 @@ export async function downloadToFile(options: DownloadOptions): Promise<{ bytes:
|
|
|
65
94
|
} catch (error) {
|
|
66
95
|
// AbortSignal timeouts and network-level failures are retryable.
|
|
67
96
|
lastError = error
|
|
97
|
+
|
|
68
98
|
continue
|
|
69
99
|
}
|
|
70
100
|
|
|
@@ -73,6 +103,7 @@ export async function downloadToFile(options: DownloadOptions): Promise<{ bytes:
|
|
|
73
103
|
|
|
74
104
|
if (!isTransientStatus(res.status)) throw error
|
|
75
105
|
lastError = error
|
|
106
|
+
|
|
76
107
|
continue
|
|
77
108
|
}
|
|
78
109
|
|
|
@@ -90,7 +121,9 @@ export async function downloadToFile(options: DownloadOptions): Promise<{ bytes:
|
|
|
90
121
|
throw lastError instanceof Error ? lastError : new Error(String(lastError))
|
|
91
122
|
}
|
|
92
123
|
|
|
93
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Read a MANIFEST.json; `null` when missing or corrupt (callers re-fetch from scratch).
|
|
126
|
+
*/
|
|
94
127
|
export async function readManifest<T>(path: string): Promise<T | null> {
|
|
95
128
|
if (!existsSync(path)) return null
|
|
96
129
|
|
|
@@ -101,7 +134,9 @@ export async function readManifest<T>(path: string): Promise<T | null> {
|
|
|
101
134
|
}
|
|
102
135
|
}
|
|
103
136
|
|
|
104
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* Load manifest entries into a map so untouched keys survive a partial re-fetch.
|
|
139
|
+
*/
|
|
105
140
|
export async function loadManifestEntries<T>(path: string, key: (entry: T) => string): Promise<Map<string, T>> {
|
|
106
141
|
const entries = new Map<string, T>()
|
|
107
142
|
const parsed = await readManifest<T[]>(path)
|
|
@@ -113,7 +148,9 @@ export async function loadManifestEntries<T>(path: string, key: (entry: T) => st
|
|
|
113
148
|
return entries
|
|
114
149
|
}
|
|
115
150
|
|
|
116
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* Write a MANIFEST.json in the house shape: pretty-printed, trailing newline.
|
|
153
|
+
*/
|
|
117
154
|
export async function writeManifest(path: string, manifest: unknown): Promise<void> {
|
|
118
155
|
await writeFile(path, JSON.stringify(manifest, null, 2) + "\n")
|
|
119
156
|
}
|
package/src/tools/fetch/hrsa.ts
CHANGED
|
@@ -41,6 +41,7 @@ export async function fetchHRSA(options: FetchHRSAOptions, report?: (line: strin
|
|
|
41
41
|
const dest = join(destDir, FILENAME)
|
|
42
42
|
|
|
43
43
|
report?.(`=== ${SLUG} / ${FILENAME}`)
|
|
44
|
+
|
|
44
45
|
const { bytes } = await downloadToFile({
|
|
45
46
|
url: SOURCE_URL,
|
|
46
47
|
dest,
|
|
@@ -48,6 +49,7 @@ export async function fetchHRSA(options: FetchHRSAOptions, report?: (line: strin
|
|
|
48
49
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
49
50
|
report,
|
|
50
51
|
})
|
|
52
|
+
|
|
51
53
|
const sha = await sha256File(dest)
|
|
52
54
|
|
|
53
55
|
const manifest: SourceManifest = {
|
|
@@ -57,6 +59,7 @@ export async function fetchHRSA(options: FetchHRSAOptions, report?: (line: strin
|
|
|
57
59
|
sha256: sha,
|
|
58
60
|
bytes,
|
|
59
61
|
}
|
|
62
|
+
|
|
60
63
|
await writeManifest(join(destDir, "MANIFEST.json"), manifest)
|
|
61
64
|
|
|
62
65
|
report?.(` ✓ ${(bytes / 1024 / 1024).toFixed(1)} MB sha256=${sha}`)
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* and sha matches MANIFEST, skips download.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
/* oxlint-disable sister-software/prefer-region-over-marks -- these markers label steps inside one
|
|
24
|
+
procedure, not sections of declarations. A region there folds nothing a reader wants folded. */
|
|
25
|
+
|
|
23
26
|
import { execFile } from "node:child_process"
|
|
24
27
|
import { existsSync, mkdirSync, statSync } from "node:fs"
|
|
25
28
|
import { rm } from "node:fs/promises"
|
|
@@ -31,10 +34,17 @@ import { sha256File } from "@mailwoman/core/utils"
|
|
|
31
34
|
import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
32
35
|
import { downloadToFile, readManifest, writeManifest } from "./download.ts"
|
|
33
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Bytes per KiB — the divisor for human-readable sizes, and the floor below which a "download" is an error page rather
|
|
39
|
+
* than data.
|
|
40
|
+
*/
|
|
41
|
+
const BYTES_PER_KIB = 1024
|
|
42
|
+
|
|
34
43
|
const execFileAsync = promisify(execFile)
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The PLS FY 2023 bulk CSV ZIP (most recent as of 2026-05). If IMLS publishes a newer year, update this URL.
|
|
47
|
+
*/
|
|
38
48
|
const ZIP_URL = "https://www.imls.gov/sites/default/files/2025-08/pls_fy2023_csv.zip"
|
|
39
49
|
const SLUG = "usgov-imls-pls"
|
|
40
50
|
|
|
@@ -48,7 +58,9 @@ interface SourceManifest {
|
|
|
48
58
|
bytes: number
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Return the filenames listed inside a ZIP (the trailing column of each `unzip -l` row).
|
|
63
|
+
*/
|
|
52
64
|
async function listZipEntries(zipPath: string): Promise<string[]> {
|
|
53
65
|
const listing = await execFileAsync("unzip", ["-l", zipPath])
|
|
54
66
|
|
|
@@ -70,9 +82,8 @@ export async function fetchIMLSPLS(
|
|
|
70
82
|
|
|
71
83
|
report?.(`=== ${SLUG}`)
|
|
72
84
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
// ------------------------------------------------------------------
|
|
85
|
+
// MARK: Idempotency check
|
|
86
|
+
|
|
76
87
|
const recorded = await readManifest<Partial<SourceManifest>>(manifestPath)
|
|
77
88
|
|
|
78
89
|
if (recorded?.sha256 && recorded.filename) {
|
|
@@ -85,10 +96,10 @@ export async function fetchIMLSPLS(
|
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
// ------------------------------------------------------------------
|
|
99
|
+
// MARK: Download ZIP
|
|
100
|
+
|
|
91
101
|
report?.(` Downloading ${ZIP_URL} ...`)
|
|
102
|
+
|
|
92
103
|
const { bytes: zipSize } = await downloadToFile({
|
|
93
104
|
url: ZIP_URL,
|
|
94
105
|
dest: zipDest,
|
|
@@ -96,19 +107,18 @@ export async function fetchIMLSPLS(
|
|
|
96
107
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
97
108
|
report,
|
|
98
109
|
})
|
|
110
|
+
|
|
99
111
|
report?.(` Downloaded: ${(zipSize / 1024 / 1024).toFixed(1)} MB`)
|
|
100
112
|
|
|
101
|
-
if (zipSize <
|
|
113
|
+
if (zipSize < BYTES_PER_KIB) {
|
|
102
114
|
report?.(` ✗ Response too small (${zipSize} bytes) — probable error page`)
|
|
103
115
|
|
|
104
116
|
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] }
|
|
105
117
|
}
|
|
106
118
|
|
|
107
|
-
// ------------------------------------------------------------------
|
|
108
119
|
// Discover the outlet-level CSV inside the ZIP.
|
|
109
120
|
// Outlet files match: pls_fy*outlet*.csv (case-insensitive)
|
|
110
121
|
// Administrative-entity files match: pls_fy*ae*.csv — we skip those.
|
|
111
|
-
// ------------------------------------------------------------------
|
|
112
122
|
report?.(" Inspecting ZIP contents ...")
|
|
113
123
|
const entries = await listZipEntries(zipDest)
|
|
114
124
|
|
|
@@ -125,6 +135,7 @@ export async function fetchIMLSPLS(
|
|
|
125
135
|
for (const name of entries) {
|
|
126
136
|
report?.(` ${name}`)
|
|
127
137
|
}
|
|
138
|
+
|
|
128
139
|
report?.(" ✗ Could not identify outlet CSV — inspect above listing and update this module")
|
|
129
140
|
|
|
130
141
|
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] }
|
|
@@ -137,15 +148,13 @@ export async function fetchIMLSPLS(
|
|
|
137
148
|
const csvSize = statSync(csvDest).size
|
|
138
149
|
const csvSha = await sha256File(csvDest)
|
|
139
150
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
// ------------------------------------------------------------------
|
|
151
|
+
// MARK: Remove ZIP (small, but keep destDir clean)
|
|
152
|
+
|
|
143
153
|
await rm(zipDest, { force: true })
|
|
144
154
|
report?.(" Removed ZIP (CSV kept)")
|
|
145
155
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
// ------------------------------------------------------------------
|
|
156
|
+
// MARK: Write MANIFEST
|
|
157
|
+
|
|
149
158
|
const manifest: SourceManifest = {
|
|
150
159
|
source_url: ZIP_URL,
|
|
151
160
|
downloaded_at: new Date().toISOString(),
|
|
@@ -153,6 +162,7 @@ export async function fetchIMLSPLS(
|
|
|
153
162
|
sha256: csvSha,
|
|
154
163
|
bytes: csvSize,
|
|
155
164
|
}
|
|
165
|
+
|
|
156
166
|
await writeManifest(manifestPath, manifest)
|
|
157
167
|
|
|
158
168
|
report?.(` ✓ ${(csvSize / 1024 / 1024).toFixed(1)} MB sha256=${csvSha}`)
|