@mailwoman/corpus 5.9.0 → 5.10.1
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 +1 -1
- package/out/src/adapters/ban/adapter.d.ts +1 -1
- package/out/src/adapters/fcc-bdc/adapter.d.ts +1 -1
- package/out/src/adapters/geonames/adapter.d.ts +1 -1
- package/out/src/adapters/geonames-postal/adapter.d.ts +1 -1
- package/out/src/adapters/gnaf/adapter.d.ts +1 -1
- package/out/src/adapters/index.d.ts +19 -19
- package/out/src/adapters/openaddresses/adapter.d.ts +1 -1
- package/out/src/adapters/overture/adapter.d.ts +1 -1
- package/out/src/adapters/state-hi-schools/adapter.d.ts +1 -1
- package/out/src/adapters/state-ia-contractors/adapter.d.ts +1 -1
- package/out/src/adapters/state-ny-notaries/adapter.d.ts +1 -1
- package/out/src/adapters/state-tx-notaries/adapter.d.ts +1 -1
- package/out/src/adapters/synth-po-box/adapter.d.ts +2 -2
- package/out/src/adapters/tiger/adapter.d.ts +1 -1
- package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts +1 -1
- package/out/src/adapters/usgov-imls-pls/adapter.d.ts +1 -1
- package/out/src/adapters/usgov-irs-bmf/adapter.d.ts +1 -1
- package/out/src/adapters/usgov-nad/adapter.d.ts +2 -2
- package/out/src/adapters/usgov-nad/adapter.js +1 -1
- package/out/src/adapters/usgov-nppes/adapter.d.ts +1 -1
- package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts +1 -1
- package/out/src/adapters/wof-admin-jp/adapter.d.ts +1 -1
- package/out/src/adapters/wof-admin-json/adapter.d.ts +2 -2
- package/out/src/adapters/wof-postalcode-json/adapter.d.ts +2 -2
- package/out/src/align.d.ts +2 -2
- package/out/src/build.d.ts +4 -4
- package/out/src/index.d.ts +22 -21
- package/out/src/index.d.ts.map +1 -1
- package/out/src/index.js +1 -0
- package/out/src/index.js.map +1 -1
- package/out/src/parquet-wrapper/index.d.ts +3 -3
- package/out/src/parquet-wrapper/reader.d.ts +1 -1
- package/out/src/parquet-wrapper/writer.d.ts +4 -2
- package/out/src/parquet-wrapper/writer.d.ts.map +1 -1
- package/out/src/parquet-wrapper/writer.js +3 -1
- package/out/src/parquet-wrapper/writer.js.map +1 -1
- package/out/src/parquet.d.ts +3 -3
- package/out/src/runner.d.ts +2 -2
- package/out/src/shard-recipes/anchor-absorption.d.ts +1 -1
- package/out/src/shard-recipes/boundary-stress.d.ts +1 -1
- package/out/src/shard-recipes/country-balanced.d.ts +1 -1
- package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts +1 -1
- package/out/src/shard-recipes/fr-admin-split.d.ts +1 -1
- package/out/src/shard-recipes/fr-bare-street.d.ts +1 -1
- package/out/src/shard-recipes/fr-order.d.ts +1 -1
- package/out/src/shard-recipes/german.d.ts +1 -1
- package/out/src/shard-recipes/house-venue.d.ts +1 -1
- package/out/src/shard-recipes/index.d.ts +2 -2
- package/out/src/shard-recipes/intersection.d.ts +1 -1
- package/out/src/shard-recipes/locale.d.ts +2 -2
- package/out/src/shard-recipes/nl-postcode.d.ts +1 -1
- package/out/src/shard-recipes/no-street-led.d.ts +1 -1
- package/out/src/shard-recipes/no-street.d.ts +1 -1
- package/out/src/shard-recipes/po-box-cedex.d.ts +1 -1
- package/out/src/shard-recipes/po-box.d.ts +1 -1
- package/out/src/shard-recipes/si-bare-village.d.ts +1 -1
- package/out/src/shard-recipes/street-affix.d.ts +1 -1
- package/out/src/shard-recipes/street-bare.d.ts +1 -1
- package/out/src/shard-recipes/street.d.ts +1 -1
- package/out/src/shard-recipes/unit.d.ts +1 -1
- package/out/src/split.d.ts +1 -1
- package/out/src/split.js +1 -1
- package/out/src/split.js.map +1 -1
- package/out/src/synthesize-boundary-stress.d.ts +1 -1
- package/out/src/synthesize-german.d.ts +1 -1
- package/out/src/synthesize-house-venue.d.ts +1 -1
- package/out/src/synthesize-intersection.d.ts +1 -1
- package/out/src/synthesize-no-street.d.ts +1 -1
- package/out/src/synthesize-po-box.d.ts +1 -1
- package/out/src/synthesize-street.d.ts +1 -1
- package/out/src/synthesize.d.ts +2 -2
- package/out/src/tools/align-shard.d.ts +8 -0
- package/out/src/tools/align-shard.d.ts.map +1 -0
- package/out/src/tools/align-shard.js +58 -0
- package/out/src/tools/align-shard.js.map +1 -0
- package/out/src/tools/audit.d.ts +28 -0
- package/out/src/tools/audit.d.ts.map +1 -0
- package/out/src/tools/audit.js +273 -0
- package/out/src/tools/audit.js.map +1 -0
- package/out/src/tools/corpus-stats.d.ts +44 -0
- package/out/src/tools/corpus-stats.d.ts.map +1 -0
- package/out/src/tools/corpus-stats.js +151 -0
- package/out/src/tools/corpus-stats.js.map +1 -0
- package/out/src/tools/fetch/ban.d.ts +31 -0
- package/out/src/tools/fetch/ban.d.ts.map +1 -0
- package/out/src/tools/fetch/ban.js +248 -0
- package/out/src/tools/fetch/ban.js.map +1 -0
- package/out/src/tools/fetch/download.d.ts +49 -0
- package/out/src/tools/fetch/download.d.ts.map +1 -0
- package/out/src/tools/fetch/download.js +81 -0
- package/out/src/tools/fetch/download.js.map +1 -0
- package/out/src/tools/fetch/hrsa.d.ts +18 -0
- package/out/src/tools/fetch/hrsa.d.ts.map +1 -0
- package/out/src/tools/fetch/hrsa.js +46 -0
- package/out/src/tools/fetch/hrsa.js.map +1 -0
- package/out/src/tools/fetch/imls-pls.d.ts +25 -0
- package/out/src/tools/fetch/imls-pls.d.ts.map +1 -0
- package/out/src/tools/fetch/imls-pls.js +120 -0
- package/out/src/tools/fetch/imls-pls.js.map +1 -0
- package/out/src/tools/fetch/index.d.ts +118 -0
- package/out/src/tools/fetch/index.d.ts.map +1 -0
- package/out/src/tools/fetch/index.js +117 -0
- package/out/src/tools/fetch/index.js.map +1 -0
- package/out/src/tools/fetch/nad.d.ts +56 -0
- package/out/src/tools/fetch/nad.d.ts.map +1 -0
- package/out/src/tools/fetch/nad.js +225 -0
- package/out/src/tools/fetch/nad.js.map +1 -0
- package/out/src/tools/fetch/nppes.d.ts +26 -0
- package/out/src/tools/fetch/nppes.d.ts.map +1 -0
- package/out/src/tools/fetch/nppes.js +136 -0
- package/out/src/tools/fetch/nppes.js.map +1 -0
- package/out/src/tools/fetch/openaddresses.d.ts +59 -0
- package/out/src/tools/fetch/openaddresses.d.ts.map +1 -0
- package/out/src/tools/fetch/openaddresses.js +307 -0
- package/out/src/tools/fetch/openaddresses.js.map +1 -0
- package/out/src/tools/fetch/state-hi-schools.d.ts +25 -0
- package/out/src/tools/fetch/state-hi-schools.d.ts.map +1 -0
- package/out/src/tools/fetch/state-hi-schools.js +171 -0
- package/out/src/tools/fetch/state-hi-schools.js.map +1 -0
- package/out/src/tools/fetch/state-sources.d.ts +22 -0
- package/out/src/tools/fetch/state-sources.d.ts.map +1 -0
- package/out/src/tools/fetch/state-sources.js +110 -0
- package/out/src/tools/fetch/state-sources.js.map +1 -0
- package/out/src/tools/fetch/tiger-full.d.ts +36 -0
- package/out/src/tools/fetch/tiger-full.d.ts.map +1 -0
- package/out/src/tools/fetch/tiger-full.js +239 -0
- package/out/src/tools/fetch/tiger-full.js.map +1 -0
- package/out/src/tools/golden-expand.d.ts +74 -0
- package/out/src/tools/golden-expand.d.ts.map +1 -0
- package/out/src/tools/golden-expand.js +394 -0
- package/out/src/tools/golden-expand.js.map +1 -0
- package/out/src/tools/golden-promote.d.ts +60 -0
- package/out/src/tools/golden-promote.d.ts.map +1 -0
- package/out/src/tools/golden-promote.js +223 -0
- package/out/src/tools/golden-promote.js.map +1 -0
- package/out/src/tools/index.d.ts +24 -0
- package/out/src/tools/index.d.ts.map +1 -0
- package/out/src/tools/index.js +24 -0
- package/out/src/tools/index.js.map +1 -0
- package/out/src/tools/ingest-csv.d.ts +45 -0
- package/out/src/tools/ingest-csv.d.ts.map +1 -0
- package/out/src/tools/ingest-csv.js +309 -0
- package/out/src/tools/ingest-csv.js.map +1 -0
- package/out/src/tools/jsonl-to-parquet.d.ts +49 -0
- package/out/src/tools/jsonl-to-parquet.d.ts.map +1 -0
- package/out/src/tools/jsonl-to-parquet.js +157 -0
- package/out/src/tools/jsonl-to-parquet.js.map +1 -0
- package/out/src/tools/lint-shard-vocab.d.ts +66 -0
- package/out/src/tools/lint-shard-vocab.d.ts.map +1 -0
- package/out/src/tools/lint-shard-vocab.js +266 -0
- package/out/src/tools/lint-shard-vocab.js.map +1 -0
- package/out/src/tools/lint-shard.d.ts +73 -0
- package/out/src/tools/lint-shard.d.ts.map +1 -0
- package/out/src/tools/lint-shard.js +350 -0
- package/out/src/tools/lint-shard.js.map +1 -0
- package/out/src/tools/overlay-manifest.d.ts +35 -0
- package/out/src/tools/overlay-manifest.d.ts.map +1 -0
- package/out/src/tools/overlay-manifest.js +85 -0
- package/out/src/tools/overlay-manifest.js.map +1 -0
- package/out/src/tools/shard-kryptonite.d.ts +28 -0
- package/out/src/tools/shard-kryptonite.d.ts.map +1 -0
- package/out/src/tools/shard-kryptonite.js +101 -0
- package/out/src/tools/shard-kryptonite.js.map +1 -0
- package/out/src/tools/shard-translit.d.ts +42 -0
- package/out/src/tools/shard-translit.d.ts.map +1 -0
- package/out/src/tools/shard-translit.js +196 -0
- package/out/src/tools/shard-translit.js.map +1 -0
- package/package.json +63 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../../../src/tools/fetch/download.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Re-fetch the HRSA Health Center Service Delivery Sites CSV. Source for the `usgov-hrsa-fqhc`
|
|
7
|
+
* adapter. US Public Domain.
|
|
8
|
+
*
|
|
9
|
+
* Uses Node's built-in fetch (gzip/brotli) and streaming sha256 instead of curl + sha256sum, and
|
|
10
|
+
* writes the same sibling `MANIFEST.json` (origin URL + fetch timestamp + byte count + sha256) so
|
|
11
|
+
* downstream adapters can verify provenance.
|
|
12
|
+
*
|
|
13
|
+
* Invoke via `mailwoman corpus fetch hrsa --out-root <path>`.
|
|
14
|
+
*/
|
|
15
|
+
import type { BaseFetchOptions, FetchSummary } from "./download.ts";
|
|
16
|
+
export type FetchHRSAOptions = BaseFetchOptions;
|
|
17
|
+
export declare function fetchHRSA(options: FetchHRSAOptions, report?: (line: string) => void): Promise<FetchSummary>;
|
|
18
|
+
//# sourceMappingURL=hrsa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hrsa.d.ts","sourceRoot":"","sources":["../../../../src/tools/fetch/hrsa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAOnE,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAA;AAU/C,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BjH"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Re-fetch the HRSA Health Center Service Delivery Sites CSV. Source for the `usgov-hrsa-fqhc`
|
|
7
|
+
* adapter. US Public Domain.
|
|
8
|
+
*
|
|
9
|
+
* Uses Node's built-in fetch (gzip/brotli) and streaming sha256 instead of curl + sha256sum, and
|
|
10
|
+
* writes the same sibling `MANIFEST.json` (origin URL + fetch timestamp + byte count + sha256) so
|
|
11
|
+
* downstream adapters can verify provenance.
|
|
12
|
+
*
|
|
13
|
+
* Invoke via `mailwoman corpus fetch hrsa --out-root <path>`.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdirSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { sha256File } from "@mailwoman/core/utils";
|
|
18
|
+
import { downloadToFile, writeManifest } from "./download.js";
|
|
19
|
+
const SLUG = "usgov-hrsa-fqhc";
|
|
20
|
+
const FILENAME = "Health_Center_Service_Delivery_and_LookAlike_Sites.csv";
|
|
21
|
+
const SOURCE_URL = `https://data.hrsa.gov/DataDownload/DD_Files/${FILENAME}`;
|
|
22
|
+
export async function fetchHRSA(options, report) {
|
|
23
|
+
const destDir = join(options.outRoot, SLUG);
|
|
24
|
+
mkdirSync(destDir, { recursive: true });
|
|
25
|
+
const dest = join(destDir, FILENAME);
|
|
26
|
+
report?.(`=== ${SLUG} / ${FILENAME}`);
|
|
27
|
+
const { bytes } = await downloadToFile({
|
|
28
|
+
url: SOURCE_URL,
|
|
29
|
+
dest,
|
|
30
|
+
timeoutMs: 600_000,
|
|
31
|
+
headers: { "Accept-Encoding": "gzip, br" },
|
|
32
|
+
report,
|
|
33
|
+
});
|
|
34
|
+
const sha = await sha256File(dest);
|
|
35
|
+
const manifest = {
|
|
36
|
+
source_url: SOURCE_URL,
|
|
37
|
+
downloaded_at: new Date().toISOString(),
|
|
38
|
+
filename: FILENAME,
|
|
39
|
+
sha256: sha,
|
|
40
|
+
bytes,
|
|
41
|
+
};
|
|
42
|
+
await writeManifest(join(destDir, "MANIFEST.json"), manifest);
|
|
43
|
+
report?.(` ✓ ${(bytes / 1024 / 1024).toFixed(1)} MB sha256=${sha}`);
|
|
44
|
+
return { fetched: 1, skipped: 0, failed: 0, failedCodes: [] };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=hrsa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hrsa.js","sourceRoot":"","sources":["../../../../src/tools/fetch/hrsa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGlD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAE7D,MAAM,IAAI,GAAG,iBAAiB,CAAA;AAC9B,MAAM,QAAQ,GAAG,wDAAwD,CAAA;AACzE,MAAM,UAAU,GAAG,+CAA+C,QAAQ,EAAE,CAAA;AAY5E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB,EAAE,MAA+B;IACzF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC3C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAEpC,MAAM,EAAE,CAAC,OAAO,IAAI,MAAM,QAAQ,EAAE,CAAC,CAAA;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,cAAc,CAAC;QACtC,GAAG,EAAE,UAAU;QACf,IAAI;QACJ,SAAS,EAAE,OAAO;QAClB,OAAO,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE;QAC1C,MAAM;KACN,CAAC,CAAA;IACF,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAA;IAElC,MAAM,QAAQ,GAAmB;QAChC,UAAU,EAAE,UAAU;QACtB,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACvC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,GAAG;QACX,KAAK;KACL,CAAA;IACD,MAAM,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,QAAQ,CAAC,CAAA;IAE7D,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,GAAG,EAAE,CAAC,CAAA;IAErE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAA;AAC9D,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Re-fetch the IMLS Public Libraries Survey (PLS) outlet-level data. Each US public library branch
|
|
7
|
+
* (outlet) is one row, ~17K rows with address fields. Source for the `usgov-imls-pls` adapter. US
|
|
8
|
+
* Public Domain (federal statistical survey).
|
|
9
|
+
*
|
|
10
|
+
* The FY 2023 release is the most current as of 2026-05. IMLS ships a single ZIP containing CSV,
|
|
11
|
+
* SAS, and SPSS variants. We extract the outlet-level CSV (pls_fy*_outlet*.csv or similar) and
|
|
12
|
+
* discard the rest. The administrative-entity (system-level) CSV is intentionally skipped — it has
|
|
13
|
+
* no per-branch address detail.
|
|
14
|
+
*
|
|
15
|
+
* Uses Node's built-in fetch (gzip/brotli) and streaming sha256 instead of curl + sha256sum. The
|
|
16
|
+
* ZIP is unpacked with the `unzip` binary via `node:child_process` (no clean Node equivalent for
|
|
17
|
+
* member listing + selective extraction).
|
|
18
|
+
*
|
|
19
|
+
* Invoke via `mailwoman corpus fetch imls-pls --out-root <path>`. Idempotent: if dest CSV exists
|
|
20
|
+
* and sha matches MANIFEST, skips download.
|
|
21
|
+
*/
|
|
22
|
+
import type { BaseFetchOptions, FetchSummary } from "./download.ts";
|
|
23
|
+
export type FetchIMLSPLSOptions = BaseFetchOptions;
|
|
24
|
+
export declare function fetchIMLSPLS(options: FetchIMLSPLSOptions, report?: (line: string) => void): Promise<FetchSummary>;
|
|
25
|
+
//# sourceMappingURL=imls-pls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imls-pls.d.ts","sourceRoot":"","sources":["../../../../src/tools/fetch/imls-pls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAUnE,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,CAAA;AAoBlD,wBAAsB,YAAY,CACjC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,YAAY,CAAC,CAkGvB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Re-fetch the IMLS Public Libraries Survey (PLS) outlet-level data. Each US public library branch
|
|
7
|
+
* (outlet) is one row, ~17K rows with address fields. Source for the `usgov-imls-pls` adapter. US
|
|
8
|
+
* Public Domain (federal statistical survey).
|
|
9
|
+
*
|
|
10
|
+
* The FY 2023 release is the most current as of 2026-05. IMLS ships a single ZIP containing CSV,
|
|
11
|
+
* SAS, and SPSS variants. We extract the outlet-level CSV (pls_fy*_outlet*.csv or similar) and
|
|
12
|
+
* discard the rest. The administrative-entity (system-level) CSV is intentionally skipped — it has
|
|
13
|
+
* no per-branch address detail.
|
|
14
|
+
*
|
|
15
|
+
* Uses Node's built-in fetch (gzip/brotli) and streaming sha256 instead of curl + sha256sum. The
|
|
16
|
+
* ZIP is unpacked with the `unzip` binary via `node:child_process` (no clean Node equivalent for
|
|
17
|
+
* member listing + selective extraction).
|
|
18
|
+
*
|
|
19
|
+
* Invoke via `mailwoman corpus fetch imls-pls --out-root <path>`. Idempotent: if dest CSV exists
|
|
20
|
+
* and sha matches MANIFEST, skips download.
|
|
21
|
+
*/
|
|
22
|
+
import { execFile } from "node:child_process";
|
|
23
|
+
import { existsSync, mkdirSync, statSync } from "node:fs";
|
|
24
|
+
import { rm } from "node:fs/promises";
|
|
25
|
+
import { basename, join } from "node:path";
|
|
26
|
+
import { promisify } from "node:util";
|
|
27
|
+
import { sha256File } from "@mailwoman/core/utils";
|
|
28
|
+
import { downloadToFile, readManifest, writeManifest } from "./download.js";
|
|
29
|
+
const execFileAsync = promisify(execFile);
|
|
30
|
+
// The PLS FY 2023 bulk CSV ZIP (most recent as of 2026-05).
|
|
31
|
+
// If IMLS publishes a newer year, update this URL.
|
|
32
|
+
const ZIP_URL = "https://www.imls.gov/sites/default/files/2025-08/pls_fy2023_csv.zip";
|
|
33
|
+
const SLUG = "usgov-imls-pls";
|
|
34
|
+
/** Return the filenames listed inside a ZIP (the trailing column of each `unzip -l` row). */
|
|
35
|
+
async function listZipEntries(zipPath) {
|
|
36
|
+
const listing = await execFileAsync("unzip", ["-l", zipPath]);
|
|
37
|
+
return listing.stdout
|
|
38
|
+
.split("\n")
|
|
39
|
+
.map((line) => line.trim().split(/\s+/).pop() ?? "")
|
|
40
|
+
.filter((name) => name.length > 0);
|
|
41
|
+
}
|
|
42
|
+
export async function fetchIMLSPLS(options, report) {
|
|
43
|
+
const destDir = join(options.outRoot, SLUG);
|
|
44
|
+
mkdirSync(destDir, { recursive: true });
|
|
45
|
+
const zipDest = join(destDir, basename(ZIP_URL));
|
|
46
|
+
const manifestPath = join(destDir, "MANIFEST.json");
|
|
47
|
+
report?.(`=== ${SLUG}`);
|
|
48
|
+
// ------------------------------------------------------------------
|
|
49
|
+
// Idempotency check: if outlet CSV already exists and sha matches, skip.
|
|
50
|
+
// ------------------------------------------------------------------
|
|
51
|
+
const recorded = await readManifest(manifestPath);
|
|
52
|
+
if (recorded?.sha256 && recorded.filename) {
|
|
53
|
+
const recordedPath = join(destDir, recorded.filename);
|
|
54
|
+
if (existsSync(recordedPath) && (await sha256File(recordedPath)) === recorded.sha256) {
|
|
55
|
+
report?.(" ✓ Already current (sha256 matches MANIFEST) — skipping download.");
|
|
56
|
+
return { fetched: 0, skipped: 1, failed: 0, failedCodes: [] };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// ------------------------------------------------------------------
|
|
60
|
+
// Download ZIP
|
|
61
|
+
// ------------------------------------------------------------------
|
|
62
|
+
report?.(` Downloading ${ZIP_URL} ...`);
|
|
63
|
+
const { bytes: zipSize } = await downloadToFile({
|
|
64
|
+
url: ZIP_URL,
|
|
65
|
+
dest: zipDest,
|
|
66
|
+
timeoutMs: 600_000,
|
|
67
|
+
headers: { "Accept-Encoding": "gzip, br" },
|
|
68
|
+
report,
|
|
69
|
+
});
|
|
70
|
+
report?.(` Downloaded: ${(zipSize / 1024 / 1024).toFixed(1)} MB`);
|
|
71
|
+
if (zipSize < 1024) {
|
|
72
|
+
report?.(` ✗ Response too small (${zipSize} bytes) — probable error page`);
|
|
73
|
+
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] };
|
|
74
|
+
}
|
|
75
|
+
// ------------------------------------------------------------------
|
|
76
|
+
// Discover the outlet-level CSV inside the ZIP.
|
|
77
|
+
// Outlet files match: pls_fy*outlet*.csv (case-insensitive)
|
|
78
|
+
// Administrative-entity files match: pls_fy*ae*.csv — we skip those.
|
|
79
|
+
// ------------------------------------------------------------------
|
|
80
|
+
report?.(" Inspecting ZIP contents ...");
|
|
81
|
+
const entries = await listZipEntries(zipDest);
|
|
82
|
+
let csvName = entries.find((name) => /pls_fy.*outlet.*\.csv/i.test(name));
|
|
83
|
+
// Fallback: if IMLS renames the file, grab any CSV that is NOT the ae file.
|
|
84
|
+
if (!csvName) {
|
|
85
|
+
csvName = entries.find((name) => /\.csv$/i.test(name) && !/system|state|_ae\b|_se\b/i.test(name));
|
|
86
|
+
}
|
|
87
|
+
if (!csvName) {
|
|
88
|
+
report?.(" Available files in ZIP:");
|
|
89
|
+
for (const name of entries) {
|
|
90
|
+
report?.(` ${name}`);
|
|
91
|
+
}
|
|
92
|
+
report?.(" ✗ Could not identify outlet CSV — inspect above listing and update this module");
|
|
93
|
+
return { fetched: 0, skipped: 0, failed: 1, failedCodes: [SLUG] };
|
|
94
|
+
}
|
|
95
|
+
report?.(` Extracting outlet CSV: ${csvName}`);
|
|
96
|
+
await execFileAsync("unzip", ["-o", "-j", zipDest, csvName, "-d", destDir]);
|
|
97
|
+
const csvDest = join(destDir, basename(csvName));
|
|
98
|
+
const csvSize = statSync(csvDest).size;
|
|
99
|
+
const csvSha = await sha256File(csvDest);
|
|
100
|
+
// ------------------------------------------------------------------
|
|
101
|
+
// Remove ZIP (small, but keep destDir clean)
|
|
102
|
+
// ------------------------------------------------------------------
|
|
103
|
+
await rm(zipDest, { force: true });
|
|
104
|
+
report?.(" Removed ZIP (CSV kept)");
|
|
105
|
+
// ------------------------------------------------------------------
|
|
106
|
+
// Write MANIFEST
|
|
107
|
+
// ------------------------------------------------------------------
|
|
108
|
+
const manifest = {
|
|
109
|
+
source_url: ZIP_URL,
|
|
110
|
+
downloaded_at: new Date().toISOString(),
|
|
111
|
+
filename: basename(csvName),
|
|
112
|
+
sha256: csvSha,
|
|
113
|
+
bytes: csvSize,
|
|
114
|
+
};
|
|
115
|
+
await writeManifest(manifestPath, manifest);
|
|
116
|
+
report?.(` ✓ ${(csvSize / 1024 / 1024).toFixed(1)} MB sha256=${csvSha}`);
|
|
117
|
+
report?.(` MANIFEST written to ${manifestPath}`);
|
|
118
|
+
return { fetched: 1, skipped: 0, failed: 0, failedCodes: [] };
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=imls-pls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imls-pls.js","sourceRoot":"","sources":["../../../../src/tools/fetch/imls-pls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The `mailwoman corpus fetch <source>` family — reproducible bulk-download recovery modules for
|
|
7
|
+
* the open-data sources the corpus build pipeline consumes. Each module writes the raw download
|
|
8
|
+
* files **plus** a sibling `MANIFEST.json` capturing the origin URL, fetch timestamp, byte count,
|
|
9
|
+
* and sha256 so downstream adapters can verify provenance.
|
|
10
|
+
*
|
|
11
|
+
* The corpus build pipeline itself does NOT call these modules — the existing convention is for
|
|
12
|
+
* operators to pre-download into an out-root and point adapters at the resulting files. These
|
|
13
|
+
* modules exist for **reproducibility** (disk-loss recovery, weekly refresh, fresh-environment
|
|
14
|
+
* bootstrap).
|
|
15
|
+
*
|
|
16
|
+
* ## Usage
|
|
17
|
+
*
|
|
18
|
+
* ```sh
|
|
19
|
+
* # Default: writes under ./data/corpus/sources/ relative to the working directory
|
|
20
|
+
* mailwoman corpus fetch state-sources
|
|
21
|
+
* mailwoman corpus fetch hrsa
|
|
22
|
+
*
|
|
23
|
+
* # Or point at the standard mailwoman data root
|
|
24
|
+
* mailwoman corpus fetch state-sources --out-root /data/corpus/sources
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Each adapter under `corpus/src/adapters/<adapter>/README.md` documents the specific URL its
|
|
28
|
+
* input was pulled from; these modules mirror those URLs in a single executable place.
|
|
29
|
+
*
|
|
30
|
+
* ## Coverage
|
|
31
|
+
*
|
|
32
|
+
* - `ban` — French BAN (Base Adresse Nationale), all départements incl. DOM/TOM. Tier B (Licence
|
|
33
|
+
* Ouverte 2.0).
|
|
34
|
+
* - `nad` — US DOT National Address Database (~97M address points, ArcGIS FeatureServer). Tier A
|
|
35
|
+
* (US PD).
|
|
36
|
+
* - `hrsa` — HRSA Health Center Service Delivery Sites (federal). Tier A (US PD).
|
|
37
|
+
* - `imls-pls` — IMLS Public Libraries Survey, outlet-level (~17K library branches, FY 2023).
|
|
38
|
+
* Tier A (US PD).
|
|
39
|
+
* - `nppes` — NPPES NPI registry, full monthly dissemination (~7M provider venue+address rows).
|
|
40
|
+
* Tier A (US PD).
|
|
41
|
+
* - `openaddresses` — OpenAddresses country collections (default: Canada / `ca`). Tier B/C mixed
|
|
42
|
+
* — per-row filter.
|
|
43
|
+
* - `state-sources` — NY/TX/DE/OR notaries, IA contractors, WA health providers, HI lobbyists.
|
|
44
|
+
* Tier A (state PD-equivalent).
|
|
45
|
+
* - `state-hi-schools` — Hawaii DOE school directory (XLSX → CSV via openpyxl). Tier A (state
|
|
46
|
+
* PD-equivalent).
|
|
47
|
+
* - `tiger-full` — US Census TIGER 2024 ADDRFEAT, all US counties. Tier A (US PD).
|
|
48
|
+
*
|
|
49
|
+
* License tiers per `docs/licensing-strategy.md` (or the playpen knowledge base mirror at
|
|
50
|
+
* `docs/docs/projects/mailwoman/licensing-strategy.md`). `openaddresses` is a **Tier-mixed**
|
|
51
|
+
* source: the downloaded collection includes CC0, CC-BY, OGL, and ODbL/CC-BY-SA rows. The per-row
|
|
52
|
+
* `LICENSE` filter in the `openaddresses` adapter is essential — Tier-C (ODbL, CC-BY-SA) rows are
|
|
53
|
+
* dropped at ingest by default to protect proprietary-weights training.
|
|
54
|
+
*
|
|
55
|
+
* ### OpenAddresses authentication (as of 2026-05-18)
|
|
56
|
+
*
|
|
57
|
+
* `batch.openaddresses.io` now requires a free registered account for bulk downloads (auth gate
|
|
58
|
+
* prevents CDN abuse; data remains openly licensed). `fetchOpenAddresses` reads `OA_BATCH_TOKEN`
|
|
59
|
+
* from the environment:
|
|
60
|
+
*
|
|
61
|
+
* ```sh
|
|
62
|
+
* # One-time: register at https://batch.openaddresses.io/register
|
|
63
|
+
* # Log in → Profile → "Create Token" → copy token
|
|
64
|
+
* export OA_BATCH_TOKEN=<your-token>
|
|
65
|
+
*
|
|
66
|
+
* # Download Canada (~2 GiB compressed, ~7 GiB uncompressed)
|
|
67
|
+
* mailwoman corpus fetch openaddresses --country ca \
|
|
68
|
+
* --out-root /mnt/playpen/mailwoman-data/corpus/sources
|
|
69
|
+
*
|
|
70
|
+
* # Or any other OA country code
|
|
71
|
+
* mailwoman corpus fetch openaddresses --country fr
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* Without a token the command prints setup instructions and reports the failure.
|
|
75
|
+
*
|
|
76
|
+
* ## Adding a new source
|
|
77
|
+
*
|
|
78
|
+
* 1. Pick the right module (or create a sibling one if the source is from a meaningfully
|
|
79
|
+
* different family).
|
|
80
|
+
* 2. Append to the `SOURCES` array: `{ slug, filename, url }`.
|
|
81
|
+
* 3. Confirm the destination URL via `curl -sI -L <url> | head` before committing — state
|
|
82
|
+
* open-data portals occasionally rotate Socrata view IDs.
|
|
83
|
+
* 4. Run the command against a scratch `--out-root` to verify the download succeeds + the
|
|
84
|
+
* MANIFEST is well-formed.
|
|
85
|
+
* 5. Add the source's adapter (or extend an existing one) under `corpus/src/adapters/`.
|
|
86
|
+
*/
|
|
87
|
+
import { fetchBan } from "./ban.ts";
|
|
88
|
+
import { fetchHRSA } from "./hrsa.ts";
|
|
89
|
+
import { fetchIMLSPLS } from "./imls-pls.ts";
|
|
90
|
+
import { fetchNAD } from "./nad.ts";
|
|
91
|
+
import { fetchNPPES } from "./nppes.ts";
|
|
92
|
+
import { fetchOpenAddresses } from "./openaddresses.ts";
|
|
93
|
+
import { fetchStateHISchools } from "./state-hi-schools.ts";
|
|
94
|
+
import { fetchStateSources } from "./state-sources.ts";
|
|
95
|
+
import { fetchTigerFull } from "./tiger-full.ts";
|
|
96
|
+
export * from "./ban.ts";
|
|
97
|
+
export * from "./hrsa.ts";
|
|
98
|
+
export * from "./imls-pls.ts";
|
|
99
|
+
export * from "./nad.ts";
|
|
100
|
+
export * from "./nppes.ts";
|
|
101
|
+
export * from "./openaddresses.ts";
|
|
102
|
+
export * from "./state-hi-schools.ts";
|
|
103
|
+
export * from "./state-sources.ts";
|
|
104
|
+
export * from "./tiger-full.ts";
|
|
105
|
+
/** The fetch-source registry: id → module entry point. Each entry point takes its own options interface. */
|
|
106
|
+
export declare const FETCH_SOURCES: {
|
|
107
|
+
readonly ban: typeof fetchBan;
|
|
108
|
+
readonly nad: typeof fetchNAD;
|
|
109
|
+
readonly hrsa: typeof fetchHRSA;
|
|
110
|
+
readonly "imls-pls": typeof fetchIMLSPLS;
|
|
111
|
+
readonly nppes: typeof fetchNPPES;
|
|
112
|
+
readonly openaddresses: typeof fetchOpenAddresses;
|
|
113
|
+
readonly "state-sources": typeof fetchStateSources;
|
|
114
|
+
readonly "state-hi-schools": typeof fetchStateHISchools;
|
|
115
|
+
readonly "tiger-full": typeof fetchTigerFull;
|
|
116
|
+
};
|
|
117
|
+
export type FetchSourceID = keyof typeof FETCH_SOURCES;
|
|
118
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/tools/fetch/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAE/B,4GAA4G;AAC5G,eAAO,MAAM,aAAa;;;;;;;;;;CAUhB,CAAA;AAEV,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,aAAa,CAAA"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The `mailwoman corpus fetch <source>` family — reproducible bulk-download recovery modules for
|
|
7
|
+
* the open-data sources the corpus build pipeline consumes. Each module writes the raw download
|
|
8
|
+
* files **plus** a sibling `MANIFEST.json` capturing the origin URL, fetch timestamp, byte count,
|
|
9
|
+
* and sha256 so downstream adapters can verify provenance.
|
|
10
|
+
*
|
|
11
|
+
* The corpus build pipeline itself does NOT call these modules — the existing convention is for
|
|
12
|
+
* operators to pre-download into an out-root and point adapters at the resulting files. These
|
|
13
|
+
* modules exist for **reproducibility** (disk-loss recovery, weekly refresh, fresh-environment
|
|
14
|
+
* bootstrap).
|
|
15
|
+
*
|
|
16
|
+
* ## Usage
|
|
17
|
+
*
|
|
18
|
+
* ```sh
|
|
19
|
+
* # Default: writes under ./data/corpus/sources/ relative to the working directory
|
|
20
|
+
* mailwoman corpus fetch state-sources
|
|
21
|
+
* mailwoman corpus fetch hrsa
|
|
22
|
+
*
|
|
23
|
+
* # Or point at the standard mailwoman data root
|
|
24
|
+
* mailwoman corpus fetch state-sources --out-root /data/corpus/sources
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Each adapter under `corpus/src/adapters/<adapter>/README.md` documents the specific URL its
|
|
28
|
+
* input was pulled from; these modules mirror those URLs in a single executable place.
|
|
29
|
+
*
|
|
30
|
+
* ## Coverage
|
|
31
|
+
*
|
|
32
|
+
* - `ban` — French BAN (Base Adresse Nationale), all départements incl. DOM/TOM. Tier B (Licence
|
|
33
|
+
* Ouverte 2.0).
|
|
34
|
+
* - `nad` — US DOT National Address Database (~97M address points, ArcGIS FeatureServer). Tier A
|
|
35
|
+
* (US PD).
|
|
36
|
+
* - `hrsa` — HRSA Health Center Service Delivery Sites (federal). Tier A (US PD).
|
|
37
|
+
* - `imls-pls` — IMLS Public Libraries Survey, outlet-level (~17K library branches, FY 2023).
|
|
38
|
+
* Tier A (US PD).
|
|
39
|
+
* - `nppes` — NPPES NPI registry, full monthly dissemination (~7M provider venue+address rows).
|
|
40
|
+
* Tier A (US PD).
|
|
41
|
+
* - `openaddresses` — OpenAddresses country collections (default: Canada / `ca`). Tier B/C mixed
|
|
42
|
+
* — per-row filter.
|
|
43
|
+
* - `state-sources` — NY/TX/DE/OR notaries, IA contractors, WA health providers, HI lobbyists.
|
|
44
|
+
* Tier A (state PD-equivalent).
|
|
45
|
+
* - `state-hi-schools` — Hawaii DOE school directory (XLSX → CSV via openpyxl). Tier A (state
|
|
46
|
+
* PD-equivalent).
|
|
47
|
+
* - `tiger-full` — US Census TIGER 2024 ADDRFEAT, all US counties. Tier A (US PD).
|
|
48
|
+
*
|
|
49
|
+
* License tiers per `docs/licensing-strategy.md` (or the playpen knowledge base mirror at
|
|
50
|
+
* `docs/docs/projects/mailwoman/licensing-strategy.md`). `openaddresses` is a **Tier-mixed**
|
|
51
|
+
* source: the downloaded collection includes CC0, CC-BY, OGL, and ODbL/CC-BY-SA rows. The per-row
|
|
52
|
+
* `LICENSE` filter in the `openaddresses` adapter is essential — Tier-C (ODbL, CC-BY-SA) rows are
|
|
53
|
+
* dropped at ingest by default to protect proprietary-weights training.
|
|
54
|
+
*
|
|
55
|
+
* ### OpenAddresses authentication (as of 2026-05-18)
|
|
56
|
+
*
|
|
57
|
+
* `batch.openaddresses.io` now requires a free registered account for bulk downloads (auth gate
|
|
58
|
+
* prevents CDN abuse; data remains openly licensed). `fetchOpenAddresses` reads `OA_BATCH_TOKEN`
|
|
59
|
+
* from the environment:
|
|
60
|
+
*
|
|
61
|
+
* ```sh
|
|
62
|
+
* # One-time: register at https://batch.openaddresses.io/register
|
|
63
|
+
* # Log in → Profile → "Create Token" → copy token
|
|
64
|
+
* export OA_BATCH_TOKEN=<your-token>
|
|
65
|
+
*
|
|
66
|
+
* # Download Canada (~2 GiB compressed, ~7 GiB uncompressed)
|
|
67
|
+
* mailwoman corpus fetch openaddresses --country ca \
|
|
68
|
+
* --out-root /mnt/playpen/mailwoman-data/corpus/sources
|
|
69
|
+
*
|
|
70
|
+
* # Or any other OA country code
|
|
71
|
+
* mailwoman corpus fetch openaddresses --country fr
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* Without a token the command prints setup instructions and reports the failure.
|
|
75
|
+
*
|
|
76
|
+
* ## Adding a new source
|
|
77
|
+
*
|
|
78
|
+
* 1. Pick the right module (or create a sibling one if the source is from a meaningfully
|
|
79
|
+
* different family).
|
|
80
|
+
* 2. Append to the `SOURCES` array: `{ slug, filename, url }`.
|
|
81
|
+
* 3. Confirm the destination URL via `curl -sI -L <url> | head` before committing — state
|
|
82
|
+
* open-data portals occasionally rotate Socrata view IDs.
|
|
83
|
+
* 4. Run the command against a scratch `--out-root` to verify the download succeeds + the
|
|
84
|
+
* MANIFEST is well-formed.
|
|
85
|
+
* 5. Add the source's adapter (or extend an existing one) under `corpus/src/adapters/`.
|
|
86
|
+
*/
|
|
87
|
+
import { fetchBan } from "./ban.js";
|
|
88
|
+
import { fetchHRSA } from "./hrsa.js";
|
|
89
|
+
import { fetchIMLSPLS } from "./imls-pls.js";
|
|
90
|
+
import { fetchNAD } from "./nad.js";
|
|
91
|
+
import { fetchNPPES } from "./nppes.js";
|
|
92
|
+
import { fetchOpenAddresses } from "./openaddresses.js";
|
|
93
|
+
import { fetchStateHISchools } from "./state-hi-schools.js";
|
|
94
|
+
import { fetchStateSources } from "./state-sources.js";
|
|
95
|
+
import { fetchTigerFull } from "./tiger-full.js";
|
|
96
|
+
export * from "./ban.js";
|
|
97
|
+
export * from "./hrsa.js";
|
|
98
|
+
export * from "./imls-pls.js";
|
|
99
|
+
export * from "./nad.js";
|
|
100
|
+
export * from "./nppes.js";
|
|
101
|
+
export * from "./openaddresses.js";
|
|
102
|
+
export * from "./state-hi-schools.js";
|
|
103
|
+
export * from "./state-sources.js";
|
|
104
|
+
export * from "./tiger-full.js";
|
|
105
|
+
/** The fetch-source registry: id → module entry point. Each entry point takes its own options interface. */
|
|
106
|
+
export const FETCH_SOURCES = {
|
|
107
|
+
ban: fetchBan,
|
|
108
|
+
nad: fetchNAD,
|
|
109
|
+
hrsa: fetchHRSA,
|
|
110
|
+
"imls-pls": fetchIMLSPLS,
|
|
111
|
+
nppes: fetchNPPES,
|
|
112
|
+
openaddresses: fetchOpenAddresses,
|
|
113
|
+
"state-sources": fetchStateSources,
|
|
114
|
+
"state-hi-schools": fetchStateHISchools,
|
|
115
|
+
"tiger-full": fetchTigerFull,
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/tools/fetch/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAE/B,4GAA4G;AAC5G,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;IACb,IAAI,EAAE,SAAS;IACf,UAAU,EAAE,YAAY;IACxB,KAAK,EAAE,UAAU;IACjB,aAAa,EAAE,kBAAkB;IACjC,eAAe,EAAE,iBAAiB;IAClC,kBAAkB,EAAE,mBAAmB;IACvC,YAAY,EAAE,cAAc;CACnB,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Fetch the US DOT National Address Database (NAD) — ~97 million structured US address-point
|
|
7
|
+
* records aggregated from state and local authorities. Source for the `usgov-nad` adapter (#30).
|
|
8
|
+
* US Public Domain (17 U.S.C. § 105).
|
|
9
|
+
*
|
|
10
|
+
* - Bounded per-chunk page concurrency (4× speedup at safe pressure)
|
|
11
|
+
* - 5× larger page size (5 000 vs. the old bash fetcher's 2 000) — fewer round-trips per chunk
|
|
12
|
+
* - Honest `complete: true` flag: only set when every page in the chunk fetched cleanly
|
|
13
|
+
* - Built-in fetch with gzip/brotli decompression (no curl + Python subprocess tax)
|
|
14
|
+
* - Per-chunk manifest with sha256 + record count + error count
|
|
15
|
+
*
|
|
16
|
+
* ## Source layout
|
|
17
|
+
*
|
|
18
|
+
* The ArcGIS FeatureServer is the only fully-automated path. As of 2026-05:
|
|
19
|
+
*
|
|
20
|
+
* - **`bulk`** mode requires a pre-signed S3 URL (Akamai blocks scripted curl on the DOT page). Pass
|
|
21
|
+
* `--nad-url <presigned>` from a browser visit to
|
|
22
|
+
* [https://www.transportation.gov/gis/national-address-database](https://www.transportation.gov/gis/national-address-database).
|
|
23
|
+
* - **`featureserver`** mode (default) pages the live FeatureService via OBJECTID ranges, writing
|
|
24
|
+
* NDJSON chunks into `<outRoot>/usgov-nad/featureserver/`.
|
|
25
|
+
*
|
|
26
|
+
* ## Usage
|
|
27
|
+
*
|
|
28
|
+
* ```sh
|
|
29
|
+
* mailwoman corpus fetch nad --out-root /mnt/playpen/mailwoman-data/corpus/sources
|
|
30
|
+
*
|
|
31
|
+
* # Resume from an OID
|
|
32
|
+
* mailwoman corpus fetch nad --start-oid 34400001
|
|
33
|
+
*
|
|
34
|
+
* # Increase concurrency on a fast link
|
|
35
|
+
* mailwoman corpus fetch nad --concurrency 8 --page-size 10000
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import type { BaseFetchOptions, FetchSummary } from "./download.ts";
|
|
39
|
+
export interface FetchNADOptions extends BaseFetchOptions {
|
|
40
|
+
/** Fetch strategy. Default `featureserver`. */
|
|
41
|
+
mode?: "featureserver" | "bulk";
|
|
42
|
+
/** Pre-signed S3 URL for bulk mode. */
|
|
43
|
+
nadURL?: string;
|
|
44
|
+
/** Records per output file. Default `100000`. */
|
|
45
|
+
chunkSize?: number;
|
|
46
|
+
/** Records per HTTP request. Default `5000`. */
|
|
47
|
+
pageSize?: number;
|
|
48
|
+
/** Parallel page fetches within a chunk. Default `4`. */
|
|
49
|
+
concurrency?: number;
|
|
50
|
+
/** Start OBJECTID. Default `1`. */
|
|
51
|
+
startOID?: number;
|
|
52
|
+
/** Stop before this OID. Default = total count. */
|
|
53
|
+
endOID?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function fetchNAD(options: FetchNADOptions, report?: (line: string) => void): Promise<FetchSummary>;
|
|
56
|
+
//# sourceMappingURL=nad.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nad.d.ts","sourceRoot":"","sources":["../../../../src/tools/fetch/nad.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAOnE,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACxD,+CAA+C;IAC/C,IAAI,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;IAC/B,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAiOD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAY/G"}
|