@mailwoman/resolver-wof-sqlite 9.1.0 → 9.3.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 (501) hide show
  1. package/README.md +36 -20
  2. package/lib/address/index.ts +9 -0
  3. package/{address-point-interpolation.ts → lib/address/point-interpolation.ts} +40 -28
  4. package/{address-point-schema.ts → lib/address/point-schema.ts} +34 -8
  5. package/lib/address/point.ts +280 -0
  6. package/{ancestry-backfill.ts → lib/ancestry/backfill.ts} +14 -19
  7. package/{ancestry.ts → lib/ancestry/index.ts} +13 -8
  8. package/{build-candidate.ts → lib/build-candidate.ts} +243 -199
  9. package/{build-slim.ts → lib/build-slim.ts} +153 -168
  10. package/lib/candidate/alias-bags.ts +56 -0
  11. package/lib/candidate/ancestors-sidecar.ts +204 -0
  12. package/lib/candidate/country-display-names.ts +79 -0
  13. package/lib/candidate/extract-fold.ts +138 -0
  14. package/lib/candidate/name-roles.ts +238 -0
  15. package/lib/candidate/own-name.ts +146 -0
  16. package/lib/candidate/place-attrs.ts +44 -0
  17. package/lib/candidate-ancestors-schema.ts +195 -0
  18. package/{candidate-fts.ts → lib/candidate-fts.ts} +7 -5
  19. package/{candidate-importance.ts → lib/candidate-importance.ts} +54 -57
  20. package/lib/candidate-lookup.ts +976 -0
  21. package/{candidate-schema.ts → lib/candidate-schema.ts} +34 -6
  22. package/lib/candidate-scoring.ts +261 -0
  23. package/lib/capital-schema.ts +89 -0
  24. package/lib/capitals.ts +148 -0
  25. package/{coincident-roles.ts → lib/coincident-roles.ts} +75 -19
  26. package/{convention.ts → lib/convention/index.ts} +5 -5
  27. package/lib/convention/schema.ts +72 -0
  28. package/{coverage-manifest-schema.ts → lib/coverage-manifest-schema.ts} +15 -16
  29. package/lib/currency-backfill.ts +334 -0
  30. package/lib/env.ts +50 -0
  31. package/lib/exact-match.ts +113 -0
  32. package/{sharding.ts → lib/extracts.ts} +63 -60
  33. package/lib/fst/autocomplete.ts +176 -0
  34. package/{fst-builder.ts → lib/fst/builder.ts} +30 -27
  35. package/{fst-deserialize-web.ts → lib/fst/deserialize-web.ts} +27 -87
  36. package/lib/fst/format.ts +106 -0
  37. package/{fst-freshness.ts → lib/fst/freshness.ts} +42 -75
  38. package/lib/fst/index.ts +14 -0
  39. package/{fst-matcher.ts → lib/fst/matcher.ts} +1 -1
  40. package/{fst-serialize.ts → lib/fst/serialize.ts} +37 -115
  41. package/{fst-types.ts → lib/fst/types.ts} +4 -2
  42. package/{fts.ts → lib/fts/index.ts} +28 -23
  43. package/{fts-query.ts → lib/fts/query.ts} +2 -2
  44. package/{geonames-aliases.ts → lib/geonames/aliases.ts} +13 -13
  45. package/lib/geonames/index.ts +8 -0
  46. package/{geonames-postal.ts → lib/geonames/postal.ts} +16 -15
  47. package/{index.ts → lib/index.ts} +52 -48
  48. package/{interpolation.ts → lib/interpolation.ts} +137 -41
  49. package/lib/lookup.ts +861 -0
  50. package/{name-score.ts → lib/name-score.ts} +6 -4
  51. package/lib/nsul/index.ts +8 -0
  52. package/lib/nsul/lookup.ts +144 -0
  53. package/lib/nsul/schema.ts +130 -0
  54. package/{place-importance-schema.ts → lib/place-importance-schema.ts} +70 -22
  55. package/lib/poi/index.ts +8 -0
  56. package/{poi-lookup.ts → lib/poi/lookup.ts} +32 -39
  57. package/{poi-schema.ts → lib/poi/schema.ts} +10 -6
  58. package/lib/polygon-schema.ts +47 -0
  59. package/{postal-city-alias-lookup.ts → lib/postal/city-alias-lookup.ts} +15 -21
  60. package/{postal-city-candidate-schema.ts → lib/postal/city-candidate-schema.ts} +4 -2
  61. package/lib/postal/index.ts +9 -0
  62. package/{postcode-point-lookup.ts → lib/postcode-point-lookup.ts} +17 -15
  63. package/lib/primary-preference.ts +226 -0
  64. package/lib/proximity-rerank.ts +120 -0
  65. package/{ranking-weights.ts → lib/ranking-weights.ts} +30 -4
  66. package/lib/region-keys.ts +144 -0
  67. package/{reverse.ts → lib/reverse.ts} +45 -56
  68. package/{schema.ts → lib/schema.ts} +11 -1
  69. package/lib/search-fetch.ts +257 -0
  70. package/{sqlite-convention-source.ts → lib/sqlite-convention-source.ts} +9 -10
  71. package/lib/sqlite-utils.ts +83 -0
  72. package/{street-centroid-schema.ts → lib/street/centroid-schema.ts} +12 -6
  73. package/{street-centroid.ts → lib/street/centroid.ts} +26 -28
  74. package/lib/street/index.ts +13 -0
  75. package/{street-morphology-fst-builder.ts → lib/street/morphology-fst-builder.ts} +24 -14
  76. package/{street-morphology-fst-loader.ts → lib/street/morphology-fst-loader.ts} +14 -12
  77. package/{street-name-lookup.ts → lib/street/name-lookup.ts} +14 -30
  78. package/lib/street/normalize.ts +572 -0
  79. package/{street-segment-schema.ts → lib/street/segment-schema.ts} +13 -8
  80. package/{types.ts → lib/types.ts} +49 -9
  81. package/{unified-schema.ts → lib/unified-schema.ts} +21 -25
  82. package/lib/uprn/existence.ts +84 -0
  83. package/lib/uprn/index.ts +9 -0
  84. package/lib/uprn/lookup.ts +206 -0
  85. package/lib/uprn/schema.ts +124 -0
  86. package/lib/weights-overlay-linker.ts +878 -0
  87. package/out/address/index.d.ts +9 -0
  88. package/out/address/index.d.ts.map +1 -0
  89. package/out/address/index.js +9 -0
  90. package/out/address/index.js.map +1 -0
  91. package/out/{address-point-interpolation.d.ts → address/point-interpolation.d.ts} +11 -10
  92. package/out/address/point-interpolation.d.ts.map +1 -0
  93. package/out/{address-point-interpolation.js → address/point-interpolation.js} +29 -23
  94. package/out/address/point-interpolation.js.map +1 -0
  95. package/out/{address-point-schema.d.ts → address/point-schema.d.ts} +30 -10
  96. package/out/address/point-schema.d.ts.map +1 -0
  97. package/out/{address-point-schema.js → address/point-schema.js} +7 -3
  98. package/out/address/point-schema.js.map +1 -0
  99. package/out/{address-point.d.ts → address/point.d.ts} +16 -8
  100. package/out/address/point.d.ts.map +1 -0
  101. package/out/address/point.js +191 -0
  102. package/out/address/point.js.map +1 -0
  103. package/out/{ancestry-backfill.d.ts → ancestry/backfill.d.ts} +7 -5
  104. package/out/ancestry/backfill.d.ts.map +1 -0
  105. package/out/{ancestry-backfill.js → ancestry/backfill.js} +12 -16
  106. package/out/ancestry/backfill.js.map +1 -0
  107. package/out/{ancestry.d.ts → ancestry/index.d.ts} +6 -5
  108. package/out/ancestry/index.d.ts.map +1 -0
  109. package/out/{ancestry.js → ancestry/index.js} +7 -7
  110. package/out/ancestry/index.js.map +1 -0
  111. package/out/build-candidate.d.ts +88 -7
  112. package/out/build-candidate.d.ts.map +1 -1
  113. package/out/build-candidate.js +145 -149
  114. package/out/build-candidate.js.map +1 -1
  115. package/out/build-slim.d.ts +5 -5
  116. package/out/build-slim.d.ts.map +1 -1
  117. package/out/build-slim.js +127 -142
  118. package/out/build-slim.js.map +1 -1
  119. package/out/candidate/alias-bags.d.ts +19 -0
  120. package/out/candidate/alias-bags.d.ts.map +1 -0
  121. package/out/candidate/alias-bags.js +39 -0
  122. package/out/candidate/alias-bags.js.map +1 -0
  123. package/out/candidate/ancestors-sidecar.d.ts +32 -0
  124. package/out/candidate/ancestors-sidecar.d.ts.map +1 -0
  125. package/out/candidate/ancestors-sidecar.js +140 -0
  126. package/out/candidate/ancestors-sidecar.js.map +1 -0
  127. package/out/candidate/country-display-names.d.ts +35 -0
  128. package/out/candidate/country-display-names.d.ts.map +1 -0
  129. package/out/candidate/country-display-names.js +59 -0
  130. package/out/candidate/country-display-names.js.map +1 -0
  131. package/out/candidate/extract-fold.d.ts +32 -0
  132. package/out/candidate/extract-fold.d.ts.map +1 -0
  133. package/out/candidate/extract-fold.js +104 -0
  134. package/out/candidate/extract-fold.js.map +1 -0
  135. package/out/candidate/name-roles.d.ts +57 -0
  136. package/out/candidate/name-roles.d.ts.map +1 -0
  137. package/out/candidate/name-roles.js +166 -0
  138. package/out/candidate/name-roles.js.map +1 -0
  139. package/out/candidate/own-name.d.ts +50 -0
  140. package/out/candidate/own-name.d.ts.map +1 -0
  141. package/out/candidate/own-name.js +132 -0
  142. package/out/candidate/own-name.js.map +1 -0
  143. package/out/candidate/place-attrs.d.ts +43 -0
  144. package/out/candidate/place-attrs.d.ts.map +1 -0
  145. package/out/candidate/place-attrs.js +15 -0
  146. package/out/candidate/place-attrs.js.map +1 -0
  147. package/out/candidate-ancestors-schema.d.ts +150 -0
  148. package/out/candidate-ancestors-schema.d.ts.map +1 -0
  149. package/out/candidate-ancestors-schema.js +123 -0
  150. package/out/candidate-ancestors-schema.js.map +1 -0
  151. package/out/candidate-fts.d.ts +7 -5
  152. package/out/candidate-fts.d.ts.map +1 -1
  153. package/out/candidate-fts.js +5 -3
  154. package/out/candidate-fts.js.map +1 -1
  155. package/out/candidate-importance.d.ts +16 -16
  156. package/out/candidate-importance.d.ts.map +1 -1
  157. package/out/candidate-importance.js +47 -52
  158. package/out/candidate-importance.js.map +1 -1
  159. package/out/candidate-lookup.d.ts +31 -52
  160. package/out/candidate-lookup.d.ts.map +1 -1
  161. package/out/candidate-lookup.js +380 -172
  162. package/out/candidate-lookup.js.map +1 -1
  163. package/out/candidate-schema.d.ts +31 -7
  164. package/out/candidate-schema.d.ts.map +1 -1
  165. package/out/candidate-schema.js +3 -0
  166. package/out/candidate-schema.js.map +1 -1
  167. package/out/candidate-scoring.d.ts +34 -0
  168. package/out/candidate-scoring.d.ts.map +1 -0
  169. package/out/candidate-scoring.js +196 -0
  170. package/out/candidate-scoring.js.map +1 -0
  171. package/out/capital-schema.d.ts +51 -0
  172. package/out/capital-schema.d.ts.map +1 -0
  173. package/out/capital-schema.js +63 -0
  174. package/out/capital-schema.js.map +1 -0
  175. package/out/capitals.d.ts +69 -0
  176. package/out/capitals.d.ts.map +1 -0
  177. package/out/capitals.js +98 -0
  178. package/out/capitals.js.map +1 -0
  179. package/out/coincident-roles.d.ts +12 -4
  180. package/out/coincident-roles.d.ts.map +1 -1
  181. package/out/coincident-roles.js +43 -11
  182. package/out/coincident-roles.js.map +1 -1
  183. package/out/{convention.d.ts → convention/index.d.ts} +5 -5
  184. package/out/convention/index.d.ts.map +1 -0
  185. package/out/{convention.js → convention/index.js} +4 -4
  186. package/out/convention/index.js.map +1 -0
  187. package/out/convention/schema.d.ts +51 -0
  188. package/out/convention/schema.d.ts.map +1 -0
  189. package/out/convention/schema.js +34 -0
  190. package/out/convention/schema.js.map +1 -0
  191. package/out/coverage-manifest-schema.d.ts +8 -8
  192. package/out/coverage-manifest-schema.d.ts.map +1 -1
  193. package/out/coverage-manifest-schema.js +6 -10
  194. package/out/coverage-manifest-schema.js.map +1 -1
  195. package/out/currency-backfill.d.ts +90 -0
  196. package/out/currency-backfill.d.ts.map +1 -0
  197. package/out/currency-backfill.js +196 -0
  198. package/out/currency-backfill.js.map +1 -0
  199. package/out/env.d.ts +43 -0
  200. package/out/env.d.ts.map +1 -0
  201. package/out/env.js +48 -0
  202. package/out/env.js.map +1 -0
  203. package/out/exact-match.d.ts +25 -0
  204. package/out/exact-match.d.ts.map +1 -0
  205. package/out/exact-match.js +88 -0
  206. package/out/exact-match.js.map +1 -0
  207. package/out/extracts.d.ts +114 -0
  208. package/out/extracts.d.ts.map +1 -0
  209. package/out/{sharding.js → extracts.js} +38 -38
  210. package/out/extracts.js.map +1 -0
  211. package/out/{fst-autocomplete.d.ts → fst/autocomplete.d.ts} +13 -13
  212. package/out/fst/autocomplete.d.ts.map +1 -0
  213. package/out/fst/autocomplete.js +113 -0
  214. package/out/fst/autocomplete.js.map +1 -0
  215. package/out/{fst-builder.d.ts → fst/builder.d.ts} +5 -5
  216. package/out/fst/builder.d.ts.map +1 -0
  217. package/out/{fst-builder.js → fst/builder.js} +24 -24
  218. package/out/fst/builder.js.map +1 -0
  219. package/out/{fst-deserialize-web.d.ts → fst/deserialize-web.d.ts} +3 -3
  220. package/out/fst/deserialize-web.d.ts.map +1 -0
  221. package/out/{fst-deserialize-web.js → fst/deserialize-web.js} +10 -74
  222. package/out/fst/deserialize-web.js.map +1 -0
  223. package/out/fst/format.d.ts +80 -0
  224. package/out/fst/format.d.ts.map +1 -0
  225. package/out/fst/format.js +91 -0
  226. package/out/fst/format.js.map +1 -0
  227. package/out/{fst-freshness.d.ts → fst/freshness.d.ts} +11 -14
  228. package/out/fst/freshness.d.ts.map +1 -0
  229. package/out/{fst-freshness.js → fst/freshness.js} +42 -74
  230. package/out/fst/freshness.js.map +1 -0
  231. package/out/fst/index.d.ts +14 -0
  232. package/out/fst/index.d.ts.map +1 -0
  233. package/out/fst/index.js +14 -0
  234. package/out/fst/index.js.map +1 -0
  235. package/out/{fst-matcher.d.ts → fst/matcher.d.ts} +2 -2
  236. package/out/fst/matcher.d.ts.map +1 -0
  237. package/out/{fst-matcher.js → fst/matcher.js} +1 -1
  238. package/out/fst/matcher.js.map +1 -0
  239. package/out/{fst-serialize.d.ts → fst/serialize.d.ts} +5 -10
  240. package/out/fst/serialize.d.ts.map +1 -0
  241. package/out/{fst-serialize.js → fst/serialize.js} +18 -98
  242. package/out/fst/serialize.js.map +1 -0
  243. package/out/{fst-types.d.ts → fst/types.d.ts} +4 -3
  244. package/out/fst/types.d.ts.map +1 -0
  245. package/out/{fst-types.js → fst/types.js} +1 -1
  246. package/out/fst/types.js.map +1 -0
  247. package/out/{fts.d.ts → fts/index.d.ts} +20 -13
  248. package/out/fts/index.d.ts.map +1 -0
  249. package/out/{fts.js → fts/index.js} +20 -15
  250. package/out/fts/index.js.map +1 -0
  251. package/out/{fts-query.d.ts → fts/query.d.ts} +2 -2
  252. package/out/fts/query.d.ts.map +1 -0
  253. package/out/{fts-query.js → fts/query.js} +2 -2
  254. package/out/fts/query.js.map +1 -0
  255. package/out/{geonames-aliases.d.ts → geonames/aliases.d.ts} +8 -6
  256. package/out/geonames/aliases.d.ts.map +1 -0
  257. package/out/{geonames-aliases.js → geonames/aliases.js} +7 -7
  258. package/out/geonames/aliases.js.map +1 -0
  259. package/out/geonames/index.d.ts +8 -0
  260. package/out/geonames/index.d.ts.map +1 -0
  261. package/out/geonames/index.js +8 -0
  262. package/out/geonames/index.js.map +1 -0
  263. package/out/{geonames-postal.d.ts → geonames/postal.d.ts} +12 -10
  264. package/out/geonames/postal.d.ts.map +1 -0
  265. package/out/{geonames-postal.js → geonames/postal.js} +11 -11
  266. package/out/geonames/postal.js.map +1 -0
  267. package/out/index.d.ts +32 -31
  268. package/out/index.d.ts.map +1 -1
  269. package/out/index.js +24 -24
  270. package/out/index.js.map +1 -1
  271. package/out/interpolation.d.ts +18 -9
  272. package/out/interpolation.d.ts.map +1 -1
  273. package/out/interpolation.js +108 -36
  274. package/out/interpolation.js.map +1 -1
  275. package/out/lookup.d.ts +25 -25
  276. package/out/lookup.d.ts.map +1 -1
  277. package/out/lookup.js +174 -558
  278. package/out/lookup.js.map +1 -1
  279. package/out/name-score.d.ts +0 -10
  280. package/out/name-score.d.ts.map +1 -1
  281. package/out/name-score.js +6 -4
  282. package/out/name-score.js.map +1 -1
  283. package/out/nsul/index.d.ts +8 -0
  284. package/out/nsul/index.d.ts.map +1 -0
  285. package/out/nsul/index.js +8 -0
  286. package/out/nsul/index.js.map +1 -0
  287. package/out/nsul/lookup.d.ts +83 -0
  288. package/out/nsul/lookup.d.ts.map +1 -0
  289. package/out/nsul/lookup.js +80 -0
  290. package/out/nsul/lookup.js.map +1 -0
  291. package/out/nsul/schema.d.ts +100 -0
  292. package/out/nsul/schema.d.ts.map +1 -0
  293. package/out/nsul/schema.js +78 -0
  294. package/out/nsul/schema.js.map +1 -0
  295. package/out/place-importance-schema.d.ts +47 -10
  296. package/out/place-importance-schema.d.ts.map +1 -1
  297. package/out/place-importance-schema.js +56 -10
  298. package/out/place-importance-schema.js.map +1 -1
  299. package/out/poi/index.d.ts +8 -0
  300. package/out/poi/index.d.ts.map +1 -0
  301. package/out/poi/index.js +8 -0
  302. package/out/poi/index.js.map +1 -0
  303. package/out/{poi-lookup.d.ts → poi/lookup.d.ts} +8 -10
  304. package/out/poi/lookup.d.ts.map +1 -0
  305. package/out/{poi-lookup.js → poi/lookup.js} +24 -29
  306. package/out/poi/lookup.js.map +1 -0
  307. package/out/{poi-schema.d.ts → poi/schema.d.ts} +10 -6
  308. package/out/poi/schema.d.ts.map +1 -0
  309. package/out/{poi-schema.js → poi/schema.js} +1 -1
  310. package/out/poi/schema.js.map +1 -0
  311. package/out/polygon-schema.d.ts +37 -0
  312. package/out/polygon-schema.d.ts.map +1 -0
  313. package/out/polygon-schema.js +23 -0
  314. package/out/polygon-schema.js.map +1 -0
  315. package/out/{postal-city-alias-lookup.d.ts → postal/city-alias-lookup.d.ts} +7 -6
  316. package/out/postal/city-alias-lookup.d.ts.map +1 -0
  317. package/out/{postal-city-alias-lookup.js → postal/city-alias-lookup.js} +12 -16
  318. package/out/postal/city-alias-lookup.js.map +1 -0
  319. package/out/{postal-city-alias-schema.d.ts → postal/city-alias-schema.d.ts} +1 -1
  320. package/out/{postal-city-alias-schema.d.ts.map → postal/city-alias-schema.d.ts.map} +1 -1
  321. package/out/{postal-city-alias-schema.js → postal/city-alias-schema.js} +1 -1
  322. package/out/{postal-city-alias-schema.js.map → postal/city-alias-schema.js.map} +1 -1
  323. package/out/{postal-city-candidate-schema.d.ts → postal/city-candidate-schema.d.ts} +4 -3
  324. package/out/postal/city-candidate-schema.d.ts.map +1 -0
  325. package/out/{postal-city-candidate-schema.js → postal/city-candidate-schema.js} +2 -2
  326. package/out/{postal-city-candidate-schema.js.map → postal/city-candidate-schema.js.map} +1 -1
  327. package/out/postal/index.d.ts +9 -0
  328. package/out/postal/index.d.ts.map +1 -0
  329. package/out/postal/index.js +9 -0
  330. package/out/postal/index.js.map +1 -0
  331. package/out/postcode-point-lookup.d.ts +10 -10
  332. package/out/postcode-point-lookup.d.ts.map +1 -1
  333. package/out/postcode-point-lookup.js +13 -13
  334. package/out/postcode-point-lookup.js.map +1 -1
  335. package/out/primary-preference.d.ts +133 -0
  336. package/out/primary-preference.d.ts.map +1 -0
  337. package/out/primary-preference.js +146 -0
  338. package/out/primary-preference.js.map +1 -0
  339. package/out/proximity-rerank.d.ts +77 -0
  340. package/out/proximity-rerank.d.ts.map +1 -0
  341. package/out/proximity-rerank.js +86 -0
  342. package/out/proximity-rerank.js.map +1 -0
  343. package/out/ranking-weights.d.ts +15 -3
  344. package/out/ranking-weights.d.ts.map +1 -1
  345. package/out/ranking-weights.js +21 -3
  346. package/out/ranking-weights.js.map +1 -1
  347. package/out/region-keys.d.ts +47 -0
  348. package/out/region-keys.d.ts.map +1 -0
  349. package/out/region-keys.js +121 -0
  350. package/out/region-keys.js.map +1 -0
  351. package/out/reverse.d.ts +6 -6
  352. package/out/reverse.d.ts.map +1 -1
  353. package/out/reverse.js +23 -35
  354. package/out/reverse.js.map +1 -1
  355. package/out/schema.d.ts +10 -1
  356. package/out/schema.d.ts.map +1 -1
  357. package/out/schema.js.map +1 -1
  358. package/out/search-fetch.d.ts +59 -0
  359. package/out/search-fetch.d.ts.map +1 -0
  360. package/out/search-fetch.js +184 -0
  361. package/out/search-fetch.js.map +1 -0
  362. package/out/sqlite-convention-source.d.ts +7 -7
  363. package/out/sqlite-convention-source.d.ts.map +1 -1
  364. package/out/sqlite-convention-source.js +5 -5
  365. package/out/sqlite-convention-source.js.map +1 -1
  366. package/out/sqlite-utils.d.ts +24 -6
  367. package/out/sqlite-utils.d.ts.map +1 -1
  368. package/out/sqlite-utils.js +24 -7
  369. package/out/sqlite-utils.js.map +1 -1
  370. package/out/{street-centroid-schema.d.ts → street/centroid-schema.d.ts} +12 -7
  371. package/out/street/centroid-schema.d.ts.map +1 -0
  372. package/out/{street-centroid-schema.js → street/centroid-schema.js} +5 -5
  373. package/out/{street-centroid-schema.js.map → street/centroid-schema.js.map} +1 -1
  374. package/out/{street-centroid.d.ts → street/centroid.d.ts} +6 -6
  375. package/out/street/centroid.d.ts.map +1 -0
  376. package/out/{street-centroid.js → street/centroid.js} +18 -24
  377. package/out/street/centroid.js.map +1 -0
  378. package/out/street/index.d.ts +13 -0
  379. package/out/street/index.d.ts.map +1 -0
  380. package/out/street/index.js +13 -0
  381. package/out/street/index.js.map +1 -0
  382. package/out/{street-morphology-fst-builder.d.ts → street/morphology-fst-builder.d.ts} +4 -4
  383. package/out/street/morphology-fst-builder.d.ts.map +1 -0
  384. package/out/{street-morphology-fst-builder.js → street/morphology-fst-builder.js} +18 -13
  385. package/out/street/morphology-fst-builder.js.map +1 -0
  386. package/out/{street-morphology-fst-loader.d.ts → street/morphology-fst-loader.d.ts} +5 -5
  387. package/out/street/morphology-fst-loader.d.ts.map +1 -0
  388. package/out/{street-morphology-fst-loader.js → street/morphology-fst-loader.js} +11 -10
  389. package/out/street/morphology-fst-loader.js.map +1 -0
  390. package/out/{street-name-lookup.d.ts → street/name-lookup.d.ts} +4 -7
  391. package/out/street/name-lookup.d.ts.map +1 -0
  392. package/out/{street-name-lookup.js → street/name-lookup.js} +9 -23
  393. package/out/street/name-lookup.js.map +1 -0
  394. package/out/street/normalize.d.ts +179 -0
  395. package/out/street/normalize.d.ts.map +1 -0
  396. package/out/street/normalize.js +457 -0
  397. package/out/street/normalize.js.map +1 -0
  398. package/out/{street-segment-schema.d.ts → street/segment-schema.d.ts} +13 -9
  399. package/out/street/segment-schema.d.ts.map +1 -0
  400. package/out/{street-segment-schema.js → street/segment-schema.js} +4 -4
  401. package/out/{street-segment-schema.js.map → street/segment-schema.js.map} +1 -1
  402. package/out/types.d.ts +49 -9
  403. package/out/types.d.ts.map +1 -1
  404. package/out/types.js.map +1 -1
  405. package/out/unified-schema.d.ts +6 -5
  406. package/out/unified-schema.d.ts.map +1 -1
  407. package/out/unified-schema.js +16 -19
  408. package/out/unified-schema.js.map +1 -1
  409. package/out/uprn/existence.d.ts +53 -0
  410. package/out/uprn/existence.d.ts.map +1 -0
  411. package/out/uprn/existence.js +61 -0
  412. package/out/uprn/existence.js.map +1 -0
  413. package/out/uprn/index.d.ts +9 -0
  414. package/out/uprn/index.d.ts.map +1 -0
  415. package/out/uprn/index.js +9 -0
  416. package/out/uprn/index.js.map +1 -0
  417. package/out/uprn/lookup.d.ts +85 -0
  418. package/out/uprn/lookup.d.ts.map +1 -0
  419. package/out/uprn/lookup.js +149 -0
  420. package/out/uprn/lookup.js.map +1 -0
  421. package/out/uprn/schema.d.ts +93 -0
  422. package/out/uprn/schema.d.ts.map +1 -0
  423. package/out/uprn/schema.js +78 -0
  424. package/out/uprn/schema.js.map +1 -0
  425. package/out/weights-overlay-linker.d.ts +299 -0
  426. package/out/weights-overlay-linker.d.ts.map +1 -0
  427. package/out/weights-overlay-linker.js +511 -0
  428. package/out/weights-overlay-linker.js.map +1 -0
  429. package/package.json +622 -154
  430. package/address-point.ts +0 -125
  431. package/candidate-lookup.ts +0 -720
  432. package/fst-autocomplete.ts +0 -204
  433. package/geo.ts +0 -121
  434. package/lookup.ts +0 -1354
  435. package/out/address-point-interpolation.d.ts.map +0 -1
  436. package/out/address-point-interpolation.js.map +0 -1
  437. package/out/address-point-schema.d.ts.map +0 -1
  438. package/out/address-point-schema.js.map +0 -1
  439. package/out/address-point.d.ts.map +0 -1
  440. package/out/address-point.js +0 -84
  441. package/out/address-point.js.map +0 -1
  442. package/out/ancestry-backfill.d.ts.map +0 -1
  443. package/out/ancestry-backfill.js.map +0 -1
  444. package/out/ancestry.d.ts.map +0 -1
  445. package/out/ancestry.js.map +0 -1
  446. package/out/convention.d.ts.map +0 -1
  447. package/out/convention.js.map +0 -1
  448. package/out/fst-autocomplete.d.ts.map +0 -1
  449. package/out/fst-autocomplete.js +0 -130
  450. package/out/fst-autocomplete.js.map +0 -1
  451. package/out/fst-builder.d.ts.map +0 -1
  452. package/out/fst-builder.js.map +0 -1
  453. package/out/fst-deserialize-web.d.ts.map +0 -1
  454. package/out/fst-deserialize-web.js.map +0 -1
  455. package/out/fst-freshness.d.ts.map +0 -1
  456. package/out/fst-freshness.js.map +0 -1
  457. package/out/fst-matcher.d.ts.map +0 -1
  458. package/out/fst-matcher.js.map +0 -1
  459. package/out/fst-serialize.d.ts.map +0 -1
  460. package/out/fst-serialize.js.map +0 -1
  461. package/out/fst-types.d.ts.map +0 -1
  462. package/out/fst-types.js.map +0 -1
  463. package/out/fts-query.d.ts.map +0 -1
  464. package/out/fts-query.js.map +0 -1
  465. package/out/fts.d.ts.map +0 -1
  466. package/out/fts.js.map +0 -1
  467. package/out/geo.d.ts +0 -74
  468. package/out/geo.d.ts.map +0 -1
  469. package/out/geo.js +0 -71
  470. package/out/geo.js.map +0 -1
  471. package/out/geonames-aliases.d.ts.map +0 -1
  472. package/out/geonames-aliases.js.map +0 -1
  473. package/out/geonames-postal.d.ts.map +0 -1
  474. package/out/geonames-postal.js.map +0 -1
  475. package/out/poi-lookup.d.ts.map +0 -1
  476. package/out/poi-lookup.js.map +0 -1
  477. package/out/poi-schema.d.ts.map +0 -1
  478. package/out/poi-schema.js.map +0 -1
  479. package/out/postal-city-alias-lookup.d.ts.map +0 -1
  480. package/out/postal-city-alias-lookup.js.map +0 -1
  481. package/out/postal-city-candidate-schema.d.ts.map +0 -1
  482. package/out/sharding.d.ts +0 -114
  483. package/out/sharding.d.ts.map +0 -1
  484. package/out/sharding.js.map +0 -1
  485. package/out/street-centroid-schema.d.ts.map +0 -1
  486. package/out/street-centroid.d.ts.map +0 -1
  487. package/out/street-centroid.js.map +0 -1
  488. package/out/street-morphology-fst-builder.d.ts.map +0 -1
  489. package/out/street-morphology-fst-builder.js.map +0 -1
  490. package/out/street-morphology-fst-loader.d.ts.map +0 -1
  491. package/out/street-morphology-fst-loader.js.map +0 -1
  492. package/out/street-name-lookup.d.ts.map +0 -1
  493. package/out/street-name-lookup.js.map +0 -1
  494. package/out/street-normalize.d.ts +0 -95
  495. package/out/street-normalize.d.ts.map +0 -1
  496. package/out/street-normalize.js +0 -272
  497. package/out/street-normalize.js.map +0 -1
  498. package/out/street-segment-schema.d.ts.map +0 -1
  499. package/sqlite-utils.ts +0 -46
  500. package/street-normalize.ts +0 -318
  501. /package/{postal-city-alias-schema.ts → lib/postal/city-alias-schema.ts} +0 -0
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * The locality-ranking weights and their shipped defaults. Its own module because every value here is
7
7
  * a measured tuning decision with its rationale attached — the block reads as a reference table, not
8
- * as part of the lookup's control flow, and the tests import it directly to pin one lever at a time.
8
+ * as part of the lookup's control flow, and the tests import it directly to pin one change at a time.
9
9
  */
10
10
 
11
11
  /**
@@ -96,7 +96,7 @@ export interface RankingWeights {
96
96
  * Fixes unscoped "Åbo" → Turku (its official Swedish name) over a hamlet literally named Åbo; population still orders
97
97
  * within the sub-tier, so Paris → Paris FR is untouched.
98
98
  *
99
- * Default true (operator-promoted 2026-07-03 after the pre-registered gate battery: four intended exonym flips —
99
+ * Default true (operator-promoted 2026-07-03 after the pre-registered eval battery: four intended exonym flips —
100
100
  * Berne→Bern, Bruges→Brugge, Roma→Rome, Åbo→Turku — with the namesake/abbreviation rows and the US/FI panels
101
101
  * byte-identical). Requires a gazetteer carrying the #940 ingest bit — on older DBs without the `official` column the
102
102
  * probe fails soft and behavior is identical to the flag being off.
@@ -114,7 +114,7 @@ export interface RankingWeights {
114
114
 
115
115
  /**
116
116
  * The shipped weights. Every value is a measured decision — change one and re-run the resolver eval; the per-field docs
117
- * on {@link RankingWeights} say what each lever moves and what motivated its current value.
117
+ * on {@link RankingWeights} say what each change moves and what motivated its current value.
118
118
  */
119
119
  export const DEFAULT_WEIGHTS: RankingWeights = {
120
120
  placetypeMatchBoost: 0.5,
@@ -142,7 +142,33 @@ export const DEFAULT_WEIGHTS: RankingWeights = {
142
142
  // consulted — keeps population as an intra-tier prominence tiebreaker, not a cross-tier promoter.
143
143
  // Fixes the 2-letter-region-abbrev bug ("ME" → Maine, not the more-populous Missouri).
144
144
  exactMatchTiering: true,
145
- // #936 option 3 — promoted default-ON 2026-07-03 (gate battery PASS; see the RankingWeights docstring).
145
+ // #936 option 3 — promoted default-ON 2026-07-03 (eval battery PASS; see the RankingWeights docstring).
146
146
  officialNameExact: true,
147
147
  officialNameExactFloor: 100_000,
148
148
  }
149
+
150
+ /**
151
+ * The population contribution as a 0..1 fraction: `min(1, log10(1 + population) / populationScaleLog10)`. Zero for an
152
+ * absent or non-positive population — and zero for a non-positive scale, so a magnitude never carries its own absence.
153
+ * The coordinate-first locality path consumes this fraction directly; {@link populationBoostTerm} scales it.
154
+ */
155
+ export function populationScaleTerm(
156
+ population: number | null | undefined,
157
+ weights: Pick<RankingWeights, "populationScaleLog10">
158
+ ): number {
159
+ if (population == null || population <= 0 || weights.populationScaleLog10 <= 0) return 0
160
+
161
+ return Math.min(1, Math.log10(1 + population) / weights.populationScaleLog10)
162
+ }
163
+
164
+ /**
165
+ * The additive population boost: `populationBoost * populationScaleTerm(...)`, capped at `populationBoost` magnitude at
166
+ * `10^populationScaleLog10` people. Missing population contributes 0 — never a penalty. The one formula behind the Node
167
+ * weighted sum and the WASM re-rank, so the two backends cannot drift.
168
+ */
169
+ export function populationBoostTerm(
170
+ population: number | null | undefined,
171
+ weights: Pick<RankingWeights, "populationBoost" | "populationScaleLog10">
172
+ ): number {
173
+ return weights.populationBoost * populationScaleTerm(population, weights)
174
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The comparable-key expansion for a parsed REGION qualifier — one function, both consumers (the
7
+ * #861 rule). The admin-coherence verdicts (`mailwoman/admin-coherence.ts`) fold a qualifier and a
8
+ * winner-ancestry name through this to decide `confirmed`/`contradicted`; the candidate backend's
9
+ * admin-containment re-rank (#1717 stage 2, `candidate-lookup.ts`) folds the same qualifier through
10
+ * it to find the qualifier's own region-class rows in the candidate table. A check that says
11
+ * `contradicted` and a re-rank that cannot find the qualifier would otherwise be two readings of
12
+ * one string that silently disagree — the exact drift the shared-function rule exists to prevent.
13
+ *
14
+ * Lives HERE rather than in `mailwoman` because the dependency points this way: `mailwoman`
15
+ * depends on `@mailwoman/resolver-wof-sqlite` (which owns the fold and already depends on codex),
16
+ * never the reverse.
17
+ */
18
+
19
+ import { matchSubdivision, matchSubdivisionIn } from "@mailwoman/codex/country"
20
+
21
+ import { normalizeLocalityForKey } from "#street/normalize"
22
+
23
+ /**
24
+ * The ancestry placetypes that answer for a parsed `region` qualifier — WOF's admin band between country and locality.
25
+ * Deliberately the whole band: a qualifier stated at any grain ("Lancashire", a ceremonial county; "Thüringen", a Land)
26
+ * may confirm against whichever level the backend stored, and `contradicted` requires the entire band to miss, so
27
+ * widening the band only ever makes the check more conservative.
28
+ */
29
+ export const REGION_CLASS_PLACETYPES: ReadonlySet<string> = new Set(["region", "macroregion", "county", "macrocounty"])
30
+
31
+ /**
32
+ * County-style qualifier prefixes stripped to produce a comparison VARIANT. Ireland writes `Co. Westmeath` where WOF
33
+ * stores `Westmeath`, so the prefix defeats the fold and every Irish county qualifier read `contradicted` on the first
34
+ * board census (2026-08-17, five rows). The stripped form is ADDED to the key set, never substituted — `County Durham`
35
+ * is a real name whose stripped variant simply also matches, and a set union can only widen confirmation, so the
36
+ * closure is monotone: `contradicted → confirmed` is the only movement it can cause.
37
+ */
38
+ const COUNTY_QUALIFIER_PREFIXES = ["county", "co.", "co"] as const
39
+
40
+ /**
41
+ * Trailing admin-qualifier words, the suffix sibling of the prefix above: `San José Province` (CR board row) folds
42
+ * against stored `San José` only with the word removed. Same monotone rule — the stripped form joins the set, never
43
+ * replaces the original.
44
+ */
45
+ const ADMIN_QUALIFIER_SUFFIXES = ["province", "prov.", "prov"] as const
46
+
47
+ function withoutPrefix(value: string, prefixes: readonly string[]): string {
48
+ const folded = value.toLowerCase()
49
+
50
+ for (const prefix of prefixes) {
51
+ if (!folded.startsWith(prefix) || !/\s/u.test(value[prefix.length] ?? "")) continue
52
+
53
+ let offset = prefix.length
54
+
55
+ while (/\s/u.test(value[offset] ?? "")) {
56
+ offset++
57
+ }
58
+
59
+ return value.slice(offset)
60
+ }
61
+
62
+ return value
63
+ }
64
+
65
+ function withoutSuffix(value: string, suffixes: readonly string[]): string {
66
+ const folded = value.toLowerCase()
67
+
68
+ for (const suffix of suffixes) {
69
+ const offset = value.length - suffix.length
70
+
71
+ if (offset <= 0 || !folded.endsWith(suffix) || !/\s/u.test(value[offset - 1] ?? "")) continue
72
+
73
+ let end = offset
74
+
75
+ while (end > 0 && /\s/u.test(value[end - 1] ?? "")) {
76
+ end--
77
+ }
78
+
79
+ return value.slice(0, end)
80
+ }
81
+
82
+ return value
83
+ }
84
+
85
+ /**
86
+ * The comparable keys a region string expands to: its own fold (the shared candidate.db `name_key` normalizer,
87
+ * {@link normalizeLocalityForKey} — build side and check side agree by construction); a county-prefix-stripped variant;
88
+ * the codex subdivision expansions — the disjoint US+CA table always, plus the COUNTRY-SCOPED table when the caller
89
+ * knows a country (`WA` under AU is Western Australia; under US, Washington — the collision that keeps AU out of the
90
+ * unscoped table). Every expansion lands the canonical name and code folds in the set, so `IL`/`Illinois` and
91
+ * `WA`/`Western Australia` meet from either side.
92
+ */
93
+ export function regionKeys(value: string, countryAlpha2?: string): Set<string> {
94
+ const keys = new Set([normalizeLocalityForKey(value)])
95
+
96
+ for (const stripped of [
97
+ withoutPrefix(value, COUNTY_QUALIFIER_PREFIXES),
98
+ withoutSuffix(value, ADMIN_QUALIFIER_SUFFIXES),
99
+ ]) {
100
+ if (stripped !== value && stripped.trim()) {
101
+ keys.add(normalizeLocalityForKey(stripped))
102
+ }
103
+ }
104
+
105
+ const expansions = [matchSubdivision(value), countryAlpha2 ? matchSubdivisionIn(countryAlpha2, value) : null]
106
+
107
+ for (const subdivision of expansions) {
108
+ if (subdivision) {
109
+ keys.add(normalizeLocalityForKey(subdivision.name))
110
+ keys.add(normalizeLocalityForKey(subdivision.code))
111
+ }
112
+ }
113
+
114
+ // The empty fold stays IN the set on purpose — the admin-coherence verdicts have always compared
115
+ // the empty key (two empty-folding strings intersect → `confirmed`), and this move must not shift
116
+ // a verdict. A consumer probing a table by key filters the empty string out itself.
117
+ return keys
118
+ }
119
+
120
+ /**
121
+ * The PROBE-side expansion for a region qualifier — {@link regionKeys} plus the county-PREFIXED variant of every key.
122
+ *
123
+ * The verdict implementation intersects two {@link regionKeys} SETS, so `Co. Donegal` meets stored `County Donegal` at
124
+ * the shared stripped key `donegal`. A table probe is one-sided: it matches the STORED fold verbatim, and WOF stores
125
+ * Irish counties under `county donegal` with no bare `donegal` key (measured on the shipped candidate.db — the
126
+ * qualifier probe missed every Irish county until this variant landed). Adding `county <key>` restores the
127
+ * two-sidedness for the one stored-form family with an evidenced case; the union is monotone (a wider qualifier set can
128
+ * only find more BEARERS, each of which must still genuinely contain a candidate before anything moves). The suffix
129
+ * sibling (`<key> province`) is deliberately absent — no stored-form case has been evidenced, and a change without a
130
+ * board does not get built.
131
+ */
132
+ export function regionQualifierProbeKeys(value: string, countryAlpha2?: string): Set<string> {
133
+ const keys = regionKeys(value, countryAlpha2)
134
+ // Snapshot before widening: the loop adds `county <key>` members that must not themselves be revisited.
135
+ const bare = [...keys]
136
+
137
+ for (const key of bare) {
138
+ if (key && !key.startsWith("county ")) {
139
+ keys.add(`county ${key}`)
140
+ }
141
+ }
142
+
143
+ return keys
144
+ }
@@ -4,7 +4,7 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Reverse geocoding (#484): `(lat, lon)` → the containing admin hierarchy. Assembly over existing
7
- * machinery, per the 2026-06-11 scoping notes:
7
+ * implementation, per the 2026-06-11 scoping notes:
8
8
  *
9
9
  * 1. **Candidate fetch** — the admin DB's `place_bbox` R*Tree (built by `fts.ts`) for places whose
10
10
  * bbox contains the point, smallest-area-first (so the FIRST polygon confirmation is the
@@ -28,14 +28,16 @@
28
28
  * says so per result rather than pretending.
29
29
  */
30
30
 
31
- import { DatabaseSync } from "node:sqlite"
31
+ import { tryParsingJSON } from "@mailwoman/core/json"
32
+ import { bboxAround, geometryContains, haversineKm, type ParsedGeometry } from "@mailwoman/spatial"
33
+ import { DatabaseClient } from "@mailwoman/sqlite/client"
34
+ import { tableExists } from "@mailwoman/sqlite/introspection"
32
35
 
33
- import { tryParsingJSON } from "@mailwoman/core/objects"
34
-
35
- import { ancestorLineage, placetypeDepth } from "./ancestry.ts"
36
- import { PLACE_BBOX_TABLE } from "./fts.ts"
37
- import { geometryContains, haversineKm, type GeojsonGeometry } from "./geo.ts"
38
- import type { PlaceCandidate, WOFPlacetype } from "./types.ts"
36
+ import { ancestorLineage, placetypeDepth } from "#ancestry/index"
37
+ import { PLACE_BBOX_TABLE } from "#fts/index"
38
+ import type { WOFDatabase } from "#schema"
39
+ import { allRows } from "#sqlite-utils"
40
+ import type { PlaceCandidate, WOFPlacetype } from "#types"
39
41
 
40
42
  /**
41
43
  * Largest absolute latitude in WGS-84 degrees.
@@ -79,7 +81,7 @@ export interface WOFReverseGeocoderOpts {
79
81
  /**
80
82
  * Pre-opened admin DB — primarily for tests against an inline fixture.
81
83
  */
82
- adminDatabase?: DatabaseSync
84
+ adminDatabase?: DatabaseClient<WOFDatabase>
83
85
  /**
84
86
  * Path to the polygon sidecar DB (`wof-polygons.db`, table `polygons(id, geom)`). OPTIONAL — without it every result
85
87
  * is `containment: "approximate"` (centroid-only mode). Mutually exclusive with `polygonDatabase`.
@@ -88,7 +90,7 @@ export interface WOFReverseGeocoderOpts {
88
90
  /**
89
91
  * Pre-opened polygon DB — primarily for tests.
90
92
  */
91
- polygonDatabase?: DatabaseSync
93
+ polygonDatabase?: DatabaseClient<WOFDatabase>
92
94
  }
93
95
 
94
96
  export interface ReverseGeocodeOpts {
@@ -159,9 +161,9 @@ function toPlaceCandidate(row: CandidateRow, distanceKm?: number): PlaceCandidat
159
161
  }
160
162
 
161
163
  export class WOFReverseGeocoder implements Disposable {
162
- readonly #admin: DatabaseSync
164
+ readonly #admin: DatabaseClient<WOFDatabase>
163
165
  readonly #ownsAdmin: boolean
164
- readonly #polygons: DatabaseSync | null
166
+ readonly #polygons: DatabaseClient<WOFDatabase> | null
165
167
  readonly #ownsPolygons: boolean
166
168
  /**
167
169
  * Parsed-geometry cache. Reverse queries cluster geographically (an eval run hits the same ~15 county polygons 1400
@@ -169,7 +171,7 @@ export class WOFReverseGeocoder implements Disposable {
169
171
  * LRU-tracked; the polygons are DP-simplified and small, the cap exists only to keep a long-lived server process
170
172
  * honest.
171
173
  */
172
- readonly #geometryCache = new Map<number, GeojsonGeometry | null>()
174
+ readonly #geometryCache = new Map<number, ParsedGeometry | null>()
173
175
  static readonly #GEOMETRY_CACHE_CAP = 4096
174
176
 
175
177
  constructor(opts: WOFReverseGeocoderOpts) {
@@ -185,38 +187,29 @@ export class WOFReverseGeocoder implements Disposable {
185
187
  throw new Error("WOFReverseGeocoder: pass either `polygonDatabase` or `polygonDBPath`, not both")
186
188
  }
187
189
 
188
- this.#admin = opts.adminDatabase ?? new DatabaseSync(opts.adminDBPath!, { readOnly: true })
190
+ this.#admin = opts.adminDatabase ?? new DatabaseClient<WOFDatabase>(opts.adminDBPath!, { readOnly: true })
189
191
  this.#ownsAdmin = !opts.adminDatabase
190
192
 
191
193
  this.#polygons =
192
- opts.polygonDatabase ?? (opts.polygonDBPath ? new DatabaseSync(opts.polygonDBPath, { readOnly: true }) : null)
194
+ opts.polygonDatabase ??
195
+ (opts.polygonDBPath ? new DatabaseClient<WOFDatabase>(opts.polygonDBPath, { readOnly: true }) : null)
193
196
 
194
197
  this.#ownsPolygons = !opts.polygonDatabase && Boolean(opts.polygonDBPath)
195
198
 
196
199
  // Fail loudly up front — the R*Tree is a build artifact, not part of the upstream WOF
197
200
  // distribution, and a missing index would otherwise surface as an opaque SQL error per query.
198
- const hasBbox = this.#admin
199
- .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`)
200
- .get(PLACE_BBOX_TABLE)
201
-
202
- if (!hasBbox) {
201
+ if (!tableExists(this.#admin, PLACE_BBOX_TABLE)) {
203
202
  throw new Error(
204
203
  `WOFReverseGeocoder: the admin DB has no \`${PLACE_BBOX_TABLE}\` R*Tree. Build it with ` +
205
204
  "`mailwoman gazetteer build fts <path-to-wof.db>` (see resolver-wof-sqlite/README.md)."
206
205
  )
207
206
  }
208
207
 
209
- if (this.#polygons) {
210
- const hasPolygons = this.#polygons
211
- .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'polygons'`)
212
- .get()
213
-
214
- if (!hasPolygons) {
215
- throw new Error(
216
- "WOFReverseGeocoder: the polygon DB has no `polygons` table. Expected a `wof-polygons.db` " +
217
- "built by scripts/build-wof-polygons.mjs."
218
- )
219
- }
208
+ if (this.#polygons && !tableExists(this.#polygons, "polygons")) {
209
+ throw new Error(
210
+ "WOFReverseGeocoder: the polygon DB has no `polygons` table. Expected a `wof-polygons.db` " +
211
+ "built by scripts/build-wof-polygons.mjs."
212
+ )
220
213
  }
221
214
  }
222
215
 
@@ -389,16 +382,17 @@ export class WOFReverseGeocoder implements Disposable {
389
382
 
390
383
  params.push(opts.maxCandidates ?? DEFAULT_MAX_CANDIDATES)
391
384
 
392
- return this.#admin
393
- .prepare(
385
+ return allRows<CandidateRow>(
386
+ this.#admin.prepare(
394
387
  `SELECT spr.id AS id, spr.name AS name, spr.placetype AS placetype, spr.country AS country,
395
388
  spr.parent_id AS parent_id, spr.latitude AS lat, spr.longitude AS lon
396
389
  FROM ${PLACE_BBOX_TABLE} bbox JOIN spr ON spr.id = bbox.id
397
390
  WHERE ${where.join(" AND ")}
398
391
  ORDER BY (bbox.max_lat - bbox.min_lat) * (bbox.max_lon - bbox.min_lon) ASC
399
392
  LIMIT ?`
400
- )
401
- .all(...params) as unknown as CandidateRow[]
393
+ ),
394
+ ...params
395
+ )
402
396
  }
403
397
 
404
398
  /**
@@ -413,30 +407,29 @@ export class WOFReverseGeocoder implements Disposable {
413
407
  lon: number,
414
408
  maxApproximateKm: number
415
409
  ): CandidateRow[] {
416
- const windowDeg = (maxApproximateKm * 4) / 111
410
+ const window = bboxAround(lat, lon, maxApproximateKm * 4)
417
411
 
418
- return this.#admin
419
- .prepare(
412
+ return allRows<CandidateRow>(
413
+ this.#admin.prepare(
420
414
  `SELECT s.id AS id, s.name AS name, s.placetype AS placetype, s.country AS country,
421
415
  s.parent_id AS parent_id, s.latitude AS lat, s.longitude AS lon
422
416
  FROM ancestors a JOIN spr s ON s.id = a.id
423
417
  WHERE a.ancestor_id = ? AND s.placetype = ? AND s.is_current != 0 AND s.is_deprecated = 0
424
418
  AND s.latitude BETWEEN ? AND ? AND s.longitude BETWEEN ? AND ?`
425
- )
426
- .all(
427
- parentID,
428
- placetype,
429
- lat - windowDeg,
430
- lat + windowDeg,
431
- lon - windowDeg,
432
- lon + windowDeg
433
- ) as unknown as CandidateRow[]
419
+ ),
420
+ parentID,
421
+ placetype,
422
+ window.minLat,
423
+ window.maxLat,
424
+ window.minLon,
425
+ window.maxLon
426
+ )
434
427
  }
435
428
 
436
429
  /**
437
430
  * Parsed GeoJSON geometry for a WOF id, or null when absent / unparseable / no polygon DB.
438
431
  */
439
- #geometry(id: number): GeojsonGeometry | null {
432
+ #geometry(id: number): ParsedGeometry | null {
440
433
  if (!this.#polygons) return null
441
434
  const cached = this.#geometryCache.get(id)
442
435
 
@@ -448,24 +441,20 @@ export class WOFReverseGeocoder implements Disposable {
448
441
 
449
442
  const row = this.#polygons.prepare(`SELECT geom FROM polygons WHERE id = ?`).get(id) as { geom: string } | undefined
450
443
  // Malformed row parses to null — treat as no-polygon rather than failing the query.
451
- const geometry = row ? tryParsingJSON<GeojsonGeometry>(row.geom) : null
444
+ const geometry = row ? tryParsingJSON<ParsedGeometry>(row.geom) : null
452
445
 
453
446
  this.#geometryCache.set(id, geometry)
454
447
 
455
448
  return geometry
456
449
  }
457
450
 
458
- close(): void {
451
+ [Symbol.dispose](): void {
459
452
  if (this.#ownsAdmin) {
460
- this.#admin.close()
453
+ this.#admin.destroy()
461
454
  }
462
455
 
463
456
  if (this.#ownsPolygons) {
464
- this.#polygons?.close()
457
+ this.#polygons?.destroy()
465
458
  }
466
459
  }
467
-
468
- [Symbol.dispose](): void {
469
- this.close()
470
- }
471
460
  }
@@ -159,11 +159,21 @@ export interface CoincidentRolesTable {
159
159
 
160
160
  /**
161
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
162
+ * will fail type-checked queries — by design. The reader ({@link WOFSQLitePlaceLookup}) already consumes this; the
163
163
  * build/augment WRITERS adopt it so a column rename is a compile error on both sides (the drift that bit the corpus
164
164
  * TIGER adapter).
165
165
  */
166
+ /**
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`.
169
+ */
170
+ export interface ExtractMetaTable {
171
+ key: string
172
+ value: string | null
173
+ }
174
+
166
175
  export interface WOFDatabase {
176
+ meta: ExtractMetaTable
167
177
  place_search: PlaceSearchTable
168
178
  spr: SprTable
169
179
  names: NamesTable