@mailwoman/resolver-wof-sqlite 10.0.0 → 10.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 (477) hide show
  1. package/README.md +9 -8
  2. package/lib/address/point/interpolation.ts +45 -39
  3. package/lib/address/point/schema.ts +17 -40
  4. package/lib/address/point.ts +316 -0
  5. package/lib/{address/index.ts → address.ts} +1 -1
  6. package/lib/ancestry/backfill.ts +20 -86
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +122 -174
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +44 -114
  11. package/lib/candidate/alias-bags.ts +16 -17
  12. package/lib/candidate/ancestors/schema.ts +77 -61
  13. package/lib/candidate/ancestors/sidecar.ts +15 -22
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +10 -55
  16. package/lib/candidate/fts.ts +5 -23
  17. package/lib/candidate/importance.ts +65 -68
  18. package/lib/candidate/lookup.ts +124 -415
  19. package/lib/candidate/name-roles.ts +15 -33
  20. package/lib/candidate/own-name.ts +35 -32
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +29 -65
  23. package/lib/candidate/scoring.ts +54 -93
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +29 -27
  26. package/lib/coincident-roles.ts +85 -55
  27. package/lib/convention/schema.ts +15 -16
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +40 -45
  30. package/lib/currency-backfill.ts +34 -79
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +32 -21
  33. package/lib/extracts.ts +97 -74
  34. package/lib/fst/autocomplete.ts +54 -37
  35. package/lib/fst/builder.ts +37 -41
  36. package/lib/fst/deserialize-web.ts +16 -17
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +43 -120
  39. package/lib/fst/matcher.ts +7 -5
  40. package/lib/fst/serialize.ts +54 -74
  41. package/lib/fst/types.ts +78 -75
  42. package/lib/{fst/index.ts → fst.ts} +1 -0
  43. package/lib/fts/query.ts +5 -36
  44. package/lib/fts.ts +280 -0
  45. package/lib/geonames/aliases.ts +73 -144
  46. package/lib/geonames/postal.ts +162 -71
  47. package/lib/index.ts +9 -8
  48. package/lib/interpolation.ts +48 -100
  49. package/lib/lookup.ts +155 -270
  50. package/lib/name-score.ts +9 -7
  51. package/lib/nsul/lookup.ts +37 -54
  52. package/lib/nsul/schema.ts +16 -43
  53. package/lib/paths.ts +159 -0
  54. package/lib/place-importance-schema.ts +62 -169
  55. package/lib/poi/lookup.ts +104 -121
  56. package/lib/poi/schema.ts +42 -26
  57. package/lib/polygon-schema.ts +10 -6
  58. package/lib/postal/city/alias/lookup.ts +22 -40
  59. package/lib/postal/city/alias/schema.ts +14 -10
  60. package/lib/postal/city/candidate-schema.ts +26 -20
  61. package/lib/postcode-point-lookup.ts +8 -27
  62. package/lib/primary-preference.ts +46 -126
  63. package/lib/proximity-rerank.ts +44 -33
  64. package/lib/ranking-weights.ts +105 -78
  65. package/lib/region-keys.ts +70 -42
  66. package/lib/reverse.ts +149 -86
  67. package/lib/schema.ts +73 -50
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +15 -23
  70. package/lib/sqlite/utils.ts +19 -22
  71. package/lib/street/centroid/schema.ts +37 -42
  72. package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
  73. package/lib/street/morphology/fst/builder.ts +60 -42
  74. package/lib/street/morphology/fst/loader.ts +40 -32
  75. package/lib/street/name-lookup.ts +45 -41
  76. package/lib/street/normalize.ts +88 -219
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +1 -1
  79. package/lib/types.ts +109 -150
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +29 -68
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +236 -252
  85. package/out/address/point/interpolation.d.ts +20 -23
  86. package/out/address/point/interpolation.d.ts.map +1 -1
  87. package/out/address/point/interpolation.js +42 -39
  88. package/out/address/point/interpolation.js.map +1 -1
  89. package/out/address/point/schema.d.ts +12 -35
  90. package/out/address/point/schema.d.ts.map +1 -1
  91. package/out/address/point/schema.js +3 -19
  92. package/out/address/point/schema.js.map +1 -1
  93. package/out/address/point.d.ts +57 -0
  94. package/out/address/point.d.ts.map +1 -0
  95. package/out/address/point.js +206 -0
  96. package/out/address/point.js.map +1 -0
  97. package/out/{address/index.js → address.d.ts} +2 -2
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +2 -2
  100. package/out/address.js.map +1 -0
  101. package/out/ancestry/backfill.d.ts +11 -64
  102. package/out/ancestry/backfill.d.ts.map +1 -1
  103. package/out/ancestry/backfill.js +14 -74
  104. package/out/ancestry/backfill.js.map +1 -1
  105. package/out/ancestry.d.ts +49 -0
  106. package/out/ancestry.d.ts.map +1 -0
  107. package/out/ancestry.js +59 -0
  108. package/out/ancestry.js.map +1 -0
  109. package/out/build/candidate.d.ts +82 -112
  110. package/out/build/candidate.d.ts.map +1 -1
  111. package/out/build/candidate.js +17 -100
  112. package/out/build/candidate.js.map +1 -1
  113. package/out/build/population-corrections.d.ts +52 -0
  114. package/out/build/population-corrections.d.ts.map +1 -0
  115. package/out/build/population-corrections.js +40 -0
  116. package/out/build/population-corrections.js.map +1 -0
  117. package/out/build/slim.d.ts +8 -44
  118. package/out/build/slim.d.ts.map +1 -1
  119. package/out/build/slim.js +33 -94
  120. package/out/build/slim.js.map +1 -1
  121. package/out/candidate/alias-bags.d.ts +6 -6
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +16 -17
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +73 -57
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -1
  127. package/out/candidate/ancestors/schema.js +56 -48
  128. package/out/candidate/ancestors/schema.js.map +1 -1
  129. package/out/candidate/ancestors/sidecar.d.ts +3 -8
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
  131. package/out/candidate/ancestors/sidecar.js +15 -22
  132. package/out/candidate/ancestors/sidecar.js.map +1 -1
  133. package/out/candidate/country-display-names.d.ts +5 -15
  134. package/out/candidate/country-display-names.d.ts.map +1 -1
  135. package/out/candidate/country-display-names.js +6 -20
  136. package/out/candidate/country-display-names.js.map +1 -1
  137. package/out/candidate/extract-fold.d.ts +7 -15
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +5 -47
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +5 -23
  142. package/out/candidate/fts.d.ts.map +1 -1
  143. package/out/candidate/fts.js +5 -23
  144. package/out/candidate/fts.js.map +1 -1
  145. package/out/candidate/importance.d.ts +60 -65
  146. package/out/candidate/importance.d.ts.map +1 -1
  147. package/out/candidate/importance.js +56 -63
  148. package/out/candidate/importance.js.map +1 -1
  149. package/out/candidate/lookup.d.ts +18 -55
  150. package/out/candidate/lookup.d.ts.map +1 -1
  151. package/out/candidate/lookup.js +82 -385
  152. package/out/candidate/lookup.js.map +1 -1
  153. package/out/candidate/name-roles.d.ts +9 -21
  154. package/out/candidate/name-roles.d.ts.map +1 -1
  155. package/out/candidate/name-roles.js +15 -33
  156. package/out/candidate/name-roles.js.map +1 -1
  157. package/out/candidate/own-name.d.ts +23 -25
  158. package/out/candidate/own-name.d.ts.map +1 -1
  159. package/out/candidate/own-name.js +35 -32
  160. package/out/candidate/own-name.js.map +1 -1
  161. package/out/candidate/place-attrs.d.ts +20 -13
  162. package/out/candidate/place-attrs.d.ts.map +1 -1
  163. package/out/candidate/place-attrs.js +4 -5
  164. package/out/candidate/place-attrs.js.map +1 -1
  165. package/out/candidate/schema.d.ts +27 -62
  166. package/out/candidate/schema.d.ts.map +1 -1
  167. package/out/candidate/schema.js +10 -22
  168. package/out/candidate/schema.js.map +1 -1
  169. package/out/candidate/scoring.d.ts +7 -8
  170. package/out/candidate/scoring.d.ts.map +1 -1
  171. package/out/candidate/scoring.js +52 -89
  172. package/out/candidate/scoring.js.map +1 -1
  173. package/out/capital-schema.d.ts +21 -17
  174. package/out/capital-schema.d.ts.map +1 -1
  175. package/out/capital-schema.js +17 -15
  176. package/out/capital-schema.js.map +1 -1
  177. package/out/capitals.d.ts +27 -25
  178. package/out/capitals.d.ts.map +1 -1
  179. package/out/capitals.js +23 -21
  180. package/out/capitals.js.map +1 -1
  181. package/out/coincident-roles.d.ts +60 -43
  182. package/out/coincident-roles.d.ts.map +1 -1
  183. package/out/coincident-roles.js +72 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +14 -14
  186. package/out/convention/schema.d.ts.map +1 -1
  187. package/out/convention/schema.js +6 -4
  188. package/out/convention/schema.js.map +1 -1
  189. package/out/convention.d.ts +97 -0
  190. package/out/convention.d.ts.map +1 -0
  191. package/out/convention.js +81 -0
  192. package/out/convention.js.map +1 -0
  193. package/out/coverage-manifest-schema.d.ts +34 -39
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +25 -34
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +16 -38
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +19 -46
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +1 -1
  202. package/out/env.js +1 -1
  203. package/out/exact-match.d.ts +19 -10
  204. package/out/exact-match.d.ts.map +1 -1
  205. package/out/exact-match.js +32 -21
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +66 -55
  208. package/out/extracts.d.ts.map +1 -1
  209. package/out/extracts.js +63 -55
  210. package/out/extracts.js.map +1 -1
  211. package/out/fst/autocomplete.d.ts +29 -21
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +39 -28
  214. package/out/fst/autocomplete.js.map +1 -1
  215. package/out/fst/builder.d.ts.map +1 -1
  216. package/out/fst/builder.js +37 -41
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +4 -4
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +15 -16
  221. package/out/fst/deserialize-web.js.map +1 -1
  222. package/out/fst/format.d.ts +41 -23
  223. package/out/fst/format.d.ts.map +1 -1
  224. package/out/fst/format.js +41 -23
  225. package/out/fst/format.js.map +1 -1
  226. package/out/fst/freshness.d.ts +19 -81
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +31 -94
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +5 -4
  231. package/out/fst/matcher.d.ts.map +1 -1
  232. package/out/fst/matcher.js +2 -2
  233. package/out/fst/matcher.js.map +1 -1
  234. package/out/fst/serialize.d.ts +28 -28
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +53 -71
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +78 -74
  239. package/out/fst/types.d.ts.map +1 -1
  240. package/out/fst/types.js +1 -4
  241. package/out/fst/types.js.map +1 -1
  242. package/out/{fst/index.js → fst.d.ts} +2 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +2 -1
  245. package/out/fst.js.map +1 -0
  246. package/out/fts/query.d.ts +2 -27
  247. package/out/fts/query.d.ts.map +1 -1
  248. package/out/fts/query.js +5 -36
  249. package/out/fts/query.js.map +1 -1
  250. package/out/fts.d.ts +126 -0
  251. package/out/fts.d.ts.map +1 -0
  252. package/out/fts.js +188 -0
  253. package/out/fts.js.map +1 -0
  254. package/out/geonames/aliases.d.ts +26 -62
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +45 -117
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -44
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -73
  261. package/out/geonames/postal.js.map +1 -1
  262. package/out/{geonames/index.js → geonames.d.ts} +1 -1
  263. package/out/geonames.d.ts.map +1 -0
  264. package/out/{geonames/index.d.ts → geonames.js} +1 -1
  265. package/out/geonames.js.map +1 -0
  266. package/out/index.d.ts +9 -8
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +6 -5
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +18 -55
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +27 -69
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +23 -51
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +136 -234
  277. package/out/lookup.js.map +1 -1
  278. package/out/name-score.d.ts +2 -2
  279. package/out/name-score.d.ts.map +1 -1
  280. package/out/name-score.js +9 -7
  281. package/out/name-score.js.map +1 -1
  282. package/out/nsul/lookup.d.ts +33 -34
  283. package/out/nsul/lookup.d.ts.map +1 -1
  284. package/out/nsul/lookup.js +26 -40
  285. package/out/nsul/lookup.js.map +1 -1
  286. package/out/nsul/schema.d.ts +16 -43
  287. package/out/nsul/schema.d.ts.map +1 -1
  288. package/out/nsul/schema.js +9 -35
  289. package/out/nsul/schema.js.map +1 -1
  290. package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
  291. package/out/nsul.d.ts.map +1 -0
  292. package/out/{nsul/index.js → nsul.js} +1 -1
  293. package/out/nsul.js.map +1 -0
  294. package/out/paths.d.ts +124 -0
  295. package/out/paths.d.ts.map +1 -0
  296. package/out/paths.js +104 -0
  297. package/out/paths.js.map +1 -0
  298. package/out/place-importance-schema.d.ts +51 -143
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +28 -143
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +40 -47
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +69 -96
  305. package/out/poi/lookup.js.map +1 -1
  306. package/out/poi/schema.d.ts +41 -25
  307. package/out/poi/schema.d.ts.map +1 -1
  308. package/out/poi/schema.js +25 -17
  309. package/out/poi/schema.js.map +1 -1
  310. package/out/{poi/index.js → poi.d.ts} +1 -1
  311. package/out/poi.d.ts.map +1 -0
  312. package/out/{poi/index.d.ts → poi.js} +1 -1
  313. package/out/poi.js.map +1 -0
  314. package/out/polygon-schema.d.ts +10 -6
  315. package/out/polygon-schema.d.ts.map +1 -1
  316. package/out/polygon-schema.js +2 -2
  317. package/out/polygon-schema.js.map +1 -1
  318. package/out/postal/city/alias/lookup.d.ts +20 -23
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -1
  320. package/out/postal/city/alias/lookup.js +17 -30
  321. package/out/postal/city/alias/lookup.js.map +1 -1
  322. package/out/postal/city/alias/schema.d.ts +14 -10
  323. package/out/postal/city/alias/schema.d.ts.map +1 -1
  324. package/out/postal/city/alias/schema.js +10 -8
  325. package/out/postal/city/alias/schema.js.map +1 -1
  326. package/out/postal/city/candidate-schema.d.ts +25 -19
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -1
  328. package/out/postal/city/candidate-schema.js +21 -17
  329. package/out/postal/city/candidate-schema.js.map +1 -1
  330. package/out/{postal/index.d.ts → postal.d.ts} +1 -1
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/{postal/index.js → postal.js} +1 -1
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +6 -24
  335. package/out/postcode-point-lookup.d.ts.map +1 -1
  336. package/out/postcode-point-lookup.js +5 -17
  337. package/out/postcode-point-lookup.js.map +1 -1
  338. package/out/primary-preference.d.ts +38 -92
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +15 -99
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +41 -30
  343. package/out/proximity-rerank.d.ts.map +1 -1
  344. package/out/proximity-rerank.js +38 -29
  345. package/out/proximity-rerank.js.map +1 -1
  346. package/out/ranking-weights.d.ts +98 -64
  347. package/out/ranking-weights.d.ts.map +1 -1
  348. package/out/ranking-weights.js +26 -25
  349. package/out/ranking-weights.js.map +1 -1
  350. package/out/region-keys.d.ts +50 -31
  351. package/out/region-keys.d.ts.map +1 -1
  352. package/out/region-keys.js +68 -40
  353. package/out/region-keys.js.map +1 -1
  354. package/out/reverse.d.ts +78 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +98 -61
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +72 -49
  359. package/out/schema.d.ts.map +1 -1
  360. package/out/schema.js +4 -5
  361. package/out/schema.js.map +1 -1
  362. package/out/search-fetch.d.ts +16 -12
  363. package/out/search-fetch.d.ts.map +1 -1
  364. package/out/search-fetch.js +83 -63
  365. package/out/search-fetch.js.map +1 -1
  366. package/out/sqlite/convention-source.d.ts +12 -16
  367. package/out/sqlite/convention-source.d.ts.map +1 -1
  368. package/out/sqlite/convention-source.js +13 -22
  369. package/out/sqlite/convention-source.js.map +1 -1
  370. package/out/sqlite/utils.d.ts +19 -19
  371. package/out/sqlite/utils.d.ts.map +1 -1
  372. package/out/sqlite/utils.js +14 -15
  373. package/out/sqlite/utils.js.map +1 -1
  374. package/out/street/centroid/schema.d.ts +37 -42
  375. package/out/street/centroid/schema.d.ts.map +1 -1
  376. package/out/street/centroid/schema.js +9 -21
  377. package/out/street/centroid/schema.js.map +1 -1
  378. package/out/street/centroid.d.ts +30 -0
  379. package/out/street/centroid.d.ts.map +1 -0
  380. package/out/street/{centroid/index.js → centroid.js} +13 -24
  381. package/out/street/centroid.js.map +1 -0
  382. package/out/street/morphology/fst/builder.d.ts +20 -10
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -1
  384. package/out/street/morphology/fst/builder.js +44 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -1
  386. package/out/street/morphology/fst/loader.d.ts +33 -26
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -1
  388. package/out/street/morphology/fst/loader.js +23 -22
  389. package/out/street/morphology/fst/loader.js.map +1 -1
  390. package/out/street/name-lookup.d.ts +24 -18
  391. package/out/street/name-lookup.d.ts.map +1 -1
  392. package/out/street/name-lookup.js +34 -35
  393. package/out/street/name-lookup.js.map +1 -1
  394. package/out/street/normalize.d.ts +35 -136
  395. package/out/street/normalize.d.ts.map +1 -1
  396. package/out/street/normalize.js +77 -194
  397. package/out/street/normalize.js.map +1 -1
  398. package/out/street/segment-schema.d.ts +79 -39
  399. package/out/street/segment-schema.d.ts.map +1 -1
  400. package/out/street/segment-schema.js +71 -21
  401. package/out/street/segment-schema.js.map +1 -1
  402. package/out/{street/index.js → street.d.ts} +2 -2
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.d.ts → street.js} +2 -2
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +84 -149
  407. package/out/types.d.ts.map +1 -1
  408. package/out/types.js +0 -6
  409. package/out/types.js.map +1 -1
  410. package/out/unified-schema.d.ts +8 -12
  411. package/out/unified-schema.d.ts.map +1 -1
  412. package/out/unified-schema.js +29 -29
  413. package/out/unified-schema.js.map +1 -1
  414. package/out/uprn/existence.d.ts +28 -17
  415. package/out/uprn/existence.d.ts.map +1 -1
  416. package/out/uprn/existence.js +26 -16
  417. package/out/uprn/existence.js.map +1 -1
  418. package/out/uprn/lookup.d.ts +14 -44
  419. package/out/uprn/lookup.d.ts.map +1 -1
  420. package/out/uprn/lookup.js +21 -57
  421. package/out/uprn/lookup.js.map +1 -1
  422. package/out/uprn/schema.d.ts +18 -40
  423. package/out/uprn/schema.d.ts.map +1 -1
  424. package/out/uprn/schema.js +10 -32
  425. package/out/uprn/schema.js.map +1 -1
  426. package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
  427. package/out/uprn.d.ts.map +1 -0
  428. package/out/{uprn/index.js → uprn.js} +1 -1
  429. package/out/uprn.js.map +1 -0
  430. package/out/weights-overlay-linker.d.ts +138 -144
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +105 -159
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +91 -596
  435. package/lib/address/point/index.ts +0 -328
  436. package/lib/convention/index.ts +0 -154
  437. package/lib/fts/index.ts +0 -361
  438. package/out/address/index.d.ts.map +0 -1
  439. package/out/address/index.js.map +0 -1
  440. package/out/address/point/index.d.ts +0 -53
  441. package/out/address/point/index.d.ts.map +0 -1
  442. package/out/address/point/index.js +0 -222
  443. package/out/address/point/index.js.map +0 -1
  444. package/out/ancestry/index.d.ts +0 -45
  445. package/out/ancestry/index.d.ts.map +0 -1
  446. package/out/ancestry/index.js +0 -55
  447. package/out/ancestry/index.js.map +0 -1
  448. package/out/convention/index.d.ts +0 -106
  449. package/out/convention/index.d.ts.map +0 -1
  450. package/out/convention/index.js +0 -93
  451. package/out/convention/index.js.map +0 -1
  452. package/out/fst/index.d.ts.map +0 -1
  453. package/out/fst/index.js.map +0 -1
  454. package/out/fts/index.d.ts +0 -184
  455. package/out/fts/index.d.ts.map +0 -1
  456. package/out/fts/index.js +0 -277
  457. package/out/fts/index.js.map +0 -1
  458. package/out/geonames/index.d.ts.map +0 -1
  459. package/out/geonames/index.js.map +0 -1
  460. package/out/nsul/index.d.ts.map +0 -1
  461. package/out/nsul/index.js.map +0 -1
  462. package/out/poi/index.d.ts.map +0 -1
  463. package/out/poi/index.js.map +0 -1
  464. package/out/postal/index.d.ts.map +0 -1
  465. package/out/postal/index.js.map +0 -1
  466. package/out/street/centroid/index.d.ts +0 -40
  467. package/out/street/centroid/index.d.ts.map +0 -1
  468. package/out/street/centroid/index.js.map +0 -1
  469. package/out/street/index.d.ts.map +0 -1
  470. package/out/street/index.js.map +0 -1
  471. package/out/uprn/index.d.ts.map +0 -1
  472. package/out/uprn/index.js.map +0 -1
  473. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  474. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  475. /package/lib/{poi/index.ts → poi.ts} +0 -0
  476. /package/lib/{postal/index.ts → postal.ts} +0 -0
  477. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
package/out/fts.js ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ import { tableExists } from "@mailwoman/sqlite/introspection";
7
+ /**
8
+ * The name of the FTS5 virtual table that indexes WOF place names for `match` queries.
9
+ */
10
+ export const PLACE_SEARCH_TABLE = "place_search";
11
+ /**
12
+ * The separator between aliases in the `alt_names` bag.
13
+ * It stops a phrase query from matching across two aliases.
14
+ *
15
+ * FTS5 assigns positions only to tokens.
16
+ * Spaces and punctuation are not tokens and would leave the aliases adjacent,
17
+ * but unicode61 indexes the Private Use Area codepoint U+E000 as a token.
18
+ *
19
+ * Query sanitizers strip this character, so no user query can match the separator.
20
+ */
21
+ export const ALIAS_SEPARATOR = "\uE000";
22
+ const ALIAS_SEPARATOR_CODEPOINT = ALIAS_SEPARATOR.codePointAt(0);
23
+ /**
24
+ * Folds a query or name for exact-tier comparison by lowercasing, trimming
25
+ * and collapsing internal whitespace.
26
+ *
27
+ * Every exact-tier consumer uses this function, so its output compares equal to
28
+ * the aliases that {@link aliasBagExactMatch} parses.
29
+ */
30
+ export function foldQueryText(input) {
31
+ return input.toLowerCase().trim().replaceAll(/\s+/g, " ");
32
+ }
33
+ /**
34
+ * Returns whether any alias in an `alt_names` bag exactly equals the folded query.
35
+ *
36
+ * A bag without {@link ALIAS_SEPARATOR} is a legacy space-joined bag without alias boundaries.
37
+ * For such a bag, the function checks word-bounded containment only when no candidate matched strictly.
38
+ *
39
+ * Containment by itself would promote fragments such as "York" inside "New York City".
40
+ *
41
+ * @param altNames The `alt_names` bag from `place_search`, or null when the row has no aliases.
42
+ * @param normalizedQuery The query folded by {@link foldQueryText}.
43
+ * @param anyStrictExact Whether any candidate already matched on its canonical name or region abbreviation.
44
+ * Only legacy bags use it.
45
+ */
46
+ export function aliasBagExactMatch(altNames, normalizedQuery, anyStrictExact) {
47
+ if (!altNames || altNames === "" || !normalizedQuery)
48
+ return false;
49
+ if (altNames.includes(ALIAS_SEPARATOR)) {
50
+ return altNames.split(ALIAS_SEPARATOR).some((alias) => foldQueryText(alias) === normalizedQuery);
51
+ }
52
+ if (anyStrictExact)
53
+ return false;
54
+ return ` ${foldQueryText(altNames)} `.includes(` ${normalizedQuery} `);
55
+ }
56
+ /**
57
+ * The name of the R*Tree virtual table that indexes WOF place bounding boxes for bbox and proximity lookups.
58
+ */
59
+ export const PLACE_BBOX_TABLE = "place_bbox";
60
+ /**
61
+ * The name of the sparse table that stores `wof:population` per place.
62
+ *
63
+ * The lookup uses this value to apply the population ranking boost.
64
+ *
65
+ * A place without a row gets no boost and no penalty.
66
+ */
67
+ export const PLACE_POPULATION_TABLE = "place_population";
68
+ /**
69
+ * The name of the table of per-place `referential` and `encyclopedic` scores,
70
+ * built by `mailwoman gazetteer importance`.
71
+ *
72
+ * The lookup copies `encyclopedic` onto results without ranking by it.
73
+ * It refuses to read an older table with a single `importance` column.
74
+ */
75
+ export const PLACE_IMPORTANCE_TABLE = "place_importance";
76
+ /**
77
+ * Builds the `place_search` FTS5 table and the `place_bbox` R*Tree from a WOF
78
+ * database's `spr` and `names` tables.
79
+ *
80
+ * Without `drop: true`, an existing index is kept.
81
+ */
82
+ export function buildPlaceSearchFTS(db, opts = {}) {
83
+ const start = Date.now();
84
+ const onProgress = opts.onProgress ?? (() => { });
85
+ onProgress("checking");
86
+ const ftsExisting = tableExists(db, PLACE_SEARCH_TABLE);
87
+ const bboxExisting = tableExists(db, PLACE_BBOX_TABLE);
88
+ let ftsCreated = false;
89
+ if (ftsExisting && opts.drop) {
90
+ onProgress("dropping", PLACE_SEARCH_TABLE);
91
+ db.exec(`DROP TABLE ${PLACE_SEARCH_TABLE}`);
92
+ }
93
+ if (!ftsExisting || opts.drop) {
94
+ onProgress("creating");
95
+ db.exec(`
96
+ CREATE VIRTUAL TABLE ${PLACE_SEARCH_TABLE} USING fts5(
97
+ wof_id UNINDEXED,
98
+ name,
99
+ alt_names,
100
+ tokenize = 'unicode61 remove_diacritics 2'
101
+ );
102
+ `);
103
+ onProgress("populating");
104
+ db.exec(`
105
+ INSERT INTO ${PLACE_SEARCH_TABLE} (wof_id, name, alt_names)
106
+ SELECT
107
+ spr.id,
108
+ spr.name,
109
+ COALESCE((
110
+ SELECT GROUP_CONCAT(
111
+ REPLACE(name, char(${ALIAS_SEPARATOR_CODEPOINT}), ' '),
112
+ ' ' || char(${ALIAS_SEPARATOR_CODEPOINT}) || ' '
113
+ ) || ' ' || char(${ALIAS_SEPARATOR_CODEPOINT})
114
+ FROM names WHERE names.id = spr.id
115
+ ), '')
116
+ FROM spr
117
+ WHERE spr.is_current != 0
118
+ AND spr.is_deprecated = 0
119
+ AND spr.name IS NOT NULL;
120
+ `);
121
+ ftsCreated = true;
122
+ }
123
+ const ftsCountRow = db.prepare(`SELECT COUNT(*) AS n FROM ${PLACE_SEARCH_TABLE}`).get();
124
+ let bboxCreated = false;
125
+ if (bboxExisting && opts.drop) {
126
+ onProgress("dropping", PLACE_BBOX_TABLE);
127
+ db.exec(`DROP TABLE ${PLACE_BBOX_TABLE}`);
128
+ }
129
+ if (!bboxExisting || opts.drop) {
130
+ onProgress("creating-bbox");
131
+ db.exec(`
132
+ CREATE VIRTUAL TABLE ${PLACE_BBOX_TABLE} USING rtree(
133
+ id,
134
+ min_lat, max_lat,
135
+ min_lon, max_lon
136
+ );
137
+ `);
138
+ onProgress("populating-bbox");
139
+ db.exec(`
140
+ INSERT INTO ${PLACE_BBOX_TABLE} (id, min_lat, max_lat, min_lon, max_lon)
141
+ SELECT
142
+ spr.id,
143
+ spr.min_latitude,
144
+ spr.max_latitude,
145
+ spr.min_longitude,
146
+ spr.max_longitude
147
+ FROM spr
148
+ WHERE spr.is_current != 0
149
+ AND spr.is_deprecated = 0
150
+ AND spr.min_latitude IS NOT NULL
151
+ AND spr.max_latitude IS NOT NULL
152
+ AND spr.min_longitude IS NOT NULL
153
+ AND spr.max_longitude IS NOT NULL
154
+ AND NOT (spr.min_latitude = 0 AND spr.max_latitude = 0
155
+ AND spr.min_longitude = 0 AND spr.max_longitude = 0);
156
+ `);
157
+ bboxCreated = true;
158
+ }
159
+ const bboxCountRow = db.prepare(`SELECT COUNT(*) AS n FROM ${PLACE_BBOX_TABLE}`).get();
160
+ onProgress("done", `${ftsCountRow.n} FTS rows + ${bboxCountRow.n} bbox rows ` +
161
+ `(${ftsCreated ? "built" : "preexisting"} / ${bboxCreated ? "built" : "preexisting"})`);
162
+ return {
163
+ created: ftsCreated,
164
+ indexedRows: ftsCountRow.n,
165
+ bboxCreated,
166
+ bboxIndexedRows: bboxCountRow.n,
167
+ durationMs: Date.now() - start,
168
+ };
169
+ }
170
+ /**
171
+ * Returns whether the connected database has the `place_search` FTS table.
172
+ */
173
+ export function placeSearchFTSExists(db) {
174
+ return tableExists(db, PLACE_SEARCH_TABLE);
175
+ }
176
+ /**
177
+ * Returns whether the connected database has the `place_bbox` R*Tree table.
178
+ */
179
+ export function placeBboxExists(db) {
180
+ return tableExists(db, PLACE_BBOX_TABLE);
181
+ }
182
+ /**
183
+ * Returns whether the connected database has the `place_population` table.
184
+ */
185
+ export function placePopulationExists(db) {
186
+ return tableExists(db, PLACE_POPULATION_TABLE);
187
+ }
188
+ //# sourceMappingURL=fts.js.map
package/out/fts.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts.js","sourceRoot":"","sources":["../lib/fts.ts"],"names":[],"mappings":"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"}
@@ -2,94 +2,58 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * #743/#193 — fold GeoNames bilingual / alt-language place-names into a WOF/unified admin DB as
7
- * first-class places. The hard-filter recall gap on bilingual countries (the address says
8
- * "Karjaa" but the table holds the Swedish "Karis") is missing alt-LANGUAGE names, not missing
9
- * places: the WOF/Overture `names` carried only the primary, so the candidate build's Latin-alias
10
- * explode (build-candidate pass 2) had nothing to widen. GeoNames' per-country dump carries the
11
- * variants inline (the Karis row's `alternatenames` includes "Karjaa").
12
- *
13
- * For each POPULATED place (feature class `P`) this writes an `spr` row + `names` rows (primary +
14
- * Latin alt-names) + population into the SAME tables the WOF/Overture paths use — synthetic ids
15
- * based at {@link GEONAMES_ID_BASE} so the three sources never collide. The caller then rebuilds
16
- * `place_search` ({@link buildPlaceSearchFTS} with `drop: true`) so the candidate build carries
17
- * Karjaa↔Karis. Proven (FI hard-resolve 69.5 → 85.8 %, coverage 74.4 → 94.0 %); duplicating a
18
- * place already held under another source is benign — the rows share name_key+coord and the
19
- * candidate ranking dedupes by score.
20
- *
21
- * This is the package home so the canonical `build-unified-wof --geonames-countries`, the
22
- * pipeline fold (`gazetteer-pipeline/admin/fold-geonames`), AND the `mailwoman gazetteer` commands all share
23
- * ONE implementation. GeoNames dump = `download.geonames.org/export/dump/<CC>.zip` → `<CC>.txt`
24
- * (TSV).
25
5
  */
26
6
  import type { DatabaseClient } from "@mailwoman/sqlite/client";
27
7
  import { type PathBuilderLike } from "path-ts";
28
8
  import type { WOFDatabase } from "#schema";
29
9
  /**
30
- * Clear everything the alias fold owns — `[GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE)` across {@link
31
- * FOLD_OWNED_TABLES}
32
- * — so a fold's output is a function of its country list and dumps ALONE, never of what the DB happened to hold first.
33
- * Returns the rows removed per table.
34
- *
35
- * This exists because the synthetic id is a POSITION (`GEONAMES_ID_BASE + n`, counted across the country list in
36
- * order), not a derivation from the geonameid: change the list, its order, or a dump's contents and every subsequent id
37
- * shifts. Without the purge, a second fold overwrote the `spr` PREFIX it reached (`INSERT OR REPLACE`) while its
38
- * `names`/`ancestors` rows merely appended (bare `INSERT`) and `place_population` skipped unpopulated places entirely —
39
- * so the previous run's names, ancestry and populations stayed bound to ids now describing other places.
10
+ * Deletes every row the GeoNames alias fold owns, with ids in
11
+ * `[GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE)`, and returns the count removed per table.
40
12
  *
41
- * Measured on the live 2026-08-05 artifact: a 14-country re-fold over the 161-country fold baked into
42
- * `admin-global-priority.db` put 522,184 of 2,110,096 name rows in the range (24.7 %) on a place from a different
43
- * country. Gaborone/BW at id 9000000121151 became Aichegg/AT and kept all 26 of its names; Kinshasa's 16,000,000
44
- * population landed on a Lithuanian hamlet.
45
- *
46
- * The upper bound is NOT the end of the id space. Each later extract owns a range above this one — GeoNames-postal @
47
- * 9.5e12, NL-PC6 @ 9.6e12, Code-Point @ 9.7e12, NI @ 9.8e12 — and a purge that ran past {@link GEONAMES_POSTAL_ID_BASE}
48
- * would delete them.
13
+ * Fold ids are assigned sequentially, so a re-fold without this purge would attach
14
+ * stale names, ancestors and populations to other places.
15
+ * The upper bound leaves the postal and postcode id ranges above it untouched.
49
16
  */
50
17
  export declare function purgeGeonamesAliasRange<DB>(db: DatabaseClient<DB>): Record<string, number>;
51
18
  /**
52
- * Per-country progress for the ingest — one event per country dump processed (or skipped).
19
+ * One progress event per country dump that the ingest processed or skipped.
53
20
  */
54
21
  export interface GeonamesIngestProgress {
55
22
  /**
56
- * ISO 3166-1 alpha-2 code.
23
+ * The ISO 3166-1 alpha-2 country code.
57
24
  */
58
25
  country: string;
59
26
  /**
60
- * Populated places ingested from this country's dump (0 when skipped).
27
+ * The number of populated places ingested from this country's dump, or 0 when it was skipped.
61
28
  */
62
29
  places: number;
63
30
  /**
64
- * True when the country's `<CC>.txt` dump was missing — the country is skipped, not fatal.
31
+ * Whether the country's `<CC>.txt` dump was missing.
32
+ *
33
+ * A missing dump skips the country without failing the ingest.
65
34
  */
66
35
  skipped: boolean;
36
+ /**
37
+ * The number of alternate names that the alias filter rejected.
38
+ *
39
+ * It distinguishes a sparse source from aliases that the fold discarded.
40
+ */
41
+ aliasesRefused: number | null;
67
42
  }
68
43
  /**
69
- * Fold the GeoNames `P`-class places (+ their Latin alt-names) for `countries` into `db`'s `spr` / `names` /
70
- * `place_population` tables. Returns the total places ingested.
44
+ * Formats a GeoNames fold progress event as one log line.
45
+ */
46
+ export declare function formatGeonamesIngestProgress(event: GeonamesIngestProgress, missingFile?: PathBuilderLike): string;
47
+ /**
48
+ * Purges the fold's id range, then adds GeoNames `P`-class places and their
49
+ * alternate names for `countries` to `db`.
50
+ * It returns the number of places ingested.
71
51
  *
72
- * `onProgress` receives one event per country (default: a stderr line, matching the build scripts' legacy output). The
73
- * caller MUST rebuild `place_search` afterward (`buildPlaceSearchFTS(db, { drop: true })`) for the new names to reach
74
- * the candidate build's alias pass.
52
+ * The caller must then rebuild `place_search` with `buildPlaceSearchFTS(db, { drop: true })`
53
+ * to make the new names searchable.
75
54
  */
76
55
  export declare function ingestGeonamesAliases(db: DatabaseClient<WOFDatabase>, countries: string[], geonamesDir: PathBuilderLike, onProgress?: (event: GeonamesIngestProgress) => void, opts?: {
77
- /**
78
- * #267: the countries for which to ALSO fold the GeoNames A-class admin (PCLI country + ADM1 regions) and link each
79
- * locality's `parent_id` + ancestry chain (locality → region → country). PER-COUNTRY because a country that already
80
- * carries WOF admin would double up — pass only the ZERO-COVERAGE gap countries (the coverage-expansion targets),
81
- * never the EU alias set. Without admin, a gap country's localities are orphans (`parent_id=-1`, no ancestors), so
82
- * `parentID` scoping and adminCoherence can't reach them and "Tbilisi, GE" can't resolve.
83
- */
84
56
  adminForCountries?: ReadonlySet<string>;
85
- /**
86
- * #936: directory of per-country alternateNamesV2 dumps
87
- * (`download.geonames.org/export/dump/alternatenames/<CC>.zip` → `<CC>.txt`). When a country's file is present,
88
- * alias rows gain their language tag, `privateuse` ("preferred" from `isPreferredName`), and the `official` bit
89
- * (language is CLDR-official for the country, colloquial/historic excluded — the rule the #936 risk probe measured
90
- * at 7 new name-exact collisions globally). The main dump's bare `alternatenames` list still decides WHICH rows
91
- * exist; V2 only decorates them. Missing file = the pre-#936 untagged behavior, not an error.
92
- */
93
57
  alternateDir?: PathBuilderLike;
94
58
  }): Promise<number>;
95
59
  //# sourceMappingURL=aliases.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../../lib/geonames/aliases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAQ,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAGpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAQ1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY1F;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;CAChB;AAqFD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,cAAc,CAAC,WAAW,CAAC,EAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,eAAe,EAC5B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,EACpD,IAAI,CAAC,EAAE;IACN;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACvC;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,eAAe,CAAA;CAC9B,GACC,OAAO,CAAC,MAAM,CAAC,CA8MjB"}
1
+ {"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../../lib/geonames/aliases.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,SAAS,CAAA;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAI1C;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY1F;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACtC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IAEd;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAEhB;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,EAAE,WAAW,CAAC,EAAE,eAAe,GAAG,MAAM,CAMjH;AAgED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,cAAc,CAAC,WAAW,CAAC,EAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,eAAe,EAC5B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,EACpD,IAAI,CAAC,EAAE;IACN,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAEvC,YAAY,CAAC,EAAE,eAAe,CAAA;CAC9B,GACC,OAAO,CAAC,MAAM,CAAC,CAyLjB"}
@@ -2,57 +2,20 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * #743/#193 — fold GeoNames bilingual / alt-language place-names into a WOF/unified admin DB as
7
- * first-class places. The hard-filter recall gap on bilingual countries (the address says
8
- * "Karjaa" but the table holds the Swedish "Karis") is missing alt-LANGUAGE names, not missing
9
- * places: the WOF/Overture `names` carried only the primary, so the candidate build's Latin-alias
10
- * explode (build-candidate pass 2) had nothing to widen. GeoNames' per-country dump carries the
11
- * variants inline (the Karis row's `alternatenames` includes "Karjaa").
12
- *
13
- * For each POPULATED place (feature class `P`) this writes an `spr` row + `names` rows (primary +
14
- * Latin alt-names) + population into the SAME tables the WOF/Overture paths use — synthetic ids
15
- * based at {@link GEONAMES_ID_BASE} so the three sources never collide. The caller then rebuilds
16
- * `place_search` ({@link buildPlaceSearchFTS} with `drop: true`) so the candidate build carries
17
- * Karjaa↔Karis. Proven (FI hard-resolve 69.5 → 85.8 %, coverage 74.4 → 94.0 %); duplicating a
18
- * place already held under another source is benign — the rows share name_key+coord and the
19
- * candidate ranking dedupes by score.
20
- *
21
- * This is the package home so the canonical `build-unified-wof --geonames-countries`, the
22
- * pipeline fold (`gazetteer-pipeline/admin/fold-geonames`), AND the `mailwoman gazetteer` commands all share
23
- * ONE implementation. GeoNames dump = `download.geonames.org/export/dump/<CC>.zip` → `<CC>.txt`
24
- * (TSV).
25
5
  */
26
6
  import { isOfficialLanguage } from "@mailwoman/codex/country";
27
- import { pathExists } from "@mailwoman/core/fs/readers";
7
+ import { readUnquotedTSV } from "@mailwoman/core/fs/delimited";
8
+ import { pathExists } from "@mailwoman/core/fs/readers/stat";
28
9
  import { GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE } from "@mailwoman/core/resolver/synthetic-id-ranges";
29
- import { join } from "path-ts";
30
- import { TSVSpliterator } from "spliterator";
31
- /**
32
- * The four tables the alias fold writes into the range it owns. The purge and the ingest must agree on this list — a
33
- * table written but not purged is exactly the #1514 defect.
34
- */
10
+ import { resolvePathBuilder } from "path-ts";
35
11
  const FOLD_OWNED_TABLES = ["spr", "names", "place_population", "ancestors"];
36
12
  /**
37
- * Clear everything the alias fold owns — `[GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE)` across {@link
38
- * FOLD_OWNED_TABLES}
39
- * — so a fold's output is a function of its country list and dumps ALONE, never of what the DB happened to hold first.
40
- * Returns the rows removed per table.
41
- *
42
- * This exists because the synthetic id is a POSITION (`GEONAMES_ID_BASE + n`, counted across the country list in
43
- * order), not a derivation from the geonameid: change the list, its order, or a dump's contents and every subsequent id
44
- * shifts. Without the purge, a second fold overwrote the `spr` PREFIX it reached (`INSERT OR REPLACE`) while its
45
- * `names`/`ancestors` rows merely appended (bare `INSERT`) and `place_population` skipped unpopulated places entirely —
46
- * so the previous run's names, ancestry and populations stayed bound to ids now describing other places.
47
- *
48
- * Measured on the live 2026-08-05 artifact: a 14-country re-fold over the 161-country fold baked into
49
- * `admin-global-priority.db` put 522,184 of 2,110,096 name rows in the range (24.7 %) on a place from a different
50
- * country. Gaborone/BW at id 9000000121151 became Aichegg/AT and kept all 26 of its names; Kinshasa's 16,000,000
51
- * population landed on a Lithuanian hamlet.
13
+ * Deletes every row the GeoNames alias fold owns, with ids in
14
+ * `[GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE)`, and returns the count removed per table.
52
15
  *
53
- * The upper bound is NOT the end of the id space. Each later extract owns a range above this one — GeoNames-postal @
54
- * 9.5e12, NL-PC6 @ 9.6e12, Code-Point @ 9.7e12, NI @ 9.8e12 — and a purge that ran past {@link GEONAMES_POSTAL_ID_BASE}
55
- * would delete them.
16
+ * Fold ids are assigned sequentially, so a re-fold without this purge would attach
17
+ * stale names, ancestors and populations to other places.
18
+ * The upper bound leaves the postal and postcode id ranges above it untouched.
56
19
  */
57
20
  export function purgeGeonamesAliasRange(db) {
58
21
  const removed = {};
@@ -65,26 +28,18 @@ export function purgeGeonamesAliasRange(db) {
65
28
  return removed;
66
29
  }
67
30
  /**
68
- * Parse a country's alternateNamesV2 file into `geonameid -> spelling -> attribution`, restricted to the populated
69
- * places (`P`) in `wanted`.
31
+ * Formats a GeoNames fold progress event as one log line.
70
32
  */
33
+ export function formatGeonamesIngestProgress(event, missingFile) {
34
+ if (event.skipped) {
35
+ return `${event.country}: ${missingFile ? `${missingFile} missing` : "dump missing"} — download from download.geonames.org/export/dump/${event.country}.zip; skipped`;
36
+ }
37
+ return `${event.country}: ${event.places.toLocaleString()} places, ${(event.aliasesRefused ?? 0).toLocaleString()} alt-names refused`;
38
+ }
71
39
  async function parseAlternateNamesV2(v2File, cc, wanted) {
72
40
  const v2 = new Map();
73
- // V2 columns (0-indexed): 1 geonameid, 2 isolanguage, 3 name, 4 isPreferredName, 5 isShortName,
74
- // 6 isColloquial, 7 isHistoric, 8 from, 9 to.
75
- //
76
- // Two passes, because historic-ness is a fact about the NAME, not the row: GeoNames splits one
77
- // spelling across rows — Malabo carries "Santa Isabel" as (es, unflagged) AND as (no-language,
78
- // isHistoric=1, to=1973). Officialness must see the flags from EVERY row for the spelling, or the
79
- // colonial-era name sails through on the language-tagged row (the #936 review's Malabo finding).
80
- // Do NOT condition on isPreferredName instead — it's sparse annotation, not a signal (Turku's sv "Åbo"
81
- // is unflagged; FI has 1,746 flags across the whole dump).
82
- //
83
- // Both passes STREAM the file rather than sharing one materialized array: NO's V2 dump is 33 MB,
84
- // and a second read off the page cache costs less than holding half a million line strings.
85
- // `header: false` — the dump is headerless.
86
41
  const historicNames = new Set();
87
- for await (const f of TSVSpliterator.fromAsync(v2File, { header: false })) {
42
+ for await (const f of readUnquotedTSV(v2File)) {
88
43
  if (f[6] === "1" || f[7] === "1" || (f[9] ?? "").trim() !== "") {
89
44
  const alt = (f[3] ?? "").trim();
90
45
  if (alt && wanted.has(Number(f[1]))) {
@@ -92,12 +47,11 @@ async function parseAlternateNamesV2(v2File, cc, wanted) {
92
47
  }
93
48
  }
94
49
  }
95
- for await (const f of TSVSpliterator.fromAsync(v2File, { header: false })) {
50
+ for await (const f of readUnquotedTSV(v2File)) {
96
51
  const gid = Number(f[1]);
97
52
  if (!wanted.has(gid))
98
53
  continue;
99
54
  const lang = f[2] ?? "";
100
- // ISO 639 codes are 2-3 letters; GeoNames' pseudo-codes (post, link, iata, wkdt, …) are 4+.
101
55
  if (!/^[a-z]{2,3}$/.test(lang))
102
56
  continue;
103
57
  const alt = (f[3] ?? "").trim();
@@ -126,70 +80,59 @@ async function parseAlternateNamesV2(v2File, cc, wanted) {
126
80
  return v2;
127
81
  }
128
82
  /**
129
- * Fold the GeoNames `P`-class places (+ their Latin alt-names) for `countries` into `db`'s `spr` / `names` /
130
- * `place_population` tables. Returns the total places ingested.
83
+ * Purges the fold's id range, then adds GeoNames `P`-class places and their
84
+ * alternate names for `countries` to `db`.
85
+ * It returns the number of places ingested.
131
86
  *
132
- * `onProgress` receives one event per country (default: a stderr line, matching the build scripts' legacy output). The
133
- * caller MUST rebuild `place_search` afterward (`buildPlaceSearchFTS(db, { drop: true })`) for the new names to reach
134
- * the candidate build's alias pass.
87
+ * The caller must then rebuild `place_search` with `buildPlaceSearchFTS(db, { drop: true })`
88
+ * to make the new names searchable.
135
89
  */
136
90
  export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgress, opts) {
137
- // Latin-only, no bracket/paren noise GeoNames packs into `alternatenames` ("(( Karis Landskommun ))",
138
- // airport codes), 2–60 chars, at least one letter (drops bare postcodes/numbers).
139
91
  const LATIN_NAME = /^[\p{Script=Latin}\p{M}\s\-'.]{2,60}$/u;
140
92
  const clean = (s) => {
141
93
  const t = s.trim();
142
94
  return t && LATIN_NAME.test(t) && /\p{L}/u.test(t) ? t : null;
143
95
  };
96
+ const NAME_NOISE = /[()[\]{}<>|/\\_@#$%^*+=~`"]/u;
97
+ const NAME_MIN_LENGTH = 2;
98
+ const NAME_MAX_LENGTH = 60;
99
+ const cleanAlias = (s) => {
100
+ const t = s.trim();
101
+ return t.length >= NAME_MIN_LENGTH && t.length <= NAME_MAX_LENGTH && /\p{L}/u.test(t) && !NAME_NOISE.test(t)
102
+ ? t
103
+ : null;
104
+ };
144
105
  const sprInsert = db.prepare(`INSERT OR REPLACE INTO spr (id, parent_id, name, placetype, country, latitude, longitude, min_latitude, min_longitude, max_latitude, max_longitude, is_current, is_deprecated, is_ceased, is_superseded, is_superseding, lastmodified) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
145
106
  const namesInsert = db.prepare(`INSERT INTO names (id, name, placetype, country, language, privateuse, official, lastmodified) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
146
107
  const populationInsert = db.prepare(`INSERT OR REPLACE INTO place_population (id, population) VALUES (?, ?)`);
147
- // #267 admin linkage: ancestor rows (locality→region→country) so parentID scoping + adminCoherence reach
148
- // the gap countries. Only used for a country in opts.adminForCountries.
149
108
  const ancestorInsert = db.prepare(`INSERT INTO ancestors (id, ancestor_id, ancestor_placetype, lastmodified) VALUES (?, ?, ?, 0)`);
150
109
  const report = (event, missingFile) => {
151
110
  if (onProgress) {
152
111
  onProgress(event);
153
112
  }
154
- else if (event.skipped) {
155
- console.error(` GeoNames ${event.country}: ${missingFile} missing — download from download.geonames.org/export/dump/${event.country}.zip; skipped`);
156
- }
157
113
  else {
158
- console.error(` GeoNames ${event.country}: ${event.places.toLocaleString()} populated places (+ Latin alt-names)`);
114
+ console.error(` GeoNames ${formatGeonamesIngestProgress(event, missingFile)}`);
159
115
  }
160
116
  };
161
117
  let id = GEONAMES_ID_BASE;
162
118
  let total = 0;
163
119
  db.exec("BEGIN");
164
- // #1514: clear the range before writing it. Inside the transaction, so a fold that throws leaves the
165
- // DB as it found it rather than half-purged. See purgeGeonamesAliasRange for why the ids demand this.
166
120
  purgeGeonamesAliasRange(db);
167
121
  for (const cc of countries) {
168
- const file = join(geonamesDir, `${cc}.txt`);
122
+ const file = resolvePathBuilder(geonamesDir, `${cc}.txt`);
169
123
  if (!(await pathExists(file))) {
170
- report({ country: cc, places: 0, skipped: true }, file);
124
+ report({ country: cc, places: 0, skipped: true, aliasesRefused: null }, file);
171
125
  continue;
172
126
  }
173
127
  let nc = 0;
174
- // #267: add A-class admin + ancestry only for the gap countries this country is in (never the EU set).
128
+ let refused = 0;
175
129
  const addAdmin = opts?.adminForCountries?.has(cc) ?? false;
176
- const v2File = opts?.alternateDir ? join(opts.alternateDir, `${cc}.txt`) : undefined;
130
+ const v2File = opts?.alternateDir ? resolvePathBuilder(opts.alternateDir, `${cc}.txt`) : undefined;
177
131
  const readV2 = Boolean(v2File && (await pathExists(v2File)));
178
- // Survey pass. The dump is STREAMED — NO's is 71 MB and only the caller knows which country
179
- // comes next — so what a later pass needs has to be collected here rather than re-scanned off a
180
- // materialized array. Two things qualify, and both are small: the P-class geonameid set the V2
181
- // decoration is restricted to, and the handful of A-class rows the admin pre-pass writes (one
182
- // PCL* + the ADM1s). Everything else is re-read in the emit pass below. Neither is wanted
183
- // without V2 tags or admin, and then the pass is skipped rather than read for nothing.
184
- //
185
- // GeoNames dump columns (0-indexed): 0 geonameid, 1 name, 2 asciiname, 3 alternatenames, 4 lat, 5 lon,
186
- // 6 feature_class, 7 feature_code, 10 admin1 code, 14 pop.
187
- //
188
- // `header: false` — the dump is headerless, so row 1 is a place, not column names.
189
132
  const wanted = new Set();
190
133
  const adminRows = [];
191
134
  if (readV2 || addAdmin) {
192
- for await (const f of TSVSpliterator.fromAsync(file, { header: false })) {
135
+ for await (const f of readUnquotedTSV(file)) {
193
136
  if (f[6] === "P") {
194
137
  if (readV2) {
195
138
  wanted.add(Number(f[0]));
@@ -201,12 +144,7 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
201
144
  }
202
145
  }
203
146
  }
204
- // #936: V2 tags for this country's P-class rows — geonameid → exact alias spelling → tag. The V2
205
- // dump repeats one spelling under several languages ("Åbo" sv/da/no); the merged tag is official /
206
- // preferred if ANY qualifying row is.
207
147
  const v2 = readV2 ? await parseAlternateNamesV2(v2File, cc, wanted) : undefined;
208
- // #267 admin pre-pass (gap countries): fold the country (PCLI) + regions (ADM1), self+ancestry them, and
209
- // build the admin1→region map the localities link through. Point bbox (GeoNames gives a centroid only).
210
148
  let countryID = -1;
211
149
  const adminMap = new Map();
212
150
  if (addAdmin) {
@@ -217,11 +155,8 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
217
155
  const lat = Number(f[4]) || 0;
218
156
  const lon = Number(f[5]) || 0;
219
157
  if (f[7]?.startsWith("PCL")) {
220
- // Any country-level political entity — PCLI (independent), PCLD (dependent territory),
221
- // PCLF (freely associated), PCLS (special administrative region: HK/MO/PS). All are the
222
- // country tier; restricting to PCLI left those ~17 territories without a country row.
223
158
  if (countryID >= 0)
224
- continue; // one country row
159
+ continue;
225
160
  countryID = id++;
226
161
  sprInsert.run(countryID, -1, aname, "country", cc, lat, lon, lat, lon, lat, lon, 1, 0, 0, 0, 0, 0);
227
162
  namesInsert.run(countryID, aname, "country", cc, "", "", 0, 0);
@@ -235,7 +170,6 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
235
170
  adminMap.set(f[10], rid);
236
171
  }
237
172
  }
238
- // Re-parent regions + ancestor them to the (now-known) country.
239
173
  if (countryID >= 0) {
240
174
  for (const rid of adminMap.values()) {
241
175
  db.prepare("UPDATE spr SET parent_id = ? WHERE id = ?").run(countryID, rid);
@@ -243,10 +177,9 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
243
177
  }
244
178
  }
245
179
  }
246
- // Emit pass — a second stream over the same file, off the page cache the survey pass just warmed.
247
- for await (const f of TSVSpliterator.fromAsync(file, { header: false })) {
180
+ for await (const f of readUnquotedTSV(file)) {
248
181
  if (f[6] !== "P")
249
- continue; // populated places only
182
+ continue;
250
183
  const lat = Number(f[4]);
251
184
  const lon = Number(f[5]);
252
185
  if (!Number.isFinite(lat) || !Number.isFinite(lon))
@@ -255,18 +188,10 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
255
188
  if (!name)
256
189
  continue;
257
190
  const nid = id++;
258
- // #267: link to the locality's region (else country) for gap countries; -1 (orphan) otherwise.
259
191
  const regionID = addAdmin ? (adminMap.get(f[10] ?? "") ?? -1) : -1;
260
192
  const parentID = regionID >= 0 ? regionID : addAdmin && countryID >= 0 ? countryID : -1;
261
- // Point bbox — a GeoNames row is a centroid; the candidate's region-bbox disambiguation just
262
- // sees it as contained in itself, fine for a locality.
263
193
  sprInsert.run(nid, parentID, name, "locality", cc, lat, lon, lat, lon, lat, lon, 1, 0, 0, 0, 0, 0);
264
194
  namesInsert.run(nid, name, "locality", cc, "", "", 0, 0);
265
- // The SELF row is unconditional (#1514). `populateAncestors` writes one for every spr row it
266
- // sees, so a full build's freeze phase produces it either way — but the freeze does not run on
267
- // the fold-on-copy path, and since the purge now clears this range the fold has to emit
268
- // everything the closure would. Measured on the 161-country fold: 212,993 self rows, exactly the
269
- // non-gap countries' localities, which is what the base artifact carried and a fold-on-copy lost.
270
195
  ancestorInsert.run(nid, nid, "locality");
271
196
  if (addAdmin) {
272
197
  if (regionID >= 0) {
@@ -279,7 +204,10 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
279
204
  const seen = new Set([name]);
280
205
  const tags = v2?.get(Number(f[0]));
281
206
  for (const raw of [f[2] ?? "", ...(f[3] ? f[3].split(",") : [])]) {
282
- const alt = clean(raw);
207
+ const alt = cleanAlias(raw);
208
+ if (!alt && raw.trim()) {
209
+ refused++;
210
+ }
283
211
  if (alt && !seen.has(alt)) {
284
212
  seen.add(alt);
285
213
  const tag = tags?.get(alt);
@@ -292,7 +220,7 @@ export async function ingestGeonamesAliases(db, countries, geonamesDir, onProgre
292
220
  }
293
221
  nc++;
294
222
  }
295
- report({ country: cc, places: nc, skipped: false });
223
+ report({ country: cc, places: nc, skipped: false, aliasesRefused: refused });
296
224
  total += nc;
297
225
  }
298
226
  db.exec("COMMIT");