@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
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * SQLite implementation of core's `AddressPointLookup` (#476): exact `(street, number)` within a
7
+ * postcode (preferred), locality, or — for extracts whose points carry no scope tag (OSM, #247) —
8
+ * the resolved locality's BBOX. Query-side normalization is THE shared normalizer
9
+ * (`street-normalize.ts`), selected per the extract's `streetLocale` so build-side and probe-side
10
+ * stay identical by construction (US delegates to the USPS pipeline; FR/DE/NL use the locale rules).
11
+ *
12
+ * Matching is exact-after-normalization only — no fuzzy street matching in this tier (measure how
13
+ * far exact gets first; fuzz is a later, separate decision). Scope order is most-selective first:
14
+ * postcode, then locality, then the bbox fall-through (only when a bbox is supplied AND the prior
15
+ * scopes missed). Multiple hits return the first by rowid — unit siblings share the building coord.
16
+ */
17
+
18
+ import type { AddressPointHit, AddressPointLookup } from "@mailwoman/resolver"
19
+ import { DatabaseClient } from "@mailwoman/sqlite/client"
20
+
21
+ import type { AddressPointDatabase, AddressPointTable } from "#address/point-schema"
22
+ import { hasTable, prepareGet, type PreparedGet } from "#sqlite-utils"
23
+ import {
24
+ normalizeLocalityForKey,
25
+ type NameKey,
26
+ type StreetKey,
27
+ streetKeyVariants,
28
+ streetLocaleForSurface,
29
+ stripArrondissement,
30
+ type StreetLocale,
31
+ } from "#street/normalize"
32
+
33
+ /**
34
+ * The columns this lookup projects — a typed slice of the SHARED {@link AddressPointTable}, so a column rename in
35
+ * `build-address-point-extract.ts` (the writer) is a compile error here (the reader).
36
+ */
37
+ type AddressPointRow = Pick<AddressPointTable, "lat" | "lon" | "source" | "release" | "locality_norm" | "postcode">
38
+
39
+ /**
40
+ * The 4 columns the reader SELECTs, in the schema's order — referenced by the prepared SELECTs so the projected
41
+ * `AddressPointRow` stays in lockstep with the shared schema.
42
+ */
43
+ const SELECT_COLS = "lat, lon, source, release, locality_norm, postcode"
44
+
45
+ export class AddressPointSqliteLookup<DB extends AddressPointDatabase = AddressPointDatabase>
46
+ implements AddressPointLookup, Disposable
47
+ {
48
+ readonly #db: DatabaseClient<DB>
49
+ readonly #locale: StreetLocale
50
+ readonly #localityKeys: "full" | "abbreviated"
51
+ readonly #byPostcode: PreparedGet<[postcode: string, street: StreetKey, number: string], AddressPointRow> | undefined
52
+ readonly #byPostcodeLocality:
53
+ | PreparedGet<[postcode: string, locality: NameKey, street: StreetKey, number: string], AddressPointRow>
54
+ | undefined
55
+ readonly #byLocality: PreparedGet<[locality: NameKey, street: StreetKey, number: string], AddressPointRow> | undefined
56
+ readonly #byBbox:
57
+ | PreparedGet<
58
+ [street: StreetKey, number: string, minLat: number, maxLat: number, minLon: number, maxLon: number],
59
+ AddressPointRow
60
+ >
61
+ | undefined
62
+
63
+ /**
64
+ * @param dbPath Extract path.
65
+ * @param opts.streetLocale The street-normalization locale this extract was BUILT with — must match, or every key
66
+ * misses. Defaults to `"us"` (the situs tier), so existing callers are unchanged.
67
+ * @param opts.localityKeys Whether the extract's `locality_norm` is a FULL place name a query can be held to. The BAN
68
+ * and OSM extracts write the commune or `addr:city` in full; the US situs extract writes the NAD city field, which
69
+ * several counties abbreviate (`addi` for Addison on 5,174 Texas rows, 327,264 Texas rows at four characters or
70
+ * fewer) or give as the parent town (`easton` for North Easton). A key like that can steer WHICH row answers but
71
+ * cannot refuse one, so it never contradicts. Defaults from the street locale: `"us"` is abbreviated, the rest
72
+ * full.
73
+ */
74
+ constructor(dbPath: string, opts: { streetLocale?: StreetLocale; localityKeys?: "full" | "abbreviated" } = {}) {
75
+ this.#db = new DatabaseClient<DB>(dbPath, { readOnly: true })
76
+ this.#locale = opts.streetLocale ?? "us"
77
+ this.#localityKeys = opts.localityKeys ?? (this.#locale === "us" ? "abbreviated" : "full")
78
+
79
+ // Degrade gracefully on an empty/tableless extract (interrupted build, stray 0-byte file): with no
80
+ // `address_point` table this lookup is a no-op miss, not a crash that loses the whole state (#568).
81
+ if (hasTable(this.#db, "address_point")) {
82
+ this.#byPostcode = prepareGet(
83
+ this.#db,
84
+ `SELECT ${SELECT_COLS} FROM address_point
85
+ WHERE postcode = ? AND street_norm = ? AND number = ? LIMIT 1`
86
+ )
87
+
88
+ this.#byPostcodeLocality = prepareGet(
89
+ this.#db,
90
+ `SELECT ${SELECT_COLS} FROM address_point
91
+ WHERE postcode = ? AND locality_norm = ? AND street_norm = ? AND number = ? LIMIT 1`
92
+ )
93
+
94
+ this.#byLocality = prepareGet(
95
+ this.#db,
96
+ `SELECT ${SELECT_COLS} FROM address_point
97
+ WHERE locality_norm = ? AND street_norm = ? AND number = ? LIMIT 1`
98
+ )
99
+
100
+ this.#byBbox = prepareGet(
101
+ this.#db,
102
+ `SELECT ${SELECT_COLS} FROM address_point
103
+ WHERE street_norm = ? AND number = ? AND lat BETWEEN ? AND ? AND lon BETWEEN ? AND ? LIMIT 1`
104
+ )
105
+ }
106
+ }
107
+
108
+ find(query: {
109
+ street: string
110
+ number: string
111
+ postcode?: string
112
+ locality?: string
113
+ bbox?: { minLat: number; maxLat: number; minLon: number; maxLon: number }
114
+ }): AddressPointHit | null {
115
+ if (!this.#byPostcode || !this.#byLocality || !this.#byBbox) return null
116
+ const number = query.number.trim().toLowerCase()
117
+
118
+ if (!number) return null
119
+
120
+ // Key-variant ladder (see `streetKeyVariants`): the literal key first, then the doubled-type
121
+ // collapse and the saint↔st register swap — each variant runs the FULL number ladder below, and
122
+ // the first variant to answer wins, so an attested literal key is never second-guessed.
123
+ let row: AddressPointRow | undefined
124
+
125
+ for (const streetNorm of streetKeyVariants(query.street, streetLocaleForSurface(query.street, this.#locale))) {
126
+ row = this.#findForKey(streetNorm, number, query)
127
+
128
+ if (row) break
129
+ }
130
+
131
+ if (!row) return null
132
+
133
+ return {
134
+ lat: row.lat,
135
+ lon: row.lon,
136
+ source: row.source,
137
+ release: row.release,
138
+ ...(row.locality_norm ? { localityNorm: row.locality_norm } : {}),
139
+ ...(row.postcode ? { postcode: row.postcode } : {}),
140
+ }
141
+ }
142
+
143
+ /**
144
+ * The full number ladder for ONE normalized street key: exact, then the range low-end, then letter-suffix spacing
145
+ * with the base-number fall — see each rung's note in place.
146
+ */
147
+ #findForKey(
148
+ streetNorm: StreetKey,
149
+ number: string,
150
+ query: {
151
+ street: string
152
+ number: string
153
+ postcode?: string
154
+ locality?: string
155
+ bbox?: { minLat: number; maxLat: number; minLon: number; maxLon: number }
156
+ }
157
+ ) {
158
+ let row = this.#probe(streetNorm, number, query)
159
+
160
+ // Range-surface fallback: every register this reader serves stores ONE number per point
161
+ // (G-NAF `NUMBER_FIRST`, BAN, OA, OSM `addr:housenumber`), but the attested surface is often
162
+ // a range — "385-387 Esplanade" keys `385`. Null-only: an exact range key that matched above
163
+ // (some OSM points DO carry "385-387" verbatim) is never second-guessed.
164
+ if (!row) {
165
+ const low = /^(\d+[a-z]?)-\d+[a-z]?$/.exec(number)?.[1]
166
+
167
+ if (low) {
168
+ row = this.#probe(streetNorm, low, query)
169
+ }
170
+ }
171
+
172
+ // Letter-suffix spacing fallback: the registers disagree on the joint — BAN stores "3 a"
173
+ // (space-separated), G-NAF and most OA sources store "3a" — and the parsed surface can arrive
174
+ // either way. On a miss, retry the OTHER spacing; on a double miss, the BASE number (the
175
+ // register attests no 3A but does attest 3 — the adjacent-parcel approximation, priced the
176
+ // same as the range fallback's low end). Null-only throughout, and only for the
177
+ // digits+single-letter shape (never touches "12 1/2" or unit-bearing forms).
178
+ if (!row) {
179
+ const joined = /^(\d+)\s+([a-z])$/.exec(number)
180
+ const spaced = /^(\d+)([a-z])$/.exec(number)
181
+
182
+ if (joined) {
183
+ row = this.#probe(streetNorm, `${joined[1]}${joined[2]}`, query) ?? this.#probe(streetNorm, joined[1]!, query)
184
+ } else if (spaced) {
185
+ row = this.#probe(streetNorm, `${spaced[1]} ${spaced[2]}`, query) ?? this.#probe(streetNorm, spaced[1]!, query)
186
+ }
187
+ }
188
+
189
+ return row
190
+ }
191
+
192
+ /**
193
+ * The scope ladder for one (street, number) key: postcode, then locality, then the bbox fall-through — each rung only
194
+ * when the prior missed.
195
+ */
196
+ #probe(
197
+ streetNorm: StreetKey,
198
+ number: string,
199
+ query: {
200
+ postcode?: string
201
+ locality?: string
202
+ bbox?: { minLat: number; maxLat: number; minLon: number; maxLon: number }
203
+ }
204
+ ): AddressPointRow | undefined {
205
+ let row: AddressPointRow | undefined
206
+
207
+ if (query.postcode) {
208
+ const postcode = query.postcode.trim()
209
+ const localityKey = query.locality ? this.#localityKey(query.locality) : undefined
210
+
211
+ // A postcode can span several places — DE 04509 covers Schönwölkau and Werlitzsch, both with a Teichstraße 3 —
212
+ // so when the query names a locality the row whose own locality agrees is asked for FIRST. Only when no such
213
+ // row exists does the postcode-only row answer, and then only if its locality does not name a different place
214
+ // (see `#scopeContradicts` for what "different" tolerates): a query naming a third village under the postcode
215
+ // falls through to the locality rung rather than answering the wrong rooftop.
216
+ const agreeing = localityKey ? this.#byPostcodeLocality!(postcode, localityKey, streetNorm, number) : undefined
217
+ const candidate = agreeing ?? this.#byPostcode!(postcode, streetNorm, number)
218
+
219
+ row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : undefined
220
+ }
221
+
222
+ if (!row && query.locality) {
223
+ // FR extracts key arrondissement communes at the base city (both-sides fold, see the BAN
224
+ // builder + stripArrondissement) — fold the probe too so "Paris 13e Arrondissement" and
225
+ // "Paris" both hit. No-op for "us" extracts and every non-arrondissement commune.
226
+ row = this.#byLocality!(this.#localityKey(query.locality), streetNorm, number)
227
+ }
228
+
229
+ // Bbox fall-through (#247): the point carries no postcode/locality of its own, but its coordinate falls
230
+ // inside the resolved locality's box. Only reached when the scoped probes missed AND a bbox was supplied.
231
+ if (!row && query.bbox) {
232
+ const b = query.bbox
233
+ const candidate = this.#byBbox!(streetNorm, number, b.minLat, b.maxLat, b.minLon, b.maxLon)
234
+
235
+ // A register row that carries its own scope and was NOT found by the scoped rungs is a different address that
236
+ // happens to share the street and number inside the box: `10 rue de la République, 75008 Paris` reached
237
+ // Servon's `10 rue de la République` (postcode 77170) 26 km away this way, at rooftop tier and 1 m uncertainty.
238
+ // The rung exists for points with no scope of their own; a point whose scope disagrees with the query is a miss.
239
+ row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : undefined
240
+ }
241
+
242
+ return row
243
+ }
244
+
245
+ /**
246
+ * The query's locality folded the way the extract's builder folded its `locality_norm`. FR extracts key
247
+ * arrondissement communes at the base city (both-sides fold, see the BAN builder + stripArrondissement), so "Paris
248
+ * 13e Arrondissement" and "Paris" both hit; a no-op for every other locale.
249
+ */
250
+ #localityKey(locality: string): NameKey {
251
+ return this.#locale === "fr"
252
+ ? stripArrondissement(normalizeLocalityForKey(locality))
253
+ : normalizeLocalityForKey(locality)
254
+ }
255
+
256
+ /**
257
+ * Whether a row's OWN postcode or locality names a different place than the query did. Absent scope on the row is not
258
+ * a contradiction — it is the case the bbox rung was built for — and a rung that matched ON a field cannot contradict
259
+ * it, so at the postcode rung only the locality can disagree and at the bbox rung either can.
260
+ *
261
+ * The locality is consulted only on an extract whose keys are full names (the constructor's `localityKeys`). Under
262
+ * exact comparison against the US extract's abbreviated keys, the postcode rung refused `4900 Airport Pkwy, Addison
263
+ * TX 75001`'s own rooftop row (`addi`) and `678 Depot St, North Easton, MA 02356`'s (`easton`), and both `pass` board
264
+ * rows fell to interpolation 144–198 m away — invisible to a 1 km grade (#2155). `servon` against `paris` and
265
+ * `werlitzsch` against `krensitz`, on the BAN and OSM extracts, are different places and still refuse.
266
+ */
267
+ #scopeContradicts(row: AddressPointRow, query: { postcode?: string; locality?: string }): boolean {
268
+ if (query.postcode && row.postcode && row.postcode.trim() !== query.postcode.trim()) return true
269
+
270
+ if (this.#localityKeys === "full" && query.locality && row.locality_norm) {
271
+ return row.locality_norm !== this.#localityKey(query.locality)
272
+ }
273
+
274
+ return false
275
+ }
276
+
277
+ [Symbol.dispose](): void {
278
+ this.#db[Symbol.dispose]()
279
+ }
280
+ }
@@ -45,14 +45,12 @@
45
45
  * nothing.
46
46
  */
47
47
 
48
- import { readdirSync } from "node:fs"
49
- import type { DatabaseSync } from "node:sqlite"
50
-
51
- import { DatabaseClient } from "@mailwoman/core/kysley/client"
48
+ import { readDirectoryEntries } from "@mailwoman/core/fs/readers"
52
49
  import { readWOFFeature } from "@mailwoman/core/resources/whosonfirst"
53
- import { join } from "path-ts"
50
+ import type { DatabaseClient } from "@mailwoman/sqlite/client"
51
+ import { join, resolvePath, type PathBuilderLike } from "path-ts"
54
52
 
55
- import type { WOFDatabase } from "./schema.ts"
53
+ import type { WOFDatabase } from "#schema"
56
54
 
57
55
  /**
58
56
  * Genuinely top-level placetypes — they never have (or need) an ancestor, so skip them.
@@ -76,23 +74,21 @@ export interface AncestryBackfillResult {
76
74
  }
77
75
 
78
76
  /**
79
- * Discover the `data` directories under a WOF repos root that hold sharded geojson, e.g.
77
+ * Discover the `data` directories under a WOF repos root that hold attached geojson, e.g.
80
78
  * `<root>/whosonfirst-data/whosonfirst-data-admin-us/data`. Resolves an id to its geojson via these roots. Accepts both
81
79
  * the nested lab layout (a `whosonfirst-data` group dir holding the admin repos) and a flat layout (admin repos
82
80
  * directly under the root); searches at most two directory levels deep.
83
81
  */
84
- export function discoverAdminDataRoots(reposRoot: string): string[] {
82
+ export async function discoverAdminDataRoots(reposRoot: PathBuilderLike): Promise<string[]> {
85
83
  const roots: string[] = []
86
84
 
87
- const visit = (dir: string, depth: number): void => {
85
+ const visit = async (dir: string, depth: number): Promise<void> => {
88
86
  if (depth > 2) return
89
87
 
90
88
  let names: string[]
91
89
 
92
90
  try {
93
- names = readdirSync(dir, { withFileTypes: true })
94
- .filter((e) => e.isDirectory())
95
- .map((e) => e.name)
91
+ names = (await readDirectoryEntries(dir)).filter((e) => e.isDirectory()).map((e) => e.name)
96
92
  } catch {
97
93
  return
98
94
  }
@@ -103,12 +99,12 @@ export function discoverAdminDataRoots(reposRoot: string): string[] {
103
99
  if (name === "data") {
104
100
  roots.push(child)
105
101
  } else if (name.startsWith("whosonfirst-data")) {
106
- visit(child, depth + 1)
102
+ await visit(child, depth + 1)
107
103
  }
108
104
  }
109
105
  }
110
106
 
111
- visit(reposRoot, 0)
107
+ await visit(resolvePath(reposRoot), 0)
112
108
 
113
109
  return roots
114
110
  }
@@ -136,18 +132,17 @@ function placetypeFromKey(key: string): string | null {
136
132
  * skipped rows would have `noGeojson`-skipped anyway. Omit `maxID` (default) for the legacy WOF-only DBs.
137
133
  */
138
134
  export async function backfillAncestorsFromHierarchy(
139
- db: DatabaseSync,
135
+ db: DatabaseClient<WOFDatabase>,
140
136
  geojsonRoots: readonly string[],
141
137
  opts: { maxID?: number } = {}
142
138
  ): Promise<AncestryBackfillResult> {
143
139
  const maxID = opts.maxID ?? Number.MAX_SAFE_INTEGER
144
- const kdb = new DatabaseClient<WOFDatabase>({ database: db })
145
140
 
146
141
  // "No country-tier ancestor" is the dead-end signal at any depth — see the module docstring. The
147
142
  // earlier "<= 1 ancestor row" test only caught the dead end's origin, never the children that
148
143
  // inherit it (a child of a repaired -4 place has two rows: itself and that parent) (#1445).
149
144
  // The id bound is stated first so SQLite prunes by the PK index before the NOT EXISTS runs at all.
150
- const candidateBase = kdb
145
+ const candidateBase = db
151
146
  .selectFrom("spr")
152
147
  .where("id", "<", maxID)
153
148
  .where((eb) =>
@@ -172,7 +167,7 @@ export async function backfillAncestorsFromHierarchy(
172
167
  // (measured 2026-08-04).
173
168
  const alreadyPresent = new Map<number, Set<number>>()
174
169
 
175
- for (const row of await kdb
170
+ for (const row of await db
176
171
  .selectFrom("ancestors")
177
172
  .select(["id", "ancestor_id"])
178
173
  .where("id", "in", candidateBase.select("spr.id"))
@@ -198,7 +193,7 @@ export async function backfillAncestorsFromHierarchy(
198
193
 
199
194
  for (const { id, placetype } of candidates) {
200
195
  if (placetype && TOP_PLACETYPES.has(placetype)) continue
201
- const gj = readWOFFeature(id, geojsonRoots)
196
+ const gj = await readWOFFeature(id, geojsonRoots)
202
197
  const hierarchy = gj?.properties?.["wof:hierarchy"]
203
198
 
204
199
  if (!hierarchy || !hierarchy.length) {
@@ -7,7 +7,7 @@
7
7
  * joined with `spr` for canonical names + centroids, ordered NEAREST-FIRST (deepest placetype
8
8
  * first, country last).
9
9
  *
10
- * Factored out of `WOFSqlitePlaceLookup.ancestors()` (#404) so the reverse geocoder (`reverse.ts`,
10
+ * Factored out of `WOFSQLitePlaceLookup.ancestors()` (#404) so the reverse geocoder (`reverse.ts`,
11
11
  * #484) reuses the SAME walk instead of growing a second one. The placetype-specificity ordering
12
12
  * lives here as `PLACETYPE_DEPTH` — a single TS map instead of the previous SQL CASE, and
13
13
  * extended below `localadmin` (locality/borough/neighbourhood/microhood now rank correctly
@@ -15,7 +15,9 @@
15
15
  * geocoding always does).
16
16
  */
17
17
 
18
- import type { DatabaseSync } from "node:sqlite"
18
+ import type { DatabaseClient } from "@mailwoman/sqlite/client"
19
+
20
+ import { allRows } from "#sqlite-utils"
19
21
 
20
22
  /**
21
23
  * WOF placetype → containment depth, coarsest = 1. Higher = finer. Placetypes we never resolve (continent, empire, …)
@@ -57,19 +59,22 @@ export interface AncestorPlaceRow {
57
59
 
58
60
  /**
59
61
  * The ancestor lineage of `id` — self excluded, nearest-first. Returns `[]` when the place has no recorded ancestry.
60
- * NOT memoized here; `WOFSqlitePlaceLookup` keeps its own per-id cache.
62
+ * NOT memoized here; `WOFSQLitePlaceLookup` keeps its own per-id cache.
61
63
  */
62
- export function ancestorLineage(db: DatabaseSync, id: number, schemaName = "main"): AncestorPlaceRow[] {
63
- const rows = db
64
- .prepare(
64
+ export function ancestorLineage<DB>(db: DatabaseClient<DB>, id: number, schemaName = "main"): AncestorPlaceRow[] {
65
+ const rows = allRows<AncestorPlaceRow>(
66
+ db.prepare(
65
67
  `SELECT a.ancestor_id AS id, a.ancestor_placetype AS placetype, s.name AS name,
66
68
  s.country AS country, s.latitude AS lat, s.longitude AS lon
67
69
  FROM ${schemaName}.ancestors a JOIN ${schemaName}.spr s ON s.id = a.ancestor_id
68
70
  WHERE a.id = ? AND a.ancestor_id != a.id`
69
- )
70
- .all(id) as unknown as AncestorPlaceRow[]
71
+ ),
72
+ id
73
+ )
71
74
 
72
75
  rows.sort((a, b) => placetypeDepth(b.placetype) - placetypeDepth(a.placetype))
73
76
 
74
77
  return rows
75
78
  }
79
+
80
+ export * from "#ancestry/backfill"