@mailwoman/resolver-wof-sqlite 9.4.0 → 10.1.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 (548) hide show
  1. package/README.md +8 -8
  2. package/lib/address/{point-interpolation.ts → point/interpolation.ts} +40 -38
  3. package/lib/address/point/schema.ts +132 -0
  4. package/lib/address/point.ts +68 -81
  5. package/lib/{address/index.ts → address.ts} +2 -2
  6. package/lib/ancestry/backfill.ts +24 -88
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +608 -0
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +338 -0
  11. package/lib/candidate/alias-bags.ts +74 -14
  12. package/lib/candidate/ancestors/schema.ts +211 -0
  13. package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +17 -24
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +12 -57
  16. package/lib/candidate/fts.ts +28 -0
  17. package/lib/candidate/importance.ts +221 -0
  18. package/lib/candidate/lookup.ts +669 -0
  19. package/lib/candidate/name-roles.ts +16 -34
  20. package/lib/candidate/own-name.ts +34 -31
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +211 -0
  23. package/lib/candidate/scoring.ts +222 -0
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +26 -24
  26. package/lib/coincident-roles.ts +74 -55
  27. package/lib/convention/schema.ts +13 -7
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +34 -39
  30. package/lib/currency-backfill.ts +24 -69
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +31 -20
  33. package/lib/extracts.ts +96 -73
  34. package/lib/fst/autocomplete.ts +52 -35
  35. package/lib/fst/builder.ts +30 -36
  36. package/lib/fst/deserialize-web.ts +8 -9
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +17 -95
  39. package/lib/fst/matcher.ts +2 -2
  40. package/lib/fst/serialize.ts +45 -60
  41. package/lib/fst/types.ts +71 -59
  42. package/lib/fts/query.ts +5 -36
  43. package/lib/fts.ts +280 -0
  44. package/lib/geonames/aliases.ts +72 -149
  45. package/lib/geonames/postal.ts +162 -77
  46. package/lib/index.ts +20 -27
  47. package/lib/interpolation.ts +43 -96
  48. package/lib/lookup.ts +137 -257
  49. package/lib/name-score.ts +9 -7
  50. package/lib/nsul/lookup.ts +36 -53
  51. package/lib/nsul/schema.ts +16 -43
  52. package/lib/paths.ts +159 -0
  53. package/lib/place-importance-schema.ts +57 -164
  54. package/lib/poi/lookup.ts +100 -109
  55. package/lib/poi/schema.ts +42 -26
  56. package/lib/polygon-schema.ts +10 -6
  57. package/lib/postal/city/alias/lookup.ts +79 -0
  58. package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +14 -10
  59. package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +26 -20
  60. package/lib/postal.ts +9 -0
  61. package/lib/postcode-point-lookup.ts +9 -19
  62. package/lib/primary-preference.ts +45 -125
  63. package/lib/proximity-rerank.ts +43 -32
  64. package/lib/ranking-weights.ts +98 -76
  65. package/lib/region-keys.ts +68 -40
  66. package/lib/reverse.ts +108 -72
  67. package/lib/schema.ts +74 -42
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +57 -0
  70. package/lib/sqlite/utils.ts +79 -0
  71. package/lib/street/centroid/schema.ts +149 -0
  72. package/lib/street/centroid.ts +16 -25
  73. package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +56 -41
  74. package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +36 -29
  75. package/lib/street/name-lookup.ts +43 -35
  76. package/lib/street/normalize.ts +85 -216
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +3 -3
  79. package/lib/types.ts +94 -140
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +28 -67
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +204 -232
  85. package/out/address/point/interpolation.d.ts +45 -0
  86. package/out/address/point/interpolation.d.ts.map +1 -0
  87. package/out/address/{point-interpolation.js → point/interpolation.js} +38 -38
  88. package/out/address/point/interpolation.js.map +1 -0
  89. package/out/address/point/schema.d.ts +74 -0
  90. package/out/address/point/schema.d.ts.map +1 -0
  91. package/out/address/{point-schema.js → point/schema.js} +4 -20
  92. package/out/address/point/schema.js.map +1 -0
  93. package/out/address/point.d.ts +24 -21
  94. package/out/address/point.d.ts.map +1 -1
  95. package/out/address/point.js +60 -77
  96. package/out/address/point.js.map +1 -1
  97. package/out/{address/index.js → address.d.ts} +3 -3
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +3 -3
  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 +18 -76
  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 +170 -0
  110. package/out/build/candidate.d.ts.map +1 -0
  111. package/out/{build-candidate.js → build/candidate.js} +33 -102
  112. package/out/build/candidate.js.map +1 -0
  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 +44 -0
  118. package/out/build/slim.d.ts.map +1 -0
  119. package/out/{build-slim.js → build/slim.js} +33 -94
  120. package/out/build/slim.js.map +1 -0
  121. package/out/candidate/alias-bags.d.ts +13 -5
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +55 -12
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +166 -0
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -0
  127. package/out/candidate/ancestors/schema.js +131 -0
  128. package/out/candidate/ancestors/schema.js.map +1 -0
  129. package/out/candidate/ancestors/sidecar.d.ts +27 -0
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
  131. package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +17 -24
  132. package/out/candidate/ancestors/sidecar.js.map +1 -0
  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 +8 -16
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +6 -48
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +19 -0
  142. package/out/candidate/fts.d.ts.map +1 -0
  143. package/out/candidate/fts.js +22 -0
  144. package/out/candidate/fts.js.map +1 -0
  145. package/out/candidate/importance.d.ts +127 -0
  146. package/out/candidate/importance.d.ts.map +1 -0
  147. package/out/candidate/importance.js +162 -0
  148. package/out/candidate/importance.js.map +1 -0
  149. package/out/candidate/lookup.d.ts +36 -0
  150. package/out/candidate/lookup.d.ts.map +1 -0
  151. package/out/candidate/lookup.js +456 -0
  152. package/out/candidate/lookup.js.map +1 -0
  153. package/out/candidate/name-roles.d.ts +10 -22
  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 +34 -31
  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 +120 -0
  166. package/out/candidate/schema.d.ts.map +1 -0
  167. package/out/{candidate-schema.js → candidate/schema.js} +11 -23
  168. package/out/candidate/schema.js.map +1 -0
  169. package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +8 -9
  170. package/out/candidate/scoring.d.ts.map +1 -0
  171. package/out/candidate/scoring.js +157 -0
  172. package/out/candidate/scoring.js.map +1 -0
  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 +26 -24
  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 +61 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +12 -6
  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 +33 -38
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +20 -29
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +13 -35
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +12 -39
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +2 -2
  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 +31 -20
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +65 -54
  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 +28 -20
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +38 -27
  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 +30 -36
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +3 -3
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +8 -9
  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 +11 -73
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +12 -76
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +1 -1
  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 +30 -26
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +44 -58
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +71 -59
  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} +1 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +1 -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 -67
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +44 -121
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -50
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -78
  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 +20 -21
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +14 -15
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +15 -52
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +25 -68
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +24 -53
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +117 -223
  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 +49 -141
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +25 -140
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +38 -42
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +67 -90
  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 → city/alias/lookup.d.ts} +22 -25
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -0
  320. package/out/postal/city/alias/lookup.js +49 -0
  321. package/out/postal/city/alias/lookup.js.map +1 -0
  322. package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +15 -11
  323. package/out/postal/city/alias/schema.d.ts.map +1 -0
  324. package/out/postal/{city-alias-schema.js → city/alias/schema.js} +11 -9
  325. package/out/postal/city/alias/schema.js.map +1 -0
  326. package/out/postal/city/candidate-schema.d.ts +72 -0
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -0
  328. package/out/postal/city/candidate-schema.js +60 -0
  329. package/out/postal/city/candidate-schema.js.map +1 -0
  330. package/out/postal.d.ts +9 -0
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/postal.js +9 -0
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +9 -19
  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 +39 -93
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +13 -97
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +40 -29
  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 +91 -62
  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 +48 -29
  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 +75 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +61 -49
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +74 -42
  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 +25 -0
  367. package/out/sqlite/convention-source.d.ts.map +1 -0
  368. package/out/sqlite/convention-source.js +48 -0
  369. package/out/sqlite/convention-source.js.map +1 -0
  370. package/out/sqlite/utils.d.ts +45 -0
  371. package/out/sqlite/utils.d.ts.map +1 -0
  372. package/out/sqlite/utils.js +56 -0
  373. package/out/sqlite/utils.js.map +1 -0
  374. package/out/street/centroid/schema.d.ts +100 -0
  375. package/out/street/centroid/schema.d.ts.map +1 -0
  376. package/out/street/centroid/schema.js +66 -0
  377. package/out/street/centroid/schema.js.map +1 -0
  378. package/out/street/centroid.d.ts +9 -19
  379. package/out/street/centroid.d.ts.map +1 -1
  380. package/out/street/centroid.js +11 -22
  381. package/out/street/centroid.js.map +1 -1
  382. package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +21 -11
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -0
  384. package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +40 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -0
  386. package/out/street/morphology/fst/loader.d.ts +74 -0
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -0
  388. package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +21 -21
  389. package/out/street/morphology/fst/loader.js.map +1 -0
  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 +32 -31
  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 +74 -191
  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.d.ts → street.d.ts} +4 -4
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.js → street.js} +4 -4
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +69 -138
  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 +20 -56
  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 +132 -141
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +86 -154
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +94 -599
  435. package/lib/address/point-schema.ts +0 -155
  436. package/lib/build-candidate.ts +0 -632
  437. package/lib/build-slim.ts +0 -408
  438. package/lib/candidate-ancestors-schema.ts +0 -195
  439. package/lib/candidate-fts.ts +0 -46
  440. package/lib/candidate-importance.ts +0 -224
  441. package/lib/candidate-lookup.ts +0 -977
  442. package/lib/candidate-schema.ts +0 -247
  443. package/lib/candidate-scoring.ts +0 -261
  444. package/lib/convention/index.ts +0 -154
  445. package/lib/fts/index.ts +0 -361
  446. package/lib/postal/city-alias-lookup.ts +0 -97
  447. package/lib/postal/index.ts +0 -9
  448. package/lib/sqlite-convention-source.ts +0 -65
  449. package/lib/sqlite-utils.ts +0 -82
  450. package/lib/street/centroid-schema.ts +0 -154
  451. package/out/address/index.d.ts.map +0 -1
  452. package/out/address/index.js.map +0 -1
  453. package/out/address/point-interpolation.d.ts +0 -49
  454. package/out/address/point-interpolation.d.ts.map +0 -1
  455. package/out/address/point-interpolation.js.map +0 -1
  456. package/out/address/point-schema.d.ts +0 -97
  457. package/out/address/point-schema.d.ts.map +0 -1
  458. package/out/address/point-schema.js.map +0 -1
  459. package/out/ancestry/index.d.ts +0 -45
  460. package/out/ancestry/index.d.ts.map +0 -1
  461. package/out/ancestry/index.js +0 -55
  462. package/out/ancestry/index.js.map +0 -1
  463. package/out/build-candidate.d.ts +0 -184
  464. package/out/build-candidate.d.ts.map +0 -1
  465. package/out/build-candidate.js.map +0 -1
  466. package/out/build-slim.d.ts +0 -80
  467. package/out/build-slim.d.ts.map +0 -1
  468. package/out/build-slim.js.map +0 -1
  469. package/out/candidate/ancestors-sidecar.d.ts +0 -32
  470. package/out/candidate/ancestors-sidecar.d.ts.map +0 -1
  471. package/out/candidate/ancestors-sidecar.js.map +0 -1
  472. package/out/candidate-ancestors-schema.d.ts +0 -150
  473. package/out/candidate-ancestors-schema.d.ts.map +0 -1
  474. package/out/candidate-ancestors-schema.js +0 -123
  475. package/out/candidate-ancestors-schema.js.map +0 -1
  476. package/out/candidate-fts.d.ts +0 -37
  477. package/out/candidate-fts.d.ts.map +0 -1
  478. package/out/candidate-fts.js +0 -40
  479. package/out/candidate-fts.js.map +0 -1
  480. package/out/candidate-importance.d.ts +0 -132
  481. package/out/candidate-importance.d.ts.map +0 -1
  482. package/out/candidate-importance.js +0 -169
  483. package/out/candidate-importance.js.map +0 -1
  484. package/out/candidate-lookup.d.ts +0 -76
  485. package/out/candidate-lookup.d.ts.map +0 -1
  486. package/out/candidate-lookup.js +0 -768
  487. package/out/candidate-lookup.js.map +0 -1
  488. package/out/candidate-schema.d.ts +0 -155
  489. package/out/candidate-schema.d.ts.map +0 -1
  490. package/out/candidate-schema.js.map +0 -1
  491. package/out/candidate-scoring.d.ts.map +0 -1
  492. package/out/candidate-scoring.js +0 -196
  493. package/out/candidate-scoring.js.map +0 -1
  494. package/out/convention/index.d.ts +0 -106
  495. package/out/convention/index.d.ts.map +0 -1
  496. package/out/convention/index.js +0 -93
  497. package/out/convention/index.js.map +0 -1
  498. package/out/fst/index.d.ts.map +0 -1
  499. package/out/fst/index.js.map +0 -1
  500. package/out/fts/index.d.ts +0 -184
  501. package/out/fts/index.d.ts.map +0 -1
  502. package/out/fts/index.js +0 -277
  503. package/out/fts/index.js.map +0 -1
  504. package/out/geonames/index.d.ts.map +0 -1
  505. package/out/geonames/index.js.map +0 -1
  506. package/out/nsul/index.d.ts.map +0 -1
  507. package/out/nsul/index.js.map +0 -1
  508. package/out/poi/index.d.ts.map +0 -1
  509. package/out/poi/index.js.map +0 -1
  510. package/out/postal/city-alias-lookup.d.ts.map +0 -1
  511. package/out/postal/city-alias-lookup.js +0 -62
  512. package/out/postal/city-alias-lookup.js.map +0 -1
  513. package/out/postal/city-alias-schema.d.ts.map +0 -1
  514. package/out/postal/city-alias-schema.js.map +0 -1
  515. package/out/postal/city-candidate-schema.d.ts +0 -66
  516. package/out/postal/city-candidate-schema.d.ts.map +0 -1
  517. package/out/postal/city-candidate-schema.js +0 -56
  518. package/out/postal/city-candidate-schema.js.map +0 -1
  519. package/out/postal/index.d.ts +0 -9
  520. package/out/postal/index.d.ts.map +0 -1
  521. package/out/postal/index.js +0 -9
  522. package/out/postal/index.js.map +0 -1
  523. package/out/sqlite-convention-source.d.ts +0 -29
  524. package/out/sqlite-convention-source.d.ts.map +0 -1
  525. package/out/sqlite-convention-source.js +0 -57
  526. package/out/sqlite-convention-source.js.map +0 -1
  527. package/out/sqlite-utils.d.ts +0 -45
  528. package/out/sqlite-utils.d.ts.map +0 -1
  529. package/out/sqlite-utils.js +0 -57
  530. package/out/sqlite-utils.js.map +0 -1
  531. package/out/street/centroid-schema.d.ts +0 -105
  532. package/out/street/centroid-schema.d.ts.map +0 -1
  533. package/out/street/centroid-schema.js +0 -78
  534. package/out/street/centroid-schema.js.map +0 -1
  535. package/out/street/index.d.ts.map +0 -1
  536. package/out/street/index.js.map +0 -1
  537. package/out/street/morphology-fst-builder.d.ts.map +0 -1
  538. package/out/street/morphology-fst-builder.js.map +0 -1
  539. package/out/street/morphology-fst-loader.d.ts +0 -67
  540. package/out/street/morphology-fst-loader.d.ts.map +0 -1
  541. package/out/street/morphology-fst-loader.js.map +0 -1
  542. package/out/uprn/index.d.ts.map +0 -1
  543. package/out/uprn/index.js.map +0 -1
  544. /package/lib/{fst/index.ts → fst.ts} +0 -0
  545. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  546. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  547. /package/lib/{poi/index.ts → poi.ts} +0 -0
  548. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
package/lib/reverse.ts CHANGED
@@ -3,29 +3,23 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Reverse geocoding (#484): `(lat, lon)` → the containing admin hierarchy. Assembly over existing
7
- * implementation, per the 2026-06-11 scoping notes:
6
+ * Reverse geocoding: map a `(lat, lon)` point to the containing admin hierarchy.
8
7
  *
9
- * 1. **Candidate fetch** — the admin DB's `place_bbox` R*Tree (built by `fts.ts`) for places whose
10
- * bbox contains the point, smallest-area-first (so the FIRST polygon confirmation is the
11
- * deepest).
12
- * 2. **PIP confirmation** — ray-cast (geo.ts, the canonical TS port of
13
- * `scripts/eval/pip-containment.py`) against the polygon sidecar DB (`wof-polygons.db`,
14
- * `polygons(id, geom)` with GeoJSON text — built by `scripts/build-wof-polygons.mjs` for the
15
- * demo map). A candidate whose polygon EXISTS but rejects the point is a bbox false positive
16
- * and is dropped entirely; a candidate with no polygon row stays eligible for the
17
- * approximate fallback.
18
- * 3. **Approximate descent** — WOF carries point geometry for most localities (#292: ~99% of JP
19
- * municipalities; ~half of US localities have degenerate bboxes too), so the polygon walk
20
- * usually bottoms out at county level. We then descend tier-by-tier (county → localadmin →
21
- * locality → …) through the winner's DESCENDANTS (the `ancestors` table, reversed), taking
22
- * the PIP-confirmed child when a polygon exists and the nearest-centroid child otherwise —
23
- * the latter flagged `containment: "approximate"`, the demo's honesty convention.
24
- * 4. **Hierarchy assembly** — the deepest place's ancestor chain via the SAME walk forward resolution
25
- * uses (`ancestry.ts`, #404), so consumers get a symmetric tree.
8
+ * 1. Fetch the admin DB's `place_bbox` R*Tree candidates whose bbox contains the point,
9
+ * smallest-area-first, so the first polygon confirmation is the deepest.
10
+ * 2. Confirm each candidate by ray-cast (geo.ts, the canonical TS port of
11
+ * `scripts/eval/pip-containment.py`) against the polygon sidecar DB (`wof-polygons.db`,
12
+ * `polygons(id, geom)` with GeoJSON text, built by `scripts/build-wof-polygons.mjs`). A
13
+ * candidate whose polygon exists but rejects the point is a bbox false positive and is
14
+ * dropped. A candidate with no polygon row stays eligible for the approximate fallback.
15
+ * 3. Descend tier-by-tier through the winner's descendants (the `ancestors` table, reversed),
16
+ * taking the PIP-confirmed child when a polygon exists and the nearest-centroid child
17
+ * otherwise. A centroid-fallback step is flagged `containment: "approximate"`.
18
+ * 4. Assemble the deepest place's ancestor chain through the same walk forward resolution uses,
19
+ * so consumers get a symmetric tree.
26
20
  *
27
- * Reverse quality is country-dependent (polygon coverage: see the #292 JP finding); `containment`
28
- * says so per result rather than pretending.
21
+ * Reverse quality is country-dependent, because polygon coverage is. `containment` records this per
22
+ * result.
29
23
  */
30
24
 
31
25
  import { tryParsingJSON } from "@mailwoman/core/json"
@@ -33,9 +27,10 @@ import { allRows } from "@mailwoman/core/utils"
33
27
  import { bboxAround, geometryContains, haversineKm, type ParsedGeometry } from "@mailwoman/spatial"
34
28
  import { DatabaseClient } from "@mailwoman/sqlite/client"
35
29
  import { tableExists } from "@mailwoman/sqlite/introspection"
30
+ import type { PathBuilderLike } from "path-ts"
36
31
 
37
- import { ancestorLineage, placetypeDepth } from "#ancestry/index"
38
- import { PLACE_BBOX_TABLE } from "#fts/index"
32
+ import { ancestorLineage, placetypeDepth } from "#ancestry"
33
+ import { PLACE_BBOX_TABLE } from "#fts"
39
34
  import type { WOFDatabase } from "#schema"
40
35
  import type { PlaceCandidate, WOFPlacetype } from "#types"
41
36
  /**
@@ -51,62 +46,85 @@ const MAX_ABS_LONGITUDE = 180
51
46
  /**
52
47
  * How the deepest returned place was confirmed:
53
48
  *
54
- * - `"polygon"` — the point ray-cast INSIDE the place's real (DP-simplified) admin boundary.
55
- * - `"approximate"` — the place has no polygon on record; it won by nearest-centroid among the candidates whose bbox (or
56
- * parent) contains the point. The same honesty convention as the demo's approximate circles — country-dependent data
57
- * reality, surfaced instead of hidden.
49
+ * - `"polygon"`: the point ray-cast inside the place's real (DP-simplified) admin boundary.
50
+ * - `"approximate"`: the place has no polygon on record.
51
+ * It returns the nearest-centroid candidate among places whose bbox or parent contains the point.
52
+ * The demo's approximate circles use the same convention.
53
+ * Both results expose country-dependent data coverage.
58
54
  */
59
55
  export type ContainmentKind = "polygon" | "approximate"
60
56
 
61
57
  export interface ReverseGeocodeResult {
62
58
  /**
63
- * The containment chain, DEEPEST-FIRST (`[0]` is the winning place, then its ancestors up to country) — the same tree
64
- * shape forward resolution attaches via `includeAncestors`. Empty when no candidate's bbox contains the point (open
65
- * ocean, or outside the gazetteer's coverage).
59
+ * The containment chain, deepest-first.
60
+ *
61
+ * `[0]` is the winning place, then its ancestors up to country.
62
+ * This is the same tree shape forward resolution attaches via `includeAncestors`.
63
+ *
64
+ * Empty when no candidate's bbox contains the point (open ocean, or outside the gazetteer's coverage).
66
65
  */
67
66
  hierarchy: PlaceCandidate[]
68
67
  /**
69
- * Containment kind of the DEEPEST place in `hierarchy` (see {@link ContainmentKind}).
68
+ * Containment kind of the deepest place in `hierarchy` (see {@link ContainmentKind}).
70
69
  */
71
70
  containment: ContainmentKind
72
71
  }
73
72
 
74
73
  export interface WOFReverseGeocoderOpts {
75
74
  /**
76
- * Path to the admin gazetteer DB (e.g. `admin-global-priority.db`) — must carry `spr`, `ancestors`, and the
77
- * package-built `place_bbox` R*Tree (`mailwoman gazetteer build fts`). Mutually exclusive with `adminDatabase`.
75
+ * Path to the admin gazetteer DB (e.g. `admin-global-priority.db`).
76
+ *
77
+ * It must include `spr`, `ancestors`, and the package-built `place_bbox`
78
+ * R*Tree (`mailwoman gazetteer build fts`).
79
+ *
80
+ * Mutually exclusive with `adminDatabase`.
78
81
  */
79
- adminDBPath?: string
82
+ adminDBPath?: PathBuilderLike
80
83
  /**
81
- * Pre-opened admin DB — primarily for tests against an inline fixture.
84
+ * Pre-opened admin DB, primarily for tests against an inline fixture.
82
85
  */
83
86
  adminDatabase?: DatabaseClient<WOFDatabase>
84
87
  /**
85
- * Path to the polygon sidecar DB (`wof-polygons.db`, table `polygons(id, geom)`). OPTIONAL — without it every result
86
- * is `containment: "approximate"` (centroid-only mode). Mutually exclusive with `polygonDatabase`.
88
+ * Path to the polygon sidecar DB (`wof-polygons.db`, table `polygons(id, geom)`).
89
+ *
90
+ * Optional.
91
+ * Without it every result is `containment: "approximate"` (centroid-only mode).
92
+ *
93
+ * Mutually exclusive with `polygonDatabase`.
87
94
  */
88
- polygonDBPath?: string
95
+ polygonDBPath?: PathBuilderLike
89
96
  /**
90
- * Pre-opened polygon DB — primarily for tests.
97
+ * Pre-opened polygon DB, primarily for tests.
91
98
  */
92
99
  polygonDatabase?: DatabaseClient<WOFDatabase>
93
100
  }
94
101
 
95
102
  export interface ReverseGeocodeOpts {
96
103
  /**
97
- * Restrict the hierarchy to these placetypes (both the bbox candidates and the descent tiers). Default: every admin
98
- * placetype the gazetteer carries. E.g. `["region", "county", "locality"]` to skip the neighbourhood grain.
104
+ * Restrict the hierarchy to these placetypes (both the bbox candidates and the descent tiers).
105
+ *
106
+ * Default: every admin placetype in the gazetteer.
107
+ * E.g.
108
+ *
109
+ * `["region", "county", "locality"]` to skip the neighborhood grain.
99
110
  */
100
111
  placetypes?: WOFPlacetype[]
101
112
  /**
102
- * Cap on the bbox candidate fetch. Default 128 — comfortably covers a dense metro (the most bbox-overlapping point
103
- * we've measured is a few dozen neighbourhoods + the admin chain).
113
+ * Cap on the bbox candidate fetch.
114
+ *
115
+ * The default is 128.
116
+ * Current measurements show that this covers a dense metro.
117
+ *
118
+ * The most bbox-overlapping point we have measured is a few dozen neighborhoods plus the admin chain.
104
119
  */
105
120
  maxCandidates?: number
106
121
  /**
107
- * Approximate (nearest-centroid) steps further than this from the query point are not taken — keeps a sparse
108
- * gazetteer from "refining" to a far-away sibling. Polygon-confirmed steps ignore it (containment is exact regardless
109
- * of centroid distance). Default 25 km.
122
+ * The lookup skips approximate (nearest-centroid) steps farther than this from the query point.
123
+ *
124
+ * This keeps a sparse gazetteer from "refining" to a far-away sibling.
125
+ *
126
+ * Polygon-confirmed steps ignore it (containment is exact regardless of centroid distance).
127
+ * Default 25 km.
110
128
  */
111
129
  maxApproximateKm?: number
112
130
  }
@@ -115,8 +133,10 @@ const DEFAULT_MAX_CANDIDATES = 128
115
133
  const DEFAULT_MAX_APPROXIMATE_KM = 25
116
134
 
117
135
  /**
118
- * The tier ladder for the approximate descent, coarsest-first. Each tier is attempted among the CURRENT winner's
119
- * descendants; a tier with no rows is skipped (e.g. counties without localadmins jump straight to locality).
136
+ * The tier ladder for the approximate descent, coarsest-first.
137
+ *
138
+ * Each tier is attempted among the current winner's descendants.
139
+ * A tier with no rows is skipped (e.g. counties without localadmins jump straight to locality).
120
140
  */
121
141
  const DESCENT_TIERS: readonly WOFPlacetype[] = [
122
142
  "county",
@@ -165,10 +185,12 @@ export class WOFReverseGeocoder implements Disposable {
165
185
  readonly #polygons: DatabaseClient<WOFDatabase> | null
166
186
  readonly #ownsPolygons: boolean
167
187
  /**
168
- * Parsed-geometry cache. Reverse queries cluster geographically (an eval run hits the same ~15 county polygons 1400
169
- * times), so caching the JSON.parse pays for itself immediately. Bounded — cleared wholesale at the cap rather than
170
- * LRU-tracked; the polygons are DP-simplified and small, the cap exists only to keep a long-lived server process
171
- * honest.
188
+ * Parsed-geometry cache.
189
+ *
190
+ * Reverse queries cluster geographically, so caching the JSON.parse avoids repeated work.
191
+ * The cache is bounded and clears wholesale at the cap.
192
+ *
193
+ * The polygons are DP-simplified and small, so the cap only bounds a long-lived server process.
172
194
  */
173
195
  readonly #geometryCache = new Map<number, ParsedGeometry | null>()
174
196
  static readonly #GEOMETRY_CACHE_CAP = 4096
@@ -195,8 +217,9 @@ export class WOFReverseGeocoder implements Disposable {
195
217
 
196
218
  this.#ownsPolygons = !opts.polygonDatabase && Boolean(opts.polygonDBPath)
197
219
 
198
- // Fail loudly up front — the R*Tree is a build artifact, not part of the upstream WOF
199
- // distribution, and a missing index would otherwise surface as an opaque SQL error per query.
220
+ // Fail loudly up front.
221
+ // The R*Tree is a build artifact and is not part of the upstream WOF distribution,
222
+ // so a missing index would otherwise surface as an opaque SQL error per query.
200
223
  if (!tableExists(this.#admin, PLACE_BBOX_TABLE)) {
201
224
  throw new Error(
202
225
  `WOFReverseGeocoder: the admin DB has no \`${PLACE_BBOX_TABLE}\` R*Tree. Build it with ` +
@@ -213,18 +236,25 @@ export class WOFReverseGeocoder implements Disposable {
213
236
  }
214
237
 
215
238
  /**
216
- * Resolve a WGS-84 point to its containing admin hierarchy. Async for symmetry with `PlaceLookup.findPlace` (the work
217
- * is sync `node:sqlite` underneath — same convention). Thin wrapper over {@link reverseGeocodeSync}.
239
+ * Resolve a WGS-84 point to its containing admin hierarchy.
240
+ *
241
+ * Async for symmetry with `PlaceLookup.findPlace`
242
+ * (the work is sync `node:sqlite` underneath, the same convention).
243
+ * Thin wrapper over {@link reverseGeocodeSync}.
218
244
  */
219
245
  async reverseGeocode(lat: number, lon: number, opts: ReverseGeocodeOpts = {}): Promise<ReverseGeocodeResult> {
220
246
  return this.reverseGeocodeSync(lat, lon, opts)
221
247
  }
222
248
 
223
249
  /**
224
- * Synchronous core of {@link reverseGeocode} — every step underneath is already sync `node:sqlite`, so this is the
225
- * REAL implementation; the async method above exists only for call-site symmetry with `PlaceLookup.findPlace`.
226
- * Exposed directly for callers that can't await mid-call (e.g. `mailwoman/poi-executor.ts`'s `createPOIExecutor`,
227
- * whose `POIIntentOutcome` return type is synchronous by contract — see `poi-intent.ts`'s `deps.execute`).
250
+ * Synchronous core of {@link reverseGeocode}.
251
+ *
252
+ * Every step underneath is already sync `node:sqlite`, so this is the real implementation.
253
+ * The async method above exists only for call-site symmetry with `PlaceLookup.findPlace`.
254
+ *
255
+ * Exposed directly for callers that can't await mid-call
256
+ * (e.g. `mailwoman/poi-executor.ts`'s `createPOIExecutor`, whose `POIIntentOutcome`
257
+ * return type is synchronous by interface. See `poi-intent.ts`'s `deps.execute`).
228
258
  */
229
259
  reverseGeocodeSync(lat: number, lon: number, opts: ReverseGeocodeOpts = {}): ReverseGeocodeResult {
230
260
  if (
@@ -239,8 +269,9 @@ export class WOFReverseGeocoder implements Disposable {
239
269
  const maxApproximateKm = opts.maxApproximateKm ?? DEFAULT_MAX_APPROXIMATE_KM
240
270
  const candidates = this.#bboxCandidates(lat, lon, opts)
241
271
 
242
- // PIP walk, smallest-bbox-first: the first polygon that contains the point is the deepest
243
- // polygon-confirmable place. Polygon-rejected candidates are bbox false positives — dropped.
272
+ // PIP walk, smallest-bbox-first.
273
+ // The first polygon that contains the point is the deepest polygon-confirmable place.
274
+ // Drop candidates whose bounding boxes match but whose polygons reject the point.
244
275
  let winner: CandidateRow | null = null
245
276
  let winnerConfirmed = false
246
277
  const pointOnly: CandidateRow[] = []
@@ -261,7 +292,7 @@ export class WOFReverseGeocoder implements Disposable {
261
292
  }
262
293
 
263
294
  if (!winner) {
264
- // No polygon confirmed anywhere — nearest centroid among the polygon-less bbox candidates.
295
+ // No polygon confirmed anywhere, so take the nearest centroid among the polygon-less bbox candidates.
265
296
  let bestKm = Infinity
266
297
 
267
298
  for (const c of pointOnly) {
@@ -302,7 +333,7 @@ export class WOFReverseGeocoder implements Disposable {
302
333
  break
303
334
  }
304
335
 
305
- if (contains === false) continue // known not-here — polygon rejected
336
+ if (contains === false) continue // known not-here, polygon rejected
306
337
  const km = haversineKm(lat, lon, k.lat, k.lon)
307
338
 
308
339
  if (km <= maxApproximateKm && km < bestKm) {
@@ -318,12 +349,13 @@ export class WOFReverseGeocoder implements Disposable {
318
349
  currentConfirmed = nextConfirmed
319
350
  currentDistanceKm = nextKm
320
351
  }
321
- // An empty tier is NOT terminal — counties without localadmins jump straight to locality.
352
+ // An empty tier is not terminal.
353
+ // Counties without localadmins jump straight to locality.
322
354
  }
323
355
 
324
- // Hierarchy assembly via the shared ancestor walk. If the descent crossed an ancestry gap
325
- // (the deepest place's recorded lineage misses the PIP root), merge the root's own chain so
326
- // region/country are always present when a polygon confirmed them.
356
+ // Hierarchy assembly via the shared ancestor walk.
357
+ // If the descent crossed an ancestry gap (the deepest place's recorded lineage misses the PIP root),
358
+ // merge the root's own chain so region/country are always present when a polygon confirmed them.
327
359
  const byID = new Map<number, PlaceCandidate>([[current.id, toPlaceCandidate(current, currentDistanceKm)]])
328
360
 
329
361
  for (const a of ancestorLineage(this.#admin, current.id)) {
@@ -395,9 +427,12 @@ export class WOFReverseGeocoder implements Disposable {
395
427
  }
396
428
 
397
429
  /**
398
- * Descendants of `parentID` at one placetype tier, pre-filtered to a centroid window around the query point (a
399
- * generous 4× the approximate cap — polygon-holding children may legitimately have far centroids, e.g. a sprawling
400
- * consolidated city; the precise cap is applied per-candidate in the caller, and only to centroid-fallback steps).
430
+ * Descendants of `parentID` at one placetype tier, pre-filtered to a centroid window
431
+ * around the query point (a generous 4× the approximate cap, since polygon-holding
432
+ * children may legitimately have far centroids, e.g. a sprawling consolidated city).
433
+ *
434
+ * The caller applies the precise cap per candidate.
435
+ * It limits centroid-fallback steps only.
401
436
  */
402
437
  #descendants(
403
438
  parentID: number,
@@ -439,7 +474,8 @@ export class WOFReverseGeocoder implements Disposable {
439
474
  }
440
475
 
441
476
  const row = this.#polygons.prepare(`SELECT geom FROM polygons WHERE id = ?`).get(id) as { geom: string } | undefined
442
- // Malformed row parses to null — treat as no-polygon rather than failing the query.
477
+ // Malformed row parses to null.
478
+ // Treat it as no-polygon so the query does not fail.
443
479
  const geometry = row ? tryParsingJSON<ParsedGeometry>(row.geom) : null
444
480
 
445
481
  this.#geometryCache.set(id, geometry)
package/lib/schema.ts CHANGED
@@ -3,22 +3,23 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Kysely table types for the subset of the Who's On First SQLite schema we touch in Phase 4.2.
6
+ * Kysely table types for the subset of the Who's On First SQLite schema this package touches.
7
7
  *
8
- * The full upstream distribution at data.geocode.earth/wof/dist/sqlite/ ships ~7 tables; this file
9
- * models only the ones we read. Pretending to model the others would be misleading — we haven't
10
- * verified their shapes and they're not part of the resolver's contract.
8
+ * The full upstream distribution at data.geocode.earth/wof/dist/sqlite/ ships about seven tables.
9
+ * This file models only the tables we read. The others are outside the resolver interface. Their shapes remain unverified.
11
10
  *
12
11
  * Authoritative schema docs:
13
12
  *
14
- * - Whosonfirst SQLite README: https://github.com/whosonfirst/go-whosonfirst-sqlite
13
+ * - Whosonfirst SQLite readme: https://github.com/whosonfirst/go-whosonfirst-sqlite
15
14
  * - Per-table sources under https://github.com/whosonfirst/go-whosonfirst-sqlite-features
16
15
  */
17
16
 
18
17
  /**
19
- * The FTS5 virtual table built by this package on first open (NOT shipped by upstream WOF).
18
+ * The FTS5 virtual table this package builds on first open.
19
+ * It is local to this repository.
20
20
  *
21
- * `content` is unindexed — it's there so we can roundtrip the original name back to the caller without a second SELECT.
21
+ * `content` is unindexed and is there so we can roundtrip the original name
22
+ * back to the caller without a second select.
22
23
  * The actual FTS rebuild happens in `fts.ts::buildPlaceSearchFTS`.
23
24
  */
24
25
  export interface PlaceSearchTable {
@@ -29,16 +30,19 @@ export interface PlaceSearchTable {
29
30
  }
30
31
 
31
32
  /**
32
- * `spr` — the Who's On First "Standard Places Response": a denormalized lightweight summary of one row per place. The
33
- * resolver's main lookup table.
33
+ * `spr`, the Who's On First "Standard Places Response": a denormalized lightweight
34
+ * summary of one row per place.
34
35
  *
35
- * Lifecycle flags carry TWO conventions, both meaning "currently valid": `is_current = -1` (modern Who's On First) and
36
- * `is_current = 1` (legacy Mapzen-era). Only `is_current = 0` means "not current". Filters in `lookup.ts` and `fts.ts`
37
- * use `is_current != 0 AND is_deprecated = 0` — see #91 for the diagnostic that uncovered the mixed-convention
38
- * reality.
36
+ * The resolver's main lookup table.
39
37
  *
40
- * Lat/lon live directly on this row — no GeoJSON extraction needed for centroid resolution. `min_*` / `max_*` form a
41
- * bounding box if callers want one (Phase 4.3 candidate).
38
+ * Lifecycle flags use two conventions, both meaning "currently valid": `is_current = -1`
39
+ * for modern Who's On First and `is_current = 1` for the legacy Mapzen era.
40
+ * Only `is_current = 0` means "not current".
41
+ *
42
+ * Filters in `lookup.ts` and `fts.ts` use `is_current != 0 and is_deprecated = 0`.
43
+ *
44
+ * Lat/lon live directly on this row, so centroid resolution needs no GeoJSON extraction.
45
+ * `min_*` and `max_*` form a bounding box if callers want one.
42
46
  */
43
47
  export interface SprTable {
44
48
  id: number
@@ -63,15 +67,21 @@ export interface SprTable {
63
67
  }
64
68
 
65
69
  /**
66
- * Alternate names per place, keyed by language tag subfields (BCP-47 components). Joins back to `spr.id` via `id` (NOT
67
- * `place_id` — the real WOF schema uses the same column name as the spr primary key; this is a normal join across two
68
- * tables with the same FK column name).
70
+ * Alternate names per place, keyed by language tag subfields (BCP-47 components).
71
+ *
72
+ * Joins back to `spr.id` via `id`.
73
+ * The real WOF schema uses the same column name as the spr primary key, so this is
74
+ * a normal join across two tables with the same FK column name.
75
+ *
76
+ * No `kind` column in real WOF.
77
+ * The FTS build concatenates all names per id.
69
78
  *
70
- * No `kind` column in real WOF — the FTS build just concatenates ALL names per id.
79
+ * `official` marks a preferred-form name in an official language of the place's country.
80
+ * This column appears only in unified builds.
71
81
  *
72
- * `official` (#936 ingest bit, our unified builds only; absent in real WOF dumps) marks a PREFERRED-form name in an
73
- * official language of the place's country — the aliases eligible to join the name-exact tier under the option-3 rule.
74
- * See `unified-schema.ts` for the full contract.
82
+ * Real WOF dumps omit it.
83
+ * These are the aliases eligible to join the name-exact tier.
84
+ * See `unified-schema.ts` for the full interface.
75
85
  */
76
86
  export interface NamesTable {
77
87
  id: number
@@ -90,8 +100,11 @@ export interface NamesTable {
90
100
  }
91
101
 
92
102
  /**
93
- * Per-place GeoJSON blob. Centroid lat/lon are already exposed via `spr.{latitude,longitude}` so the resolver doesn't
94
- * need to parse this; we keep the table modeled in case Phase 4.3 wants the full geometry for bbox / polygon work.
103
+ * Per-place GeoJSON blob.
104
+ *
105
+ * Centroid lat/lon are already exposed via `spr.{latitude,longitude}`,
106
+ * so the resolver does not need to parse this.
107
+ * The table stays modeled so a caller that wants the full geometry for bbox or polygon work can read it.
95
108
  */
96
109
  export interface GeojsonTable {
97
110
  id: number
@@ -103,8 +116,10 @@ export interface GeojsonTable {
103
116
  }
104
117
 
105
118
  /**
106
- * Adjacency table for ancestor relationships. One row per (place, ancestor) pair, including transitive ancestors. Used
107
- * to implement `FindPlaceQuery.parentID` (descendant lookup).
119
+ * Adjacency table for ancestor relationships.
120
+ *
121
+ * One row per (place, ancestor) pair, including transitive ancestors.
122
+ * Used to implement `FindPlaceQuery.parentID` (descendant lookup).
108
123
  */
109
124
  export interface AncestorsTable {
110
125
  id: number
@@ -114,9 +129,14 @@ export interface AncestorsTable {
114
129
  }
115
130
 
116
131
  /**
117
- * `place_population` — `id → wof:population`, split off `spr` so a population-rank join is a single indexed probe.
118
- * Written by the build/augment ingest + the GeoNames backfill; read by the candidate build's `neg_rank`. WOF carries
119
- * population for ~15% of localities; absent = unknown, not zero.
132
+ * `place_population` — `id → wof:population`, split off `spr` so a population-rank
133
+ * join is a single indexed probe.
134
+ *
135
+ * Written by the build/augment ingest + the GeoNames backfill.
136
+ * Read by the candidate build's `neg_rank`.
137
+ *
138
+ * WOF records population for ~15% of localities.
139
+ * Absent = unknown rather than zero.
120
140
  */
121
141
  export interface PlacePopulationTable {
122
142
  id: number
@@ -124,8 +144,10 @@ export interface PlacePopulationTable {
124
144
  }
125
145
 
126
146
  /**
127
- * `place_abbr` — `id → abbreviation` (e.g. `IL → Illinois`), derived from `names` rows whose `language = 'abbr'`. Lets
128
- * the resolver accept a 2-letter region abbreviation as an exact match.
147
+ * `place_abbr` — `id → abbreviation` (e.g. `IL → Illinois`), derived from
148
+ * `names` rows whose `language = 'abbr'`.
149
+ *
150
+ * Lets the resolver accept a 2-letter region abbreviation as an exact match.
129
151
  */
130
152
  export interface PlaceAbbrTable {
131
153
  id: number
@@ -133,8 +155,11 @@ export interface PlaceAbbrTable {
133
155
  }
134
156
 
135
157
  /**
136
- * `concordances` — external-id cross-references per place (`id → (other_source, other_id)`), e.g. a GeoNames or
137
- * Overture GERS id. Metadata only; not part of the resolve path.
158
+ * `concordances` — external-id cross-references per place (`id → (other_source, other_id)`),
159
+ * e.g. a GeoNames or Overture gers id.
160
+ *
161
+ * Metadata only.
162
+ * Not part of the resolve path.
138
163
  */
139
164
  export interface ConcordancesTable {
140
165
  id: number
@@ -144,9 +169,12 @@ export interface ConcordancesTable {
144
169
  }
145
170
 
146
171
  /**
147
- * `coincident_roles` (#402) — the dual-role relation: a place that is BOTH an admin region AND a locality (Berlin the
148
- * city-state). One row per (admin, locality) pair the resolver can complete a hierarchy with. Surfaced by
149
- * {@link MailwomanLookupLike.coincidentRolesFor}.
172
+ * `coincident_roles`.
173
+ *
174
+ * The dual-role relation, where a place is both an admin region and a locality (Berlin the city-state).
175
+ *
176
+ * One row per (admin, locality) pair the resolver can complete a hierarchy with.
177
+ * Surfaced by {@link MailwomanLookupLike.coincidentRolesFor}.
150
178
  */
151
179
  export interface CoincidentRolesTable {
152
180
  admin_id: number
@@ -158,14 +186,18 @@ export interface CoincidentRolesTable {
158
186
  }
159
187
 
160
188
  /**
161
- * The full schema we hand to `Kysely<WOFDatabase>` / `new DatabaseClient<WOFDatabase>(...)`. Tables not listed here
162
- * will fail type-checked queries — by design. The reader ({@link WOFSQLitePlaceLookup}) already consumes this; the
163
- * build/augment WRITERS adopt it so a column rename is a compile error on both sides (the drift that bit the corpus
164
- * TIGER adapter).
189
+ * The full schema we hand to `Kysely<WOFDatabase>` / `new DatabaseClient<WOFDatabase>(...)`.
190
+ *
191
+ * Tables not listed here will fail type-checked queries, by design.
192
+ * The reader ({@link WOFSQLitePlaceLookup}) already consumes this.
193
+ *
194
+ * The build and augment writers adopt it so a column rename is a compile error on both sides.
165
195
  */
166
196
  /**
167
- * The provenance row every built extract carries: source fingerprints travelling WITH the database rather than in a
168
- * document that can drift from it. Written by the postcode builders in `mailwoman/gazetteer-pipeline`.
197
+ * The provenance row every built extract includes: source fingerprints stored with
198
+ * the database rather than in a document that can drift from it.
199
+ *
200
+ * Written by the postcode builders in `mailwoman/gazetteer-pipeline`.
169
201
  */
170
202
  export interface ExtractMetaTable {
171
203
  key: string