@mailwoman/resolver-wof-sqlite 10.0.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/README.md +9 -8
  2. package/lib/address/point/interpolation.ts +45 -39
  3. package/lib/address/point/schema.ts +17 -40
  4. package/lib/address/point.ts +316 -0
  5. package/lib/{address/index.ts → address.ts} +1 -1
  6. package/lib/ancestry/backfill.ts +20 -86
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +122 -174
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +44 -114
  11. package/lib/candidate/alias-bags.ts +16 -17
  12. package/lib/candidate/ancestors/schema.ts +77 -61
  13. package/lib/candidate/ancestors/sidecar.ts +15 -22
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +10 -55
  16. package/lib/candidate/fts.ts +5 -23
  17. package/lib/candidate/importance.ts +65 -68
  18. package/lib/candidate/lookup.ts +124 -415
  19. package/lib/candidate/name-roles.ts +15 -33
  20. package/lib/candidate/own-name.ts +35 -32
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +29 -65
  23. package/lib/candidate/scoring.ts +54 -93
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +29 -27
  26. package/lib/coincident-roles.ts +85 -55
  27. package/lib/convention/schema.ts +15 -16
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +40 -45
  30. package/lib/currency-backfill.ts +34 -79
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +32 -21
  33. package/lib/extracts.ts +97 -74
  34. package/lib/fst/autocomplete.ts +54 -37
  35. package/lib/fst/builder.ts +37 -41
  36. package/lib/fst/deserialize-web.ts +16 -17
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +43 -120
  39. package/lib/fst/matcher.ts +7 -5
  40. package/lib/fst/serialize.ts +54 -74
  41. package/lib/fst/types.ts +78 -75
  42. package/lib/{fst/index.ts → fst.ts} +1 -0
  43. package/lib/fts/query.ts +5 -36
  44. package/lib/fts.ts +280 -0
  45. package/lib/geonames/aliases.ts +73 -144
  46. package/lib/geonames/postal.ts +162 -71
  47. package/lib/index.ts +9 -8
  48. package/lib/interpolation.ts +48 -100
  49. package/lib/lookup.ts +155 -270
  50. package/lib/name-score.ts +9 -7
  51. package/lib/nsul/lookup.ts +37 -54
  52. package/lib/nsul/schema.ts +16 -43
  53. package/lib/paths.ts +159 -0
  54. package/lib/place-importance-schema.ts +62 -169
  55. package/lib/poi/lookup.ts +104 -121
  56. package/lib/poi/schema.ts +42 -26
  57. package/lib/polygon-schema.ts +10 -6
  58. package/lib/postal/city/alias/lookup.ts +22 -40
  59. package/lib/postal/city/alias/schema.ts +14 -10
  60. package/lib/postal/city/candidate-schema.ts +26 -20
  61. package/lib/postcode-point-lookup.ts +8 -27
  62. package/lib/primary-preference.ts +46 -126
  63. package/lib/proximity-rerank.ts +44 -33
  64. package/lib/ranking-weights.ts +105 -78
  65. package/lib/region-keys.ts +70 -42
  66. package/lib/reverse.ts +149 -86
  67. package/lib/schema.ts +73 -50
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +15 -23
  70. package/lib/sqlite/utils.ts +19 -22
  71. package/lib/street/centroid/schema.ts +37 -42
  72. package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
  73. package/lib/street/morphology/fst/builder.ts +60 -42
  74. package/lib/street/morphology/fst/loader.ts +40 -32
  75. package/lib/street/name-lookup.ts +45 -41
  76. package/lib/street/normalize.ts +88 -219
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +1 -1
  79. package/lib/types.ts +109 -150
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +29 -68
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +236 -252
  85. package/out/address/point/interpolation.d.ts +20 -23
  86. package/out/address/point/interpolation.d.ts.map +1 -1
  87. package/out/address/point/interpolation.js +42 -39
  88. package/out/address/point/interpolation.js.map +1 -1
  89. package/out/address/point/schema.d.ts +12 -35
  90. package/out/address/point/schema.d.ts.map +1 -1
  91. package/out/address/point/schema.js +3 -19
  92. package/out/address/point/schema.js.map +1 -1
  93. package/out/address/point.d.ts +57 -0
  94. package/out/address/point.d.ts.map +1 -0
  95. package/out/address/point.js +206 -0
  96. package/out/address/point.js.map +1 -0
  97. package/out/{address/index.js → address.d.ts} +2 -2
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +2 -2
  100. package/out/address.js.map +1 -0
  101. package/out/ancestry/backfill.d.ts +11 -64
  102. package/out/ancestry/backfill.d.ts.map +1 -1
  103. package/out/ancestry/backfill.js +14 -74
  104. package/out/ancestry/backfill.js.map +1 -1
  105. package/out/ancestry.d.ts +49 -0
  106. package/out/ancestry.d.ts.map +1 -0
  107. package/out/ancestry.js +59 -0
  108. package/out/ancestry.js.map +1 -0
  109. package/out/build/candidate.d.ts +82 -112
  110. package/out/build/candidate.d.ts.map +1 -1
  111. package/out/build/candidate.js +17 -100
  112. package/out/build/candidate.js.map +1 -1
  113. package/out/build/population-corrections.d.ts +52 -0
  114. package/out/build/population-corrections.d.ts.map +1 -0
  115. package/out/build/population-corrections.js +40 -0
  116. package/out/build/population-corrections.js.map +1 -0
  117. package/out/build/slim.d.ts +8 -44
  118. package/out/build/slim.d.ts.map +1 -1
  119. package/out/build/slim.js +33 -94
  120. package/out/build/slim.js.map +1 -1
  121. package/out/candidate/alias-bags.d.ts +6 -6
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +16 -17
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +73 -57
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -1
  127. package/out/candidate/ancestors/schema.js +56 -48
  128. package/out/candidate/ancestors/schema.js.map +1 -1
  129. package/out/candidate/ancestors/sidecar.d.ts +3 -8
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
  131. package/out/candidate/ancestors/sidecar.js +15 -22
  132. package/out/candidate/ancestors/sidecar.js.map +1 -1
  133. package/out/candidate/country-display-names.d.ts +5 -15
  134. package/out/candidate/country-display-names.d.ts.map +1 -1
  135. package/out/candidate/country-display-names.js +6 -20
  136. package/out/candidate/country-display-names.js.map +1 -1
  137. package/out/candidate/extract-fold.d.ts +7 -15
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +5 -47
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +5 -23
  142. package/out/candidate/fts.d.ts.map +1 -1
  143. package/out/candidate/fts.js +5 -23
  144. package/out/candidate/fts.js.map +1 -1
  145. package/out/candidate/importance.d.ts +60 -65
  146. package/out/candidate/importance.d.ts.map +1 -1
  147. package/out/candidate/importance.js +56 -63
  148. package/out/candidate/importance.js.map +1 -1
  149. package/out/candidate/lookup.d.ts +18 -55
  150. package/out/candidate/lookup.d.ts.map +1 -1
  151. package/out/candidate/lookup.js +82 -385
  152. package/out/candidate/lookup.js.map +1 -1
  153. package/out/candidate/name-roles.d.ts +9 -21
  154. package/out/candidate/name-roles.d.ts.map +1 -1
  155. package/out/candidate/name-roles.js +15 -33
  156. package/out/candidate/name-roles.js.map +1 -1
  157. package/out/candidate/own-name.d.ts +23 -25
  158. package/out/candidate/own-name.d.ts.map +1 -1
  159. package/out/candidate/own-name.js +35 -32
  160. package/out/candidate/own-name.js.map +1 -1
  161. package/out/candidate/place-attrs.d.ts +20 -13
  162. package/out/candidate/place-attrs.d.ts.map +1 -1
  163. package/out/candidate/place-attrs.js +4 -5
  164. package/out/candidate/place-attrs.js.map +1 -1
  165. package/out/candidate/schema.d.ts +27 -62
  166. package/out/candidate/schema.d.ts.map +1 -1
  167. package/out/candidate/schema.js +10 -22
  168. package/out/candidate/schema.js.map +1 -1
  169. package/out/candidate/scoring.d.ts +7 -8
  170. package/out/candidate/scoring.d.ts.map +1 -1
  171. package/out/candidate/scoring.js +52 -89
  172. package/out/candidate/scoring.js.map +1 -1
  173. package/out/capital-schema.d.ts +21 -17
  174. package/out/capital-schema.d.ts.map +1 -1
  175. package/out/capital-schema.js +17 -15
  176. package/out/capital-schema.js.map +1 -1
  177. package/out/capitals.d.ts +27 -25
  178. package/out/capitals.d.ts.map +1 -1
  179. package/out/capitals.js +23 -21
  180. package/out/capitals.js.map +1 -1
  181. package/out/coincident-roles.d.ts +60 -43
  182. package/out/coincident-roles.d.ts.map +1 -1
  183. package/out/coincident-roles.js +72 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +14 -14
  186. package/out/convention/schema.d.ts.map +1 -1
  187. package/out/convention/schema.js +6 -4
  188. package/out/convention/schema.js.map +1 -1
  189. package/out/convention.d.ts +97 -0
  190. package/out/convention.d.ts.map +1 -0
  191. package/out/convention.js +81 -0
  192. package/out/convention.js.map +1 -0
  193. package/out/coverage-manifest-schema.d.ts +34 -39
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +25 -34
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +16 -38
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +19 -46
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +1 -1
  202. package/out/env.js +1 -1
  203. package/out/exact-match.d.ts +19 -10
  204. package/out/exact-match.d.ts.map +1 -1
  205. package/out/exact-match.js +32 -21
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +66 -55
  208. package/out/extracts.d.ts.map +1 -1
  209. package/out/extracts.js +63 -55
  210. package/out/extracts.js.map +1 -1
  211. package/out/fst/autocomplete.d.ts +29 -21
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +39 -28
  214. package/out/fst/autocomplete.js.map +1 -1
  215. package/out/fst/builder.d.ts.map +1 -1
  216. package/out/fst/builder.js +37 -41
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +4 -4
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +15 -16
  221. package/out/fst/deserialize-web.js.map +1 -1
  222. package/out/fst/format.d.ts +41 -23
  223. package/out/fst/format.d.ts.map +1 -1
  224. package/out/fst/format.js +41 -23
  225. package/out/fst/format.js.map +1 -1
  226. package/out/fst/freshness.d.ts +19 -81
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +31 -94
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +5 -4
  231. package/out/fst/matcher.d.ts.map +1 -1
  232. package/out/fst/matcher.js +2 -2
  233. package/out/fst/matcher.js.map +1 -1
  234. package/out/fst/serialize.d.ts +28 -28
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +53 -71
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +78 -74
  239. package/out/fst/types.d.ts.map +1 -1
  240. package/out/fst/types.js +1 -4
  241. package/out/fst/types.js.map +1 -1
  242. package/out/{fst/index.js → fst.d.ts} +2 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +2 -1
  245. package/out/fst.js.map +1 -0
  246. package/out/fts/query.d.ts +2 -27
  247. package/out/fts/query.d.ts.map +1 -1
  248. package/out/fts/query.js +5 -36
  249. package/out/fts/query.js.map +1 -1
  250. package/out/fts.d.ts +126 -0
  251. package/out/fts.d.ts.map +1 -0
  252. package/out/fts.js +188 -0
  253. package/out/fts.js.map +1 -0
  254. package/out/geonames/aliases.d.ts +26 -62
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +45 -117
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -44
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -73
  261. package/out/geonames/postal.js.map +1 -1
  262. package/out/{geonames/index.js → geonames.d.ts} +1 -1
  263. package/out/geonames.d.ts.map +1 -0
  264. package/out/{geonames/index.d.ts → geonames.js} +1 -1
  265. package/out/geonames.js.map +1 -0
  266. package/out/index.d.ts +9 -8
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +6 -5
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +18 -55
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +27 -69
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +23 -51
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +136 -234
  277. package/out/lookup.js.map +1 -1
  278. package/out/name-score.d.ts +2 -2
  279. package/out/name-score.d.ts.map +1 -1
  280. package/out/name-score.js +9 -7
  281. package/out/name-score.js.map +1 -1
  282. package/out/nsul/lookup.d.ts +33 -34
  283. package/out/nsul/lookup.d.ts.map +1 -1
  284. package/out/nsul/lookup.js +26 -40
  285. package/out/nsul/lookup.js.map +1 -1
  286. package/out/nsul/schema.d.ts +16 -43
  287. package/out/nsul/schema.d.ts.map +1 -1
  288. package/out/nsul/schema.js +9 -35
  289. package/out/nsul/schema.js.map +1 -1
  290. package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
  291. package/out/nsul.d.ts.map +1 -0
  292. package/out/{nsul/index.js → nsul.js} +1 -1
  293. package/out/nsul.js.map +1 -0
  294. package/out/paths.d.ts +124 -0
  295. package/out/paths.d.ts.map +1 -0
  296. package/out/paths.js +104 -0
  297. package/out/paths.js.map +1 -0
  298. package/out/place-importance-schema.d.ts +51 -143
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +28 -143
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +40 -47
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +69 -96
  305. package/out/poi/lookup.js.map +1 -1
  306. package/out/poi/schema.d.ts +41 -25
  307. package/out/poi/schema.d.ts.map +1 -1
  308. package/out/poi/schema.js +25 -17
  309. package/out/poi/schema.js.map +1 -1
  310. package/out/{poi/index.js → poi.d.ts} +1 -1
  311. package/out/poi.d.ts.map +1 -0
  312. package/out/{poi/index.d.ts → poi.js} +1 -1
  313. package/out/poi.js.map +1 -0
  314. package/out/polygon-schema.d.ts +10 -6
  315. package/out/polygon-schema.d.ts.map +1 -1
  316. package/out/polygon-schema.js +2 -2
  317. package/out/polygon-schema.js.map +1 -1
  318. package/out/postal/city/alias/lookup.d.ts +20 -23
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -1
  320. package/out/postal/city/alias/lookup.js +17 -30
  321. package/out/postal/city/alias/lookup.js.map +1 -1
  322. package/out/postal/city/alias/schema.d.ts +14 -10
  323. package/out/postal/city/alias/schema.d.ts.map +1 -1
  324. package/out/postal/city/alias/schema.js +10 -8
  325. package/out/postal/city/alias/schema.js.map +1 -1
  326. package/out/postal/city/candidate-schema.d.ts +25 -19
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -1
  328. package/out/postal/city/candidate-schema.js +21 -17
  329. package/out/postal/city/candidate-schema.js.map +1 -1
  330. package/out/{postal/index.d.ts → postal.d.ts} +1 -1
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/{postal/index.js → postal.js} +1 -1
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +6 -24
  335. package/out/postcode-point-lookup.d.ts.map +1 -1
  336. package/out/postcode-point-lookup.js +5 -17
  337. package/out/postcode-point-lookup.js.map +1 -1
  338. package/out/primary-preference.d.ts +38 -92
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +15 -99
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +41 -30
  343. package/out/proximity-rerank.d.ts.map +1 -1
  344. package/out/proximity-rerank.js +38 -29
  345. package/out/proximity-rerank.js.map +1 -1
  346. package/out/ranking-weights.d.ts +98 -64
  347. package/out/ranking-weights.d.ts.map +1 -1
  348. package/out/ranking-weights.js +26 -25
  349. package/out/ranking-weights.js.map +1 -1
  350. package/out/region-keys.d.ts +50 -31
  351. package/out/region-keys.d.ts.map +1 -1
  352. package/out/region-keys.js +68 -40
  353. package/out/region-keys.js.map +1 -1
  354. package/out/reverse.d.ts +78 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +98 -61
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +72 -49
  359. package/out/schema.d.ts.map +1 -1
  360. package/out/schema.js +4 -5
  361. package/out/schema.js.map +1 -1
  362. package/out/search-fetch.d.ts +16 -12
  363. package/out/search-fetch.d.ts.map +1 -1
  364. package/out/search-fetch.js +83 -63
  365. package/out/search-fetch.js.map +1 -1
  366. package/out/sqlite/convention-source.d.ts +12 -16
  367. package/out/sqlite/convention-source.d.ts.map +1 -1
  368. package/out/sqlite/convention-source.js +13 -22
  369. package/out/sqlite/convention-source.js.map +1 -1
  370. package/out/sqlite/utils.d.ts +19 -19
  371. package/out/sqlite/utils.d.ts.map +1 -1
  372. package/out/sqlite/utils.js +14 -15
  373. package/out/sqlite/utils.js.map +1 -1
  374. package/out/street/centroid/schema.d.ts +37 -42
  375. package/out/street/centroid/schema.d.ts.map +1 -1
  376. package/out/street/centroid/schema.js +9 -21
  377. package/out/street/centroid/schema.js.map +1 -1
  378. package/out/street/centroid.d.ts +30 -0
  379. package/out/street/centroid.d.ts.map +1 -0
  380. package/out/street/{centroid/index.js → centroid.js} +13 -24
  381. package/out/street/centroid.js.map +1 -0
  382. package/out/street/morphology/fst/builder.d.ts +20 -10
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -1
  384. package/out/street/morphology/fst/builder.js +44 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -1
  386. package/out/street/morphology/fst/loader.d.ts +33 -26
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -1
  388. package/out/street/morphology/fst/loader.js +23 -22
  389. package/out/street/morphology/fst/loader.js.map +1 -1
  390. package/out/street/name-lookup.d.ts +24 -18
  391. package/out/street/name-lookup.d.ts.map +1 -1
  392. package/out/street/name-lookup.js +34 -35
  393. package/out/street/name-lookup.js.map +1 -1
  394. package/out/street/normalize.d.ts +35 -136
  395. package/out/street/normalize.d.ts.map +1 -1
  396. package/out/street/normalize.js +77 -194
  397. package/out/street/normalize.js.map +1 -1
  398. package/out/street/segment-schema.d.ts +79 -39
  399. package/out/street/segment-schema.d.ts.map +1 -1
  400. package/out/street/segment-schema.js +71 -21
  401. package/out/street/segment-schema.js.map +1 -1
  402. package/out/{street/index.js → street.d.ts} +2 -2
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.d.ts → street.js} +2 -2
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +84 -149
  407. package/out/types.d.ts.map +1 -1
  408. package/out/types.js +0 -6
  409. package/out/types.js.map +1 -1
  410. package/out/unified-schema.d.ts +8 -12
  411. package/out/unified-schema.d.ts.map +1 -1
  412. package/out/unified-schema.js +29 -29
  413. package/out/unified-schema.js.map +1 -1
  414. package/out/uprn/existence.d.ts +28 -17
  415. package/out/uprn/existence.d.ts.map +1 -1
  416. package/out/uprn/existence.js +26 -16
  417. package/out/uprn/existence.js.map +1 -1
  418. package/out/uprn/lookup.d.ts +14 -44
  419. package/out/uprn/lookup.d.ts.map +1 -1
  420. package/out/uprn/lookup.js +21 -57
  421. package/out/uprn/lookup.js.map +1 -1
  422. package/out/uprn/schema.d.ts +18 -40
  423. package/out/uprn/schema.d.ts.map +1 -1
  424. package/out/uprn/schema.js +10 -32
  425. package/out/uprn/schema.js.map +1 -1
  426. package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
  427. package/out/uprn.d.ts.map +1 -0
  428. package/out/{uprn/index.js → uprn.js} +1 -1
  429. package/out/uprn.js.map +1 -0
  430. package/out/weights-overlay-linker.d.ts +138 -144
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +105 -159
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +91 -596
  435. package/lib/address/point/index.ts +0 -328
  436. package/lib/convention/index.ts +0 -154
  437. package/lib/fts/index.ts +0 -361
  438. package/out/address/index.d.ts.map +0 -1
  439. package/out/address/index.js.map +0 -1
  440. package/out/address/point/index.d.ts +0 -53
  441. package/out/address/point/index.d.ts.map +0 -1
  442. package/out/address/point/index.js +0 -222
  443. package/out/address/point/index.js.map +0 -1
  444. package/out/ancestry/index.d.ts +0 -45
  445. package/out/ancestry/index.d.ts.map +0 -1
  446. package/out/ancestry/index.js +0 -55
  447. package/out/ancestry/index.js.map +0 -1
  448. package/out/convention/index.d.ts +0 -106
  449. package/out/convention/index.d.ts.map +0 -1
  450. package/out/convention/index.js +0 -93
  451. package/out/convention/index.js.map +0 -1
  452. package/out/fst/index.d.ts.map +0 -1
  453. package/out/fst/index.js.map +0 -1
  454. package/out/fts/index.d.ts +0 -184
  455. package/out/fts/index.d.ts.map +0 -1
  456. package/out/fts/index.js +0 -277
  457. package/out/fts/index.js.map +0 -1
  458. package/out/geonames/index.d.ts.map +0 -1
  459. package/out/geonames/index.js.map +0 -1
  460. package/out/nsul/index.d.ts.map +0 -1
  461. package/out/nsul/index.js.map +0 -1
  462. package/out/poi/index.d.ts.map +0 -1
  463. package/out/poi/index.js.map +0 -1
  464. package/out/postal/index.d.ts.map +0 -1
  465. package/out/postal/index.js.map +0 -1
  466. package/out/street/centroid/index.d.ts +0 -40
  467. package/out/street/centroid/index.d.ts.map +0 -1
  468. package/out/street/centroid/index.js.map +0 -1
  469. package/out/street/index.d.ts.map +0 -1
  470. package/out/street/index.js.map +0 -1
  471. package/out/uprn/index.d.ts.map +0 -1
  472. package/out/uprn/index.js.map +0 -1
  473. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  474. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  475. /package/lib/{poi/index.ts → poi.ts} +0 -0
  476. /package/lib/{postal/index.ts → postal.ts} +0 -0
  477. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
package/lib/poi/lookup.ts CHANGED
@@ -3,59 +3,47 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Node reader for `poi.db` (spec §3.4) — the res-9 k-ring reader over the clustered `poi`
7
- * `WITHOUT ROWID` B-tree `poi-schema.ts` builds. Three search modes share one
8
- * artifact:
6
+ * Node reader for `poi.db` (spec §3.4), the res-9 k-ring reader over the clustered `poi`
7
+ * `without rowid` B-tree `poi-schema.ts` builds. Category, brand and name searches share one
8
+ * artifact. Each path has a method that returns its details.
9
9
  *
10
- * - **Category**: `latLngToCell(center, 9)` → `gridDisk` ring-by-ring expansion, probing each
11
- * cell's clustered `(h3_cell, category_id, neg_rank, …)` range. Rings accumulate until `limit`
12
- * rows are on hand after a completed ring, or `maxRings` is exhausted; the pool is sorted by
13
- * haversine distance from `center` after every ring.
14
- * - **Brand**: NOT a k-ring walk. Brand rows are globally sparse (~0.31% of poi.db; median nearest
15
- * tagged instance ~110 km), so ring expansion could never reach them. Instead a single brand-wide
16
- * indexed fetch on `brand_wikidata` (the partial `poi_brand_wikidata` index) pulls EVERY row for
17
- * the QID — category unconstrained — then haversine-sorts from `center` and takes the nearest
18
- * `limit`, bounded by a `BRAND_MAX_DISTANCE_KM` sanity radius.
19
- * - **Name**: FTS5 `MATCH` against the `poi_search` virtual table, hydrated back to full rows by
20
- * `name_key`. No center required; if one is given, hits are still distance-sorted.
21
- *
22
- * `latLngToCell`/`gridDisk` come from `h3-js`; the 48-bit short-cell packing that turns a raw H3
23
- * cell into the integer `poi.h3_cell` stores is `@mailwoman/spatial`'s `shortCellToInt` — that math
24
- * is NEVER reimplemented here (see AGENTS.md on `@mailwoman/spatial` being the one true home for
25
- * it).
10
+ * `latLngToCell` and `gridDisk` come from `h3-js`. The 48-bit short-cell packing that turns a raw
11
+ * H3 cell into the integer `poi.h3_cell` stores is `@mailwoman/spatial`'s `shortCellToInt`, and
12
+ * that math is never reimplemented here.
26
13
  */
27
14
 
28
15
  import { allRows } from "@mailwoman/core/utils"
29
16
  import { haversineKm, shortCellToInt, type H3Cell } from "@mailwoman/spatial"
30
- import { DatabaseClient } from "@mailwoman/sqlite/client"
17
+ import type { DatabaseClient } from "@mailwoman/sqlite/client"
18
+ import { SQLiteLookup, type SQLiteLookupOptions } from "@mailwoman/sqlite/lookup"
31
19
  import { gridDisk, latLngToCell } from "h3-js"
32
20
 
33
21
  import type { POICategoryCodeTable, POIDatabase, POITable } from "#poi/schema"
34
22
  /**
35
- * Resolution the `poi` table's `h3_cell` column is keyed at — matches the builder (spec §3.4).
23
+ * Resolution the `poi` table's `h3_cell` column is keyed at, matching the builder (spec §3.4).
36
24
  */
37
25
  export const POI_H3_RESOLUTION = 9
38
26
 
39
27
  /**
40
- * Ring budget default: 16 res-9 k-rings ≈ ~5.4 km (corner) / ~4.3 km worst-bearing. Category path only — the brand path
41
- * ignores rings entirely.
28
+ * Ring budget default: 16 res-9 k-rings, about 5.4 km at the corner and 4.3 km worst-case.
29
+ *
30
+ * Category path only, since the brand path ignores rings entirely.
31
+ * Dense categories never reach this ceiling, because the loop breaks once a ring accumulates `limit` rows.
42
32
  *
43
- * Raised from 12 (≈4 km) after nm-04 ("hiking trail near Marseille") exposed a boundary miss for SPARSE categories: the
44
- * nearest `trail` instance sits at 3.90 km, but the res-9 disk of radius 11 (maxRings 12) reaches only ~3.16 km in its
45
- * worst bearing — the cell holding that trail isn't covered until ring 13 (maxRings 14). Dense categories are
46
- * unaffected: the loop breaks the ring it accumulates `limit` rows (cafe@Paris fills 20 by ring 2), so this ceiling
47
- * never enters their probe budget. Only sparse-but-present categories that never reach `limit` scan the fuller budget —
48
- * a cold, one-shot `mailwoman poi` path, not per-keystroke. 16 (not the bare threshold 14) leaves ~2 rings of margin so
49
- * the radius is stable against small db rebuilds, while staying ~4x tighter than the board's 25 km "roughly right
50
- * place" window (no wrong-city false positives). The browser reader passes its own smaller `maxRings` and is
51
- * untouched.
33
+ * Sparse-but-present categories that never reach `limit` scan the fuller budget.
34
+ * The extra rings keep the radius stable across small DB rebuilds.
35
+ * The browser reader passes its own smaller `maxRings`.
52
36
  */
53
37
  const DEFAULT_MAX_RINGS = 16
54
38
 
55
39
  /**
56
- * Brand sanity radius (km): the brand-wide fetch returns the global nearest at ANY distance, so this drops hits far
57
- * enough to be certainly the wrong continent — "Applebee's near Marseille" comes back empty rather than with a 5,700 km
58
- * hit. A product bound, not a reach cap (the index already makes the fetch cheap regardless of distance).
40
+ * Brand sanity radius (km).
41
+ *
42
+ * The brand-wide fetch returns the global nearest at any distance, so this drops
43
+ * hits far enough away to be certainly on another continent.
44
+ * "Applebee's near Marseille" comes back empty instead of with a 5,700 km hit.
45
+ *
46
+ * This is a product bound, since the index already makes the fetch cheap regardless of distance.
59
47
  */
60
48
  const BRAND_MAX_DISTANCE_KM = 500
61
49
 
@@ -66,16 +54,18 @@ const DEFAULT_LIMIT = 20
66
54
 
67
55
  export interface POISearchQuery {
68
56
  /**
69
- * Poi-taxonomy category id (string side of the dictionary). Ignored when `brandWikidata` is also set — brand wins.
70
- */
71
- categoryID?: string
72
- /**
73
- * Fan-out category ids — the Overture `taxonomy.primary` leaves a single canonical category rolls up into (e.g.
74
- * `supermarket` → `grocery_store`, `organic_grocery_store`, …). When set, the k-ring walk probes EVERY resolvable
75
- * leaf per cell and unions the rows; unknown leaves are skipped. Supersedes `categoryID` (which is treated as a
76
- * one-element list `[categoryID]` when this is absent). Ignored when `brandWikidata` is set — brand wins.
57
+ * Poi-taxonomy category ids (string side of the dictionary) to search.
58
+ *
59
+ * A single category is a one-element list.
60
+ * A canonical Overture `taxonomy.primary` category passes every leaf it rolls up into,
61
+ * as in `supermarket` → `grocery_store`, `organic_grocery_store`, ….
62
+ *
63
+ * The k-ring walk probes every resolvable leaf per cell and unions the rows.
64
+ * Unknown leaves are skipped.
65
+ *
66
+ * Ignored when `brandWikidata` is set, since brand wins.
77
67
  */
78
- categoryIDs?: string[]
68
+ categoryIDs?: readonly string[]
79
69
  /**
80
70
  * Wikidata QID for brand-exact search.
81
71
  */
@@ -85,12 +75,17 @@ export interface POISearchQuery {
85
75
  */
86
76
  name?: string
87
77
  /**
88
- * Search center. Required for category/brand queries (k-ring expansion).
78
+ * Search center.
79
+ *
80
+ * Required for category/brand queries (k-ring expansion).
89
81
  */
90
82
  center?: { latitude: number; longitude: number }
91
83
  /**
92
- * Ring budget: how many res-9 k-rings to expand before giving up (default 16 ≈ ~5.4 km). Counts ring 0, so k reaches
93
- * `maxRings - 1`.
84
+ * Ring budget: how many res-9 k-rings to expand before giving up.
85
+ *
86
+ * Counts ring 0, so k reaches `maxRings - 1`.
87
+ *
88
+ * @defaultValue `16` ≈ ~5.4 km
94
89
  */
95
90
  maxRings?: number
96
91
  limit?: number
@@ -105,25 +100,25 @@ export interface POISearchHit {
105
100
  country: string
106
101
  confidence: number
107
102
  /**
108
- * Overture GERS id — nullable METADATA ONLY, never a key (the #470 rule; see `POITable.gers_id`).
103
+ * Overture gers id.
104
+ *
105
+ * Nullable metadata, never a key.
106
+ * See `POITable.gers_id`.
109
107
  */
110
108
  gersID: string | null
111
- distanceM?: number
109
+ distanceM: number | null
112
110
  }
113
111
 
114
- export interface POILookupOpts<DB extends POIDatabase = POIDatabase> {
115
- /**
116
- * Path to a `poi.db` built by the (future) POI builder. Opened read-only.
117
- */
118
- databasePath?: string
119
- /**
120
- * Pre-opened handle (tests / shared connections). Mutually exclusive with `databasePath`.
121
- */
122
- database?: DatabaseClient<DB>
123
- }
112
+ /**
113
+ * Where a {@link POILookup} reads from.
114
+ *
115
+ * The source is a `poi.db` built by the POI builder, opened read-only,
116
+ * or a connection the caller already holds.
117
+ */
118
+ export type POILookupOpts<DB extends POIDatabase = POIDatabase> = SQLiteLookupOptions<DB>
124
119
 
125
120
  /**
126
- * The `poi` columns every search mode hydrates — a typed projection of the SHARED {@link POITable}.
121
+ * The `poi` columns every search mode hydrates, a typed projection of the shared {@link POITable}.
127
122
  */
128
123
  type POIRow = Pick<
129
124
  POITable,
@@ -141,13 +136,7 @@ type POIRow = Pick<
141
136
  /**
142
137
  * Node reader over `poi.db`.
143
138
  */
144
- export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposable {
145
- #db: DatabaseClient<DB>
146
- /**
147
- * Resources this instance opened. A connection handed in by a caller is NOT in here, so disposal cannot reach it —
148
- * ownership is membership rather than a flag a later branch has to check.
149
- */
150
- readonly #resources = new DisposableStack()
139
+ export class POILookup<DB extends POIDatabase = POIDatabase> extends SQLiteLookup<DB> {
151
140
  readonly #categoryToID = new Map<string, number>()
152
141
  readonly #idToCategory = new Map<number, string>()
153
142
 
@@ -156,39 +145,36 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
156
145
  */
157
146
  readonly #categoryCellProbe: ReturnType<DatabaseClient["prepare"]>
158
147
  /**
159
- * `brand_wikidata` → ALL of a brand's rows globally (partial-index range-scan); distance-sorted in JS, not SQL.
148
+ * `brand_wikidata` → all of a brand's rows globally (partial-index range-scan).
149
+ * The sort happens in JS.
160
150
  */
161
151
  readonly #brandProbe: ReturnType<DatabaseClient["prepare"]>
162
152
  /**
163
- * FTS5 `MATCH` over `poi_search`, returning candidate `name_key`s to hydrate.
153
+ * FTS5 `match` over `poi_search`, returning candidate `name_key`s to hydrate.
164
154
  */
165
155
  readonly #nameFTSProbe: ReturnType<DatabaseClient["prepare"]>
166
156
 
167
157
  constructor(opts: POILookupOpts<DB>) {
168
- if (opts.database) {
169
- this.#db = opts.database
170
- } else if (opts.databasePath) {
171
- this.#db = this.#resources.use(new DatabaseClient<DB>(opts.databasePath, { readOnly: true }))
172
- } else {
173
- throw new Error("POILookup needs `databasePath` or `database`")
174
- }
158
+ super(opts)
175
159
 
176
- // The category dictionary is tiny (poi-taxonomy's category count) — load it once at
177
- // construction so `search` never round-trips to it.
178
- for (const r of allRows<POICategoryCodeTable>(this.#db.prepare("SELECT id, category FROM poi_category_codes"))) {
160
+ // The category dictionary is tiny (poi-taxonomy's category count), so load it once
161
+ // at construction and keep `search` from round-tripping to it.
162
+ for (const r of allRows<POICategoryCodeTable>(
163
+ this.database.prepare("SELECT id, category FROM poi_category_codes")
164
+ )) {
179
165
  this.#categoryToID.set(String(r.category), Number(r.id))
180
166
  this.#idToCategory.set(Number(r.id), String(r.category))
181
167
  }
182
168
 
183
169
  const columns = "name, category_id, brand_wikidata, latitude, longitude, country, confidence, name_key, gers_id"
184
170
 
185
- this.#categoryCellProbe = this.#db.prepare(
171
+ this.#categoryCellProbe = this.database.prepare(
186
172
  `SELECT ${columns} FROM poi WHERE h3_cell = ? AND category_id = ? ORDER BY neg_rank ASC LIMIT ?`
187
173
  )
188
174
 
189
- this.#brandProbe = this.#db.prepare(`SELECT ${columns} FROM poi WHERE brand_wikidata = ?`)
175
+ this.#brandProbe = this.database.prepare(`SELECT ${columns} FROM poi WHERE brand_wikidata = ?`)
190
176
 
191
- this.#nameFTSProbe = this.#db.prepare(
177
+ this.#nameFTSProbe = this.database.prepare(
192
178
  "SELECT name_key FROM poi_search WHERE poi_search MATCH ? ORDER BY bm25(poi_search) LIMIT ?"
193
179
  )
194
180
  }
@@ -200,13 +186,11 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
200
186
  return this.#searchByName(query.name, limit, query.center)
201
187
  }
202
188
 
203
- if (query.categoryID || (query.categoryIDs && query.categoryIDs.length) || query.brandWikidata) {
189
+ if (query.categoryIDs?.length || query.brandWikidata) {
204
190
  if (!query.center) {
205
191
  throw new Error("POILookup.search: category/brand search requires a `center`")
206
192
  }
207
193
 
208
- // brandWikidata wins over categoryID(s) when both are set — see POISearchQuery.categoryID. The brand path is
209
- // a brand-wide indexed fetch, NOT a k-ring walk (brand rows are too sparse for ring expansion to reach).
210
194
  if (query.brandWikidata) {
211
195
  return this.#searchBrand(query.brandWikidata, query.center, limit)
212
196
  }
@@ -218,10 +202,14 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
218
202
  }
219
203
 
220
204
  /**
221
- * Brand path: a single brand-wide indexed fetch — NO k-ring. Fetch EVERY row for the QID (the partial
222
- * `poi_brand_wikidata` index makes this a range-scan, not a 13.68M full scan), haversine-sort from `center`, drop
223
- * anything past the {@link BRAND_MAX_DISTANCE_KM} sanity radius, and take the nearest `limit`. Returns the true
224
- * nearest at any distance — the reach ceiling k-ring hits on sparse brand rows is gone.
205
+ * Brand path: a single brand-wide indexed fetch and no k-ring.
206
+ *
207
+ * The partial `poi_brand_wikidata` index turns the QID fetch into a range-scan
208
+ * instead of a 13.68M full scan.
209
+ * Rows are sorted by haversine distance from `center`.
210
+ *
211
+ * The lookup drops rows past {@link BRAND_MAX_DISTANCE_KM}.
212
+ * It returns the nearest `limit` rows.
225
213
  */
226
214
  #searchBrand(brandWikidata: string, center: { latitude: number; longitude: number }, limit: number): POISearchHit[] {
227
215
  const rows = allRows<POIRow>(this.#brandProbe, brandWikidata)
@@ -242,11 +230,9 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
242
230
  const maxRings = query.maxRings ?? DEFAULT_MAX_RINGS
243
231
  const categoryIDs: number[] = []
244
232
 
245
- // `categoryIDs` (the fan-out list) supersedes the single `categoryID`; either way, resolve each id through the
246
- // dictionary and drop the ones the db doesn't carry (Overture-taxonomy drift, or an identity id with no rows).
247
- const seedIDs = query.categoryIDs?.length ? query.categoryIDs : query.categoryID ? [query.categoryID] : []
248
-
249
- for (const id of seedIDs) {
233
+ // Each id resolves through the dictionary.
234
+ // Unresolved ids are dropped to cover Overture taxonomy drift and identity ids with no rows.
235
+ for (const id of query.categoryIDs ?? []) {
250
236
  const resolved = this.#categoryToID.get(id)
251
237
 
252
238
  if (resolved !== undefined) {
@@ -254,7 +240,8 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
254
240
  }
255
241
  }
256
242
 
257
- // No resolvable leaf (every id unknown to the dictionary) can't have rows — a clean miss, not a throw.
243
+ // No resolvable leaf can have rows, since every id is unknown to the dictionary.
244
+ // This is a clean miss instead of a throw.
258
245
  if (!categoryIDs.length) return []
259
246
 
260
247
  const origin = latLngToCell(center.latitude, center.longitude, POI_H3_RESOLUTION) as H3Cell
@@ -263,8 +250,8 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
263
250
 
264
251
  // `ring` starts at 0 (the origin cell itself), so this loop's k reaches `maxRings - 1`.
265
252
  for (let ring = 0; ring < maxRings; ring++) {
266
- // gridDisk(origin, ring) returns the WHOLE disk out to `ring`; diffing against what's
267
- // already been probed derives just this ring's new cells.
253
+ // gridDisk(origin, ring) returns the whole disk out to `ring`, so diffing against
254
+ // the already-probed set derives just this ring's new cells.
268
255
  const diskCells = gridDisk(origin, ring) as string[]
269
256
  const newCells = diskCells.filter((cell) => !seenCells.has(cell))
270
257
 
@@ -272,8 +259,6 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
272
259
  seenCells.add(cell)
273
260
  const shortCell = shortCellToInt(cell as H3Cell)
274
261
 
275
- // Fan-out: probe every resolved Overture leaf for this canonical category, unioning the rows. The
276
- // post-ring distance sort + `slice(0, limit)` below dedupes the pool down to the nearest `limit`.
277
262
  for (const categoryID of categoryIDs) {
278
263
  rows.push(...allRows<POIRow>(this.#categoryCellProbe, shortCell, categoryID, limit))
279
264
  }
@@ -288,11 +273,13 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
288
273
  }
289
274
 
290
275
  /**
291
- * Name path: FTS5 MATCH → hydrate by name_key. No center required; distance-sorts if one is given anyway.
276
+ * Name path: FTS5 match → hydrate by name_key.
277
+ *
278
+ * No center is required.
279
+ * Hits are distance-sorted when one is given.
292
280
  *
293
- * Hydration is ONE batched `WHERE name_key IN (...)` query over the FTS hits' unique `name_key`s, not a per-hit probe
294
- * — with up to `limit` FTS hits, a per-hit probe was up to `limit` full table scans before `createPOINameKeyIndex`
295
- * (poi-schema.ts) + this batching.
281
+ * Hydration is one batched `where name_key IN (...)` query over the unique `name_key`s
282
+ * of the FTS hits, since a per-hit probe would be up to `limit` full table scans.
296
283
  */
297
284
  #searchByName(name: string, limit: number, center?: { latitude: number; longitude: number }): POISearchHit[] {
298
285
  const matchQuery = sanitizePOINameQuery(name)
@@ -339,21 +326,19 @@ export class POILookup<DB extends POIDatabase = POIDatabase> implements Disposab
339
326
  }
340
327
 
341
328
  /**
342
- * Batched hydration for the FTS name path: `WHERE name_key IN (?, ?, …)`, one query for the whole batch instead of
343
- * one probe per FTS hit. This is a cold path (name search only) with variable arity per call, so the statement is
344
- * prepared fresh each time rather than cached.
329
+ * Batched hydration for the FTS name path: `where name_key IN (?, ?, …)`,
330
+ * one query for the whole batch instead of one probe per FTS hit.
331
+ *
332
+ * This is a cold path (name search only) with variable arity per call,
333
+ * so the statement is prepared fresh each time rather than cached.
345
334
  */
346
335
  #hydrateByNameKeys(nameKeys: string[]): POIRow[] {
347
336
  const columns = "name, category_id, brand_wikidata, latitude, longitude, country, confidence, name_key, gers_id"
348
337
  const placeholders = nameKeys.map(() => "?").join(", ")
349
- const stmt = this.#db.prepare(`SELECT ${columns} FROM poi WHERE name_key IN (${placeholders})`)
338
+ const stmt = this.database.prepare(`SELECT ${columns} FROM poi WHERE name_key IN (${placeholders})`)
350
339
 
351
340
  return allRows<POIRow>(stmt, ...nameKeys)
352
341
  }
353
-
354
- [Symbol.dispose](): void {
355
- this.#resources[Symbol.dispose]()
356
- }
357
342
  }
358
343
 
359
344
  function sortByDistance(rows: POIRow[], center: { latitude: number; longitude: number }): POIRow[] {
@@ -378,27 +363,25 @@ function toHit(
378
363
  country: row.country,
379
364
  confidence: row.confidence,
380
365
  gersID: row.gers_id,
381
- ...(center
382
- ? { distanceM: haversineKm(center.latitude, center.longitude, row.latitude, row.longitude) * 1000 }
383
- : {}),
366
+ distanceM: center ? haversineKm(center.latitude, center.longitude, row.latitude, row.longitude) * 1000 : null,
384
367
  }
385
368
  }
386
369
 
387
370
  /**
388
- * Sanitize free text into an FTS5-safe MATCH query: strip the characters FTS5 would otherwise read as syntax (`"`
389
- * phrase delimiters, `*` prefix wildcards, `:` column-filter separators), then phrase-quote each whitespace-separated
390
- * token (AND-joined).
371
+ * Sanitize free text into an FTS5-safe `match` query: strip the characters FTS5 would otherwise
372
+ * read as syntax (`"` phrase delimiters, `*` prefix wildcards, `:` column-filter separators),
373
+ * then phrase-quote each whitespace-separated token (and-joined).
391
374
  *
392
- * `resolver-wof-sqlite` already has this discipline — `lookup.ts`'s `sanitizeFTSQuery` — but that function is
393
- * module-private there (not re-exported from `fts.ts` or the package's `index.ts`), so this replicates the same
394
- * discipline locally rather than reaching across the module boundary for a private helper.
375
+ * `lookup.ts` already has this discipline in `sanitizeFTSQuery`, but that function is
376
+ * module-private there and is not re-exported from `fts.ts` or the package's `index.ts`.
377
+ * This replicates the discipline locally rather than reach across the module boundary for a private helper.
395
378
  */
396
379
  function sanitizePOINameQuery(text: string): string {
397
380
  return text
398
381
  .replaceAll(/["*:]/g, "")
399
382
  .trim()
400
383
  .split(/\s+/u)
401
- .filter((token) => token.length > 0)
384
+ .filter((token) => token.length)
402
385
  .map((token) => `"${token.replaceAll('"', '""')}"`)
403
386
  .join(" ")
404
387
  }
package/lib/poi/schema.ts CHANGED
@@ -3,23 +3,25 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Typed schema for poi.db — spatial layer #1 (spec §3.4). One clustered `WITHOUT ROWID` B-tree
7
- * keyed `(h3_cell, category_id, neg_rank, rowid_key)` so "everything near this res-9 cell" is a
8
- * contiguous key range (the byte-range/httpvfs access pattern, same discipline as the candidate
9
- * gazetteer). Rows carry denormalized name/brand/coords; category ids are small ints via the
10
- * `poi_category_codes` dictionary (poi-taxonomy category ids are the string side). The DB also
11
- * embeds the layer-contract tables from `@mailwoman/core/layers` — the builder writes the
12
- * manifest (tier `shipped`, spine `h3` res 9) and per-res-6-cell coverage.
6
+ * Typed schema for poi.db, the first spatial layer (spec §3.4). One clustered `without rowid`
7
+ * B-tree uses `(h3_cell, category_id, neg_rank, rowid_key)` as its key. All rows near a res-9 cell
8
+ * occupy a contiguous key range. This matches byte-range and httpvfs access and follows the
9
+ * candidate gazetteer's layout. Rows include denormalized name, brand and coordinates.
10
+ * The `poi_category_codes` dictionary stores category ids as small integers. POI-taxonomy category
11
+ * ids remain strings. The database embeds the layer-interface tables from `@mailwoman/core/layers`.
12
+ * The builder writes a manifest with tier `shipped` and spine `h3` res 9. It also writes per-res-6-cell coverage.
13
13
  */
14
14
 
15
- import type { LayerContractDatabase } from "@mailwoman/core/layers"
15
+ import type { layerschemadatabase } from "@mailwoman/core/layers"
16
16
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
17
17
  import { sql, type Kysely } from "kysely"
18
18
 
19
19
  import type { NameKey } from "#street/normalize"
20
20
 
21
21
  /**
22
- * One POI row. Clustered PK: h3_cell → category_id → neg_rank → rowid_key.
22
+ * One POI row.
23
+ *
24
+ * Clustered PK: h3_cell → category_id → neg_rank → rowid_key.
23
25
  */
24
26
  export interface POITable {
25
27
  /**
@@ -40,10 +42,12 @@ export interface POITable {
40
42
  rowid_key: number
41
43
  name: string | null
42
44
  /**
43
- * Probe key for exact name lookups, minted by {@link normalizeLocalityForKey} at build AND at query.
45
+ * Probe key for exact name lookups, minted by {@link normalizeLocalityForKey} at build and at query.
46
+ *
47
+ * Branded because a `toLowerCase()` approximation of the fold is still a `string`.
48
+ * That approximation binds to the parameter and returns fewer rows.
44
49
  *
45
- * Branded because a `toLowerCase()` approximation of the fold is still a `string`: it binds to the parameter, returns
46
- * fewer rows, and the shortfall reads as a coverage gap in the data rather than a defect in the probe.
50
+ * The shortfall looks like a data coverage gap instead of a probe defect.
47
51
  */
48
52
  name_key: NameKey | null
49
53
  brand_wikidata: string | null
@@ -58,14 +62,18 @@ export interface POITable {
58
62
  */
59
63
  confidence: number
60
64
  /**
61
- * GERS id — nullable METADATA ONLY, never a key (the #470 rule).
65
+ * Gers id.
66
+ * Nullable metadata, never a key.
62
67
  */
63
68
  gers_id: string | null
64
69
  }
65
70
 
66
71
  /**
67
- * Staging mirror — every column nullable except the coords (the loader fills positionally; the materialize SELECT
68
- * enforces completeness).
72
+ * The staging mirror.
73
+ * Every column is nullable except the coords.
74
+ *
75
+ * The loader fills this table positionally.
76
+ * The materialize select enforces completeness.
69
77
  */
70
78
  export interface POIStageTable {
71
79
  h3_cell: number | null
@@ -90,14 +98,14 @@ export interface POICategoryCodeTable {
90
98
  category: string
91
99
  }
92
100
 
93
- export interface POIDatabase extends LayerContractDatabase {
101
+ export interface POIDatabase extends layerschemadatabase {
94
102
  poi: POITable
95
103
  poi_stage: POIStageTable
96
104
  poi_category_codes: POICategoryCodeTable
97
105
  }
98
106
 
99
107
  /**
100
- * Clustered-key-order column list shared by builder + `INSERT INTO poi SELECT … FROM poi_stage`.
108
+ * Clustered-key-order column list shared by builder + `insert into poi select … from poi_stage`.
101
109
  */
102
110
  export const POI_COLUMNS = [
103
111
  "h3_cell",
@@ -154,25 +162,31 @@ export async function createPOITable(db: Kysely<POIDatabase>): Promise<void> {
154
162
  .addColumn("confidence", "real", (c) => c.notNull())
155
163
  .addColumn("gers_id", "text")
156
164
  .addPrimaryKeyConstraint("poi_pk", ["h3_cell", "category_id", "neg_rank", "rowid_key"])
157
- // `WITHOUT ROWID` has no first-class builder; the raw modifier is the idiomatic fallback.
165
+ // `without rowid` has no first-class builder. The raw modifier is the idiomatic fallback.
158
166
  .modifyEnd(sql`without rowid`)
159
167
  .execute()
160
168
  }
161
169
 
162
170
  /**
163
- * Secondary index for the FTS-hydration path. Builders call this AFTER the bulk materialize (index-after-load).
171
+ * Secondary index for the FTS-hydration path.
172
+ *
173
+ * Builders call this after the bulk materialize (index-after-load).
164
174
  */
165
175
  export async function createPOINameKeyIndex(db: Kysely<POIDatabase>): Promise<void> {
166
176
  await db.schema.createIndex("poi_name_key").on("poi").column("name_key").execute()
167
177
  }
168
178
 
169
179
  /**
170
- * Secondary index for the BRAND path — a brand-wide fetch by `brand_wikidata` (no `h3_cell` prefix). Brand rows are
171
- * globally sparse (~0.31% of poi.db, median nearest tagged instance ~110 km), so the k-ring walk can never reach them;
172
- * the reader instead fetches ALL of a brand's rows and distance-sorts. Without this index that is a full-table scan
173
- * (~600 ms); with it, a range-scan (<1 ms p50). PARTIAL (`WHERE brand_wikidata IS NOT NULL`) so the ~99.7% of rows that
174
- * carry no QID never enter the B-tree — the index holds only the ~43k branded rows. Builders call this AFTER the bulk
175
- * materialize (index-after-load), same phase as {@link createPOINameKeyIndex}.
180
+ * Secondary index for the brand path, a brand-wide fetch by `brand_wikidata` with no `h3_cell` prefix.
181
+ *
182
+ * Brand rows are globally sparse, so the k-ring walk can never reach them and the reader
183
+ * instead fetches all of a brand's rows and distance-sorts.
184
+ * Without this index that fetch is a full-table scan, while with it the fetch is a range-scan.
185
+ *
186
+ * The partial index (`where brand_wikidata is not NULL`) keeps the mostly unbranded rows out of the B-tree.
187
+ *
188
+ * Builders call this after the bulk materialize (index-after-load),
189
+ * same phase as {@link createPOINameKeyIndex}.
176
190
  */
177
191
  export async function createPOIBrandIndex(db: Kysely<POIDatabase>): Promise<void> {
178
192
  await db.schema
@@ -189,7 +203,9 @@ export async function createPOIBrandIndex(db: Kysely<POIDatabase>): Promise<void
189
203
  export const POI_FTS_TABLE = "poi_search"
190
204
 
191
205
  /**
192
- * FTS5 stays raw SQL by project rule (Kysely can't express virtual tables). Content-keyed by name_key.
206
+ * FTS5 stays raw SQL by project rule (Kysely can't express virtual tables).
207
+ *
208
+ * Content-keyed by name_key.
193
209
  */
194
210
  export function createPOISearchFTS<DB>(db: DatabaseClient<DB>): void {
195
211
  db.exec(
@@ -4,10 +4,10 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Typed schema for the polygon sidecar (`wof-polygons.db`) — the one table `WOFReverseGeocoder` probes for a
7
- * place's geometry. The interface is the read/write contract and {@link createPolygonsTable} creates the table, so a
7
+ * place's geometry. The interface is the read/write interface and {@link createPolygonsTable} creates the table, so a
8
8
  * column added to one is a compile error against the other.
9
9
  *
10
- * The sidecar is OPTIONAL to the reverse geocoder: without it every result falls back to a centroid, so the reader
10
+ * The sidecar is optional to the reverse geocoder: without it every result falls back to a centroid, so the reader
11
11
  * checks for the table's presence rather than assuming it.
12
12
  */
13
13
 
@@ -19,8 +19,10 @@ import type { Kysely } from "kysely"
19
19
  export interface PolygonsTable {
20
20
  id: number
21
21
  /**
22
- * The GeoJSON geometry, JSON-encoded. A row that fails to parse reads as no-polygon, never as an error — a malformed
23
- * geometry must not fail the whole reverse query.
22
+ * The GeoJSON geometry, JSON-encoded.
23
+ *
24
+ * A row that fails to parse reads as no-polygon, never as an error.
25
+ * A malformed geometry must not fail the whole reverse query.
24
26
  */
25
27
  geom: string
26
28
  }
@@ -30,8 +32,10 @@ export interface PolygonDatabase {
30
32
  }
31
33
 
32
34
  /**
33
- * The slice of a Kysely handle the polygon DDL touches. Kysely is invariant in its schema parameter, so naming only
34
- * `schema` lets a builder holding a wider handle pass it without a cast.
35
+ * The subset of a Kysely handle the polygon DDL touches.
36
+ *
37
+ * Kysely is invariant in its schema parameter, so naming only `schema` lets a
38
+ * builder holding a wider handle pass it without a cast.
35
39
  */
36
40
  export type PolygonSchemaHandle = Pick<Kysely<PolygonDatabase>, "schema">
37
41