@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/fetch/index.ts
CHANGED
|
@@ -105,7 +105,9 @@ export * from "./state-hi-schools.ts"
|
|
|
105
105
|
export * from "./state-sources.ts"
|
|
106
106
|
export * from "./tiger-full.ts"
|
|
107
107
|
|
|
108
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* The fetch-source registry: id → module entry point. Each entry point takes its own options interface.
|
|
110
|
+
*/
|
|
109
111
|
export const FETCH_SOURCES = {
|
|
110
112
|
ban: fetchBan,
|
|
111
113
|
nad: fetchNAD,
|
package/src/tools/fetch/nad.ts
CHANGED
|
@@ -46,23 +46,38 @@ import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
|
46
46
|
import { downloadToFile, readManifest, writeManifest } from "./download.ts"
|
|
47
47
|
|
|
48
48
|
const SLUG = "usgov-nad"
|
|
49
|
+
|
|
49
50
|
const FEATURE_SERVICE_URL =
|
|
50
51
|
"https://services.arcgis.com/xOi1kZaI0eWDREZv/ArcGIS/rest/services/Address_Points_from_National_Address_Database_view/FeatureServer/0"
|
|
51
52
|
|
|
52
53
|
export interface FetchNADOptions extends BaseFetchOptions {
|
|
53
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Fetch strategy. Default `featureserver`.
|
|
56
|
+
*/
|
|
54
57
|
mode?: "featureserver" | "bulk"
|
|
55
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Pre-signed S3 URL for bulk mode.
|
|
60
|
+
*/
|
|
56
61
|
nadURL?: string
|
|
57
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Records per output file. Default `100000`.
|
|
64
|
+
*/
|
|
58
65
|
chunkSize?: number
|
|
59
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Records per HTTP request. Default `5000`.
|
|
68
|
+
*/
|
|
60
69
|
pageSize?: number
|
|
61
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Parallel page fetches within a chunk. Default `4`.
|
|
72
|
+
*/
|
|
62
73
|
concurrency?: number
|
|
63
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Start OBJECTID. Default `1`.
|
|
76
|
+
*/
|
|
64
77
|
startOID?: number
|
|
65
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Stop before this OID. Default = total count.
|
|
80
|
+
*/
|
|
66
81
|
endOID?: number
|
|
67
82
|
}
|
|
68
83
|
|
|
@@ -138,7 +153,9 @@ async function fetchChunk(
|
|
|
138
153
|
rows: [],
|
|
139
154
|
error: null,
|
|
140
155
|
}))
|
|
156
|
+
|
|
141
157
|
let nextSlot = 0
|
|
158
|
+
|
|
142
159
|
const workers = Array.from({ length: Math.min(concurrency, pageRanges.length) }, async () => {
|
|
143
160
|
while (true) {
|
|
144
161
|
const slot = nextSlot++
|
|
@@ -148,12 +165,13 @@ async function fetchChunk(
|
|
|
148
165
|
|
|
149
166
|
try {
|
|
150
167
|
pageResults[slot]!.rows = await fetchPage(s, e, pageSize)
|
|
151
|
-
} catch (
|
|
152
|
-
pageResults[slot]!.error =
|
|
153
|
-
report?.(` ✗ page ${s}-${e}: ${(
|
|
168
|
+
} catch (error) {
|
|
169
|
+
pageResults[slot]!.error = error as Error
|
|
170
|
+
report?.(` ✗ page ${s}-${e}: ${(error as Error).message}`)
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
})
|
|
174
|
+
|
|
157
175
|
await Promise.all(workers)
|
|
158
176
|
|
|
159
177
|
// Single-writer phase — write all pages in OID order to keep NDJSON deterministic.
|
|
@@ -163,6 +181,7 @@ async function fetchChunk(
|
|
|
163
181
|
for (const { rows, error } of pageResults) {
|
|
164
182
|
if (error) {
|
|
165
183
|
errors++
|
|
184
|
+
|
|
166
185
|
continue
|
|
167
186
|
}
|
|
168
187
|
|
|
@@ -170,14 +189,15 @@ async function fetchChunk(
|
|
|
170
189
|
lines.push(JSON.stringify(row))
|
|
171
190
|
}
|
|
172
191
|
}
|
|
173
|
-
|
|
192
|
+
|
|
193
|
+
await writeFile(chunkPath, !lines.length ? "" : lines.join("\n") + "\n")
|
|
174
194
|
|
|
175
195
|
return { recordCount: lines.length, errors }
|
|
176
196
|
}
|
|
177
197
|
|
|
178
198
|
async function featureserverMode(options: FetchNADOptions, report?: (line: string) => void): Promise<FetchSummary> {
|
|
179
199
|
const chunkSize = options.chunkSize ?? 100_000
|
|
180
|
-
const pageSize = options.pageSize ??
|
|
200
|
+
const pageSize = options.pageSize ?? 5000
|
|
181
201
|
const concurrency = options.concurrency ?? 4
|
|
182
202
|
const startOID = options.startOID ?? 1
|
|
183
203
|
|
|
@@ -211,6 +231,7 @@ async function featureserverMode(options: FetchNADOptions, report?: (line: strin
|
|
|
211
231
|
|
|
212
232
|
if (recorded?.complete) {
|
|
213
233
|
skipped++
|
|
234
|
+
|
|
214
235
|
continue
|
|
215
236
|
}
|
|
216
237
|
}
|
|
@@ -233,12 +254,15 @@ async function featureserverMode(options: FetchNADOptions, report?: (line: strin
|
|
|
233
254
|
page_errors: errors,
|
|
234
255
|
complete: errors === 0,
|
|
235
256
|
}
|
|
257
|
+
|
|
236
258
|
await writeManifest(manifestPath, manifest)
|
|
237
259
|
|
|
238
260
|
const status = errors === 0 ? "✓" : `⚠ ${errors} page errors`
|
|
261
|
+
|
|
239
262
|
report?.(
|
|
240
263
|
` ${status} ${recordCount.toLocaleString()} records in ${elapsed}s (${(bytes / 1024 / 1024).toFixed(1)} MB)`
|
|
241
264
|
)
|
|
265
|
+
|
|
242
266
|
fetched++
|
|
243
267
|
totalRecords += recordCount
|
|
244
268
|
totalErrors += errors
|
|
@@ -266,6 +290,7 @@ async function bulkMode(options: FetchNADOptions, report?: (line: string) => voi
|
|
|
266
290
|
`accept the disclaimer, and re-run with the pre-signed S3 URL.`
|
|
267
291
|
)
|
|
268
292
|
}
|
|
293
|
+
|
|
269
294
|
const destDir = join(options.outRoot, SLUG)
|
|
270
295
|
mkdirSync(destDir, { recursive: true })
|
|
271
296
|
const filename = new URL(options.nadURL).pathname.split("/").pop() ?? "NAD.zip"
|
|
@@ -277,6 +302,7 @@ async function bulkMode(options: FetchNADOptions, report?: (line: string) => voi
|
|
|
277
302
|
const { bytes } = await downloadToFile({ url: options.nadURL, dest: destPath, timeoutMs: 3 * 3600 * 1000, report })
|
|
278
303
|
|
|
279
304
|
const sha = await sha256File(destPath)
|
|
305
|
+
|
|
280
306
|
await writeManifest(join(destDir, "MANIFEST.json"), {
|
|
281
307
|
source_url: options.nadURL,
|
|
282
308
|
downloaded_at: new Date().toISOString(),
|
|
@@ -284,6 +310,7 @@ async function bulkMode(options: FetchNADOptions, report?: (line: string) => voi
|
|
|
284
310
|
sha256: sha,
|
|
285
311
|
bytes,
|
|
286
312
|
})
|
|
313
|
+
|
|
287
314
|
report?.(` ✓ ${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB sha256=${sha}`)
|
|
288
315
|
|
|
289
316
|
return { fetched: 1, skipped: 0, failed: 0, failedCodes: [] }
|
package/src/tools/fetch/nppes.ts
CHANGED
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
* sha256 matches MANIFEST, skips download.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
/* oxlint-disable sister-software/prefer-region-over-marks -- these markers label steps inside one
|
|
25
|
+
procedure, not sections of declarations. A region there folds nothing a reader wants folded. */
|
|
26
|
+
|
|
24
27
|
import { execFile } from "node:child_process"
|
|
25
28
|
import { existsSync, mkdirSync, statSync } from "node:fs"
|
|
26
29
|
import { rm } from "node:fs/promises"
|
|
@@ -70,7 +73,9 @@ async function discoverLatestZip(): Promise<string | undefined> {
|
|
|
70
73
|
return undefined
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Extract the main registry CSV name (npidata_pfile_*.csv) from a ZIP's `unzip -l` listing.
|
|
78
|
+
*/
|
|
74
79
|
async function findNpidataCSV(zipPath: string): Promise<string | undefined> {
|
|
75
80
|
const listing = await execFileAsync("unzip", ["-l", zipPath])
|
|
76
81
|
|
|
@@ -103,10 +108,8 @@ export async function fetchNPPES(options: FetchNPPESOptions, report?: (line: str
|
|
|
103
108
|
const zipDest = join(destDir, zipFilename)
|
|
104
109
|
report?.(` Latest full file: ${zipFilename}`)
|
|
105
110
|
|
|
106
|
-
// ------------------------------------------------------------------
|
|
107
111
|
// Idempotency check: if the main CSV already exists and sha matches,
|
|
108
112
|
// skip re-download.
|
|
109
|
-
// ------------------------------------------------------------------
|
|
110
113
|
const recorded = await readManifest<Partial<SourceManifest>>(manifestPath)
|
|
111
114
|
|
|
112
115
|
if (recorded?.sha256 && recorded.filename) {
|
|
@@ -119,10 +122,10 @@ export async function fetchNPPES(options: FetchNPPESOptions, report?: (line: str
|
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
// ------------------------------------------------------------------
|
|
125
|
+
// MARK: Download ZIP (large; 60-minute timeout)
|
|
126
|
+
|
|
125
127
|
report?.(` Downloading ${zipURL} ...`)
|
|
128
|
+
|
|
126
129
|
const { bytes: zipSize } = await downloadToFile({
|
|
127
130
|
url: zipURL,
|
|
128
131
|
dest: zipDest,
|
|
@@ -130,11 +133,11 @@ export async function fetchNPPES(options: FetchNPPESOptions, report?: (line: str
|
|
|
130
133
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
131
134
|
report,
|
|
132
135
|
})
|
|
136
|
+
|
|
133
137
|
report?.(` Downloaded: ${(zipSize / 1024 / 1024).toFixed(1)} MB`)
|
|
134
138
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
// ------------------------------------------------------------------
|
|
139
|
+
// MARK: Extract only the main registry CSV (npidata_pfile_*.csv)
|
|
140
|
+
|
|
138
141
|
report?.(" Extracting npidata_pfile CSV from ZIP ...")
|
|
139
142
|
const csvName = await findNpidataCSV(zipDest)
|
|
140
143
|
|
|
@@ -152,15 +155,13 @@ export async function fetchNPPES(options: FetchNPPESOptions, report?: (line: str
|
|
|
152
155
|
const csvSha = await sha256File(csvDest)
|
|
153
156
|
report?.(` CSV size: ${(csvSize / 1024 / 1024).toFixed(1)} MB`)
|
|
154
157
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
// ------------------------------------------------------------------
|
|
158
|
+
// MARK: Remove the ZIP to reclaim ~1 GB
|
|
159
|
+
|
|
158
160
|
await rm(zipDest, { force: true })
|
|
159
161
|
report?.(" Removed ZIP (CSV kept)")
|
|
160
162
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
// ------------------------------------------------------------------
|
|
163
|
+
// MARK: Write MANIFEST (records the extracted CSV, not the ZIP)
|
|
164
|
+
|
|
164
165
|
const manifest: SourceManifest = {
|
|
165
166
|
source_url: zipURL,
|
|
166
167
|
downloaded_at: new Date().toISOString(),
|
|
@@ -168,6 +169,7 @@ export async function fetchNPPES(options: FetchNPPESOptions, report?: (line: str
|
|
|
168
169
|
sha256: csvSha,
|
|
169
170
|
bytes: csvSize,
|
|
170
171
|
}
|
|
172
|
+
|
|
171
173
|
await writeManifest(manifestPath, manifest)
|
|
172
174
|
|
|
173
175
|
report?.(` ✓ ${(csvSize / 1024 / 1024).toFixed(1)} MB sha256=${csvSha}`)
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
* ```
|
|
52
52
|
*/
|
|
53
53
|
|
|
54
|
+
/* oxlint-disable sister-software/prefer-region-over-marks -- these markers label steps inside one
|
|
55
|
+
procedure, not sections of declarations. A region there folds nothing a reader wants folded. */
|
|
56
|
+
|
|
54
57
|
import { execFile, spawn } from "node:child_process"
|
|
55
58
|
import { createReadStream, createWriteStream, existsSync, mkdirSync, renameSync, rmSync, statSync } from "node:fs"
|
|
56
59
|
import { join } from "node:path"
|
|
@@ -65,6 +68,22 @@ import { sha256File } from "@mailwoman/core/utils"
|
|
|
65
68
|
import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
66
69
|
import { isTransientStatus, writeManifest } from "./download.ts"
|
|
67
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Bytes per KiB — the divisor for human-readable sizes, and the floor below which a "download" is an error page rather
|
|
73
|
+
* than data.
|
|
74
|
+
*/
|
|
75
|
+
/**
|
|
76
|
+
* A successful fetch; anything else is an error page or a redirect we did not follow.
|
|
77
|
+
*/
|
|
78
|
+
const HTTP_OK = 200
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Smallest plausible OpenAddresses shard. Below 10 KiB the file is a stub or an error body.
|
|
82
|
+
*/
|
|
83
|
+
const MIN_PLAUSIBLE_SHARD_BYTES = 10_240
|
|
84
|
+
|
|
85
|
+
const BYTES_PER_KIB = 1024
|
|
86
|
+
|
|
68
87
|
const execFileAsync = promisify(execFile)
|
|
69
88
|
|
|
70
89
|
const OA_BASE = "https://batch.openaddresses.io"
|
|
@@ -83,7 +102,9 @@ const OA_COLLECTION_IDS: Record<string, number> = {
|
|
|
83
102
|
}
|
|
84
103
|
|
|
85
104
|
export interface FetchOpenAddressesOptions extends BaseFetchOptions {
|
|
86
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* OA country collection code. Default `ca`.
|
|
107
|
+
*/
|
|
87
108
|
country?: string
|
|
88
109
|
}
|
|
89
110
|
|
|
@@ -94,13 +115,15 @@ interface OaCollection {
|
|
|
94
115
|
size?: number
|
|
95
116
|
}
|
|
96
117
|
|
|
97
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Stream-count newlines, matching `wc -l` (memory-safe for the multi-GB collection).
|
|
120
|
+
*/
|
|
98
121
|
async function countLines(path: string): Promise<number> {
|
|
99
122
|
let count = 0
|
|
100
123
|
|
|
101
124
|
for await (const chunk of createReadStream(path) as AsyncIterable<Buffer>) {
|
|
102
|
-
for (
|
|
103
|
-
if (
|
|
125
|
+
for (const byte of chunk) {
|
|
126
|
+
if (byte === 0x0a) {
|
|
104
127
|
count++
|
|
105
128
|
}
|
|
106
129
|
}
|
|
@@ -114,8 +137,9 @@ function humanBytes(bytes: number): string {
|
|
|
114
137
|
let value = bytes
|
|
115
138
|
let unit = 0
|
|
116
139
|
|
|
117
|
-
while (value >=
|
|
140
|
+
while (value >= BYTES_PER_KIB && unit < units.length - 1) {
|
|
118
141
|
value /= 1024
|
|
142
|
+
|
|
119
143
|
unit++
|
|
120
144
|
}
|
|
121
145
|
|
|
@@ -154,6 +178,7 @@ async function streamDownload(url: string, dest: string, opts: StreamDownloadOpt
|
|
|
154
178
|
|
|
155
179
|
if (attempt < opts.retries && isTransientStatus(res.status)) {
|
|
156
180
|
await sleep(opts.retryDelayMs)
|
|
181
|
+
|
|
157
182
|
continue
|
|
158
183
|
}
|
|
159
184
|
|
|
@@ -161,6 +186,7 @@ async function streamDownload(url: string, dest: string, opts: StreamDownloadOpt
|
|
|
161
186
|
} catch {
|
|
162
187
|
if (attempt < opts.retries) {
|
|
163
188
|
await sleep(opts.retryDelayMs)
|
|
189
|
+
|
|
164
190
|
continue
|
|
165
191
|
}
|
|
166
192
|
|
|
@@ -171,12 +197,16 @@ async function streamDownload(url: string, dest: string, opts: StreamDownloadOpt
|
|
|
171
197
|
return 0
|
|
172
198
|
}
|
|
173
199
|
|
|
174
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Decompress `src` → `dest` with the same deprioritized subprocess the old fetcher used.
|
|
202
|
+
*/
|
|
175
203
|
async function gunzipToFile(src: string, dest: string): Promise<void> {
|
|
176
204
|
const child = spawn("nice", ["-n", "15", "ionice", "-c", "3", "gunzip", "-c", src], {
|
|
177
205
|
stdio: ["ignore", "pipe", "inherit"],
|
|
178
206
|
})
|
|
207
|
+
|
|
179
208
|
await pipeline(child.stdout!, createWriteStream(dest))
|
|
209
|
+
|
|
180
210
|
await new Promise<void>((resolve, reject) => {
|
|
181
211
|
child.on("close", (code) => (code === 0 ? resolve() : reject(new Error(`gunzip exited with code ${code}`))))
|
|
182
212
|
child.on("error", reject)
|
|
@@ -201,9 +231,8 @@ export async function fetchOpenAddresses(
|
|
|
201
231
|
|
|
202
232
|
mkdirSync(destDir, { recursive: true })
|
|
203
233
|
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
// -------------------------------------------------------------------------
|
|
234
|
+
// MARK: Authentication check
|
|
235
|
+
|
|
207
236
|
if (!token) {
|
|
208
237
|
report?.(`
|
|
209
238
|
ERROR: OA_BATCH_TOKEN is not set.
|
|
@@ -227,13 +256,13 @@ The Canada collection (ca) is ~2 GiB compressed / ~7 GiB uncompressed
|
|
|
227
256
|
return fail("OA_BATCH_TOKEN")
|
|
228
257
|
}
|
|
229
258
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
// -------------------------------------------------------------------------
|
|
259
|
+
// MARK: Determine collection ID
|
|
260
|
+
|
|
233
261
|
let collectionID = OA_COLLECTION_IDS[country]
|
|
234
262
|
|
|
235
263
|
if (collectionID === undefined) {
|
|
236
264
|
report?.(`Unknown country code '${country}'. Fetching collection list to find ID...`)
|
|
265
|
+
|
|
237
266
|
const res = await fetch(`${OA_BASE}/api/collections`, {
|
|
238
267
|
headers: { Authorization: `Bearer ${token}`, "Accept-Encoding": "gzip, br" },
|
|
239
268
|
signal: AbortSignal.timeout(30_000),
|
|
@@ -264,9 +293,8 @@ The Canada collection (ca) is ~2 GiB compressed / ~7 GiB uncompressed
|
|
|
264
293
|
report?.(` Found collection id=${collectionID} for '${country}'`)
|
|
265
294
|
}
|
|
266
295
|
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
// -------------------------------------------------------------------------
|
|
296
|
+
// MARK: Download via the collections download endpoint (302s to S3)
|
|
297
|
+
|
|
270
298
|
report?.(` Resolving download URL for collection id=${collectionID}...`)
|
|
271
299
|
report?.(` Attempting authenticated download...`)
|
|
272
300
|
|
|
@@ -281,7 +309,7 @@ The Canada collection (ca) is ~2 GiB compressed / ~7 GiB uncompressed
|
|
|
281
309
|
retryDelayMs: 30_000,
|
|
282
310
|
})
|
|
283
311
|
|
|
284
|
-
if (httpStatus !==
|
|
312
|
+
if (httpStatus !== HTTP_OK) {
|
|
285
313
|
// Try the geojsonl.gz directly with token as query param (alternate URL shape).
|
|
286
314
|
httpStatus = await streamDownload(`${OA_BASE}/api/collections/${collectionID}/geojsonl.gz?token=${token}`, tmpGz, {
|
|
287
315
|
timeoutMs: 7_200_000,
|
|
@@ -290,8 +318,9 @@ The Canada collection (ca) is ~2 GiB compressed / ~7 GiB uncompressed
|
|
|
290
318
|
})
|
|
291
319
|
}
|
|
292
320
|
|
|
293
|
-
if (httpStatus !==
|
|
321
|
+
if (httpStatus !== HTTP_OK) {
|
|
294
322
|
rmSync(tmpGz, { force: true })
|
|
323
|
+
|
|
295
324
|
report?.(`
|
|
296
325
|
ERROR: Download returned HTTP ${httpStatus}.
|
|
297
326
|
|
|
@@ -312,9 +341,8 @@ URL tried: ${OA_BASE}/api/collections/${collectionID}/download
|
|
|
312
341
|
return fail(country)
|
|
313
342
|
}
|
|
314
343
|
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
// -------------------------------------------------------------------------
|
|
344
|
+
// MARK: Decompress if the downloaded file is gzipped
|
|
345
|
+
|
|
318
346
|
const fileMagic = (await execFileAsync("file", ["--brief", tmpGz]).catch(() => ({ stdout: "" }))).stdout
|
|
319
347
|
|
|
320
348
|
if (/gzip|compressed/i.test(fileMagic)) {
|
|
@@ -332,9 +360,8 @@ URL tried: ${OA_BASE}/api/collections/${collectionID}/download
|
|
|
332
360
|
report?.(` WARNING: Downloaded file type is '${fileMagic.trim()}' — may need manual decompression.`)
|
|
333
361
|
}
|
|
334
362
|
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
// -------------------------------------------------------------------------
|
|
363
|
+
// MARK: Verify + write MANIFEST
|
|
364
|
+
|
|
338
365
|
if (!existsSync(outputFile)) {
|
|
339
366
|
report?.(`ERROR: Output file not found at ${outputFile} after download.`)
|
|
340
367
|
|
|
@@ -343,7 +370,7 @@ URL tried: ${OA_BASE}/api/collections/${collectionID}/download
|
|
|
343
370
|
|
|
344
371
|
const size = statSync(outputFile).size
|
|
345
372
|
|
|
346
|
-
if (size <
|
|
373
|
+
if (size < MIN_PLAUSIBLE_SHARD_BYTES) {
|
|
347
374
|
report?.(`ERROR: File is suspiciously small (${size} bytes) — likely an error response.`)
|
|
348
375
|
|
|
349
376
|
return fail(country)
|
|
@@ -365,6 +392,7 @@ URL tried: ${OA_BASE}/api/collections/${collectionID}/download
|
|
|
365
392
|
notes:
|
|
366
393
|
"batch.openaddresses.io requires a free registered account for downloads. License is mixed per-row; use the openaddresses adapter with allowShareAlike=false (default) to filter Tier-C rows.",
|
|
367
394
|
}
|
|
395
|
+
|
|
368
396
|
await writeManifest(manifestPath, manifest)
|
|
369
397
|
|
|
370
398
|
report?.(` ✓ ${humanBytes(size)} rows=${rowCount} sha256=${sha}`)
|
package/src/tools/fetch/ppd.ts
CHANGED
|
@@ -48,25 +48,36 @@ export async function extractPPDTuples(
|
|
|
48
48
|
write("NUMBER,STREET,CITY,DISTRICT,REGION,POSTCODE")
|
|
49
49
|
|
|
50
50
|
for await (const cells of input) {
|
|
51
|
-
|
|
51
|
+
// HM Land Registry Price Paid Data column order (see the PPD FAQ for the full field list).
|
|
52
|
+
const postcode = cells[3]
|
|
53
|
+
const paon = cells[7]
|
|
54
|
+
const saon = cells[8]
|
|
55
|
+
const street = cells[9]
|
|
56
|
+
const locality = cells[10]
|
|
57
|
+
const town = cells[11]
|
|
58
|
+
const county = cells[13]
|
|
52
59
|
|
|
53
60
|
if (saon) {
|
|
54
61
|
stats.skippedSAON++
|
|
62
|
+
|
|
55
63
|
continue
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
if (!paon || !HOUSE_NUMBER_PATTERN.test(paon)) {
|
|
59
67
|
stats.skippedPAON++
|
|
68
|
+
|
|
60
69
|
continue
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
if (!street) {
|
|
64
73
|
stats.skippedNoStreet++
|
|
74
|
+
|
|
65
75
|
continue
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
if (!postcode) {
|
|
69
79
|
stats.skippedNoPostcode++
|
|
80
|
+
|
|
70
81
|
continue
|
|
71
82
|
}
|
|
72
83
|
|
|
@@ -83,13 +94,16 @@ export async function extractPPDTuples(
|
|
|
83
94
|
postcode,
|
|
84
95
|
].join(",")
|
|
85
96
|
)
|
|
97
|
+
|
|
86
98
|
stats.kept++
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
return stats
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Stream `inputPath` (PPD CSV) → `outputPath` (OA-shaped tuples CSV), returning the row-count stats.
|
|
106
|
+
*/
|
|
93
107
|
export async function runPPDExtract(inputPath: string, outputPath: string): Promise<PPDExtractStats> {
|
|
94
108
|
// No `encoding` — CSVSpliterator delimits raw bytes and decodes utf-8 itself (see readTuples in
|
|
95
109
|
// shard-recipes/locale.ts). `header: false` yields every row as data — PPD ships no header row.
|
|
@@ -98,10 +112,13 @@ export async function runPPDExtract(inputPath: string, outputPath: string): Prom
|
|
|
98
112
|
header: false,
|
|
99
113
|
enableQuoteHandling: true,
|
|
100
114
|
})
|
|
115
|
+
|
|
101
116
|
const out = createWriteStream(outputPath, { encoding: "utf8" })
|
|
102
117
|
const stats = await extractPPDTuples(rows, (line) => out.write(line + "\n"))
|
|
103
118
|
|
|
104
|
-
await new Promise<void>((res) =>
|
|
119
|
+
await new Promise<void>((res) => {
|
|
120
|
+
out.end(res)
|
|
121
|
+
})
|
|
105
122
|
|
|
106
123
|
return stats
|
|
107
124
|
}
|
|
@@ -113,6 +130,8 @@ runIfScript(import.meta, async () => {
|
|
|
113
130
|
output: { type: "string", default: dataRootPath("ppd", "2026-07-22", "gb-tuples.csv") },
|
|
114
131
|
},
|
|
115
132
|
})
|
|
133
|
+
|
|
116
134
|
const stats = await runPPDExtract(values.input!, values.output!)
|
|
135
|
+
|
|
117
136
|
console.log(`[ppd] ${JSON.stringify(stats)}`)
|
|
118
137
|
})
|
|
@@ -30,6 +30,12 @@ import { sha256File } from "@mailwoman/core/utils"
|
|
|
30
30
|
import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
31
31
|
import { downloadToFile, readManifest, writeManifest } from "./download.ts"
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Bytes per KiB — the divisor for human-readable sizes, and the floor below which a "download" is an error page rather
|
|
35
|
+
* than data.
|
|
36
|
+
*/
|
|
37
|
+
const BYTES_PER_KIB = 1024
|
|
38
|
+
|
|
33
39
|
const SOURCE_URL = "https://www.hawaiipublicschools.org/DOE%20Forms/SchoolList.xlsx"
|
|
34
40
|
const SLUG = "state-hi-schools"
|
|
35
41
|
const CSV_FILENAME = "HI_Public_Schools_List.csv"
|
|
@@ -90,15 +96,18 @@ interface Manifest {
|
|
|
90
96
|
notes: string
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Mimic `numfmt --to=iec` for a friendly byte-size log line.
|
|
101
|
+
*/
|
|
94
102
|
function iec(bytes: number): string {
|
|
95
|
-
if (bytes <
|
|
103
|
+
if (bytes < BYTES_PER_KIB) return String(bytes)
|
|
96
104
|
const units = ["K", "M", "G", "T", "P"]
|
|
97
105
|
let value = bytes / 1024
|
|
98
106
|
let i = 0
|
|
99
107
|
|
|
100
|
-
while (value >=
|
|
108
|
+
while (value >= BYTES_PER_KIB && i < units.length - 1) {
|
|
101
109
|
value /= 1024
|
|
110
|
+
|
|
102
111
|
i++
|
|
103
112
|
}
|
|
104
113
|
|
|
@@ -115,6 +124,7 @@ async function convertXLSXToCSV(xlsxPath: string, csvPath: string): Promise<void
|
|
|
115
124
|
const child = spawn("python3", ["-c", PY_CONVERT, xlsxPath, csvPath], {
|
|
116
125
|
stdio: ["ignore", "inherit", "inherit"],
|
|
117
126
|
})
|
|
127
|
+
|
|
118
128
|
await new Promise<void>((resolve, reject) => {
|
|
119
129
|
child.on("close", (code) => (code === 0 ? resolve() : reject(new Error(`python3 converter exited with ${code}`))))
|
|
120
130
|
child.on("error", reject)
|
|
@@ -173,8 +183,8 @@ export async function fetchStateHISchools(
|
|
|
173
183
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
174
184
|
report,
|
|
175
185
|
})
|
|
176
|
-
} catch (
|
|
177
|
-
report?.(` ✗ Download failed (${(
|
|
186
|
+
} catch (error) {
|
|
187
|
+
report?.(` ✗ Download failed (${(error as Error).message})`)
|
|
178
188
|
|
|
179
189
|
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] }
|
|
180
190
|
}
|
|
@@ -182,7 +192,7 @@ export async function fetchStateHISchools(
|
|
|
182
192
|
const xlsxSize = statSync(xlsxDest).size
|
|
183
193
|
report?.(` Downloaded XLSX: ${iec(xlsxSize)}`)
|
|
184
194
|
|
|
185
|
-
if (xlsxSize <
|
|
195
|
+
if (xlsxSize < BYTES_PER_KIB) {
|
|
186
196
|
report?.(` ✗ Response too small (${xlsxSize} bytes) — probable error page`)
|
|
187
197
|
|
|
188
198
|
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] }
|
|
@@ -208,6 +218,7 @@ export async function fetchStateHISchools(
|
|
|
208
218
|
bytes: csvSize,
|
|
209
219
|
notes: "Converted from XLSX (sheets HIDOE + PCS concatenated under shared header).",
|
|
210
220
|
}
|
|
221
|
+
|
|
211
222
|
await writeManifest(manifestPath, manifest)
|
|
212
223
|
|
|
213
224
|
report?.(` ✓ ${iec(csvSize)} sha256=${csvSha}`)
|
|
@@ -25,6 +25,12 @@ import { sha256File } from "@mailwoman/core/utils"
|
|
|
25
25
|
import type { BaseFetchOptions, FetchSummary } from "./download.ts"
|
|
26
26
|
import { downloadToFile, writeManifest } from "./download.ts"
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Bytes per KiB — the divisor for human-readable sizes, and the floor below which a "download" is an error page rather
|
|
30
|
+
* than data.
|
|
31
|
+
*/
|
|
32
|
+
const BYTES_PER_KIB = 1024
|
|
33
|
+
|
|
28
34
|
export type FetchStateSourcesOptions = BaseFetchOptions
|
|
29
35
|
|
|
30
36
|
interface Source {
|
|
@@ -106,21 +112,26 @@ export async function fetchStateSources(
|
|
|
106
112
|
headers: { "Accept-Encoding": "gzip, br" },
|
|
107
113
|
report,
|
|
108
114
|
}))
|
|
109
|
-
} catch (
|
|
110
|
-
report?.(` ✗ download failed for ${url}: ${(
|
|
115
|
+
} catch (error) {
|
|
116
|
+
report?.(` ✗ download failed for ${url}: ${(error as Error).message}`)
|
|
117
|
+
|
|
111
118
|
failed++
|
|
112
119
|
failedCodes.push(slug)
|
|
120
|
+
|
|
113
121
|
continue
|
|
114
122
|
}
|
|
115
123
|
|
|
116
|
-
if (bytes <
|
|
124
|
+
if (bytes < BYTES_PER_KIB) {
|
|
117
125
|
report?.(` ✗ response too small (${bytes} bytes) — probable 404 / error page`)
|
|
126
|
+
|
|
118
127
|
failed++
|
|
119
128
|
failedCodes.push(slug)
|
|
129
|
+
|
|
120
130
|
continue
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
const sha = await sha256File(dest)
|
|
134
|
+
|
|
124
135
|
const manifest: SourceManifest = {
|
|
125
136
|
source_url: url,
|
|
126
137
|
downloaded_at: new Date().toISOString(),
|
|
@@ -128,9 +139,11 @@ export async function fetchStateSources(
|
|
|
128
139
|
sha256: sha,
|
|
129
140
|
bytes,
|
|
130
141
|
}
|
|
142
|
+
|
|
131
143
|
await writeManifest(join(destDir, "MANIFEST.json"), manifest)
|
|
132
144
|
|
|
133
145
|
report?.(` ✓ ${(bytes / 1024 / 1024).toFixed(1)} MB sha256=${sha}`)
|
|
146
|
+
|
|
134
147
|
fetched++
|
|
135
148
|
}
|
|
136
149
|
|