@mailwoman/corpus 9.0.0 → 9.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 (524) hide show
  1. package/README.md +3 -3
  2. package/data/PROVENANCE.md +227 -0
  3. package/data/reviewed-ve-postcode-tuples.json +74 -0
  4. package/data/sub-venue-lexicon.json +18269 -0
  5. package/out/src/adapters/ban/adapter.d.ts +1 -1
  6. package/out/src/adapters/ban/adapter.d.ts.map +1 -1
  7. package/out/src/adapters/ban/adapter.js +5 -4
  8. package/out/src/adapters/ban/adapter.js.map +1 -1
  9. package/out/src/adapters/fcc-bdc/adapter.d.ts +1 -1
  10. package/out/src/adapters/fcc-bdc/adapter.d.ts.map +1 -1
  11. package/out/src/adapters/fcc-bdc/adapter.js +3 -3
  12. package/out/src/adapters/fcc-bdc/adapter.js.map +1 -1
  13. package/out/src/adapters/geonames/adapter.d.ts +1 -1
  14. package/out/src/adapters/geonames/adapter.d.ts.map +1 -1
  15. package/out/src/adapters/geonames/adapter.js +2 -2
  16. package/out/src/adapters/geonames/adapter.js.map +1 -1
  17. package/out/src/adapters/geonames-postal/adapter.d.ts +1 -1
  18. package/out/src/adapters/geonames-postal/adapter.d.ts.map +1 -1
  19. package/out/src/adapters/geonames-postal/adapter.js +3 -3
  20. package/out/src/adapters/geonames-postal/adapter.js.map +1 -1
  21. package/out/src/adapters/gnaf/adapter.d.ts +1 -1
  22. package/out/src/adapters/gnaf/adapter.d.ts.map +1 -1
  23. package/out/src/adapters/gnaf/adapter.js +2 -2
  24. package/out/src/adapters/gnaf/adapter.js.map +1 -1
  25. package/out/src/adapters/gnaf/assemble.js +1 -1
  26. package/out/src/adapters/gnaf/assemble.js.map +1 -1
  27. package/out/src/adapters/index.d.ts +1 -1
  28. package/out/src/adapters/index.d.ts.map +1 -1
  29. package/out/src/adapters/index.js +1 -1
  30. package/out/src/adapters/index.js.map +1 -1
  31. package/out/src/adapters/openaddresses/adapter.d.ts +1 -1
  32. package/out/src/adapters/openaddresses/adapter.d.ts.map +1 -1
  33. package/out/src/adapters/openaddresses/adapter.js +3 -3
  34. package/out/src/adapters/openaddresses/adapter.js.map +1 -1
  35. package/out/src/adapters/overture/adapter.d.ts +1 -1
  36. package/out/src/adapters/overture/adapter.d.ts.map +1 -1
  37. package/out/src/adapters/overture/adapter.js +2 -2
  38. package/out/src/adapters/overture/adapter.js.map +1 -1
  39. package/out/src/adapters/state-hi-schools/adapter.d.ts +1 -1
  40. package/out/src/adapters/state-hi-schools/adapter.d.ts.map +1 -1
  41. package/out/src/adapters/state-hi-schools/adapter.js +7 -6
  42. package/out/src/adapters/state-hi-schools/adapter.js.map +1 -1
  43. package/out/src/adapters/state-ia-contractors/adapter.d.ts +1 -1
  44. package/out/src/adapters/state-ia-contractors/adapter.d.ts.map +1 -1
  45. package/out/src/adapters/state-ia-contractors/adapter.js +12 -7
  46. package/out/src/adapters/state-ia-contractors/adapter.js.map +1 -1
  47. package/out/src/adapters/state-ny-notaries/adapter.d.ts +1 -1
  48. package/out/src/adapters/state-ny-notaries/adapter.d.ts.map +1 -1
  49. package/out/src/adapters/state-ny-notaries/adapter.js +12 -18
  50. package/out/src/adapters/state-ny-notaries/adapter.js.map +1 -1
  51. package/out/src/adapters/state-tx-notaries/adapter.d.ts +1 -1
  52. package/out/src/adapters/state-tx-notaries/adapter.d.ts.map +1 -1
  53. package/out/src/adapters/state-tx-notaries/adapter.js +12 -7
  54. package/out/src/adapters/state-tx-notaries/adapter.js.map +1 -1
  55. package/out/src/adapters/synth-po-box/adapter.d.ts +3 -3
  56. package/out/src/adapters/synth-po-box/adapter.d.ts.map +1 -1
  57. package/out/src/adapters/synth-po-box/adapter.js +3 -3
  58. package/out/src/adapters/synth-po-box/adapter.js.map +1 -1
  59. package/out/src/adapters/tiger/adapter.d.ts +1 -1
  60. package/out/src/adapters/tiger/adapter.d.ts.map +1 -1
  61. package/out/src/adapters/tiger/adapter.js +2 -2
  62. package/out/src/adapters/tiger/adapter.js.map +1 -1
  63. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts +1 -1
  64. package/out/src/adapters/usgov-hrsa-fqhc/adapter.d.ts.map +1 -1
  65. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js +7 -6
  66. package/out/src/adapters/usgov-hrsa-fqhc/adapter.js.map +1 -1
  67. package/out/src/adapters/usgov-imls-pls/adapter.d.ts +1 -1
  68. package/out/src/adapters/usgov-imls-pls/adapter.d.ts.map +1 -1
  69. package/out/src/adapters/usgov-imls-pls/adapter.js +11 -7
  70. package/out/src/adapters/usgov-imls-pls/adapter.js.map +1 -1
  71. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts +1 -1
  72. package/out/src/adapters/usgov-irs-bmf/adapter.d.ts.map +1 -1
  73. package/out/src/adapters/usgov-irs-bmf/adapter.js +6 -5
  74. package/out/src/adapters/usgov-irs-bmf/adapter.js.map +1 -1
  75. package/out/src/adapters/usgov-nad/adapter.d.ts +1 -1
  76. package/out/src/adapters/usgov-nad/adapter.d.ts.map +1 -1
  77. package/out/src/adapters/usgov-nad/adapter.js +8 -8
  78. package/out/src/adapters/usgov-nad/adapter.js.map +1 -1
  79. package/out/src/adapters/usgov-nppes/adapter.d.ts +1 -1
  80. package/out/src/adapters/usgov-nppes/adapter.d.ts.map +1 -1
  81. package/out/src/adapters/usgov-nppes/adapter.js +10 -10
  82. package/out/src/adapters/usgov-nppes/adapter.js.map +1 -1
  83. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts +1 -1
  84. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.d.ts.map +1 -1
  85. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js +7 -6
  86. package/out/src/adapters/usgov-samhsa-treatment-locator/adapter.js.map +1 -1
  87. package/out/src/{adapter.d.ts → adapters/utils/index.d.ts} +8 -2
  88. package/out/src/adapters/utils/index.d.ts.map +1 -0
  89. package/out/src/{adapter.js → adapters/utils/index.js} +11 -3
  90. package/out/src/adapters/utils/index.js.map +1 -0
  91. package/out/src/adapters/wof-admin-jp/adapter.d.ts +1 -1
  92. package/out/src/adapters/wof-admin-jp/adapter.d.ts.map +1 -1
  93. package/out/src/adapters/wof-admin-jp/adapter.js +5 -5
  94. package/out/src/adapters/wof-admin-jp/adapter.js.map +1 -1
  95. package/out/src/adapters/wof-admin-json/adapter.d.ts +4 -15
  96. package/out/src/adapters/wof-admin-json/adapter.d.ts.map +1 -1
  97. package/out/src/adapters/wof-admin-json/adapter.js +11 -35
  98. package/out/src/adapters/wof-admin-json/adapter.js.map +1 -1
  99. package/out/src/adapters/wof-json-rows.d.ts +32 -0
  100. package/out/src/adapters/wof-json-rows.d.ts.map +1 -0
  101. package/out/src/adapters/wof-json-rows.js +45 -0
  102. package/out/src/adapters/wof-json-rows.js.map +1 -0
  103. package/out/src/adapters/wof-postalcode-json/adapter.d.ts +4 -9
  104. package/out/src/adapters/wof-postalcode-json/adapter.d.ts.map +1 -1
  105. package/out/src/adapters/wof-postalcode-json/adapter.js +12 -37
  106. package/out/src/adapters/wof-postalcode-json/adapter.js.map +1 -1
  107. package/out/src/build.d.ts +4 -4
  108. package/out/src/build.d.ts.map +1 -1
  109. package/out/src/build.js +7 -7
  110. package/out/src/build.js.map +1 -1
  111. package/out/src/index.d.ts +3 -17
  112. package/out/src/index.d.ts.map +1 -1
  113. package/out/src/index.js +3 -17
  114. package/out/src/index.js.map +1 -1
  115. package/out/src/name-prone-us-suffixes.d.ts +12 -0
  116. package/out/src/name-prone-us-suffixes.d.ts.map +1 -0
  117. package/out/src/name-prone-us-suffixes.js +12 -0
  118. package/out/src/name-prone-us-suffixes.js.map +1 -0
  119. package/out/src/parquet-wrapper/reader.d.ts.map +1 -1
  120. package/out/src/parquet-wrapper/reader.js +5 -0
  121. package/out/src/parquet-wrapper/reader.js.map +1 -1
  122. package/out/src/runner.d.ts +2 -2
  123. package/out/src/runner.d.ts.map +1 -1
  124. package/out/src/runner.js +1 -1
  125. package/out/src/runner.js.map +1 -1
  126. package/out/src/shard-recipes/anchor-absorption.d.ts.map +1 -1
  127. package/out/src/shard-recipes/anchor-absorption.js +4 -5
  128. package/out/src/shard-recipes/anchor-absorption.js.map +1 -1
  129. package/out/src/shard-recipes/bare-country.d.ts +25 -0
  130. package/out/src/shard-recipes/bare-country.d.ts.map +1 -0
  131. package/out/src/shard-recipes/bare-country.js +87 -0
  132. package/out/src/shard-recipes/bare-country.js.map +1 -0
  133. package/out/src/shard-recipes/boundary-stress.d.ts.map +1 -1
  134. package/out/src/shard-recipes/boundary-stress.js +2 -2
  135. package/out/src/shard-recipes/boundary-stress.js.map +1 -1
  136. package/out/src/shard-recipes/country-balanced.d.ts.map +1 -1
  137. package/out/src/shard-recipes/country-balanced.js +14 -21
  138. package/out/src/shard-recipes/country-balanced.js.map +1 -1
  139. package/out/src/shard-recipes/cz-pcfirst-preposition.d.ts.map +1 -1
  140. package/out/src/shard-recipes/cz-pcfirst-preposition.js +28 -10
  141. package/out/src/shard-recipes/cz-pcfirst-preposition.js.map +1 -1
  142. package/out/src/shard-recipes/fr-admin-split.d.ts.map +1 -1
  143. package/out/src/shard-recipes/fr-admin-split.js +4 -3
  144. package/out/src/shard-recipes/fr-admin-split.js.map +1 -1
  145. package/out/src/shard-recipes/fr-bare-street.d.ts.map +1 -1
  146. package/out/src/shard-recipes/fr-bare-street.js +72 -11
  147. package/out/src/shard-recipes/fr-bare-street.js.map +1 -1
  148. package/out/src/shard-recipes/fr-lieudit.d.ts.map +1 -1
  149. package/out/src/shard-recipes/fr-lieudit.js +4 -3
  150. package/out/src/shard-recipes/fr-lieudit.js.map +1 -1
  151. package/out/src/shard-recipes/fr-order.d.ts.map +1 -1
  152. package/out/src/shard-recipes/fr-order.js +11 -20
  153. package/out/src/shard-recipes/fr-order.js.map +1 -1
  154. package/out/src/shard-recipes/german.d.ts.map +1 -1
  155. package/out/src/shard-recipes/german.js +8 -14
  156. package/out/src/shard-recipes/german.js.map +1 -1
  157. package/out/src/shard-recipes/house-venue.d.ts.map +1 -1
  158. package/out/src/shard-recipes/house-venue.js +1 -1
  159. package/out/src/shard-recipes/house-venue.js.map +1 -1
  160. package/out/src/shard-recipes/index.d.ts.map +1 -1
  161. package/out/src/shard-recipes/index.js +8 -1
  162. package/out/src/shard-recipes/index.js.map +1 -1
  163. package/out/src/shard-recipes/intersection.d.ts.map +1 -1
  164. package/out/src/shard-recipes/intersection.js +7 -13
  165. package/out/src/shard-recipes/intersection.js.map +1 -1
  166. package/out/src/shard-recipes/locale.d.ts +5 -5
  167. package/out/src/shard-recipes/locale.d.ts.map +1 -1
  168. package/out/src/shard-recipes/locale.js +13 -20
  169. package/out/src/shard-recipes/locale.js.map +1 -1
  170. package/out/src/shard-recipes/no-street.d.ts.map +1 -1
  171. package/out/src/shard-recipes/no-street.js +1 -1
  172. package/out/src/shard-recipes/no-street.js.map +1 -1
  173. package/out/src/shard-recipes/po-box-cedex.d.ts.map +1 -1
  174. package/out/src/shard-recipes/po-box-cedex.js +60 -43
  175. package/out/src/shard-recipes/po-box-cedex.js.map +1 -1
  176. package/out/src/shard-recipes/po-box.d.ts.map +1 -1
  177. package/out/src/shard-recipes/po-box.js +1 -1
  178. package/out/src/shard-recipes/po-box.js.map +1 -1
  179. package/out/src/shard-recipes/reviewed-postcode-tail.d.ts +47 -0
  180. package/out/src/shard-recipes/reviewed-postcode-tail.d.ts.map +1 -0
  181. package/out/src/shard-recipes/reviewed-postcode-tail.js +125 -0
  182. package/out/src/shard-recipes/reviewed-postcode-tail.js.map +1 -0
  183. package/out/src/shard-recipes/scaffold.d.ts +51 -23
  184. package/out/src/shard-recipes/scaffold.d.ts.map +1 -1
  185. package/out/src/shard-recipes/scaffold.js +59 -23
  186. package/out/src/shard-recipes/scaffold.js.map +1 -1
  187. package/out/src/shard-recipes/street-affix.d.ts +54 -0
  188. package/out/src/shard-recipes/street-affix.d.ts.map +1 -1
  189. package/out/src/shard-recipes/street-affix.js +203 -35
  190. package/out/src/shard-recipes/street-affix.js.map +1 -1
  191. package/out/src/shard-recipes/street-bare.d.ts.map +1 -1
  192. package/out/src/shard-recipes/street-bare.js +3 -3
  193. package/out/src/shard-recipes/street-bare.js.map +1 -1
  194. package/out/src/shard-recipes/street.d.ts.map +1 -1
  195. package/out/src/shard-recipes/street.js +2 -2
  196. package/out/src/shard-recipes/street.js.map +1 -1
  197. package/out/src/shard-recipes/sub-venue-sources.d.ts +3 -6
  198. package/out/src/shard-recipes/sub-venue-sources.d.ts.map +1 -1
  199. package/out/src/shard-recipes/sub-venue-sources.js +4 -9
  200. package/out/src/shard-recipes/sub-venue-sources.js.map +1 -1
  201. package/out/src/shard-recipes/sub-venue.d.ts +2 -2
  202. package/out/src/shard-recipes/sub-venue.d.ts.map +1 -1
  203. package/out/src/shard-recipes/sub-venue.js +7 -6
  204. package/out/src/shard-recipes/sub-venue.js.map +1 -1
  205. package/out/src/shard-recipes/trailing-region.d.ts +74 -0
  206. package/out/src/shard-recipes/trailing-region.d.ts.map +1 -0
  207. package/out/src/shard-recipes/trailing-region.js +157 -0
  208. package/out/src/shard-recipes/trailing-region.js.map +1 -0
  209. package/out/src/shard-recipes/unit.d.ts.map +1 -1
  210. package/out/src/shard-recipes/unit.js +6 -12
  211. package/out/src/shard-recipes/unit.js.map +1 -1
  212. package/out/src/{synthesize-anchor-absorption.d.ts → synthesizers/anchor-absorption.d.ts} +1 -1
  213. package/out/src/synthesizers/anchor-absorption.d.ts.map +1 -0
  214. package/out/src/{synthesize-anchor-absorption.js → synthesizers/anchor-absorption.js} +1 -1
  215. package/out/src/synthesizers/anchor-absorption.js.map +1 -0
  216. package/out/src/{synthesize-boundary-stress.d.ts → synthesizers/boundary-stress.d.ts} +3 -3
  217. package/out/src/synthesizers/boundary-stress.d.ts.map +1 -0
  218. package/out/src/{synthesize-boundary-stress.js → synthesizers/boundary-stress.js} +2 -2
  219. package/out/src/synthesizers/boundary-stress.js.map +1 -0
  220. package/out/src/{synthesize-german.d.ts → synthesizers/german.d.ts} +2 -2
  221. package/out/src/synthesizers/german.d.ts.map +1 -0
  222. package/out/src/{synthesize-german.js → synthesizers/german.js} +2 -2
  223. package/out/src/synthesizers/german.js.map +1 -0
  224. package/out/src/{synthesize-house-venue.d.ts → synthesizers/house-venue.d.ts} +4 -4
  225. package/out/src/synthesizers/house-venue.d.ts.map +1 -0
  226. package/out/src/{synthesize-house-venue.js → synthesizers/house-venue.js} +20 -6
  227. package/out/src/synthesizers/house-venue.js.map +1 -0
  228. package/out/src/{synthesize-intersection.d.ts → synthesizers/intersection.d.ts} +2 -2
  229. package/out/src/synthesizers/intersection.d.ts.map +1 -0
  230. package/out/src/{synthesize-intersection.js → synthesizers/intersection.js} +1 -1
  231. package/out/src/synthesizers/intersection.js.map +1 -0
  232. package/out/src/{synthesize-no-street.d.ts → synthesizers/no-street.d.ts} +3 -3
  233. package/out/src/synthesizers/no-street.d.ts.map +1 -0
  234. package/out/src/{synthesize-no-street.js → synthesizers/no-street.js} +2 -2
  235. package/out/src/synthesizers/no-street.js.map +1 -0
  236. package/out/src/{synthesize-po-box.d.ts → synthesizers/po-box.d.ts} +2 -2
  237. package/out/src/synthesizers/po-box.d.ts.map +1 -0
  238. package/out/src/{synthesize-po-box.js → synthesizers/po-box.js} +1 -1
  239. package/out/src/synthesizers/po-box.js.map +1 -0
  240. package/out/src/{synthesize-street.d.ts → synthesizers/street.d.ts} +2 -2
  241. package/out/src/synthesizers/street.d.ts.map +1 -0
  242. package/out/src/{synthesize-street.js → synthesizers/street.js} +4 -3
  243. package/out/src/synthesizers/street.js.map +1 -0
  244. package/out/src/{synthesize.d.ts → synthesizers/utils.d.ts} +3 -3
  245. package/out/src/synthesizers/utils.d.ts.map +1 -0
  246. package/out/src/{synthesize.js → synthesizers/utils.js} +5 -4
  247. package/out/src/synthesizers/utils.js.map +1 -0
  248. package/out/src/tools/align-shard.d.ts.map +1 -1
  249. package/out/src/tools/align-shard.js +2 -2
  250. package/out/src/tools/align-shard.js.map +1 -1
  251. package/out/src/tools/fetch/ban.d.ts.map +1 -1
  252. package/out/src/tools/fetch/ban.js +2 -18
  253. package/out/src/tools/fetch/ban.js.map +1 -1
  254. package/out/src/tools/fetch/download.d.ts +12 -3
  255. package/out/src/tools/fetch/download.d.ts.map +1 -1
  256. package/out/src/tools/fetch/download.js +20 -4
  257. package/out/src/tools/fetch/download.js.map +1 -1
  258. package/out/src/tools/fetch/geonames-dump.d.ts +87 -0
  259. package/out/src/tools/fetch/geonames-dump.d.ts.map +1 -0
  260. package/out/src/tools/fetch/geonames-dump.js +178 -0
  261. package/out/src/tools/fetch/geonames-dump.js.map +1 -0
  262. package/out/src/tools/fetch/geonames-postal.d.ts +76 -0
  263. package/out/src/tools/fetch/geonames-postal.d.ts.map +1 -0
  264. package/out/src/tools/fetch/geonames-postal.js +125 -0
  265. package/out/src/tools/fetch/geonames-postal.js.map +1 -0
  266. package/out/src/tools/fetch/imls-pls.d.ts.map +1 -1
  267. package/out/src/tools/fetch/imls-pls.js +5 -17
  268. package/out/src/tools/fetch/imls-pls.js.map +1 -1
  269. package/out/src/tools/fetch/index.d.ts +11 -0
  270. package/out/src/tools/fetch/index.d.ts.map +1 -1
  271. package/out/src/tools/fetch/index.js +11 -0
  272. package/out/src/tools/fetch/index.js.map +1 -1
  273. package/out/src/tools/fetch/nad.d.ts.map +1 -1
  274. package/out/src/tools/fetch/nad.js +20 -11
  275. package/out/src/tools/fetch/nad.js.map +1 -1
  276. package/out/src/tools/fetch/nppes.d.ts.map +1 -1
  277. package/out/src/tools/fetch/nppes.js +17 -22
  278. package/out/src/tools/fetch/nppes.js.map +1 -1
  279. package/out/src/tools/fetch/openaddresses.d.ts.map +1 -1
  280. package/out/src/tools/fetch/openaddresses.js +18 -17
  281. package/out/src/tools/fetch/openaddresses.js.map +1 -1
  282. package/out/src/tools/fetch/ourairports.d.ts.map +1 -1
  283. package/out/src/tools/fetch/ourairports.js +7 -2
  284. package/out/src/tools/fetch/ourairports.js.map +1 -1
  285. package/out/src/tools/fetch/ppd.d.ts +0 -4
  286. package/out/src/tools/fetch/ppd.d.ts.map +1 -1
  287. package/out/src/tools/fetch/ppd.js +1 -1
  288. package/out/src/tools/fetch/ppd.js.map +1 -1
  289. package/out/src/tools/fetch/state-hi-schools.d.ts.map +1 -1
  290. package/out/src/tools/fetch/state-hi-schools.js +3 -19
  291. package/out/src/tools/fetch/state-hi-schools.js.map +1 -1
  292. package/out/src/tools/fetch/state-sources.d.ts +4 -0
  293. package/out/src/tools/fetch/state-sources.d.ts.map +1 -1
  294. package/out/src/tools/fetch/state-sources.js +1 -5
  295. package/out/src/tools/fetch/state-sources.js.map +1 -1
  296. package/out/src/tools/fetch/tiger-full.d.ts.map +1 -1
  297. package/out/src/tools/fetch/tiger-full.js +15 -21
  298. package/out/src/tools/fetch/tiger-full.js.map +1 -1
  299. package/out/src/tools/golden-expand.d.ts.map +1 -1
  300. package/out/src/tools/golden-expand.js +34 -25
  301. package/out/src/tools/golden-expand.js.map +1 -1
  302. package/out/src/tools/golden-relabel-street.d.ts.map +1 -1
  303. package/out/src/tools/golden-relabel-street.js +4 -77
  304. package/out/src/tools/golden-relabel-street.js.map +1 -1
  305. package/out/src/tools/index.d.ts +2 -2
  306. package/out/src/tools/index.d.ts.map +1 -1
  307. package/out/src/tools/index.js +2 -2
  308. package/out/src/tools/index.js.map +1 -1
  309. package/out/src/tools/ingest-csv.d.ts.map +1 -1
  310. package/out/src/tools/ingest-csv.js +0 -11
  311. package/out/src/tools/ingest-csv.js.map +1 -1
  312. package/out/src/tools/overlay-manifest.d.ts +4 -0
  313. package/out/src/tools/overlay-manifest.d.ts.map +1 -1
  314. package/out/src/tools/overlay-manifest.js +16 -6
  315. package/out/src/tools/overlay-manifest.js.map +1 -1
  316. package/out/src/tools/postcode-triples.d.ts +175 -0
  317. package/out/src/tools/postcode-triples.d.ts.map +1 -0
  318. package/out/src/tools/postcode-triples.js +304 -0
  319. package/out/src/tools/postcode-triples.js.map +1 -0
  320. package/out/src/tools/shard-kryptonite.d.ts.map +1 -1
  321. package/out/src/tools/shard-kryptonite.js +1 -3
  322. package/out/src/tools/shard-kryptonite.js.map +1 -1
  323. package/out/src/tools/shard-translit.d.ts.map +1 -1
  324. package/out/src/tools/shard-translit.js +4 -27
  325. package/out/src/tools/shard-translit.js.map +1 -1
  326. package/out/src/tools/sub-venue/harvest.d.ts +100 -0
  327. package/out/src/tools/sub-venue/harvest.d.ts.map +1 -0
  328. package/out/src/tools/sub-venue/harvest.js +168 -0
  329. package/out/src/tools/sub-venue/harvest.js.map +1 -0
  330. package/out/src/tools/sub-venue/head-nouns.d.ts +50 -0
  331. package/out/src/tools/sub-venue/head-nouns.d.ts.map +1 -0
  332. package/out/src/tools/sub-venue/head-nouns.js +210 -0
  333. package/out/src/tools/sub-venue/head-nouns.js.map +1 -0
  334. package/out/src/tools/sub-venue/surfaces.d.ts +45 -0
  335. package/out/src/tools/sub-venue/surfaces.d.ts.map +1 -0
  336. package/out/src/tools/sub-venue/surfaces.js +77 -0
  337. package/out/src/tools/sub-venue/surfaces.js.map +1 -0
  338. package/out/src/tools/sub-venue/table.d.ts +229 -0
  339. package/out/src/tools/sub-venue/table.d.ts.map +1 -0
  340. package/out/src/tools/sub-venue/table.js +108 -0
  341. package/out/src/tools/sub-venue/table.js.map +1 -0
  342. package/out/src/tools/sub-venue/wikidata.d.ts +26 -0
  343. package/out/src/tools/sub-venue/wikidata.d.ts.map +1 -0
  344. package/out/src/tools/sub-venue/wikidata.js +62 -0
  345. package/out/src/tools/sub-venue/wikidata.js.map +1 -0
  346. package/out/src/tools/sub-venue-lexicon.d.ts +27 -378
  347. package/out/src/tools/sub-venue-lexicon.d.ts.map +1 -1
  348. package/out/src/tools/sub-venue-lexicon.js +30 -565
  349. package/out/src/tools/sub-venue-lexicon.js.map +1 -1
  350. package/out/src/{align.d.ts → utils/align.d.ts} +1 -1
  351. package/out/src/utils/align.d.ts.map +1 -0
  352. package/out/src/utils/align.js.map +1 -0
  353. package/out/src/utils/golden.d.ts.map +1 -0
  354. package/out/src/{golden.js → utils/golden.js} +2 -2
  355. package/out/src/utils/golden.js.map +1 -0
  356. package/out/src/utils/index.d.ts +14 -0
  357. package/out/src/utils/index.d.ts.map +1 -0
  358. package/out/src/utils/index.js +14 -0
  359. package/out/src/utils/index.js.map +1 -0
  360. package/out/src/utils/license.d.ts.map +1 -0
  361. package/out/src/{license.js → utils/license.js} +1 -1
  362. package/out/src/utils/license.js.map +1 -0
  363. package/out/src/{parquet.d.ts → utils/parquet.d.ts} +24 -5
  364. package/out/src/utils/parquet.d.ts.map +1 -0
  365. package/out/src/{parquet.js → utils/parquet.js} +6 -3
  366. package/out/src/utils/parquet.js.map +1 -0
  367. package/out/src/{split.d.ts → utils/split.d.ts} +1 -1
  368. package/out/src/utils/split.d.ts.map +1 -0
  369. package/out/src/{split.js → utils/split.js} +2 -2
  370. package/out/src/utils/split.js.map +1 -0
  371. package/out/src/utils/tokenize.d.ts.map +1 -0
  372. package/out/src/utils/tokenize.js.map +1 -0
  373. package/out/src/utils/wof-json.d.ts.map +1 -0
  374. package/out/src/utils/wof-json.js.map +1 -0
  375. package/package.json +287 -21
  376. package/src/adapters/ban/adapter.ts +7 -5
  377. package/src/adapters/fcc-bdc/adapter.ts +5 -4
  378. package/src/adapters/geonames/adapter.ts +3 -3
  379. package/src/adapters/geonames-postal/adapter.ts +4 -4
  380. package/src/adapters/gnaf/adapter.ts +3 -3
  381. package/src/adapters/gnaf/assemble.ts +1 -1
  382. package/src/adapters/index.ts +3 -2
  383. package/src/adapters/openaddresses/adapter.ts +4 -4
  384. package/src/adapters/overture/adapter.ts +3 -3
  385. package/src/adapters/state-hi-schools/adapter.ts +8 -7
  386. package/src/adapters/state-ia-contractors/adapter.ts +13 -8
  387. package/src/adapters/state-ny-notaries/adapter.ts +13 -32
  388. package/src/adapters/state-tx-notaries/adapter.ts +13 -8
  389. package/src/adapters/synth-po-box/adapter.ts +4 -4
  390. package/src/adapters/tiger/adapter.ts +5 -3
  391. package/src/adapters/usgov-hrsa-fqhc/adapter.ts +8 -7
  392. package/src/adapters/usgov-imls-pls/adapter.ts +12 -8
  393. package/src/adapters/usgov-irs-bmf/adapter.ts +7 -6
  394. package/src/adapters/usgov-nad/adapter.ts +9 -9
  395. package/src/adapters/usgov-nppes/adapter.ts +13 -12
  396. package/src/adapters/usgov-samhsa-treatment-locator/adapter.ts +8 -7
  397. package/src/{adapter.ts → adapters/utils/index.ts} +15 -3
  398. package/src/adapters/wof-admin-jp/adapter.ts +9 -12
  399. package/src/adapters/wof-admin-json/adapter.ts +15 -54
  400. package/src/adapters/wof-json-rows.ts +79 -0
  401. package/src/adapters/wof-postalcode-json/adapter.ts +16 -50
  402. package/src/build.ts +8 -8
  403. package/src/index.ts +3 -17
  404. package/src/name-prone-us-suffixes.ts +12 -0
  405. package/src/parquet-wrapper/reader.ts +9 -0
  406. package/src/runner.ts +2 -2
  407. package/src/shard-recipes/anchor-absorption.ts +5 -5
  408. package/src/shard-recipes/bare-country.ts +95 -0
  409. package/src/shard-recipes/boundary-stress.ts +3 -2
  410. package/src/shard-recipes/country-balanced.ts +16 -27
  411. package/src/shard-recipes/cz-pcfirst-preposition.ts +29 -10
  412. package/src/shard-recipes/fr-admin-split.ts +6 -4
  413. package/src/shard-recipes/fr-bare-street.ts +85 -10
  414. package/src/shard-recipes/fr-lieudit.ts +6 -4
  415. package/src/shard-recipes/fr-order.ts +13 -26
  416. package/src/shard-recipes/german.ts +9 -18
  417. package/src/shard-recipes/house-venue.ts +2 -1
  418. package/src/shard-recipes/index.ts +8 -1
  419. package/src/shard-recipes/intersection.ts +9 -17
  420. package/src/shard-recipes/locale.ts +16 -24
  421. package/src/shard-recipes/no-street.ts +2 -1
  422. package/src/shard-recipes/po-box-cedex.ts +76 -58
  423. package/src/shard-recipes/po-box.ts +2 -1
  424. package/src/shard-recipes/reviewed-postcode-tail.ts +189 -0
  425. package/src/shard-recipes/scaffold.ts +99 -24
  426. package/src/shard-recipes/street-affix.ts +284 -45
  427. package/src/shard-recipes/street-bare.ts +4 -3
  428. package/src/shard-recipes/street.ts +3 -2
  429. package/src/shard-recipes/sub-venue-sources.ts +13 -23
  430. package/src/shard-recipes/sub-venue.ts +11 -9
  431. package/src/shard-recipes/trailing-region.ts +183 -0
  432. package/src/shard-recipes/unit.ts +8 -17
  433. package/src/{synthesize-boundary-stress.ts → synthesizers/boundary-stress.ts} +2 -2
  434. package/src/{synthesize-german.ts → synthesizers/german.ts} +3 -2
  435. package/src/{synthesize-house-venue.ts → synthesizers/house-venue.ts} +20 -6
  436. package/src/{synthesize-intersection.ts → synthesizers/intersection.ts} +1 -1
  437. package/src/{synthesize-no-street.ts → synthesizers/no-street.ts} +2 -2
  438. package/src/{synthesize-po-box.ts → synthesizers/po-box.ts} +1 -1
  439. package/src/{synthesize-street.ts → synthesizers/street.ts} +5 -3
  440. package/src/{synthesize.ts → synthesizers/utils.ts} +6 -4
  441. package/src/tools/align-shard.ts +3 -2
  442. package/src/tools/fetch/ban.ts +2 -22
  443. package/src/tools/fetch/download.ts +22 -4
  444. package/src/tools/fetch/geonames-dump.ts +267 -0
  445. package/src/tools/fetch/geonames-postal.ts +180 -0
  446. package/src/tools/fetch/imls-pls.ts +6 -20
  447. package/src/tools/fetch/index.ts +11 -0
  448. package/src/tools/fetch/nad.ts +21 -10
  449. package/src/tools/fetch/nppes.ts +17 -23
  450. package/src/tools/fetch/openaddresses.ts +18 -22
  451. package/src/tools/fetch/ourairports.ts +7 -2
  452. package/src/tools/fetch/ppd.ts +1 -1
  453. package/src/tools/fetch/state-hi-schools.ts +3 -23
  454. package/src/tools/fetch/state-sources.ts +1 -1
  455. package/src/tools/fetch/tiger-full.ts +17 -24
  456. package/src/tools/golden-expand.ts +44 -33
  457. package/src/tools/golden-relabel-street.ts +5 -68
  458. package/src/tools/index.ts +2 -2
  459. package/src/tools/ingest-csv.ts +0 -13
  460. package/src/tools/overlay-manifest.ts +20 -8
  461. package/src/tools/postcode-triples.ts +375 -0
  462. package/src/tools/shard-kryptonite.ts +4 -5
  463. package/src/tools/shard-translit.ts +16 -35
  464. package/src/tools/sub-venue/harvest.ts +235 -0
  465. package/src/tools/sub-venue/head-nouns.ts +242 -0
  466. package/src/tools/sub-venue/surfaces.ts +95 -0
  467. package/src/tools/sub-venue/table.ts +281 -0
  468. package/src/tools/sub-venue/wikidata.ts +85 -0
  469. package/src/tools/sub-venue-lexicon.ts +67 -886
  470. package/src/{align.ts → utils/align.ts} +2 -1
  471. package/src/{golden.ts → utils/golden.ts} +2 -3
  472. package/src/utils/index.ts +14 -0
  473. package/src/{license.ts → utils/license.ts} +1 -1
  474. package/src/{parquet.ts → utils/parquet.ts} +22 -9
  475. package/src/{split.ts → utils/split.ts} +3 -3
  476. package/out/src/adapter.d.ts.map +0 -1
  477. package/out/src/adapter.js.map +0 -1
  478. package/out/src/align.d.ts.map +0 -1
  479. package/out/src/align.js.map +0 -1
  480. package/out/src/format.d.ts +0 -14
  481. package/out/src/format.d.ts.map +0 -1
  482. package/out/src/format.js +0 -14
  483. package/out/src/format.js.map +0 -1
  484. package/out/src/golden.d.ts.map +0 -1
  485. package/out/src/golden.js.map +0 -1
  486. package/out/src/license.d.ts.map +0 -1
  487. package/out/src/license.js.map +0 -1
  488. package/out/src/parquet.d.ts.map +0 -1
  489. package/out/src/parquet.js.map +0 -1
  490. package/out/src/split.d.ts.map +0 -1
  491. package/out/src/split.js.map +0 -1
  492. package/out/src/synthesize-anchor-absorption.d.ts.map +0 -1
  493. package/out/src/synthesize-anchor-absorption.js.map +0 -1
  494. package/out/src/synthesize-boundary-stress.d.ts.map +0 -1
  495. package/out/src/synthesize-boundary-stress.js.map +0 -1
  496. package/out/src/synthesize-german.d.ts.map +0 -1
  497. package/out/src/synthesize-german.js.map +0 -1
  498. package/out/src/synthesize-house-venue.d.ts.map +0 -1
  499. package/out/src/synthesize-house-venue.js.map +0 -1
  500. package/out/src/synthesize-intersection.d.ts.map +0 -1
  501. package/out/src/synthesize-intersection.js.map +0 -1
  502. package/out/src/synthesize-no-street.d.ts.map +0 -1
  503. package/out/src/synthesize-no-street.js.map +0 -1
  504. package/out/src/synthesize-po-box.d.ts.map +0 -1
  505. package/out/src/synthesize-po-box.js.map +0 -1
  506. package/out/src/synthesize-street.d.ts.map +0 -1
  507. package/out/src/synthesize-street.js.map +0 -1
  508. package/out/src/synthesize.d.ts.map +0 -1
  509. package/out/src/synthesize.js.map +0 -1
  510. package/out/src/tokenize.d.ts.map +0 -1
  511. package/out/src/tokenize.js.map +0 -1
  512. package/out/src/wof-json.d.ts.map +0 -1
  513. package/out/src/wof-json.js.map +0 -1
  514. package/src/format.ts +0 -14
  515. /package/out/src/{align.js → utils/align.js} +0 -0
  516. /package/out/src/{golden.d.ts → utils/golden.d.ts} +0 -0
  517. /package/out/src/{license.d.ts → utils/license.d.ts} +0 -0
  518. /package/out/src/{tokenize.d.ts → utils/tokenize.d.ts} +0 -0
  519. /package/out/src/{tokenize.js → utils/tokenize.js} +0 -0
  520. /package/out/src/{wof-json.d.ts → utils/wof-json.d.ts} +0 -0
  521. /package/out/src/{wof-json.js → utils/wof-json.js} +0 -0
  522. /package/src/{synthesize-anchor-absorption.ts → synthesizers/anchor-absorption.ts} +0 -0
  523. /package/src/{tokenize.ts → utils/tokenize.ts} +0 -0
  524. /package/src/{wof-json.ts → utils/wof-json.ts} +0 -0
@@ -30,24 +30,33 @@
30
30
  * reproduces the legacy run byte-for-byte.
31
31
  */
32
32
 
33
- import { spawnSync } from "node:child_process"
33
+ import { existsSync } from "node:fs"
34
34
 
35
35
  import { isAuDeliveryService, isAuPostcode, isAuStateAbbreviation } from "@mailwoman/codex/au"
36
36
  import { FSA_LETTER_TO_PROVINCE, normalizeCaPostalCode } from "@mailwoman/codex/ca"
37
37
  import { isCedex } from "@mailwoman/codex/fr"
38
38
  import { isNZDeliveryService, isNZPostcode } from "@mailwoman/codex/nz"
39
39
  import { isPOBox } from "@mailwoman/codex/us"
40
+ import { readZipEntry } from "@mailwoman/core/fs/zip"
40
41
  import { dataRootPath } from "@mailwoman/core/utils"
41
- import { TextSpliterator } from "spliterator"
42
+ import { TextSpliterator, TSVSpliterator } from "spliterator"
42
43
 
43
- import { alignRow } from "../align.ts"
44
44
  import {
45
45
  maybeNoisifyBoxNumber,
46
46
  PO_BOX_LOCALE_TEMPLATES,
47
47
  synthesizeMilitaryPoBoxRow,
48
48
  type LocaleTemplate,
49
- } from "../synthesize-po-box.ts"
50
- import { makeMulberry32, readCSVRecords, shardSourceID, type CanonicalShardRow, type ShardRecipe } from "./scaffold.ts"
49
+ } from "#synthesizers/po-box"
50
+ import { alignRow } from "#utils"
51
+
52
+ import {
53
+ makeMulberry32,
54
+ readCSVRecords,
55
+ readZippedCSVRecords,
56
+ shardSourceID,
57
+ type CanonicalShardRow,
58
+ type ShardRecipe,
59
+ } from "./scaffold.ts"
51
60
 
52
61
  // ── Base-skeleton sources ────────────────────────────────────────────────────────────────────────
53
62
  // Same OA cache as the unit/affix shards. US train = every NON-Vermont state; US eval = Vermont (the
@@ -233,19 +242,11 @@ const cleanLocality = (loc: string) =>
233
242
  /**
234
243
  * Stream real US tuples (number/street/city/postcode) out of a cached OA zip.
235
244
  */
236
- function readUsTuples(source: { zip: string; csv: string; region: string }): USTuple[] {
237
- const r = spawnSync("unzip", ["-p", source.zip, source.csv], { maxBuffer: 1024 * 1024 * 1024, encoding: "buffer" })
238
-
239
- if (r.status !== 0) {
240
- console.error(` WARN: unzip failed for ${source.zip} (status ${r.status})`)
241
-
242
- return []
243
- }
244
-
245
+ async function readUsTuples(source: { zip: string; csv: string; region: string }): Promise<USTuple[]> {
245
246
  const tuples: USTuple[] = []
246
247
  const seen = new Set<string>()
247
248
 
248
- for (const row of readCSVRecords(r.stdout)) {
249
+ for await (const row of readZippedCSVRecords(source.zip, source.csv)) {
249
250
  const locality = row.city ?? ""
250
251
 
251
252
  if (!cleanLocality(locality)) continue
@@ -265,31 +266,47 @@ function readUsTuples(source: { zip: string; csv: string; region: string }): UST
265
266
  return tuples
266
267
  }
267
268
 
269
+ /**
270
+ * The FR stride: keep line 3, then every 211th. Both numbers are the awk pre-filter's, kept exactly — they select which
271
+ * rows land in the shard, so changing either re-rolls the sample.
272
+ */
273
+ const FR_STRIDE = 211
274
+ const FR_STRIDE_OFFSET = 3
275
+
276
+ /**
277
+ * GeoNames population floor for a Canadian locality to enter the pool.
278
+ */
279
+ const CA_LOCALITY_MIN_POPULATION = 1000
280
+
268
281
  /**
269
282
  * Stride-sampled FR tuples (number/street/city/postcode). The countrywide CSV is 2.5 GB and insee-ordered; `awk NR%K`
270
283
  * strides the whole country instead of reading one département.
271
284
  *
272
- * The stride counts PHYSICAL lines, so a record carrying a newline inside a quoted field is two lines to awk and can be
273
- * cut in half by the stride. That is a sampling artefact of the awk pre-filter, not of the parse: whichever lines
274
- * survive are re-assembled into records by {@link readCSVRecords}, and a halved record fails the field checks below.
285
+ * The stride counts PHYSICAL lines, so a record carrying a newline inside a quoted field is two lines to the stride and
286
+ * can be cut in half by it. That is a sampling artefact of striding before the parse, not of the parse: whichever lines
287
+ * survive are re-assembled into records by the CSV reader, and a halved record fails the field checks below. Hence
288
+ * `skipEmpty: false` and no quote handling on the line split — a blank line still advances the count, exactly as it did
289
+ * when this was `awk NR`, and the rows the stride selects are the rows every existing FR shard was built from.
275
290
  */
276
- function readFrTuples(limit: number): FRTuple[] {
277
- const r = spawnSync(
278
- "bash",
279
- ["-c", `unzip -p "${FR_SOURCE.zip}" "${FR_SOURCE.csv}" | awk 'NR==1 || NR%211==3' | head -n ${limit + 1}`],
280
- { maxBuffer: 1024 * 1024 * 1024, encoding: "buffer" }
281
- )
282
-
283
- if (r.status !== 0 && !r.stdout.length) {
284
- console.error(` WARN: unzip failed for ${FR_SOURCE.zip}`)
291
+ async function readFrTuples(limit: number): Promise<FRTuple[]> {
292
+ if (!existsSync(FR_SOURCE.zip)) {
293
+ console.error(` WARN: ${FR_SOURCE.zip} is not cached — skipping ${FR_SOURCE.csv}`)
285
294
 
286
295
  return []
287
296
  }
288
297
 
298
+ const encoder = new TextEncoder()
299
+
300
+ const strided = TextSpliterator.fromAsync(readZipEntry(FR_SOURCE.zip, FR_SOURCE.csv), { skipEmpty: false })
301
+ // `index` is 0-based where awk's NR is 1-based, hence the +1: keep the header, then every 211th line at offset 3.
302
+ .filter((_line, index) => index === 0 || (index + 1) % FR_STRIDE === FR_STRIDE_OFFSET)
303
+ .take(limit + 1)
304
+ .map((line) => encoder.encode(line + "\n"))
305
+
289
306
  const tuples: FRTuple[] = []
290
307
  const seen = new Set<string>()
291
308
 
292
- for (const row of readCSVRecords(r.stdout)) {
309
+ for await (const row of readCSVRecords(strided)) {
293
310
  const locality = row.city ?? "",
294
311
  postcode = row.postcode ?? "",
295
312
  street = row.street ?? "",
@@ -310,23 +327,29 @@ function readFrTuples(limit: number): FRTuple[] {
310
327
  * Canadian locality pools from the GeoNames dump (CC-BY 4.0): feature class P, admin1 10 (Québec) / 08 (Ontario),
311
328
  * population > 1000. GeoNames is the provenance-tracked source — no hand list.
312
329
  */
313
- function readCaLocalities(admin1: string): string[] {
314
- const r = spawnSync(
315
- "bash",
316
- [
317
- "-c",
318
- `unzip -p "${GEONAMES_CA}" CA.txt | awk -F'\\t' '$7=="P" && $9=="CA" && $11=="${admin1}" && $15>1000 {print $2}'`,
319
- ],
320
- { maxBuffer: 1024 * 1024 * 256, encoding: "utf8" }
321
- )
322
-
323
- if (r.status !== 0) {
324
- console.error(` WARN: GeoNames read failed (admin1=${admin1}) — is ${GEONAMES_CA} present?`)
330
+ async function readCaLocalities(admin1: string): Promise<string[]> {
331
+ if (!existsSync(GEONAMES_CA)) {
332
+ console.error(` WARN: ${GEONAMES_CA} is not cached — skipping CA localities (admin1=${admin1})`)
325
333
 
326
334
  return []
327
335
  }
328
336
 
329
- return [...new Set([...TextSpliterator.from(r.stdout)].filter(cleanLocality))]
337
+ const localities = new Set<string>()
338
+
339
+ // GeoNames main dump columns, 0-based: 1 name, 6 feature class, 8 country, 10 admin1, 14 population.
340
+ for await (const columns of TSVSpliterator.fromAsync(readZipEntry(GEONAMES_CA, "CA.txt"), { header: false })) {
341
+ if (columns[6] !== "P" || columns[8] !== "CA" || columns[10] !== admin1) continue
342
+
343
+ if (Number(columns[14]) <= CA_LOCALITY_MIN_POPULATION) continue
344
+
345
+ const name = columns[1] ?? ""
346
+
347
+ if (cleanLocality(name)) {
348
+ localities.add(name)
349
+ }
350
+ }
351
+
352
+ return [...localities]
330
353
  }
331
354
 
332
355
  /**
@@ -336,14 +359,12 @@ function readCaLocalities(admin1: string): string[] {
336
359
  * validated against the codex 4-digit shape — a dump row that fails the contract is skipped, not emitted as a junk
337
360
  * label.
338
361
  */
339
- function readPostalTuples(
362
+ async function readPostalTuples(
340
363
  source: { zip: string; txt: string },
341
364
  opts: { withState: boolean }
342
- ): Array<AUTuple | NZTuple> {
343
- const r = spawnSync("unzip", ["-p", source.zip, source.txt], { maxBuffer: 1024 * 1024 * 64, encoding: "utf8" })
344
-
345
- if (r.status !== 0) {
346
- console.error(` WARN: unzip failed for ${source.zip} (status ${r.status})`)
365
+ ): Promise<Array<AUTuple | NZTuple>> {
366
+ if (!existsSync(source.zip)) {
367
+ console.error(` WARN: ${source.zip} is not cached — skipping ${source.txt}`)
347
368
 
348
369
  return []
349
370
  }
@@ -352,11 +373,8 @@ function readPostalTuples(
352
373
  const seen = new Set<string>()
353
374
  const validPostcode = opts.withState ? isAuPostcode : isNZPostcode
354
375
 
355
- for (const line of TextSpliterator.from(r.stdout)) {
356
- if (!line) continue
357
-
358
- // oxlint-disable-next-line mailwoman/prefer-spliterator -- One row already streamed off the line above.
359
- const cols = line.split("\t")
376
+ // A GeoNames dump carries no header row, so every line is data.
377
+ for await (const cols of TSVSpliterator.fromAsync(readZipEntry(source.zip, source.txt), { header: false })) {
360
378
  const postcode = (cols[1] ?? "").trim()
361
379
  const locality = (cols[2] ?? "").trim()
362
380
  const region = (cols[4] ?? "").trim()
@@ -744,7 +762,7 @@ export const poBoxCedexRecipe: ShardRecipe = {
744
762
  const usPool: USTuple[] = []
745
763
 
746
764
  for (const s of opts.golden ? [US_EVAL_SOURCE] : US_TRAIN_SOURCES) {
747
- const t = readUsTuples(s)
765
+ const t = await readUsTuples(s)
748
766
 
749
767
  console.error(` ${s.csv}: ${t.length} tuples`)
750
768
 
@@ -754,21 +772,21 @@ export const poBoxCedexRecipe: ShardRecipe = {
754
772
  }
755
773
 
756
774
  // FR + CA pools: stable locality-hash holdout (golden gets hash%10==0, train the rest).
757
- const frAll = readFrTuples(80_000)
775
+ const frAll = await readFrTuples(80_000)
758
776
  const frPool = frAll.filter((t) => isHoldoutLocality(t.locality) === opts.golden)
759
777
 
760
778
  console.error(` ${FR_SOURCE.csv}: ${frAll.length} tuples (${frPool.length} after holdout split)`)
761
779
 
762
- const qcAll = readCaLocalities("10")
763
- const onAll = readCaLocalities("08")
780
+ const qcAll = await readCaLocalities("10")
781
+ const onAll = await readCaLocalities("08")
764
782
  const qcPool = qcAll.filter((l) => isHoldoutLocality(l) === opts.golden)
765
783
  const onPool = onAll.filter((l) => isHoldoutLocality(l) === opts.golden)
766
784
 
767
785
  console.error(` GeoNames CA: QC ${qcAll.length}→${qcPool.length}, ON ${onAll.length}→${onPool.length}`)
768
786
 
769
787
  // AU/NZ pools: same stable locality-hash holdout as FR/CA.
770
- const auAll = readPostalTuples(GEONAMES_POSTAL_AU, { withState: true }) as AUTuple[]
771
- const nzAll = readPostalTuples(GEONAMES_POSTAL_NZ, { withState: false }) as NZTuple[]
788
+ const auAll = (await readPostalTuples(GEONAMES_POSTAL_AU, { withState: true })) as AUTuple[]
789
+ const nzAll = (await readPostalTuples(GEONAMES_POSTAL_NZ, { withState: false })) as NZTuple[]
772
790
  const auPool = auAll.filter((t) => isHoldoutLocality(t.locality) === opts.golden)
773
791
  const nzPool = nzAll.filter((t) => isHoldoutLocality(t.locality) === opts.golden)
774
792
 
@@ -9,7 +9,8 @@
9
9
  * scripts/build-po-box-shard.mjs.
10
10
  */
11
11
 
12
- import { synthesizeMilitaryPoBoxRow, synthesizePoBoxRow, type PoBoxBaseTuple } from "../synthesize-po-box.ts"
12
+ import { synthesizeMilitaryPoBoxRow, synthesizePoBoxRow, type PoBoxBaseTuple } from "#synthesizers/po-box"
13
+
13
14
  import { alignAndWrite, makeLcg, readTuples, shardSourceID, type ShardRecipe } from "./scaffold.ts"
14
15
 
15
16
  const LICENSE = "Synthetic — derived from CC-BY / public-domain input tuples"
@@ -0,0 +1,189 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * A bounded, reviewed source for Venezuela's `locality postcode, region` convention (#1821).
7
+ * Bulk sources yielded zero Venezuelan tuples, so this recipe reads four reviewed facts and applies
8
+ * only transformations that do not create another postcode-to-place join.
9
+ */
10
+
11
+ import { readFileSync } from "node:fs"
12
+ import { fileURLToPath } from "node:url"
13
+
14
+ import { parseJSONStrict } from "@mailwoman/core/objects"
15
+
16
+ import { alignAndWrite, type ShardRecipe, shardSourceID } from "./scaffold.ts"
17
+
18
+ /**
19
+ * A separate sampler bucket so a receipt measures these reviewed after-locality rows and no other postcode placement.
20
+ */
21
+ export const REVIEWED_POSTCODE_TAIL_SOURCE = "synth-reviewed-postcode-tail"
22
+
23
+ /**
24
+ * The four postcode-to-place facts reviewed for #1821 and committed in the package data file.
25
+ */
26
+ const REVIEWED_TUPLE_COUNT = 4
27
+
28
+ interface ReviewedTupleProvenance {
29
+ publisher: string
30
+ url: string
31
+ retrievedAt: string
32
+ reviewStatus: "reviewed"
33
+ sourceLicenseNote: string
34
+ }
35
+
36
+ export interface ReviewedPostcodeTuple {
37
+ id: string
38
+ locality: string
39
+ postcode: string
40
+ region: string
41
+ country: "Venezuela"
42
+ cc: "VE"
43
+ locale: "es-VE"
44
+ postcodePlacement: "after_locality"
45
+ provenance: ReviewedTupleProvenance
46
+ }
47
+
48
+ interface ReviewedPostcodeTupleFile {
49
+ version: string
50
+ reviewedAt: string
51
+ tuples: ReviewedPostcodeTuple[]
52
+ }
53
+
54
+ type VariantID =
55
+ | "canonical-country"
56
+ | "canonical-no-country"
57
+ | "comma-free"
58
+ | "uppercase"
59
+ | "left-context"
60
+ | "accent-folded"
61
+
62
+ interface Variant {
63
+ id: VariantID
64
+ raw: string
65
+ components: Record<string, string>
66
+ }
67
+
68
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/
69
+
70
+ export function defaultReviewedPostcodeTuplePath(): string {
71
+ return fileURLToPath(import.meta.resolve("@mailwoman/corpus/data/reviewed-ve-postcode-tuples.json"))
72
+ }
73
+
74
+ export function readReviewedPostcodeTuples(path = defaultReviewedPostcodeTuplePath()): ReviewedPostcodeTuple[] {
75
+ const document = parseJSONStrict<ReviewedPostcodeTupleFile>(readFileSync(path, "utf8"))
76
+
77
+ if (!document.version || !ISO_DATE.test(document.reviewedAt) || document.tuples.length !== REVIEWED_TUPLE_COUNT) {
78
+ throw new Error("reviewed postcode tuple file must declare a version, review date, and exactly four tuples")
79
+ }
80
+
81
+ const ids = new Set<string>()
82
+ const facts = new Set<string>()
83
+
84
+ for (const tuple of document.tuples) {
85
+ const provenance = tuple.provenance
86
+ const fact = `${tuple.locality}\u0000${tuple.postcode}\u0000${tuple.region}`
87
+
88
+ if (
89
+ !tuple.id ||
90
+ !tuple.locality ||
91
+ !tuple.postcode ||
92
+ !tuple.region ||
93
+ tuple.country !== "Venezuela" ||
94
+ tuple.cc !== "VE" ||
95
+ tuple.locale !== "es-VE" ||
96
+ tuple.postcodePlacement !== "after_locality" ||
97
+ !provenance?.publisher ||
98
+ !URL.canParse(provenance.url) ||
99
+ !ISO_DATE.test(provenance.retrievedAt) ||
100
+ provenance.reviewStatus !== "reviewed" ||
101
+ !provenance.sourceLicenseNote
102
+ ) {
103
+ throw new Error(`invalid reviewed postcode tuple: ${tuple.id || "<missing id>"}`)
104
+ }
105
+
106
+ if (ids.has(tuple.id) || facts.has(fact)) throw new Error(`duplicate reviewed postcode tuple: ${tuple.id}`)
107
+ ids.add(tuple.id)
108
+ facts.add(fact)
109
+ }
110
+
111
+ return document.tuples
112
+ }
113
+
114
+ const foldAccents = (value: string): string => value.normalize("NFD").replaceAll(/\p{M}/gu, "").normalize("NFC")
115
+
116
+ export function reviewedPostcodeTailVariants(tuple: ReviewedPostcodeTuple): Variant[] {
117
+ const tail = `${tuple.locality} ${tuple.postcode}, ${tuple.region}`
118
+ const withCountry = `${tail}, ${tuple.country}`
119
+ const base = { locality: tuple.locality, postcode: tuple.postcode, region: tuple.region, country: tuple.country }
120
+ const upper = Object.fromEntries(Object.entries(base).map(([key, value]) => [key, value.toUpperCase()]))
121
+
122
+ const variants: Variant[] = [
123
+ { id: "canonical-country", raw: withCountry, components: base },
124
+ {
125
+ id: "canonical-no-country",
126
+ raw: tail,
127
+ components: { locality: tuple.locality, postcode: tuple.postcode, region: tuple.region },
128
+ },
129
+ { id: "comma-free", raw: `${tuple.locality} ${tuple.postcode} ${tuple.region} ${tuple.country}`, components: base },
130
+ { id: "uppercase", raw: withCountry.toUpperCase(), components: upper },
131
+ {
132
+ id: "left-context",
133
+ raw: `Comercio Ejemplo, Calle Principal, ${withCountry}`,
134
+ components: { venue: "Comercio Ejemplo", street: "Calle Principal", ...base },
135
+ },
136
+ ]
137
+
138
+ const foldedLocality = foldAccents(tuple.locality)
139
+ const foldedRegion = foldAccents(tuple.region)
140
+
141
+ if (foldedLocality !== tuple.locality || foldedRegion !== tuple.region) {
142
+ variants.push({
143
+ id: "accent-folded",
144
+ raw: `${foldedLocality} ${tuple.postcode}, ${foldedRegion}, ${tuple.country}`,
145
+ components: { locality: foldedLocality, postcode: tuple.postcode, region: foldedRegion, country: tuple.country },
146
+ })
147
+ }
148
+
149
+ return variants
150
+ }
151
+
152
+ /**
153
+ * Emit bounded surface variants of the committed reviewed facts without constructing another geographic join.
154
+ */
155
+ export const reviewedPostcodeTailRecipe: ShardRecipe = {
156
+ name: "reviewed-postcode-tail",
157
+ description: "Reviewed Venezuela locality-postcode-region tails with bounded surface variants",
158
+ mode: "generate",
159
+ async run(opts, write) {
160
+ const tuples = readReviewedPostcodeTuples(opts.input)
161
+ let emitted = 0
162
+ let skipped = 0
163
+
164
+ for (const tuple of tuples) {
165
+ for (const variant of reviewedPostcodeTailVariants(tuple)) {
166
+ const sourceID = shardSourceID(REVIEWED_POSTCODE_TAIL_SOURCE, { tuple: tuple.id, variant: variant.id })
167
+
168
+ const canonical = {
169
+ raw: variant.raw,
170
+ components: variant.components,
171
+ country: tuple.cc,
172
+ locale: tuple.locale,
173
+ source: REVIEWED_POSTCODE_TAIL_SOURCE,
174
+ source_id: sourceID,
175
+ corpus_version: "0.11.0",
176
+ license: `Synthetic rendering of reviewed postal facts — ${tuple.provenance.publisher}; source terms recorded in reviewed-ve-postcode-tuples.json`,
177
+ }
178
+
179
+ if (alignAndWrite(write, canonical, "reviewed-postcode-tail", tuple.id)) {
180
+ emitted++
181
+ } else {
182
+ skipped++
183
+ }
184
+ }
185
+ }
186
+
187
+ return { read: tuples.length, emitted, skipped }
188
+ },
189
+ }
@@ -9,29 +9,64 @@
9
9
  * only its synthesis + filter; the `mailwoman corpus shard <recipe>` command supplies the I/O.
10
10
  */
11
11
 
12
+ import { existsSync } from "node:fs"
13
+
14
+ import { readZipEntry } from "@mailwoman/core/fs/zip"
12
15
  import { tryParsingJSON } from "@mailwoman/core/objects"
13
- import { CSVSpliterator } from "spliterator"
16
+ import type { PathBuilderLike } from "path-ts"
17
+ import type { AsyncChunkIterator, AsyncDataResource } from "spliterator"
18
+ import { AsyncSequence, CSVSpliterator } from "spliterator"
14
19
 
15
- import { stableSourceID } from "../adapter.ts"
16
- import { alignRow } from "../align.ts"
20
+ import { stableSourceIDFromParts } from "#adapters/utils"
21
+ import { alignRow } from "#utils"
17
22
 
18
23
  /**
19
- * {@link stableSourceID}, but accepting arbitrary disambiguator keys (e.g. a variant index `v`) that aren't
20
- * `ComponentTag`s. `stableSourceID` sorts + hashes EVERY key it's given, so passing extra keys is how the legacy
21
- * builders kept per-variant ids unique — the strict typing is just too narrow for that. Centralizes the one cast.
24
+ * {@link stableSourceIDFromParts} under the name the recipes use: arbitrary disambiguator keys (e.g. a variant index
25
+ * `v`) that aren't `ComponentTag`s, which is how the legacy builders kept per-variant ids unique.
22
26
  */
23
27
  export function shardSourceID(adapterID: string, parts: Record<string, string | undefined>): string {
24
- return stableSourceID(adapterID, parts as unknown as Parameters<typeof stableSourceID>[1])
28
+ return stableSourceIDFromParts(adapterID, parts)
25
29
  }
26
30
 
31
+ /**
32
+ * Where a country's convention writes the postcode inside the `«locality», «region»[, «country»]` admin tail.
33
+ *
34
+ * Load-bearing rather than cosmetic: the same digits change TAG with position. Measured on the shipped model,
35
+ * `Heladería Frappé Manía, Avenida Country Club, Barcelona 6001, Anzoátegui, Venezuela` tags `6001` as `house_number`
36
+ * and loses the locality into the street, while the identical row written `… 6001 Barcelona, Anzoátegui, Venezuela`
37
+ * tags it `postcode` and recovers `locality: Barcelona`. So a shard emitting one placement teaches one family of
38
+ * countries.
39
+ *
40
+ * Each is attested by a gauntlet board row, which is the bar for adding another:
41
+ *
42
+ * - `leading` — `«postcode» «locality», «region»`. `Rua da Praia, 15, 8600-315 Lagos, Algarve, Portugal`
43
+ * (`pt_structured`). The default, and what every tuple written before this field existed means.
44
+ * - `after_locality` — `«locality» «postcode», «region»`. `…, Barcelona 6001, Anzoátegui, Venezuela`
45
+ * (`ve_city_postcode_trailing_state`).
46
+ * - `after_region` — `«locality», «region» «postcode»`. `12 MG Road, Indiranagar, Bengaluru, Karnataka 560038, India`
47
+ * (`in_structured`).
48
+ */
49
+ export type PostcodePlacement = "leading" | "after_locality" | "after_region"
50
+
27
51
  /**
28
52
  * A (locality, region, postcode, country) source tuple — the input to tuples-mode recipes.
29
53
  */
30
54
  export interface ShardTuple {
31
55
  locality?: string
56
+ /**
57
+ * The segment before the locality, when the source has one. A shard whose every row BEGINS with the locality teaches
58
+ * that the first named segment is the locality, which flips the model's default — measured on the v4.8.0 candidate,
59
+ * where `Ye Three Lords, 27 Minories, London EC3N 1DE` came back `locality: "Ye Three Lords"`.
60
+ */
61
+ dependentLocality?: string
32
62
  region?: string
33
63
  postcode?: string
34
64
  country?: string
65
+ /**
66
+ * Defaults to `leading` when absent, which is what every tuple file written before this field existed means — so an
67
+ * old tuples file produces byte-identical rows.
68
+ */
69
+ postcodePlacement?: PostcodePlacement
35
70
  [k: string]: unknown
36
71
  }
37
72
 
@@ -55,17 +90,26 @@ export { makeLcg, mulberry32 as makeMulberry32, makeLcg as makeRandom } from "@m
55
90
  export type CSVRecord = Record<string, string | undefined>
56
91
 
57
92
  /**
58
- * Read an in-memory CSV (an `unzip -p` buffer, say) as header-keyed records.
59
- *
60
93
  * Quote handling spans the ROW split, not only the column split: a newline inside a quoted field belongs to a single
61
94
  * record, so the record boundaries can only be found by a scanner that already knows where the quotes are. Find the
62
95
  * lines first and unquote afterwards and such a record splits in two — the first half short by however many columns
63
96
  * followed the newline, which is how a `street` value comes to be read as `city`.
64
97
  *
98
+ * `header: false` keeps the first record in the stream, so this module owns the lower-casing rather than
99
+ * `normalizeKeys` — see {@link toHeader}.
100
+ */
101
+ const CSV_INIT = { header: false, enableQuoteHandling: true } as const
102
+
103
+ /**
65
104
  * Header names are lower-cased on the way in. `normalizeKeys` will not do it: it leaves an ALL CAPS header alone, and
66
105
  * OpenAddresses ships `LON,LAT,NUMBER,STREET` while other extracts ship the same names lower-case. A recipe names its
67
106
  * columns in lower case either way.
68
- *
107
+ */
108
+ function toHeader(cells: readonly string[]): string[] {
109
+ return cells.map((name) => name.trim().toLowerCase())
110
+ }
111
+
112
+ /**
69
113
  * Line breaks inside a value become single spaces. A quote-aware parse is the first thing here able to return a value
70
114
  * CONTAINING one — `us/ia/statewide.csv` has 12, all unit designators like `"#2\n#2"` — and every consumer synthesizes
71
115
  * one-line address text from these cells with no guard, because until that parse landed no value could carry one.
@@ -75,27 +119,58 @@ export type CSVRecord = Record<string, string | undefined>
75
119
  * Only `\r` and `\n`, deliberately — NOT `\s`. Runs of spaces and tabs pass through exactly as the source wrote them
76
120
  * (OA's IA extract writes `NORTH`, three spaces, `MAIN STREET`), because those could always appear and every shard
77
121
  * built to date contains them. Widening this to `\s+` silently rewrites values on rows with no line break at all.
78
- * `scaffold.test.ts` pins both halves.
122
+ * `scaffold.test.ts` pins both halves, against both readers.
123
+ */
124
+ function toRecord(header: readonly string[], cells: readonly string[]): CSVRecord {
125
+ const record: CSVRecord = {}
126
+
127
+ for (let i = 0; i < header.length; i++) {
128
+ record[header[i]!] = (cells[i] ?? "").replaceAll(/[\r\n]+/g, " ").trim()
129
+ }
130
+
131
+ return record
132
+ }
133
+
134
+ /**
135
+ * Read a CSV as header-keyed records.
136
+ *
137
+ * Returns the spliterator's own {@linkcode AsyncSequence}, so a caller composes `take`/`drop`/`filter` onto it — those
138
+ * ops fuse into one pull loop, and a `take` that is satisfied closes the source's file handle on the way out. Wrapping
139
+ * this in an `async function*` costs an async frame per row and takes those ops away; don't.
140
+ *
141
+ * A source at or below the spliterator's 128 KiB bulk threshold is read whole and parsed by the synchronous engine, so
142
+ * this is also the right reader for small sources — there is no buffered variant to reach for.
79
143
  */
80
- export function* readCSVRecords(source: Uint8Array): Generator<CSVRecord> {
144
+ export function readCSVRecords(source: AsyncDataResource | AsyncChunkIterator): AsyncSequence<CSVRecord> {
81
145
  let header: string[] | null = null
82
146
 
83
- // `header: false` keeps the first record in the stream so this reader owns the lower-casing.
84
- for (const cells of CSVSpliterator.from(source, { header: false, enableQuoteHandling: true })) {
85
- if (!header) {
86
- header = cells.map((name) => name.trim().toLowerCase())
147
+ return CSVSpliterator.fromAsync<string[]>(source, CSV_INIT)
148
+ .filter((cells) => {
149
+ if (header) return true
87
150
 
88
- continue
89
- }
151
+ header = toHeader(cells)
90
152
 
91
- const record: CSVRecord = {}
153
+ return false
154
+ })
155
+ .map((cells) => toRecord(header!, cells))
156
+ }
92
157
 
93
- for (let i = 0; i < header.length; i++) {
94
- record[header[i]!] = (cells[i] ?? "").replaceAll(/[\r\n]+/g, " ").trim()
95
- }
158
+ /**
159
+ * {@link readCSVRecords} over one member of a zip archive — what every recipe reading a cached OA source wants.
160
+ *
161
+ * A source a checkout has not cached yields nothing, after saying so. A lab holds the archives for the countries it has
162
+ * built, so a recipe naming ten sources routinely finds three, and the `unzip -p` subprocesses these replaced behaved
163
+ * the same way by accident — a non-zero exit warned and returned no rows. A recipe that ends up with NO tuples at all
164
+ * still throws; that is the case where the cache, not the recipe, is the problem.
165
+ */
166
+ export function readZippedCSVRecords(archivePath: PathBuilderLike, entryName: string): AsyncSequence<CSVRecord> {
167
+ if (!existsSync(String(archivePath))) {
168
+ console.error(` WARN: ${archivePath} is not cached — skipping ${entryName}`)
96
169
 
97
- yield record
170
+ return AsyncSequence.from<CSVRecord>([])
98
171
  }
172
+
173
+ return readCSVRecords(readZipEntry(archivePath, entryName))
99
174
  }
100
175
 
101
176
  /**
@@ -222,7 +297,7 @@ export interface ShardRecipeOpts {
222
297
  * `sub-venue`: the `poi.db` spatial layer, read for the en-US and fr-FR venue + confound pools (the two of poi.db's
223
298
  * four countries this shard has legs for). Default `$MAILWOMAN_DATA_ROOT/poi/poi.db`.
224
299
  */
225
- poiDb?: string
300
+ poiDB?: string
226
301
  /**
227
302
  * `sub-venue`: GB/US/FR address-context tuples JSONL. Default the house-venue v3 tuples
228
303
  * (`$MAILWOMAN_DATA_ROOT/corpus/intermediate/house-venue-tuples-v3.jsonl`); DE and ES read OpenAddresses directly.