@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.
Files changed (483) hide show
  1. package/out/src/adapter.d.ts +15 -5
  2. package/out/src/adapter.d.ts.map +1 -1
  3. package/out/src/adapter.js +11 -9
  4. package/out/src/adapter.js.map +1 -1
  5. package/out/src/adapters/ban/adapter.d.ts +7 -0
  6. package/out/src/adapters/ban/adapter.d.ts.map +1 -1
  7. package/out/src/adapters/ban/adapter.js +9 -2
  8. package/out/src/adapters/ban/adapter.js.map +1 -1
  9. package/out/src/adapters/ban/street-decompose.d.ts.map +1 -1
  10. package/out/src/adapters/ban/street-decompose.js +5 -1
  11. package/out/src/adapters/ban/street-decompose.js.map +1 -1
  12. package/out/src/adapters/fcc-bdc/adapter.d.ts +14 -1
  13. package/out/src/adapters/fcc-bdc/adapter.d.ts.map +1 -1
  14. package/out/src/adapters/fcc-bdc/adapter.js +15 -2
  15. package/out/src/adapters/fcc-bdc/adapter.js.map +1 -1
  16. package/out/src/adapters/geonames/adapter.d.ts +11 -0
  17. package/out/src/adapters/geonames/adapter.d.ts.map +1 -1
  18. package/out/src/adapters/geonames/adapter.js +24 -5
  19. package/out/src/adapters/geonames/adapter.js.map +1 -1
  20. package/out/src/adapters/geonames-postal/adapter.d.ts +11 -0
  21. package/out/src/adapters/geonames-postal/adapter.d.ts.map +1 -1
  22. package/out/src/adapters/geonames-postal/adapter.js +14 -1
  23. package/out/src/adapters/geonames-postal/adapter.js.map +1 -1
  24. package/out/src/adapters/gnaf/adapter.d.ts +10 -1
  25. package/out/src/adapters/gnaf/adapter.d.ts.map +1 -1
  26. package/out/src/adapters/gnaf/adapter.js +11 -2
  27. package/out/src/adapters/gnaf/adapter.js.map +1 -1
  28. package/out/src/adapters/gnaf/assemble.d.ts +18 -6
  29. package/out/src/adapters/gnaf/assemble.d.ts.map +1 -1
  30. package/out/src/adapters/gnaf/assemble.js +16 -6
  31. package/out/src/adapters/gnaf/assemble.js.map +1 -1
  32. package/out/src/adapters/index.d.ts.map +1 -1
  33. package/out/src/adapters/index.js.map +1 -1
  34. package/out/src/adapters/openaddresses/adapter.d.ts +11 -0
  35. package/out/src/adapters/openaddresses/adapter.d.ts.map +1 -1
  36. package/out/src/adapters/openaddresses/adapter.js +18 -3
  37. package/out/src/adapters/openaddresses/adapter.js.map +1 -1
  38. package/out/src/adapters/overture/adapter.d.ts +11 -0
  39. package/out/src/adapters/overture/adapter.d.ts.map +1 -1
  40. package/out/src/adapters/overture/adapter.js +12 -1
  41. package/out/src/adapters/overture/adapter.js.map +1 -1
  42. package/out/src/adapters/state-hi-schools/adapter.d.ts +11 -0
  43. package/out/src/adapters/state-hi-schools/adapter.d.ts.map +1 -1
  44. package/out/src/adapters/state-hi-schools/adapter.js +11 -0
  45. package/out/src/adapters/state-hi-schools/adapter.js.map +1 -1
  46. package/out/src/adapters/state-ia-contractors/adapter.d.ts +11 -0
  47. package/out/src/adapters/state-ia-contractors/adapter.d.ts.map +1 -1
  48. package/out/src/adapters/state-ia-contractors/adapter.js +11 -0
  49. package/out/src/adapters/state-ia-contractors/adapter.js.map +1 -1
  50. package/out/src/adapters/state-ny-notaries/adapter.d.ts +11 -0
  51. package/out/src/adapters/state-ny-notaries/adapter.d.ts.map +1 -1
  52. package/out/src/adapters/state-ny-notaries/adapter.js +11 -0
  53. package/out/src/adapters/state-ny-notaries/adapter.js.map +1 -1
  54. package/out/src/adapters/state-tx-notaries/adapter.d.ts +11 -0
  55. package/out/src/adapters/state-tx-notaries/adapter.d.ts.map +1 -1
  56. package/out/src/adapters/state-tx-notaries/adapter.js +15 -2
  57. package/out/src/adapters/state-tx-notaries/adapter.js.map +1 -1
  58. package/out/src/adapters/synth-po-box/adapter.d.ts +11 -0
  59. package/out/src/adapters/synth-po-box/adapter.d.ts.map +1 -1
  60. package/out/src/adapters/synth-po-box/adapter.js +13 -2
  61. package/out/src/adapters/synth-po-box/adapter.js.map +1 -1
  62. package/out/src/adapters/tiger/adapter.d.ts +14 -1
  63. package/out/src/adapters/tiger/adapter.d.ts.map +1 -1
  64. package/out/src/adapters/tiger/adapter.js +19 -4
  65. package/out/src/adapters/tiger/adapter.js.map +1 -1
  66. package/out/src/adapters/tiger/street-decompose.d.ts.map +1 -1
  67. package/out/src/adapters/tiger/street-decompose.js.map +1 -1
  68. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts +11 -0
  69. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts.map +1 -1
  70. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js +12 -1
  71. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js.map +1 -1
  72. package/out/src/adapters/usgov-imls-pls/adapter.d.ts +11 -0
  73. package/out/src/adapters/usgov-imls-pls/adapter.d.ts.map +1 -1
  74. package/out/src/adapters/usgov-imls-pls/adapter.js +11 -0
  75. package/out/src/adapters/usgov-imls-pls/adapter.js.map +1 -1
  76. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts +11 -0
  77. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts.map +1 -1
  78. package/out/src/adapters/usgov-irs-bmf/adapter.js +17 -2
  79. package/out/src/adapters/usgov-irs-bmf/adapter.js.map +1 -1
  80. package/out/src/adapters/usgov-nad/adapter.d.ts +11 -0
  81. package/out/src/adapters/usgov-nad/adapter.d.ts.map +1 -1
  82. package/out/src/adapters/usgov-nad/adapter.js +12 -1
  83. package/out/src/adapters/usgov-nad/adapter.js.map +1 -1
  84. package/out/src/adapters/usgov-nppes/adapter.d.ts +11 -0
  85. package/out/src/adapters/usgov-nppes/adapter.d.ts.map +1 -1
  86. package/out/src/adapters/usgov-nppes/adapter.js +11 -0
  87. package/out/src/adapters/usgov-nppes/adapter.js.map +1 -1
  88. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts +11 -0
  89. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts.map +1 -1
  90. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js +15 -2
  91. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js.map +1 -1
  92. package/out/src/adapters/wof-admin-jp/adapter.d.ts +7 -0
  93. package/out/src/adapters/wof-admin-jp/adapter.d.ts.map +1 -1
  94. package/out/src/adapters/wof-admin-jp/adapter.js +18 -3
  95. package/out/src/adapters/wof-admin-jp/adapter.js.map +1 -1
  96. package/out/src/adapters/wof-admin-json/adapter.d.ts +13 -3
  97. package/out/src/adapters/wof-admin-json/adapter.d.ts.map +1 -1
  98. package/out/src/adapters/wof-admin-json/adapter.js +15 -5
  99. package/out/src/adapters/wof-admin-json/adapter.js.map +1 -1
  100. package/out/src/adapters/wof-postalcode-json/adapter.d.ts +7 -0
  101. package/out/src/adapters/wof-postalcode-json/adapter.d.ts.map +1 -1
  102. package/out/src/adapters/wof-postalcode-json/adapter.js +9 -2
  103. package/out/src/adapters/wof-postalcode-json/adapter.js.map +1 -1
  104. package/out/src/align.d.ts +12 -4
  105. package/out/src/align.d.ts.map +1 -1
  106. package/out/src/align.js +9 -5
  107. package/out/src/align.js.map +1 -1
  108. package/out/src/build.d.ts +24 -8
  109. package/out/src/build.d.ts.map +1 -1
  110. package/out/src/build.js +3 -3
  111. package/out/src/build.js.map +1 -1
  112. package/out/src/codex/us-fips-state.d.ts +6 -2
  113. package/out/src/codex/us-fips-state.d.ts.map +1 -1
  114. package/out/src/codex/us-fips-state.js +3 -1
  115. package/out/src/codex/us-fips-state.js.map +1 -1
  116. package/out/src/golden.d.ts +21 -7
  117. package/out/src/golden.d.ts.map +1 -1
  118. package/out/src/golden.js +18 -10
  119. package/out/src/golden.js.map +1 -1
  120. package/out/src/license.d.ts +3 -1
  121. package/out/src/license.d.ts.map +1 -1
  122. package/out/src/license.js +4 -2
  123. package/out/src/license.js.map +1 -1
  124. package/out/src/parquet-wrapper/reader.d.ts +4 -2
  125. package/out/src/parquet-wrapper/reader.d.ts.map +1 -1
  126. package/out/src/parquet-wrapper/reader.js +5 -4
  127. package/out/src/parquet-wrapper/reader.js.map +1 -1
  128. package/out/src/parquet-wrapper/schema.d.ts +14 -10
  129. package/out/src/parquet-wrapper/schema.d.ts.map +1 -1
  130. package/out/src/parquet-wrapper/schema.js +6 -2
  131. package/out/src/parquet-wrapper/schema.js.map +1 -1
  132. package/out/src/parquet-wrapper/writer.d.ts +12 -4
  133. package/out/src/parquet-wrapper/writer.d.ts.map +1 -1
  134. package/out/src/parquet-wrapper/writer.js +12 -4
  135. package/out/src/parquet-wrapper/writer.js.map +1 -1
  136. package/out/src/parquet.d.ts +21 -7
  137. package/out/src/parquet.d.ts.map +1 -1
  138. package/out/src/parquet.js +15 -4
  139. package/out/src/parquet.js.map +1 -1
  140. package/out/src/runner.d.ts +39 -13
  141. package/out/src/runner.d.ts.map +1 -1
  142. package/out/src/runner.js +7 -3
  143. package/out/src/runner.js.map +1 -1
  144. package/out/src/shard-recipes/anchor-absorption.d.ts +4 -0
  145. package/out/src/shard-recipes/anchor-absorption.d.ts.map +1 -1
  146. package/out/src/shard-recipes/anchor-absorption.js +14 -5
  147. package/out/src/shard-recipes/anchor-absorption.js.map +1 -1
  148. package/out/src/shard-recipes/boundary-stress.d.ts +4 -0
  149. package/out/src/shard-recipes/boundary-stress.d.ts.map +1 -1
  150. package/out/src/shard-recipes/boundary-stress.js +13 -7
  151. package/out/src/shard-recipes/boundary-stress.js.map +1 -1
  152. package/out/src/shard-recipes/country-balanced.d.ts +4 -0
  153. package/out/src/shard-recipes/country-balanced.d.ts.map +1 -1
  154. package/out/src/shard-recipes/country-balanced.js +39 -11
  155. package/out/src/shard-recipes/country-balanced.js.map +1 -1
  156. package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts +4 -0
  157. package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts.map +1 -1
  158. package/out/src/shard-recipes/cz-pcfirst-preposition.js +4 -0
  159. package/out/src/shard-recipes/cz-pcfirst-preposition.js.map +1 -1
  160. package/out/src/shard-recipes/fr-admin-split.d.ts +4 -0
  161. package/out/src/shard-recipes/fr-admin-split.d.ts.map +1 -1
  162. package/out/src/shard-recipes/fr-admin-split.js +14 -3
  163. package/out/src/shard-recipes/fr-admin-split.js.map +1 -1
  164. package/out/src/shard-recipes/fr-bare-street.d.ts +4 -0
  165. package/out/src/shard-recipes/fr-bare-street.d.ts.map +1 -1
  166. package/out/src/shard-recipes/fr-bare-street.js +4 -0
  167. package/out/src/shard-recipes/fr-bare-street.js.map +1 -1
  168. package/out/src/shard-recipes/fr-fragment.d.ts +4 -0
  169. package/out/src/shard-recipes/fr-fragment.d.ts.map +1 -1
  170. package/out/src/shard-recipes/fr-fragment.js +25 -8
  171. package/out/src/shard-recipes/fr-fragment.js.map +1 -1
  172. package/out/src/shard-recipes/fr-lieudit.d.ts +4 -0
  173. package/out/src/shard-recipes/fr-lieudit.d.ts.map +1 -1
  174. package/out/src/shard-recipes/fr-lieudit.js +21 -8
  175. package/out/src/shard-recipes/fr-lieudit.js.map +1 -1
  176. package/out/src/shard-recipes/fr-order.d.ts +4 -0
  177. package/out/src/shard-recipes/fr-order.d.ts.map +1 -1
  178. package/out/src/shard-recipes/fr-order.js +27 -8
  179. package/out/src/shard-recipes/fr-order.js.map +1 -1
  180. package/out/src/shard-recipes/german.d.ts +4 -0
  181. package/out/src/shard-recipes/german.d.ts.map +1 -1
  182. package/out/src/shard-recipes/german.js +16 -6
  183. package/out/src/shard-recipes/german.js.map +1 -1
  184. package/out/src/shard-recipes/house-venue.d.ts +4 -0
  185. package/out/src/shard-recipes/house-venue.d.ts.map +1 -1
  186. package/out/src/shard-recipes/house-venue.js +4 -0
  187. package/out/src/shard-recipes/house-venue.js.map +1 -1
  188. package/out/src/shard-recipes/index.d.ts +9 -3
  189. package/out/src/shard-recipes/index.d.ts.map +1 -1
  190. package/out/src/shard-recipes/index.js +12 -4
  191. package/out/src/shard-recipes/index.js.map +1 -1
  192. package/out/src/shard-recipes/intersection.d.ts +4 -0
  193. package/out/src/shard-recipes/intersection.d.ts.map +1 -1
  194. package/out/src/shard-recipes/intersection.js +27 -13
  195. package/out/src/shard-recipes/intersection.js.map +1 -1
  196. package/out/src/shard-recipes/locale.d.ts +4 -0
  197. package/out/src/shard-recipes/locale.d.ts.map +1 -1
  198. package/out/src/shard-recipes/locale.js +9 -4
  199. package/out/src/shard-recipes/locale.js.map +1 -1
  200. package/out/src/shard-recipes/nl-postcode.d.ts +4 -0
  201. package/out/src/shard-recipes/nl-postcode.d.ts.map +1 -1
  202. package/out/src/shard-recipes/nl-postcode.js +8 -2
  203. package/out/src/shard-recipes/nl-postcode.js.map +1 -1
  204. package/out/src/shard-recipes/no-fragment.d.ts +4 -0
  205. package/out/src/shard-recipes/no-fragment.d.ts.map +1 -1
  206. package/out/src/shard-recipes/no-fragment.js +8 -2
  207. package/out/src/shard-recipes/no-fragment.js.map +1 -1
  208. package/out/src/shard-recipes/no-street-led.d.ts +4 -0
  209. package/out/src/shard-recipes/no-street-led.d.ts.map +1 -1
  210. package/out/src/shard-recipes/no-street-led.js +5 -1
  211. package/out/src/shard-recipes/no-street-led.js.map +1 -1
  212. package/out/src/shard-recipes/no-street.d.ts +4 -0
  213. package/out/src/shard-recipes/no-street.d.ts.map +1 -1
  214. package/out/src/shard-recipes/no-street.js +4 -0
  215. package/out/src/shard-recipes/no-street.js.map +1 -1
  216. package/out/src/shard-recipes/po-box-cedex.d.ts +4 -0
  217. package/out/src/shard-recipes/po-box-cedex.d.ts.map +1 -1
  218. package/out/src/shard-recipes/po-box-cedex.js +99 -43
  219. package/out/src/shard-recipes/po-box-cedex.js.map +1 -1
  220. package/out/src/shard-recipes/po-box.d.ts +4 -0
  221. package/out/src/shard-recipes/po-box.d.ts.map +1 -1
  222. package/out/src/shard-recipes/po-box.js +4 -0
  223. package/out/src/shard-recipes/po-box.js.map +1 -1
  224. package/out/src/shard-recipes/scaffold.d.ts +48 -16
  225. package/out/src/shard-recipes/scaffold.d.ts.map +1 -1
  226. package/out/src/shard-recipes/scaffold.js +10 -6
  227. package/out/src/shard-recipes/scaffold.js.map +1 -1
  228. package/out/src/shard-recipes/si-bare-village.d.ts +4 -0
  229. package/out/src/shard-recipes/si-bare-village.d.ts.map +1 -1
  230. package/out/src/shard-recipes/si-bare-village.js +4 -0
  231. package/out/src/shard-recipes/si-bare-village.js.map +1 -1
  232. package/out/src/shard-recipes/street-affix.d.ts +4 -0
  233. package/out/src/shard-recipes/street-affix.d.ts.map +1 -1
  234. package/out/src/shard-recipes/street-affix.js +29 -10
  235. package/out/src/shard-recipes/street-affix.js.map +1 -1
  236. package/out/src/shard-recipes/street-bare.d.ts +4 -0
  237. package/out/src/shard-recipes/street-bare.d.ts.map +1 -1
  238. package/out/src/shard-recipes/street-bare.js +4 -0
  239. package/out/src/shard-recipes/street-bare.js.map +1 -1
  240. package/out/src/shard-recipes/street.d.ts +4 -0
  241. package/out/src/shard-recipes/street.d.ts.map +1 -1
  242. package/out/src/shard-recipes/street.js +4 -0
  243. package/out/src/shard-recipes/street.js.map +1 -1
  244. package/out/src/shard-recipes/unit.d.ts +4 -0
  245. package/out/src/shard-recipes/unit.d.ts.map +1 -1
  246. package/out/src/shard-recipes/unit.js +45 -14
  247. package/out/src/shard-recipes/unit.js.map +1 -1
  248. package/out/src/split.d.ts +18 -6
  249. package/out/src/split.d.ts.map +1 -1
  250. package/out/src/split.js +4 -2
  251. package/out/src/split.js.map +1 -1
  252. package/out/src/synthesize-anchor-absorption.d.ts +7 -1
  253. package/out/src/synthesize-anchor-absorption.d.ts.map +1 -1
  254. package/out/src/synthesize-anchor-absorption.js +28 -14
  255. package/out/src/synthesize-anchor-absorption.js.map +1 -1
  256. package/out/src/synthesize-boundary-stress.d.ts +3 -1
  257. package/out/src/synthesize-boundary-stress.d.ts.map +1 -1
  258. package/out/src/synthesize-boundary-stress.js +35 -28
  259. package/out/src/synthesize-boundary-stress.js.map +1 -1
  260. package/out/src/synthesize-german.d.ts +15 -5
  261. package/out/src/synthesize-german.d.ts.map +1 -1
  262. package/out/src/synthesize-german.js +9 -3
  263. package/out/src/synthesize-german.js.map +1 -1
  264. package/out/src/synthesize-house-venue.d.ts.map +1 -1
  265. package/out/src/synthesize-house-venue.js +15 -15
  266. package/out/src/synthesize-house-venue.js.map +1 -1
  267. package/out/src/synthesize-intersection.d.ts +9 -3
  268. package/out/src/synthesize-intersection.d.ts.map +1 -1
  269. package/out/src/synthesize-intersection.js +25 -6
  270. package/out/src/synthesize-intersection.js.map +1 -1
  271. package/out/src/synthesize-no-street.d.ts +3 -1
  272. package/out/src/synthesize-no-street.d.ts.map +1 -1
  273. package/out/src/synthesize-no-street.js +5 -6
  274. package/out/src/synthesize-no-street.js.map +1 -1
  275. package/out/src/synthesize-po-box.d.ts +15 -5
  276. package/out/src/synthesize-po-box.d.ts.map +1 -1
  277. package/out/src/synthesize-po-box.js +21 -7
  278. package/out/src/synthesize-po-box.js.map +1 -1
  279. package/out/src/synthesize-street.d.ts +3 -1
  280. package/out/src/synthesize-street.d.ts.map +1 -1
  281. package/out/src/synthesize-street.js +6 -1
  282. package/out/src/synthesize-street.js.map +1 -1
  283. package/out/src/synthesize.d.ts +39 -13
  284. package/out/src/synthesize.d.ts.map +1 -1
  285. package/out/src/synthesize.js +66 -36
  286. package/out/src/synthesize.js.map +1 -1
  287. package/out/src/tokenize.d.ts +15 -5
  288. package/out/src/tokenize.d.ts.map +1 -1
  289. package/out/src/tokenize.js.map +1 -1
  290. package/out/src/tools/align-shard.d.ts.map +1 -1
  291. package/out/src/tools/align-shard.js +3 -1
  292. package/out/src/tools/align-shard.js.map +1 -1
  293. package/out/src/tools/audit.d.ts.map +1 -1
  294. package/out/src/tools/audit.js +20 -8
  295. package/out/src/tools/audit.js.map +1 -1
  296. package/out/src/tools/corpus-stats.d.ts.map +1 -1
  297. package/out/src/tools/corpus-stats.js.map +1 -1
  298. package/out/src/tools/fetch/ban.d.ts.map +1 -1
  299. package/out/src/tools/fetch/ban.js +17 -10
  300. package/out/src/tools/fetch/ban.js.map +1 -1
  301. package/out/src/tools/fetch/download.d.ts +30 -10
  302. package/out/src/tools/fetch/download.d.ts.map +1 -1
  303. package/out/src/tools/fetch/download.js +26 -6
  304. package/out/src/tools/fetch/download.js.map +1 -1
  305. package/out/src/tools/fetch/hrsa.d.ts.map +1 -1
  306. package/out/src/tools/fetch/hrsa.js.map +1 -1
  307. package/out/src/tools/fetch/imls-pls.d.ts.map +1 -1
  308. package/out/src/tools/fetch/imls-pls.js +18 -18
  309. package/out/src/tools/fetch/imls-pls.js.map +1 -1
  310. package/out/src/tools/fetch/index.d.ts +3 -1
  311. package/out/src/tools/fetch/index.d.ts.map +1 -1
  312. package/out/src/tools/fetch/index.js +3 -1
  313. package/out/src/tools/fetch/index.js.map +1 -1
  314. package/out/src/tools/fetch/nad.d.ts +21 -7
  315. package/out/src/tools/fetch/nad.d.ts.map +1 -1
  316. package/out/src/tools/fetch/nad.js +5 -5
  317. package/out/src/tools/fetch/nad.js.map +1 -1
  318. package/out/src/tools/fetch/nppes.d.ts.map +1 -1
  319. package/out/src/tools/fetch/nppes.js +9 -15
  320. package/out/src/tools/fetch/nppes.js.map +1 -1
  321. package/out/src/tools/fetch/openaddresses.d.ts +3 -1
  322. package/out/src/tools/fetch/openaddresses.d.ts.map +1 -1
  323. package/out/src/tools/fetch/openaddresses.js +32 -23
  324. package/out/src/tools/fetch/openaddresses.js.map +1 -1
  325. package/out/src/tools/fetch/ppd.d.ts +3 -1
  326. package/out/src/tools/fetch/ppd.d.ts.map +1 -1
  327. package/out/src/tools/fetch/ppd.js +14 -3
  328. package/out/src/tools/fetch/ppd.js.map +1 -1
  329. package/out/src/tools/fetch/state-hi-schools.d.ts.map +1 -1
  330. package/out/src/tools/fetch/state-hi-schools.js +13 -6
  331. package/out/src/tools/fetch/state-hi-schools.js.map +1 -1
  332. package/out/src/tools/fetch/state-sources.d.ts.map +1 -1
  333. package/out/src/tools/fetch/state-sources.js +8 -3
  334. package/out/src/tools/fetch/state-sources.js.map +1 -1
  335. package/out/src/tools/fetch/tiger-full.d.ts +9 -3
  336. package/out/src/tools/fetch/tiger-full.d.ts.map +1 -1
  337. package/out/src/tools/fetch/tiger-full.js +35 -20
  338. package/out/src/tools/fetch/tiger-full.js.map +1 -1
  339. package/out/src/tools/gb-title-case.d.ts.map +1 -1
  340. package/out/src/tools/gb-title-case.js.map +1 -1
  341. package/out/src/tools/golden-expand.d.ts +24 -8
  342. package/out/src/tools/golden-expand.d.ts.map +1 -1
  343. package/out/src/tools/golden-expand.js +20 -12
  344. package/out/src/tools/golden-expand.js.map +1 -1
  345. package/out/src/tools/golden-promote.d.ts +18 -6
  346. package/out/src/tools/golden-promote.d.ts.map +1 -1
  347. package/out/src/tools/golden-promote.js +22 -7
  348. package/out/src/tools/golden-promote.js.map +1 -1
  349. package/out/src/tools/ingest-csv.d.ts +3 -1
  350. package/out/src/tools/ingest-csv.d.ts.map +1 -1
  351. package/out/src/tools/ingest-csv.js +17 -19
  352. package/out/src/tools/ingest-csv.js.map +1 -1
  353. package/out/src/tools/jsonl-to-parquet.d.ts +18 -6
  354. package/out/src/tools/jsonl-to-parquet.d.ts.map +1 -1
  355. package/out/src/tools/jsonl-to-parquet.js +11 -5
  356. package/out/src/tools/jsonl-to-parquet.js.map +1 -1
  357. package/out/src/tools/lint-shard-vocab.d.ts +36 -12
  358. package/out/src/tools/lint-shard-vocab.d.ts.map +1 -1
  359. package/out/src/tools/lint-shard-vocab.js +25 -11
  360. package/out/src/tools/lint-shard-vocab.js.map +1 -1
  361. package/out/src/tools/lint-shard.d.ts +30 -10
  362. package/out/src/tools/lint-shard.d.ts.map +1 -1
  363. package/out/src/tools/lint-shard.js +33 -20
  364. package/out/src/tools/lint-shard.js.map +1 -1
  365. package/out/src/tools/overlay-manifest.d.ts.map +1 -1
  366. package/out/src/tools/overlay-manifest.js +6 -4
  367. package/out/src/tools/overlay-manifest.js.map +1 -1
  368. package/out/src/tools/shard-kryptonite.d.ts +6 -2
  369. package/out/src/tools/shard-kryptonite.d.ts.map +1 -1
  370. package/out/src/tools/shard-kryptonite.js +1 -1
  371. package/out/src/tools/shard-kryptonite.js.map +1 -1
  372. package/out/src/tools/shard-translit.d.ts +9 -3
  373. package/out/src/tools/shard-translit.d.ts.map +1 -1
  374. package/out/src/tools/shard-translit.js +2 -2
  375. package/out/src/tools/shard-translit.js.map +1 -1
  376. package/out/src/types.d.ts +51 -17
  377. package/out/src/types.d.ts.map +1 -1
  378. package/out/src/wof-json.d.ts +9 -3
  379. package/out/src/wof-json.d.ts.map +1 -1
  380. package/out/src/wof-json.js +6 -4
  381. package/out/src/wof-json.js.map +1 -1
  382. package/package.json +7 -6
  383. package/src/adapter.ts +24 -13
  384. package/src/adapters/ban/adapter.ts +12 -2
  385. package/src/adapters/ban/street-decompose.ts +7 -1
  386. package/src/adapters/fcc-bdc/adapter.ts +19 -3
  387. package/src/adapters/geonames/adapter.ts +28 -5
  388. package/src/adapters/geonames-postal/adapter.ts +17 -1
  389. package/src/adapters/gnaf/adapter.ts +15 -2
  390. package/src/adapters/gnaf/assemble.ts +40 -10
  391. package/src/adapters/index.ts +13 -0
  392. package/src/adapters/openaddresses/adapter.ts +22 -3
  393. package/src/adapters/overture/adapter.ts +18 -2
  394. package/src/adapters/state-hi-schools/adapter.ts +15 -0
  395. package/src/adapters/state-ia-contractors/adapter.ts +15 -0
  396. package/src/adapters/state-ny-notaries/adapter.ts +15 -0
  397. package/src/adapters/state-tx-notaries/adapter.ts +18 -2
  398. package/src/adapters/synth-po-box/adapter.ts +19 -2
  399. package/src/adapters/tiger/adapter.ts +23 -4
  400. package/src/adapters/tiger/street-decompose.ts +1 -0
  401. package/src/adapters/usgov-hrsa-fqhc/adapter.ts +18 -2
  402. package/src/adapters/usgov-imls-pls/adapter.ts +15 -0
  403. package/src/adapters/usgov-irs-bmf/adapter.ts +19 -2
  404. package/src/adapters/usgov-nad/adapter.ts +14 -1
  405. package/src/adapters/usgov-nppes/adapter.ts +13 -0
  406. package/src/adapters/usgov-samhsa-treatment-locator/adapter.ts +21 -3
  407. package/src/adapters/wof-admin-jp/adapter.ts +20 -3
  408. package/src/adapters/wof-admin-json/adapter.ts +22 -7
  409. package/src/adapters/wof-postalcode-json/adapter.ts +10 -2
  410. package/src/align.ts +21 -8
  411. package/src/build.ts +48 -11
  412. package/src/codex/us-fips-state.ts +6 -2
  413. package/src/golden.ts +28 -13
  414. package/src/license.ts +4 -2
  415. package/src/parquet-wrapper/reader.ts +6 -4
  416. package/src/parquet-wrapper/schema.ts +14 -10
  417. package/src/parquet-wrapper/writer.ts +12 -4
  418. package/src/parquet.ts +39 -8
  419. package/src/runner.ts +49 -15
  420. package/src/shard-recipes/anchor-absorption.ts +21 -5
  421. package/src/shard-recipes/boundary-stress.ts +20 -7
  422. package/src/shard-recipes/country-balanced.ts +78 -13
  423. package/src/shard-recipes/cz-pcfirst-preposition.ts +9 -0
  424. package/src/shard-recipes/fr-admin-split.ts +30 -5
  425. package/src/shard-recipes/fr-bare-street.ts +10 -0
  426. package/src/shard-recipes/fr-fragment.ts +33 -8
  427. package/src/shard-recipes/fr-lieudit.ts +35 -9
  428. package/src/shard-recipes/fr-order.ts +47 -9
  429. package/src/shard-recipes/german.ts +32 -6
  430. package/src/shard-recipes/house-venue.ts +7 -0
  431. package/src/shard-recipes/index.ts +12 -4
  432. package/src/shard-recipes/intersection.ts +63 -14
  433. package/src/shard-recipes/locale.ts +37 -4
  434. package/src/shard-recipes/nl-postcode.ts +12 -2
  435. package/src/shard-recipes/no-fragment.ts +12 -2
  436. package/src/shard-recipes/no-street-led.ts +11 -1
  437. package/src/shard-recipes/no-street.ts +7 -0
  438. package/src/shard-recipes/po-box-cedex.ts +145 -45
  439. package/src/shard-recipes/po-box.ts +7 -0
  440. package/src/shard-recipes/scaffold.ts +52 -20
  441. package/src/shard-recipes/si-bare-village.ts +9 -0
  442. package/src/shard-recipes/street-affix.ts +88 -13
  443. package/src/shard-recipes/street-bare.ts +10 -0
  444. package/src/shard-recipes/street.ts +7 -0
  445. package/src/shard-recipes/unit.ts +79 -16
  446. package/src/split.ts +28 -7
  447. package/src/synthesize-anchor-absorption.ts +41 -14
  448. package/src/synthesize-boundary-stress.ts +57 -30
  449. package/src/synthesize-german.ts +30 -7
  450. package/src/synthesize-house-venue.ts +22 -16
  451. package/src/synthesize-intersection.ts +34 -7
  452. package/src/synthesize-no-street.ts +18 -10
  453. package/src/synthesize-po-box.ts +42 -10
  454. package/src/synthesize-street.ts +10 -2
  455. package/src/synthesize.ts +81 -38
  456. package/src/tokenize.ts +15 -5
  457. package/src/tools/align-shard.ts +6 -1
  458. package/src/tools/audit.ts +57 -11
  459. package/src/tools/corpus-stats.ts +9 -0
  460. package/src/tools/fetch/ban.ts +32 -10
  461. package/src/tools/fetch/download.ts +49 -12
  462. package/src/tools/fetch/hrsa.ts +3 -0
  463. package/src/tools/fetch/imls-pls.ts +28 -18
  464. package/src/tools/fetch/index.ts +3 -1
  465. package/src/tools/fetch/nad.ts +39 -12
  466. package/src/tools/fetch/nppes.ts +17 -15
  467. package/src/tools/fetch/openaddresses.ts +52 -24
  468. package/src/tools/fetch/ppd.ts +22 -3
  469. package/src/tools/fetch/state-hi-schools.ts +17 -6
  470. package/src/tools/fetch/state-sources.ts +16 -3
  471. package/src/tools/fetch/tiger-full.ts +66 -23
  472. package/src/tools/gb-title-case.ts +1 -0
  473. package/src/tools/golden-expand.ts +64 -20
  474. package/src/tools/golden-promote.ts +53 -13
  475. package/src/tools/ingest-csv.ts +41 -26
  476. package/src/tools/jsonl-to-parquet.ts +30 -10
  477. package/src/tools/lint-shard-vocab.ts +75 -24
  478. package/src/tools/lint-shard.ts +75 -31
  479. package/src/tools/overlay-manifest.ts +7 -4
  480. package/src/tools/shard-kryptonite.ts +11 -3
  481. package/src/tools/shard-translit.ts +20 -5
  482. package/src/types.ts +51 -17
  483. package/src/wof-json.ts +18 -7
@@ -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
- /** The fetch-source registry: id → module entry point. Each entry point takes its own options interface. */
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,
@@ -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
- /** Fetch strategy. Default `featureserver`. */
54
+ /**
55
+ * Fetch strategy. Default `featureserver`.
56
+ */
54
57
  mode?: "featureserver" | "bulk"
55
- /** Pre-signed S3 URL for bulk mode. */
58
+ /**
59
+ * Pre-signed S3 URL for bulk mode.
60
+ */
56
61
  nadURL?: string
57
- /** Records per output file. Default `100000`. */
62
+ /**
63
+ * Records per output file. Default `100000`.
64
+ */
58
65
  chunkSize?: number
59
- /** Records per HTTP request. Default `5000`. */
66
+ /**
67
+ * Records per HTTP request. Default `5000`.
68
+ */
60
69
  pageSize?: number
61
- /** Parallel page fetches within a chunk. Default `4`. */
70
+ /**
71
+ * Parallel page fetches within a chunk. Default `4`.
72
+ */
62
73
  concurrency?: number
63
- /** Start OBJECTID. Default `1`. */
74
+ /**
75
+ * Start OBJECTID. Default `1`.
76
+ */
64
77
  startOID?: number
65
- /** Stop before this OID. Default = total count. */
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 (err) {
152
- pageResults[slot]!.error = err as Error
153
- report?.(` ✗ page ${s}-${e}: ${(err as Error).message}`)
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
- await writeFile(chunkPath, lines.length === 0 ? "" : lines.join("\n") + "\n")
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 ?? 5_000
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: [] }
@@ -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
- /** Extract the main registry CSV name (npidata_pfile_*.csv) from a ZIP's `unzip -l` listing. */
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
- // Download ZIP (large; 60-minute timeout)
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
- // Extract only the main registry CSV (npidata_pfile_*.csv)
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
- // Remove the ZIP to reclaim ~1 GB (the CSV is what adapters consume)
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
- // Write MANIFEST (records the extracted CSV, not the ZIP)
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
- /** OA country collection code. Default `ca`. */
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
- /** Stream-count newlines, matching `wc -l` (memory-safe for the multi-GB collection). */
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 (let i = 0; i < chunk.length; i++) {
103
- if (chunk[i] === 0x0a) {
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 >= 1024 && unit < units.length - 1) {
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
- /** Decompress `src` → `dest` with the same deprioritized subprocess the old fetcher used. */
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
- // Authentication check
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
- // Determine collection ID
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
- // Download via the collections download endpoint (302s to S3)
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 !== 200) {
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 !== 200) {
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
- // Decompress if the downloaded file is gzipped
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
- // Verify + write MANIFEST
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 < 10240) {
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}`)
@@ -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
- const [, , , postcode, , , , paon, saon, street, locality, town, , county] = cells
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
- /** Stream `inputPath` (PPD CSV) → `outputPath` (OA-shaped tuples CSV), returning the row-count stats. */
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) => out.end(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
- /** Mimic `numfmt --to=iec` for a friendly byte-size log line. */
99
+ /**
100
+ * Mimic `numfmt --to=iec` for a friendly byte-size log line.
101
+ */
94
102
  function iec(bytes: number): string {
95
- if (bytes < 1024) return String(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 >= 1024 && i < units.length - 1) {
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 (err) {
177
- report?.(` ✗ Download failed (${(err as Error).message})`)
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 < 1024) {
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 (err) {
110
- report?.(` ✗ download failed for ${url}: ${(err as Error).message}`)
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 < 1024) {
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