@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
@@ -3,43 +3,52 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * The FTS5 candidate fetch behind the fuzzy name match: the schema-qualified `place_search`
7
- * MATCH, its population-ordered companion fetch, and the raw row shape both of them return.
6
+ * Fetches FTS5 candidates for fuzzy name matching. It runs the schema-qualified `place_search` match and its
7
+ * population-ordered companion query. Both queries return the raw row shape defined here.
8
8
  *
9
9
  * Bbox and near-with-radius narrow at the SQL level through SQLite's built-in `rtree`, whose index name and schema
10
- * live in `fts.ts` beside the FTS5 build. That is why this package pulls neither SpatiaLite nor turf: the R*Tree does
11
- * the narrowing, and the passes downstream of it operate on ≤ a few hundred candidates per query rather than the
12
- * whole corpus, so an exact haversine over the survivors is cheap.
10
+ * are defined in `fts.ts` beside the FTS5 build. This package uses SQLite's R*Tree instead of SpatiaLite or turf.
11
+ * The R*Tree narrows the search to at most a few hundred candidates per query. Downstream passes work on that set
12
+ * instead of the whole corpus, so exact haversine calculations remain cheap.
13
13
  */
14
14
 
15
15
  import { allRows } from "@mailwoman/core/utils"
16
16
  import { bboxAround } from "@mailwoman/spatial"
17
17
  import type { DatabaseClient, SQLInputValue } from "@mailwoman/sqlite/client"
18
18
 
19
- import { PLACE_BBOX_TABLE, PLACE_POPULATION_TABLE } from "#fts/index"
19
+ import { PLACE_BBOX_TABLE, PLACE_POPULATION_TABLE } from "#fts"
20
20
  import type { RankingWeights } from "#ranking-weights"
21
21
  import type { FindPlaceQuery, WOFPlacetype } from "#types"
22
22
  /**
23
- * Query length at or below which the FTS window is widened. A two- or three-character query is almost always a region
24
- * abbreviation, where the exact match can otherwise fall outside the window behind higher-bm25 partial hits — "NY"
25
- * losing to "New York".
23
+ * Query length at or below which the FTS window is widened.
24
+ *
25
+ * A two- or three-character query is almost always a region abbreviation, where the exact match
26
+ * can otherwise fall outside the window behind higher-bm25 partial hits, as "NY" losing to "New York".
26
27
  */
27
28
  const SHORT_QUERY_MAX_LENGTH = 3
28
29
 
29
30
  /**
30
- * Over-fetch floor for SHORT (≤3-char) queries — region abbreviations like "NY"/"VT". An exact-abbrev holder's BM25 is
31
- * poor (long multilingual alt-name document), so the normal `limit * 4` window can drop it before `exactMatchTiering`
32
- * promotes it. 200 comfortably covers every same-abbrev region across the 12-country gazetteer (a 2-letter token
33
- * matches a few dozen regions at most) while staying a cheap region-placetype fetch. See the `#fuzzyNameMatch`
34
- * over-fetch comment.
31
+ * Over-fetch floor for short (≤3-character) queries.
32
+ *
33
+ * These queries are usually region abbreviations such as "NY" or "VT".
34
+ *
35
+ * An exact-abbrev holder's BM25 is poor (long multilingual alt-name document),
36
+ * so the normal `limit * 4` window can drop it before `exactMatchTiering` promotes
37
+ * it. 200 comfortably covers every same-abbrev region across the 12-country gazetteer
38
+ * (a 2-letter token matches a few dozen regions at most) while staying a cheap region-placetype fetch.
39
+ * See the `#fuzzyNameMatch` over-fetch comment.
35
40
  */
36
41
  const SHORT_QUERY_OVERFETCH = 200
37
42
 
38
43
  /**
39
- * How many rows the population-ordered companion fetch (#905) adds to the candidate pool. Small on purpose: its only
40
- * job is to guarantee the FAMOUS holders of a name enter the pool at all — for "Paris"-class floods the bm25 window is
41
- * saturated by thousands of tiny same-name rows and no boost inside the bm25-based ORDER BY can rescue a candidate
42
- * whose bm25 is length-poisoned by ~15 points (see the fetch-site comment).
44
+ * How many rows the population-ordered companion fetch adds to the candidate pool.
45
+ *
46
+ * Small on purpose.
47
+ * Its only job is to guarantee the famous holders of a name enter the pool at all.
48
+ *
49
+ * For "Paris"-class floods, thousands of small same-name rows fill the BM25 window.
50
+ * A boost inside the BM25-based ORDER BY cannot rescue a candidate whose score is
51
+ * length-poisoned by about 15 points (see the fetch-site comment).
43
52
  */
44
53
  const POPULATION_FETCH_LIMIT = 15
45
54
 
@@ -49,27 +58,31 @@ export interface RawSearchRow {
49
58
  placetype: string
50
59
  country: string | null
51
60
  parent_id: number | null
52
- rank: number // BM25 (lower = better in SQLite); we negate to get higher-is-better
61
+ rank: number // BM25 (lower = better in SQLite). We negate to get higher-is-better
53
62
  lat: number | null
54
63
  lon: number | null
55
64
  min_latitude: number | null
56
65
  max_latitude: number | null
57
66
  min_longitude: number | null
58
67
  max_longitude: number | null
59
- population: number | null // from the place_population aux table; null when missing
68
+ population: number | null // from the place_population aux table. null when missing
60
69
  /**
61
- * From `place_importance.encyclopedic` when the extract's table carries the two-score split columns. NULL means the
62
- * place has no Wikipedia article, or the extract predates the split — absence either way, and never 0 (ROAD_TO_V9
63
- * §2).
70
+ * From `place_importance.encyclopedic` when the extract's table includes the two-score split columns.
71
+ *
72
+ * NULL means the place has no Wikipedia article, or the extract predates the split.
73
+ * Either case records an absence.
74
+ * The value remains null rather than 0 (ROAD_TO_V9 §2).
64
75
  */
65
76
  encyclopedic: number | null
66
77
  }
67
78
 
68
79
  /**
69
- * Fetch the raw candidate rows for a name match on one extract: the BM25-ordered window over `place_search` (widened
70
- * for short queries), plus the population-ordered companion fetch that keeps the prominent holders of a name
71
- * pool-complete. `schemaName` is the routed extract's bare schema name — validated at construction, so it is
72
- * interpolated directly.
80
+ * Fetch the raw candidate rows for a name match on one extract: the BM25-ordered
81
+ * window over `place_search` (widened for short queries), plus the population-ordered
82
+ * companion fetch that keeps the prominent holders of a name pool-complete.
83
+ *
84
+ * `schemaName` is the routed extract's bare schema name, validated at construction,
85
+ * so it is interpolated directly.
73
86
  */
74
87
  export function fetchSearchRows<DB>(options: {
75
88
  db: DatabaseClient<DB>
@@ -96,23 +109,26 @@ export function fetchSearchRows<DB>(options: {
96
109
  weights,
97
110
  } = options
98
111
 
99
- // Over-fetch so post-scoring + exact-match tiering have room to re-rank. SHORT queries (a 2–3-char
100
- // region abbreviation like "NY"/"VT") are the danger case the `exactMatchTiering` docstring flags:
101
- // the exact-abbrev holder's BM25 is poor (its long multilingual alt-name document tanks the score),
102
- // so under the normal `limit * 4` window it drops OUT of the candidate pool BEFORE tiering can
103
- // promote it — "NY" then resolves to a token-matching foreign region (Highland, GB) instead of New
104
- // York. Widen the window for short queries so the exact match is always present to be tiered.
105
- // (Cross-country abbrev collisions — "VT" is BOTH Vermont and Viterbo — still need a country/
106
- // postcode signal to disambiguate; this only rescues the window-drop class, not genuine ambiguity.
107
- // With a `country` hint every abbrev resolves; bare + no-context lifts 7→10/15 US states.)
112
+ // Over-fetch so post-scoring and exact-match tiering have room to re-rank.
113
+ // Short queries (a 2-3-char region abbreviation like "NY" or "VT") are the danger
114
+ // case the `exactMatchTiering` docstring flags.
115
+ // The exact-abbrev holder's BM25 is poor, so under the normal `limit * 4` window it
116
+ // drops out of the candidate pool before tiering can promote it.
117
+ // A wider window for short queries keeps the exact match available for tiering.
118
+ // Cross-country abbrev collisions ("VT" is both Vermont and Viterbo) still need a country
119
+ // or postcode signal to disambiguate, so this only rescues the window-drop class.
108
120
  const ftsLimit =
109
121
  query.text.trim().length <= SHORT_QUERY_MAX_LENGTH ? Math.max(limit * 4, SHORT_QUERY_OVERFETCH) : limit * 4
110
122
 
111
- // Filter out historical / superseded / deprecated places by default — they live in the same
112
- // spr table but should never win a contemporary lookup. `is_current = 0` is the only WOF
113
- // value that means "not current"; both `-1` (modern) and `1` (legacy) mean current. See #91.
114
- // Note: with schema-qualified FROM the bare `place_search` reference in MATCH resolves to
115
- // the FROM table — required by FTS5 parser, see extracts.ts header comment.
123
+ // Filter out historical, superseded and deprecated places by default.
124
+ // They live in the same spr table but should never win a contemporary lookup.
125
+ // `is_current = 0` is the only WOF value that means "not current", while both
126
+ // `-1` (modern) and `1` (legacy) mean current.
127
+ //
128
+ // The schema-qualified `from` makes the bare `place_search` reference in
129
+ // `match` resolve to the from table.
130
+ // The FTS5 parser requires that resolution.
131
+ // See the extracts.ts header comment.
116
132
  const where: string[] = ["place_search MATCH ?", "spr.is_current != 0", "spr.is_deprecated = 0"]
117
133
  const params: SQLInputValue[] = [ftsQuery]
118
134
 
@@ -131,23 +147,27 @@ export function fetchSearchRows<DB>(options: {
131
147
  params.push(query.parentID, query.parentID)
132
148
  }
133
149
 
134
- // Bbox + near-with-radius are SQL-level filters via the R*Tree. We only emit the JOIN when
135
- // the active extract has the R*Tree; missing-but-requested is silently treated as no-bbox-
136
- // filter so legacy DBs / extracts-without-bbox don't crash.
150
+ // Bbox and near-with-radius are SQL-level filters via the R*Tree.
151
+ // The join is emitted only when the active extract has the R*Tree.
152
+ // A requested filter on a legacy extract is silently treated as no bbox filter
153
+ // so the query does not crash.
137
154
  const extractHasBbox = hasBboxIndex.get(sch) === true
138
155
  const useBboxJoin = (query.bbox || query.near?.maxDistanceKm !== undefined) && extractHasBbox
139
156
  let joinClause = `JOIN ${sch}.spr ON spr.id = place_search.wof_id`
140
157
 
141
158
  if (useBboxJoin) {
142
159
  joinClause += ` JOIN ${sch}.${PLACE_BBOX_TABLE} bbox ON bbox.id = spr.id`
143
- // AABB intersection — both bbox sides must overlap. R*Tree handles this in O(log n).
160
+ // aabb intersection.
161
+ // Both bounding-box sides must overlap.
162
+ // The R*Tree checks them in O(log n).
144
163
  const filterBox = query.bbox || bboxAround(query.near!.lat, query.near!.lon, query.near!.maxDistanceKm!)
145
164
  where.push("bbox.min_lat <= ? AND bbox.max_lat >= ?", "bbox.min_lon <= ? AND bbox.max_lon >= ?")
146
165
  params.push(filterBox.maxLat, filterBox.minLat, filterBox.maxLon, filterBox.minLon)
147
166
  }
148
167
 
149
- // LEFT JOIN the population aux table when present. Missing-on-this-extract means the SELECT
150
- // just doesn't include the population column; the post-scoring loop treats it as 0.
168
+ // Left join the population aux table when present.
169
+ // If this extract lacks the table, the select omits the population column.
170
+ // The post-scoring loop then uses 0.
151
171
  const extractHasPopulation = hasPopulationIndex.get(sch) === true
152
172
 
153
173
  const populationSelect = extractHasPopulation
@@ -158,31 +178,32 @@ export function fetchSearchRows<DB>(options: {
158
178
  ? `LEFT JOIN ${sch}.${PLACE_POPULATION_TABLE} ON ${PLACE_POPULATION_TABLE}.id = spr.id`
159
179
  : ""
160
180
 
161
- // The encyclopedic score is CARRIED, never ranked on (ROAD_TO_V9 §2, ratified 2026-08-06) — it
162
- // appears in the SELECT and in no ORDER BY, here or in the companion fetch below. Conditioned on the
163
- // split column, so a pre-split extract emits a literal NULL and builds no join at all.
181
+ // The encyclopedic score is returned and never ranked on (ROAD_TO_V9 §2).
182
+ // It appears in the select and in no order BY, here or in the companion fetch below.
183
+ // It is conditioned on the split column, so a pre-split extract emits a literal NULL
184
+ // and builds no join at all.
164
185
  const { select: encyclopedicSelect, join: encyclopedicJoin } = encyclopedicClauses.get(sch)!
165
186
 
166
- // Push the population boost into the ORDER BY when the index is available, so famous places
167
- // (whose long alt-name lists hurt BM25) actually make it into the over-fetch window. The TS
168
- // post-scoring will still compute the same boost for the final score; this just ensures the
169
- // candidate set is right.
187
+ // Push the population boost into the order BY when the index is available, so famous places
188
+ // (whose long alt-name lists hurt BM25) actually make it into the over-fetch window.
189
+ // The TS post-scoring will still compute the same boost for the final score.
190
+ // This just ensures the candidate set is right.
170
191
  //
171
192
  // Formula: rank_adjusted = bm25 - populationBoost * min(1.0, log10(1 + pop) / scaleLog10)
172
193
  // Lower rank_adjusted = better (matches SQLite's bm25 convention of "more negative = better").
173
194
  //
174
- // #905 — do NOT reach for bm25 column weights here. Measured falsification (2026-07-02): FTS5's
175
- // bm25 length normalization is polluted by the row's TOTAL document size, so identical 1-token
176
- // `name` docs read −16.0 (empty alt_names) vs −0.43 (2.7 KB alt_names) EVEN with the alt_names
177
- // column weighted to zero — no weighting isolates name relevance in this schema. The famous-
178
- // holder guarantee lives in the population-ordered companion fetch below instead, and the
179
- // exact tier breaks ties by population in the post-scoring sort.
195
+ // Do not reach for bm25 column weights here.
196
+ // FTS5's bm25 length normalization is polluted by the row's total document size,
197
+ // so identical 1-token `name` docs read −16.0 (empty alt_names) against −0.43
198
+ // (2.7 KB alt_names) even with the alt_names column weighted to zero.
199
+ // The population-ordered companion fetch below returns the most populous holder of the name.
200
+ // The post-scoring sort uses population to break ties in the exact tier.
180
201
  const orderByExpr = extractHasPopulation
181
202
  ? `(bm25(place_search) - ? * MIN(1.0, COALESCE(log10(1.0 + ${PLACE_POPULATION_TABLE}.population), 0) / ?))`
182
203
  : "bm25(place_search)"
183
204
 
184
- // Schema-qualified FROM with bare-name MATCH — required syntax for FTS5 on attached schemas.
185
- // See extracts.ts header for the failure mode that drove this design.
205
+ // Schema-qualified `from` with a bare-name `match` is required syntax for FTS5 on attached schemas.
206
+ // See the extracts.ts header for the failure mode that drove this design.
186
207
  const stmt = db.prepare(`
187
208
  SELECT
188
209
  spr.id AS id,
@@ -213,12 +234,13 @@ export function fetchSearchRows<DB>(options: {
213
234
 
214
235
  const rawRows = allRows<RawSearchRow>(stmt, ...params)
215
236
 
216
- // #905 companion fetch: the same MATCH, ordered by population alone. For name floods
217
- // ("Paris" matches thousands of gap-fill villages) the bm25-based window above cannot admit
218
- // the famous holder — its bm25 is length-poisoned by the row's alias bulk (measured ~15 pts,
219
- // vs a +4.0 boost cap), so FR Paris never even reaches post-scoring. This fetch makes the
220
- // prominent holders of a name pool-complete BY CONSTRUCTION; the exact-tier sort below
221
- // decides whether they win. Skipped without a population index (nothing to order by).
237
+ // Companion fetch: the same `match`, ordered only by population.
238
+ // For name floods ("Paris" matches thousands of gap-fill villages) the bm25-based
239
+ // window above cannot admit the famous holder, because its bm25 is length-poisoned
240
+ // by the row's alias bulk (measured ~15 pts, against a +4.0 boost cap).
241
+ // This fetch includes prominent holders of a name in the candidate pool.
242
+ // The exact-tier sort below decides whether they win.
243
+ // It is skipped without a population index, since there is no column to order by.
222
244
  if (extractHasPopulation) {
223
245
  const popStmt = db.prepare(`
224
246
  SELECT
@@ -2,46 +2,40 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * `SqliteConventionSource` — a `ConventionSource` backed by the build-from-source convention asset
7
- * (#290, Direction E). Conventions live in a read-only, provenance-stamped `address_convention`
8
- * table keyed by WOF polygon id; this source queries them ON DEMAND by id (one indexed lookup,
9
- * memoized) rather than paging the whole table into memory as a code constant — the deliberate
10
- * counter to the Pelias "giant dictionary in RAM, no provenance" pattern (see the operator design
11
- * value in memory `feedback-no-irrelevant-trivia`).
12
- *
13
- * The asset is the queryable, distributable artifact; the strategy IMPLEMENTATIONS stay in code. An
14
- * unknown strategy NAME is surfaced loudly at dispatch (see `lookup.ts`), not silently
15
- * swallowed.
16
5
  */
17
6
 
18
7
  import { tryParsingJSON } from "@mailwoman/core/json"
19
8
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
20
9
 
21
- import { ADDRESS_CONVENTION_TABLE, type Convention, type ConventionSource } from "#convention/index"
10
+ import { ADDRESS_CONVENTION_TABLE, type Convention, type ConventionSource } from "#convention"
22
11
 
12
+ /**
13
+ * Reads per-place address conventions from the convention table in an attached
14
+ * SQLite schema, caching each lookup, including misses.
15
+ *
16
+ * A missing table or unparseable row reads as no convention rather than throwing.
17
+ */
23
18
  export class SqliteConventionSource<DB> implements ConventionSource {
24
19
  readonly #db: DatabaseClient<DB>
25
20
  readonly #schema: string
26
- /**
27
- * Memoize per-id lookups (including misses, as `null`) so a hot ancestor chain is queried once.
28
- */
21
+
29
22
  readonly #cache = new Map<number, Convention | null>()
30
23
 
31
24
  /**
32
- * @param db An open handle to a DB that has the convention asset attached (or is it).
33
- * @param schema The schema name the `address_convention` table lives under (`main` or an ATTACHed extract name —
34
- * `WOFSQLitePlaceLookup` auto-detects which extract carries the table).
25
+ * Creates a source over an open database that is, or has attached, the convention asset.
26
+ *
27
+ * @param db The open database handle.
28
+ * @param schema The schema that holds the convention table, either `main` or an attached extract's name.
35
29
  */
36
30
  constructor(db: DatabaseClient<DB>, schema: string) {
37
31
  this.#db = db
38
32
  this.#schema = schema
39
33
  }
40
34
 
41
- get(wofID: number): Convention | undefined {
35
+ get(wofID: number): Convention | null {
42
36
  const cached = this.#cache.get(wofID)
43
37
 
44
- if (cached !== undefined) return cached ?? undefined
38
+ if (cached !== undefined) return cached
45
39
  let value: Convention | null = null
46
40
 
47
41
  try {
@@ -53,13 +47,11 @@ export class SqliteConventionSource<DB> implements ConventionSource {
53
47
  value = tryParsingJSON<Convention>(row.convention)
54
48
  }
55
49
  } catch {
56
- // A missing table → treat as no override (the chain falls back to WORLD_DEFAULT); malformed
57
- // JSON already nulls out above. The build script validates structure, so this is purely defensive.
58
50
  value = null
59
51
  }
60
52
 
61
53
  this.#cache.set(wofID, value)
62
54
 
63
- return value ?? undefined
55
+ return value
64
56
  }
65
57
  }
@@ -2,25 +2,22 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * Small shared helpers for the SQLite-backed lookups.
5
+ * @file Shared helpers for the SQLite-backed lookups.
7
6
  */
8
7
 
9
8
  import { allRows, getRow } from "@mailwoman/core/utils"
10
9
  import type { DatabaseClient, SQLInputValue } from "@mailwoman/sqlite/client"
11
10
  import { hasColumn as columnExists, tableExists } from "@mailwoman/sqlite/introspection"
12
11
 
13
- // The row-shape assertion itself lives in `core` so the readers that cannot depend on this package reach the same
14
- // helper; re-exported here because this module is where this package's readers already look for it.
15
-
16
12
  /**
17
- * A prepared single-row query whose parameter tuple remains visible to TypeScript. `StatementSync` accepts only the
18
- * broad `SQLInputValue[]`, which otherwise erases tagged key types before they reach SQLite.
13
+ * A prepared single-row query whose parameter tuple stays visible to TypeScript.
14
+ *
15
+ * `StatementSync` accepts any `SQLInputValue[]`, which would erase tagged key types at the call site.
19
16
  */
20
- export type PreparedGet<Parameters extends SQLInputValue[], Row> = (...parameters: Parameters) => Row | undefined
17
+ export type PreparedGet<Parameters extends SQLInputValue[], Row> = (...parameters: Parameters) => Row | null
21
18
 
22
19
  /**
23
- * Prepare a single-row query while preserving its exact parameter tuple at every call site.
20
+ * Prepares a single-row query with a typed parameter tuple.
24
21
  */
25
22
  export function prepareGet<Parameters extends SQLInputValue[], Row, DB>(
26
23
  db: DatabaseClient<DB>,
@@ -32,12 +29,12 @@ export function prepareGet<Parameters extends SQLInputValue[], Row, DB>(
32
29
  }
33
30
 
34
31
  /**
35
- * Multi-row counterpart to {@link PreparedGet}.
32
+ * The multi-row counterpart to {@link PreparedGet}.
36
33
  */
37
34
  export type PreparedAll<Parameters extends SQLInputValue[], Row> = (...parameters: Parameters) => Row[]
38
35
 
39
36
  /**
40
- * Prepare a multi-row query while preserving its exact parameter tuple at every call site.
37
+ * Prepares a multi-row query with a typed parameter tuple.
41
38
  */
42
39
  export function prepareAll<Parameters extends SQLInputValue[], Row, DB>(
43
40
  db: DatabaseClient<DB>,
@@ -49,10 +46,11 @@ export function prepareAll<Parameters extends SQLInputValue[], Row, DB>(
49
46
  }
50
47
 
51
48
  /**
52
- * True when `name` is a table in the open database. The street-level lookups use this to degrade gracefully on an
53
- * empty/tableless extract — an interrupted `build-*-extract.ts`, or a stray 0-byte file (e.g. `sqlite3 <missing>.db
54
- * "…"` CREATES one) — rather than throwing `no such table` at construction and taking down a whole state's geocode
55
- * (#568). A missing table makes the lookup a no-op miss.
49
+ * Returns true when `name` is a table in the open database.
50
+ * Returns false when the check itself fails.
51
+ *
52
+ * The street-level lookups call it so that an empty extract, such as an interrupted build
53
+ * or a zero-byte file, makes every lookup miss instead of throwing `no such table`.
56
54
  */
57
55
  export function hasTable<DB>(db: DatabaseClient<DB>, name: string): boolean {
58
56
  try {
@@ -63,15 +61,14 @@ export function hasTable<DB>(db: DatabaseClient<DB>, name: string): boolean {
63
61
  }
64
62
 
65
63
  /**
66
- * True when `table` exists in the open database AND carries `column`.
64
+ * Returns true when `table` exists in the open database and has `column`.
67
65
  *
68
- * The column-level sibling of {@link hasTable}, and it exists for the same reason one layer down: an artifact built
69
- * before a column was added is still a VALID artifact, and a reader that unconditionally names the new column in its
70
- * `SELECT` turns "this gazetteer is a build behind" into `no such column` at the first keystroke. Probe once at
71
- * construction and shape the query — `table_info` is a PRAGMA, so it must not sit on a per-query path.
66
+ * An artifact built before a column was added is still valid, so readers call this once
67
+ * at construction and leave the column out of their queries when it is missing.
68
+ * Avoid calling it per query, because it runs a pragma.
72
69
  *
73
- * Note the interpolation: PRAGMA does not take bound parameters, so `table` is spliced. Every caller passes a
74
- * module-level constant; never pass user input.
70
+ * The pragma does not accept bound parameters, so `table` is interpolated into the SQL.
71
+ * Pass only constants.
75
72
  */
76
73
  export function hasColumn<DB>(db: DatabaseClient<DB>, table: string, column: string): boolean {
77
74
  try {
@@ -3,21 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Typed schema for the DERIVED STREET-CENTROID extract (`street-centroids-<cc>.db`, built by
7
- * `ban/scripts/build-street-centroid-extract.ts` — the #1042 street-level tier behind "street-only
8
- * FR queries deserve a street-level answer"). The extract is a `GROUP BY street` roll-up of the
9
- * sealed rooftop address-point extract: one row per (street, postcode, commune) carrying the street's
10
- * CENTROID + bounding-box EXTENT + member-point count. No new data source — a derived artifact.
11
- *
12
- * Single source of truth for the columns shared by the BUILDER (a positional prepared INSERT for
13
- * throughput) and the READER ({@link StreetCentroidSqliteLookup}), so a column rename in one is a
14
- * compile error in the other — the same discipline as `address-point-schema.ts`.
15
- *
16
- * Probe scopes (most-selective first): by `postcode`, else by `locality_base` (the
17
- * arrondissement-stripped commune — see `stripArrondissement`; BAN names Paris/Lyon/Marseille rows
18
- * per arrondissement, but a query names the base commune). The reader WEIGHTED-aggregates across the
19
- * matched rows (by `point_count`) so a locality-scope probe returns the street's grand centroid over
20
- * every postcode/arrondissement it spans.
6
+ * Schema for the street-centroid extract. It groups address-point rows by street.
21
7
  */
22
8
 
23
9
  import type { Kysely } from "kysely"
@@ -25,30 +11,36 @@ import type { Kysely } from "kysely"
25
11
  import type { NameKey, StreetKey } from "#street/normalize"
26
12
 
27
13
  /**
28
- * One street roll-up. `(street_norm, postcode, locality_base)` is unique. `lat`/`lon` are the UNWEIGHTED mean of the
29
- * group's member address points (each source row = one point), so a cross-group weighted mean (`SUM(lat*point_count) /
30
- * SUM(point_count)`) reconstructs the grand centroid. `min_/max_lat/lon` are the group's extent (the reader turns the
31
- * bbox diagonal into an honest `uncertainty_m`).
14
+ * One row per street, postcode and commune.
15
+ *
16
+ * The tuple `(street_norm, postcode, locality_base)` is unique.
17
+ * The reader combines rows by `SUM(lat * point_count) / SUM(point_count)` to get a
18
+ * street's centroid across every postcode and arrondissement it spans.
19
+ *
20
+ * It converts the bounding-box diagonal from the `min_*` and `max_*` columns into `uncertainty_m`.
32
21
  */
33
22
  export interface StreetCentroidTable {
34
23
  /**
35
- * Shared `normalizeStreetForKeyLocale` of the street — the build/query-consistent probe key.
24
+ * The street key from `normalizeStreetForKeyLocale`, which both the builder and the reader use.
36
25
  */
37
26
  street_norm: StreetKey
38
27
  /**
39
- * The 5-digit postcode of this group, or null when the source row carried none.
28
+ * The postcode of this group, or null when the source row has none.
40
29
  */
41
30
  postcode: string | null
42
31
  /**
43
- * Arrondissement-stripped commune (`stripArrondissement(normalizeLocalityForKey(commune))`) — the fallback scope.
32
+ * The commune key with any arrondissement removed.
33
+ *
34
+ * BAN stores Paris, Lyon and Marseille per arrondissement, but a query usually gives the base commune.
35
+ * The reader uses this column when the query has no postcode.
44
36
  */
45
37
  locality_base: NameKey
46
38
  /**
47
- * Weighted-mean centroid latitude of the street's member points.
39
+ * The mean latitude of the group's member address points.
48
40
  */
49
41
  lat: number
50
42
  /**
51
- * Weighted-mean centroid longitude of the street's member points.
43
+ * The mean longitude of the group's member address points.
52
44
  */
53
45
  lon: number
54
46
  min_lat: number
@@ -56,31 +48,32 @@ export interface StreetCentroidTable {
56
48
  min_lon: number
57
49
  max_lon: number
58
50
  /**
59
- * Member address-point count — the weight for a cross-group centroid aggregate.
51
+ * The number of member address points.
52
+ * This value weights the cross-row centroid.
60
53
  */
61
54
  point_count: number
62
55
  /**
63
- * A representative street name as it appeared in the source (display / debugging).
56
+ * One street name as it appeared in the source, kept for display and debugging.
64
57
  */
65
58
  street_raw: string
66
59
  /**
67
- * Provenance: the register this street was derived from (e.g. `ban:fr`).
60
+ * The source register, such as `ban:fr`.
68
61
  */
69
62
  source: string
70
63
  /**
71
- * The pinned data release the underlying points came from.
64
+ * The data release that the member points came from.
72
65
  */
73
66
  release: string
74
67
  /**
75
- * #727 phase-4c: `foldStreetSurface(street_raw)` — the contract-fold street-NAME existence key for
76
- * {@link StreetLocalityEvidence}. Distinct from `street_norm` (the `street-normalize` geocoding key): the
77
- * name-evidence rerank folds the model's street surface with the SAME `foldStreetSurface` used to build this column
78
- * (the fold-parity contract), so it must not drift from `street_norm`'s richer normalizer. Indexed (`idx_sc_name`)
79
- * for a direct seek.
68
+ * The value of `foldStreetSurface(street_raw)`, used by {@link StreetLocalityEvidence}
69
+ * to check that a street name exists.
70
+ *
71
+ * The name-evidence rerank folds the model's street text with the same function, so this
72
+ * column must use `foldStreetSurface` and must not follow the `street_norm` normalizer.
73
+ * The type is a plain string because `foldStreetSurface` differs from the
74
+ * {@link NameKey} fold that other `name_key` columns use.
80
75
  *
81
- * Deliberately UNBRANDED despite the `name_key` column name: `foldStreetSurface` is a different fold from the
82
- * {@link NameKey} one every other `name_key` column carries, and giving it that brand would invite exactly the
83
- * cross-fold probe the brands exist to stop.
76
+ * A `NameKey` type would allow a probe with the wrong fold.
84
77
  */
85
78
  name_key: string
86
79
  }
@@ -93,8 +86,9 @@ export interface StreetCentroidDatabase {
93
86
  }
94
87
 
95
88
  /**
96
- * The `street_centroid` columns in INSERT order. The builder's positional prepared statement derives its placeholder
97
- * list from this, so the positional order can't drift from the DDL / the reader.
89
+ * The `street_centroid` columns in insert order.
90
+ *
91
+ * The builder derives the placeholders of its positional insert from this list.
98
92
  */
99
93
  export const STREET_CENTROID_COLUMNS = [
100
94
  "street_norm",
@@ -114,7 +108,7 @@ export const STREET_CENTROID_COLUMNS = [
114
108
  ] as const
115
109
 
116
110
  /**
117
- * Create the `street_centroid` table — called before the streaming bulk load.
111
+ * Creates the `street_centroid` table before the bulk load.
118
112
  */
119
113
  export async function createStreetCentroidTable(db: Kysely<StreetCentroidDatabase>): Promise<void> {
120
114
  await db.schema
@@ -137,9 +131,10 @@ export async function createStreetCentroidTable(db: Kysely<StreetCentroidDatabas
137
131
  }
138
132
 
139
133
  /**
140
- * Create the probe indexes: the two geocoding-scope indexes (postcode, locality-base) the resolver reader relies on,
141
- * plus `idx_sc_name` — the #727 phase-4c name-existence key for a direct `name_key = ?` seek (the unscoped fragment
142
- * lookup; without it that query skip-scans `idx_sc_postcode` at ~5 ms/probe).
134
+ * Creates the postcode and locality indexes that the reader probes, plus `idx_sc_name`.
135
+ *
136
+ * The `idx_sc_name` index serves the unscoped `name_key = ?` lookup.
137
+ * Without it, SQLite would skip-scan `idx_sc_postcode`.
143
138
  */
144
139
  export async function createStreetCentroidIndexes(db: Kysely<StreetCentroidDatabase>): Promise<void> {
145
140
  await db.schema.createIndex("idx_sc_postcode").on("street_centroid").columns(["postcode", "street_norm"]).execute()