@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,33 +3,28 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Node reader over the POSTAL-CITY ALIAS table (`postal-city-alias-<cc>.db`) — the observed
6
+ * Node reader over the postal-city alias table (`postal-city-alias-<cc>.db`) — the observed
7
7
  * `postal_city → geo_locality` aliases per postcode (`build-postal-city-alias.ts`). Consumed by
8
8
  * {@link WOFSQLitePlaceLookup}'s coordinate-first locality scorer: a user-typed postal city
9
9
  * ("Antioch", postcode 37013) becomes a name-match alias for the geographic locality the postcode
10
- * actually sits in ("Nashville"), so the right place tiers to the top instead of a same-named
11
- * town in another state. Opt-in — the lookup is only constructed when a path is supplied, and
10
+ * actually sits in ("Nashville"), so the right place tiers to the top instead of a place with the same name
11
+ * town in another state. Opt-in — the lookup is only constructed when a path is supplied.
12
12
  * absent it the resolver is byte-identical.
13
13
  *
14
- * The reader returns RAW divergent rows for a postcode; normalization + name-matching against the
14
+ * The reader returns RAW divergent rows for a postcode. normalization + name-matching against the
15
15
  * candidate localities is the scorer's job (it owns the case/diacritic fold the soft name score
16
16
  * uses), keeping one normalizer in one place.
17
17
  */
18
18
 
19
- import { DatabaseClient } from "@mailwoman/sqlite/client"
19
+ import { SQLiteLookup, type SQLiteLookupOptions } from "@mailwoman/sqlite/lookup"
20
20
 
21
21
  import type { PostalCityAliasDatabase } from "#postal/city/alias/schema"
22
22
 
23
- export interface WOFPostalCityAliasLookupOpts {
24
- /**
25
- * Path to a `postal-city-alias-<cc>.db` built by `build-postal-city-alias.ts`. Opened read-only.
26
- */
27
- databasePath?: string
28
- /**
29
- * Pre-opened handle (tests / shared connections). Mutually exclusive with `databasePath`.
30
- */
31
- database?: DatabaseClient<PostalCityAliasDatabase>
32
- }
23
+ /**
24
+ * Where a {@link WOFPostalCityAliasLookup} reads from: a `postal-city-alias-<cc>.db` built by
25
+ * `build-postal-city-alias.ts`, opened read-only, or a connection the caller already holds.
26
+ */
27
+ export type WOFPostalCityAliasLookupOpts = SQLiteLookupOptions<PostalCityAliasDatabase>
33
28
 
34
29
  /**
35
30
  * One divergent alias edge: the postal-system name and the geographic locality it maps to.
@@ -50,38 +45,29 @@ export interface PostalCityAlias {
50
45
  }
51
46
 
52
47
  /**
53
- * Reader over `postal_city_alias`. The only query is a postcode-scoped probe for DIVERGENT rows (where the postal name
54
- * differs from the geographic name — the rows that carry alias signal), issued via the typed Kysely query builder
55
- * against {@link PostalCityAliasDatabase}.
48
+ * Reader over `postal_city_alias`.
49
+ *
50
+ * The only query is a postcode-scoped probe for divergent rows
51
+ * (where the postal name differs from the geographic name — the rows that provide alias signal),
52
+ * issued via the typed Kysely query builder against {@link PostalCityAliasDatabase}.
56
53
  */
57
- export class WOFPostalCityAliasLookup implements Disposable {
58
- #db: DatabaseClient<PostalCityAliasDatabase>
59
- /**
60
- * Resources this instance opened. A connection handed in by a caller is NOT in here, so disposal cannot reach it —
61
- * ownership is membership rather than a flag a later branch has to check.
62
- */
63
- readonly #resources = new DisposableStack()
64
-
54
+ export class WOFPostalCityAliasLookup extends SQLiteLookup<PostalCityAliasDatabase> {
65
55
  constructor(opts: WOFPostalCityAliasLookupOpts) {
66
- if (opts.database) {
67
- this.#db = opts.database
68
- } else if (opts.databasePath) {
69
- this.#db = this.#resources.use(new DatabaseClient<PostalCityAliasDatabase>(opts.databasePath, { readOnly: true }))
70
- } else {
71
- throw new Error("WOFPostalCityAliasLookup needs `databasePath` or `database`")
72
- }
56
+ super(opts)
73
57
  }
74
58
 
75
59
  /**
76
- * Divergent postal-city aliases for a postcode (empty when the postcode isn't in the table). The scorer groups these
77
- * by normalized `geoLocality` and appends the `postalCity` surfaces to the matching candidate locality's alias set.
60
+ * Divergent postal-city aliases for a postcode (empty when the postcode isn't in the table).
61
+ *
62
+ * The scorer groups these by normalized `geoLocality` and appends the `postalCity`
63
+ * surfaces to the matching candidate locality's alias set.
78
64
  */
79
65
  async getDivergentAliases(postcode: string): Promise<PostalCityAlias[]> {
80
66
  const pc = postcode.trim()
81
67
 
82
68
  if (!pc) return []
83
69
 
84
- const rows = await this.#db
70
+ const rows = await this.database
85
71
  .selectFrom("postal_city_alias")
86
72
  .select(["postal_city", "geo_locality", "n"])
87
73
  .where("postcode", "=", pc)
@@ -90,8 +76,4 @@ export class WOFPostalCityAliasLookup implements Disposable {
90
76
 
91
77
  return rows.map((r) => ({ postalCity: String(r.postal_city), geoLocality: String(r.geo_locality), n: Number(r.n) }))
92
78
  }
93
-
94
- [Symbol.dispose](): void {
95
- this.#resources[Symbol.dispose]()
96
- }
97
79
  }
@@ -3,15 +3,15 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Typed schema for the POSTAL-CITY ALIAS table (`postal-city-alias-<cc>.db`, built by
6
+ * Typed schema for the postal-city alias table (`postal-city-alias-<cc>.db`, built by
7
7
  * `scripts/build-postal-city-alias.ts`) — the single source of truth for the columns shared by
8
- * the BUILDER and the READER ({@link WOFPostalCityAliasLookup}). Like {@link CandidateTable}, the
9
- * contract is a Kysely `Database` interface plus the table DDL as a string, so a column rename in
8
+ * the builder and the reader ({@link WOFPostalCityAliasLookup}). Like {@link CandidateTable}, the
9
+ * interface is a Kysely `Database` interface plus the table DDL as a string, so a column rename in
10
10
  * the builder is a compile error in the reader.
11
11
  *
12
- * Provenance discipline (provenance-first): this is a SIBLING table to the PIP-derived
12
+ * Provenance discipline (provenance-first): this is a sibling table to the PIP-derived
13
13
  * `postcode_locality` data, never mixed into it — one table, one provenance class. Each row is an
14
- * OBSERVED `(postcode, postal_city, geo_locality)` aggregate from Overture's `postal_city` field
14
+ * observed `(postcode, postal_city, geo_locality)` aggregate from Overture's `postal_city` field
15
15
  * with a usage count `n`; `divergent = 1` exactly when `postal_city != geo_locality` (the alias
16
16
  * signal — the only rows the resolver consumes).
17
17
  */
@@ -19,8 +19,10 @@
19
19
  import type { Kysely } from "kysely"
20
20
 
21
21
  /**
22
- * One observed postal-city aggregate. The natural key is `(postcode, postal_city, geo_locality)`; the builder enforces
23
- * a `MIN_COUNT` floor on `n`, so every row is a non-trivial usage.
22
+ * One observed postal-city aggregate.
23
+ *
24
+ * The natural key is `(postcode, postal_city, geo_locality)`; the builder enforces a
25
+ * `MIN_COUNT` floor on `n`, so every row is a non-trivial usage.
24
26
  */
25
27
  export interface PostalCityAliasTable {
26
28
  /**
@@ -61,7 +63,7 @@ export interface PostalCityAliasDatabase {
61
63
  }
62
64
 
63
65
  /**
64
- * The `postal_city_alias` column order — the builder's INSERT derives its column list from this.
66
+ * The `postal_city_alias` column order — the builder's insert derives its column list from this.
65
67
  */
66
68
  export const POSTAL_CITY_ALIAS_COLUMNS = [
67
69
  "postcode",
@@ -74,8 +76,10 @@ export const POSTAL_CITY_ALIAS_COLUMNS = [
74
76
  ] as const
75
77
 
76
78
  /**
77
- * Create the `postal_city_alias` table + its two probe indexes. Kept here (not only in the builder) so tests can stand
78
- * up a fixture DB with the exact production shape. Pass a {@link DatabaseClient} (or any `Kysely`) over the alias DB.
79
+ * Create the `postal_city_alias` table + its two probe indexes.
80
+ *
81
+ * Kept here (not only in the builder) so tests can stand up a fixture DB with the exact production shape.
82
+ * Pass a {@link DatabaseClient} (or any `Kysely`) over the alias DB.
79
83
  */
80
84
  export async function createPostalCityAliasTable(db: Kysely<PostalCityAliasDatabase>): Promise<void> {
81
85
  await db.schema
@@ -3,19 +3,18 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Typed schema for the POSTAL-CITY CANDIDATE side-index (#741 / #475) — a small `(name_key,
7
- * postcode) → geo-locality` table that lives alongside the byte-range `candidate` table so the
8
- * candidate-backend resolver (the demo/CLI default) can do what the FTS coordinate-first scorer
9
- * does: resolve a user-typed POSTAL city ("Antioch", 37013) to the geographic locality the
10
- * postcode sits in ("Nashville").
6
+ * Typed schema for the postal-city candidate side-index, a small `(name_key, postcode)` →
7
+ * geo-locality table alongside the byte-range `candidate` table.
8
+ * The candidate-backend resolver (the demo and CLI default) can then match the FTS coordinate-first scorer.
9
+ * It resolves a user-typed postal city ("Antioch", 37013) to the geographic locality the postcode
10
+ * resolve a user-typed postal city ("Antioch", 37013) to the geographic locality the postcode
11
+ * sits in ("Nashville").
11
12
  *
12
- * Why a SIDE-INDEX, not cloned `candidate` rows: the `candidate` B-tree is keyed `(name_key,
13
- * country_id, region_id, placetype_id, …)` and ranked population-first — it has no postcode
14
- * dimension. A cloned alias row was tested (#741) and falsified: a sentinel rank is
15
- * bare-name-safe but then loses to any in-region homonym, and there is no single rank that is
16
- * both. The fix is an EXACT `(name_key, postcode)` probe that bypasses population/region ranking
17
- * entirely — consulted only when the query carries a postcode, so the common no-postcode path is
18
- * untouched.
13
+ * The `candidate` B-tree is keyed `(name_key, country_id, region_id, placetype_id, …)` and ranked
14
+ * population-first, so it has no postcode dimension. A cloned alias row cannot satisfy both
15
+ * purposes, because a sentinel rank is bare-name-safe and then loses to any in-region homonym.
16
+ * The exact `(name_key, postcode)` probe bypasses population and region ranking entirely.
17
+ * It runs only when the query includes a postcode, leaving the common no-postcode path untouched.
19
18
  */
20
19
 
21
20
  import { sql, type Kysely } from "kysely"
@@ -23,12 +22,14 @@ import { sql, type Kysely } from "kysely"
23
22
  import type { NameKey } from "#street/normalize"
24
23
 
25
24
  /**
26
- * One postal-city → geo-locality edge, keyed exactly by `(name_key, postcode)`. The probe returns the geographic
27
- * locality directly; the denormalized name/coord avoid a join back to `candidate`.
25
+ * One postal-city → geo-locality edge, keyed exactly by `(name_key, postcode)`.
26
+ *
27
+ * The probe returns the geographic locality directly.
28
+ * The denormalized name/coord avoid a join back to `candidate`.
28
29
  */
29
30
  export interface PostalCityCandidateTable {
30
31
  /**
31
- * {@link normalizeLocalityForKey} of the postal-city name — the build/query-consistent probe key.
32
+ * {@link normalizeLocalityForKey} of the postal-city name, the build/query-consistent probe key.
32
33
  */
33
34
  name_key: NameKey
34
35
  /**
@@ -60,7 +61,7 @@ export interface PostalCityCandidateDatabase {
60
61
  export const POSTAL_CITY_CANDIDATE_TABLE = "postal_city_candidate"
61
62
 
62
63
  /**
63
- * Column order for the builder's positional INSERT.
64
+ * Column order for the builder's positional insert.
64
65
  */
65
66
  export const POSTAL_CITY_CANDIDATE_COLUMNS = [
66
67
  "name_key",
@@ -72,9 +73,14 @@ export const POSTAL_CITY_CANDIDATE_COLUMNS = [
72
73
  ] as const
73
74
 
74
75
  /**
75
- * Create the side-index — a clustered `WITHOUT ROWID` B-tree on `(name_key, postcode)` so the resolve is a single exact
76
- * probe. Idempotent (`IF NOT EXISTS`); pass a {@link DatabaseClient} (or any `Kysely`) over the candidate DB. The
77
- * Kysely schema-builder is the house idiom for table creation — see `AGENTS.md` (inline-SQL → Kysely).
76
+ * Create the side-index, a clustered `without rowid` B-tree on `(name_key, postcode)`
77
+ * so the resolve is a single exact probe.
78
+ *
79
+ * Idempotent (`if not exists`).
80
+ * Pass a {@link DatabaseClient} (or any `Kysely`) over the candidate DB.
81
+ *
82
+ * The Kysely schema-builder is the house idiom for table creation.
83
+ * See `agents.md` on inline SQL and Kysely.
78
84
  */
79
85
  export async function createPostalCityCandidateTable(db: Kysely<PostalCityCandidateDatabase>): Promise<void> {
80
86
  await db.schema
@@ -87,7 +93,7 @@ export async function createPostalCityCandidateTable(db: Kysely<PostalCityCandid
87
93
  .addColumn("latitude", "real", (c) => c.notNull())
88
94
  .addColumn("longitude", "real", (c) => c.notNull())
89
95
  .addPrimaryKeyConstraint("postal_city_candidate_pk", ["name_key", "postcode"])
90
- // `WITHOUT ROWID` has no first-class builder; the raw modifier is the idiomatic fallback.
96
+ // `without rowid` has no first-class builder. The raw modifier is the idiomatic fallback.
91
97
  .modifyEnd(sql`without rowid`)
92
98
  .execute()
93
99
  }
@@ -2,53 +2,34 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * SQLite-backed postcode lookup for the postcode anchor (#240). A thin exact-match resolver over
7
- * one or more `postalcode-*.db` extracts (the `spr` schema built by `build-unified-wof --placetypes
8
- * postalcode`, then centroid-backfilled by `scripts/backfill-postcode-centroids.ts`).
9
- *
10
- * This is the production implementation of the `PostcodeResolver` interface consumed by
11
- * `@mailwoman/neural`'s `extractPostcodeAnchors`. It is deliberately dumb: an indexed exact-match
12
- * on the postcode string across every extract, unioned. No FTS, no ranking, no proximity — the
13
- * anchor only needs "does this string exist as a postcode, in which countries, near where". A
14
- * future WASM build swaps this for an FST-backed resolver behind the same `lookup()` interface.
15
- *
16
- * Why multiple extracts instead of the multi-extract `WOFSQLitePlaceLookup`: that resolver routes a
17
- * query to ONE extract by placetype, but every postcode extract shares `placetype='postalcode'`, so a
18
- * single query could only ever hit one country's extract. The anchor needs the union across
19
- * countries to build its country posterior, so it queries each extract directly.
20
5
  */
21
6
 
7
+ import type { PostcodePlace } from "@mailwoman/core/resolver"
22
8
  import { DatabaseClient } from "@mailwoman/sqlite/client"
9
+ import type { PathBuilderLike } from "path-ts"
23
10
 
24
11
  import type { WOFDatabase } from "#schema"
25
12
 
26
- /**
27
- * A gazetteer hit. `lat`/`lon` of 0 means the postcode is known but has no centroid (no admin parent).
28
- */
29
- export interface PostcodePlace {
30
- country: string
31
- lat: number
32
- lon: number
33
- }
34
-
35
13
  const LOOKUP_SQL =
36
14
  "SELECT country, latitude AS lat, longitude AS lon FROM spr WHERE name = ? AND placetype = 'postalcode' AND is_current != 0"
37
15
 
16
+ /**
17
+ * Looks up current WOF `postalcode` places by exact name across one or more read-only gazetteer databases.
18
+ */
38
19
  export class WOFPostcodeLookup {
39
20
  readonly #dbs: DatabaseClient<WOFDatabase>[]
40
21
  readonly #stmts: ReturnType<DatabaseClient["prepare"]>[]
41
22
 
42
23
  /**
43
- * Open each extract read-only and prepare its exact-match statement.
24
+ * Opens each database read-only and prepares its exact-match statement.
44
25
  */
45
- constructor(dbPaths: readonly string[]) {
26
+ constructor(dbPaths: readonly PathBuilderLike[]) {
46
27
  this.#dbs = dbPaths.map((p) => new DatabaseClient<WOFDatabase>(p, { readOnly: true }))
47
28
  this.#stmts = this.#dbs.map((db) => db.prepare(LOOKUP_SQL))
48
29
  }
49
30
 
50
31
  /**
51
- * Exact-match the postcode across every extract and union the rows.
32
+ * Returns the union of exact-name matches for `postcode` across every database, without deduplication.
52
33
  */
53
34
  lookup(postcode: string): PostcodePlace[] {
54
35
  const out: PostcodePlace[] = []
@@ -2,146 +2,91 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * Bounded cross-country PRIMARY-NAME preference over a population-ordered candidate row set — the
7
- * `is_primary` ranking signal, PURE and platform-free so the Node candidate lookup
8
- * (`candidate-lookup.ts`) and the browser twin (`packages/resolver-wof-wasm/lib/httpvfs/resolver.ts`) rank with
9
- * the SAME function rather than two copies that drift (the #861 server↔browser parity contract).
10
- *
11
- * Only type imports and arithmetic live here: anything with a `node:` import stays out.
12
5
  */
13
6
 
14
7
  import type { CandidateTable } from "#candidate/schema"
15
8
 
16
9
  /**
17
- * The row shape the re-rank needs. `is_primary` is optional: a reader over an artifact vintage that predates the column
18
- * omits it from the SELECT, no row reads as primary, and the re-rank no-ops by construction — the exact degradation the
19
- * browser reader's vintage guard relies on.
10
+ * The candidate-row fields that {@link rankByPrimaryPreference} reads.
11
+ *
12
+ * `is_primary` is optional, so an older artifact without the column skips the re-rank without an error.
20
13
  */
21
14
  export type PrimaryPreferenceRow = Pick<CandidateTable, "neg_rank" | "country_id"> &
22
15
  Partial<Pick<CandidateTable, "is_primary" | "placetype_id" | "population" | "name_role">>
23
16
 
24
17
  /**
25
- * Bounded PRIMARY-NAME preference across a CROSS-COUNTRY name collision (the `is_primary` ranking signal).
26
- *
27
- * The raw candidate order is population-first (`neg_rank ASC`) and treats an ALIAS row (a place's alt-name / exonym,
28
- * `is_primary=0`) and a PRIMARY-name row (`is_primary=1`) on equal footing. So a foreign place whose transliterated
29
- * exonym coincidentally normalizes to a query — Changchun CN stores the Turkish exonym "Çançun" (`name_key="cancun"`),
30
- * 4.19 M pop — outranks the PRIMARY-name place the query actually means (Cancún MX, 0.89 M pop). This penalty makes a
31
- * same-key alias have to clear a population MARGIN over a foreign primary before it wins.
18
+ * The log10-population penalty applied to an alias row when the top primary-name
19
+ * row for the same key is in another country.
32
20
  *
33
- * It is deliberately NOT a dominant sort key (no `ORDER BY is_primary DESC`, which would make every primary outrank
34
- * every alias and break the alt-names users depend on — "NYC"→New York, "LA"→Los Angeles, "Frisco"→San Francisco). Two
35
- * bounds keep it a soft prior:
36
- *
37
- * 1. **Cross-country only.** The penalty applies to an alias ONLY when the top-population primary sharing the key is in a
38
- * DIFFERENT country. A SAME-country nickname contest (San Francisco's alias "Frisco" vs the primary Frisco, TX —
39
- * both US) is left on pure population, so the legitimate alias still wins.
40
- * 2. **Population-bounded.** The penalty is {@link PRIMARY_PREFERENCE_LOG10} in log10-population units — an alias must be
41
- * at least 10x more populous than the foreign primary to still win. So a genuinely dominant alias keeps winning
42
- * ("Los Angeles" over La, Ghana — gap 1.6; "Las Vegas" over Vegas, Cuba — gap 2.4) while a near-tie coincidental
43
- * collision defers to the primary (Cancún over Changchun — gap 0.7).
21
+ * An alias at least 10 times as populous still wins, so alternate names such as "LA" keep working.
44
22
  */
45
23
  export const PRIMARY_PREFERENCE_LOG10 = 1
46
24
 
47
- /**
48
- * The placetype the seat preference promotes: the populated-place tier a district duplicate shares its name and its
49
- * population with. Named rather than inlined because narrowing the term to this ONE tier is what keeps it off the
50
- * region/county and locality/neighbourhood contests — see `rankByPrimaryPreference` for the measurement.
51
- */
52
25
  const SEAT_PLACETYPE = "locality"
53
26
 
54
27
  /**
55
- * Over-fetch cap for {@link rankByPrimaryPreference}: the candidate rows for one `name_key` (all same-name places
56
- * worldwide) are re-ranked in-process, so the probe fetches this many (population-ordered) before the re-rank rather
57
- * than the caller's small `limit`, ensuring the intended primary isn't pushed below the fold by a cluster of
58
- * more-populous foreign aliases. Bounded and small — a single contiguous B-tree scan.
28
+ * The number of population-ordered rows fetched per name key before
29
+ * {@link rankByPrimaryPreference} re-ranks them.
30
+ *
31
+ * A fetch of only the caller's `limit` could miss the intended primary behind more populous foreign aliases.
59
32
  */
60
33
  export const RERANK_FETCH = 64
61
34
 
62
35
  /**
63
- * A candidate row annotated with the {@link rankByPrimaryPreference} effective rank + the exact-tier demotion flag.
36
+ * A candidate row with its re-ranked `effectiveNegRank` and its `demoted` flag.
64
37
  */
65
38
  export type RankedRow<R> = R & {
66
39
  /**
67
- * `neg_rank` plus the bounded cross-country alias penalty — the value the row is ORDERED by, and the base the emitted
68
- * `prominence` is derived from (so the resolver walk's `prominence ?? score` sort, `resolve.ts`, agrees with this
69
- * order; the raw `score`/`neg_rank` is left intact for the walk's `minWinningScore` floor).
40
+ * `neg_rank` plus any cross-country alias penalty.
41
+ *
42
+ * Rows are ordered by this value.
43
+ * The emitted `prominence` derives from it.
44
+ *
45
+ * The raw `neg_rank` stays on the row for the resolver's minimum winning score.
70
46
  */
71
47
  effectiveNegRank: number
48
+
72
49
  /**
73
- * True when this row is a cross-country alias that LOST the bounded population contest to the same-key primary — a
74
- * coincidental foreign exonym (Changchun's "Çançun" for "Cancun"). Such a row is dropped out of the exact-match tier
75
- * (`exactMatch=false`) so the resolver walk's country pin — the model's `anchorPosterior`, which "never crosses the
76
- * exact/partial boundary" (`resolve.ts`) — can't ride a spurious posterior (CN 0.86 for "Cancun") back over the
77
- * primary. Only the LOSING foreign alias is demoted; a dominant alias (Los Angeles over La, Ghana) keeps its exact
78
- * tier, and a same-country nickname (San Francisco's "Frisco") is never touched.
50
+ * True when this row is a cross-country alias that the penalty moved behind
51
+ * the top primary for the same key.
52
+ *
53
+ * A demoted row loses `exactMatch`, so the resolver's country pin cannot promote
54
+ * a coincidental foreign exonym back over the primary.
55
+ * A dominant alias and a same-country alias are never demoted.
79
56
  */
80
57
  demoted: boolean
58
+
81
59
  /**
82
- * True when this row came from the TYPO-CORRECTOR tier — the FTS5-trigram fallback that fires only after the exact
83
- * and qualifier-strip probes both missed. Such a row answers a query the gazetteer does not contain, so it is a fuzzy
84
- * match by construction and must not claim `exactMatch` (#17). Recall is unaffected: the row is still returned, still
85
- * ranked, still resolvable — it just stops asserting a match quality it does not have, which is what the FTS backend
86
- * has always done and what every `exactMatch`-filtering consumer assumed.
60
+ * True when this row came from the typo-correction fallback.
61
+ * The fallback runs only after the exact probes miss.
62
+ *
63
+ * The row is still returned and ranked, but it never claims `exactMatch`.
87
64
  */
88
65
  fuzzy?: boolean
66
+
89
67
  /**
90
- * The admin-containment verdict (#1717 stage 2), stamped by `candidate-lookup.ts` when the query carried a
91
- * `regionQualifier` and the artifact carries the ancestors sidecar — the `fuzzy` precedent: a lookup-tier annotation
92
- * declared on the shared row shape. Tri-state like `ResolvedPlace.containedByQualifier`: absent means the question
93
- * was never asked, never "not contained".
68
+ * Whether the row lies inside the query's region qualifier.
69
+ *
70
+ * The candidate lookup sets it when the artifact has the ancestors sidecar.
71
+ *
72
+ * When the field is absent, containment was not checked.
94
73
  */
95
74
  containedByQualifier?: boolean
75
+
96
76
  /**
97
- * The #1882 exemption's firing mark (#1893): this row would have taken the cross-country alias penalty and
98
- * `exemptVariantAliases` prevented it. PRESENT only when the exemption changed this row's treatment; absent on every
99
- * row it merely inspected — an in-country variant, a primary, a set with no foreign top primary. The winner-level
100
- * receipt downstream (`mechanism_fired_on.variant_alias_exemption`) counts this mark only when it survives onto the
101
- * selected candidate.
77
+ * Set only when the variant-alias exemption spared this row the cross-country alias penalty.
102
78
  */
103
79
  variantExempted?: true
104
80
  }
105
81
 
106
82
  /**
107
- * Bounded cross-country primary-name preference (see {@link PRIMARY_PREFERENCE_LOG10}). Pure + total-ordered so
108
- * `candidate-lookup.test.ts` can exercise it on synthetic rows. `rows` arrive population-ordered (`neg_rank ASC`); an
109
- * alias (`is_primary=0`) is pushed back by `delta` in log10-population units ONLY when the top-population primary
110
- * sharing the key is in a different country, and is `demoted` out of the exact tier when that penalty leaves it BEHIND
111
- * the primary. Returns the top `limit` after the re-rank, each annotated.
83
+ * Re-ranks population-ordered candidate rows.
112
84
  *
113
- * `placetypes` (the artifact's own `placetype_codes` map) enables the LAST tiebreak, the SEAT preference: when
114
- * `effectiveNegRank` and raw `neg_rank` both tie — two same-key rows population cannot separate at all — a
115
- * {@link SEAT_PLACETYPE} row carrying a real population outranks every other placetype. Omit the map and every row
116
- * scores 0, the term cancels, and the order is exactly the population-then-scan-order it was before.
85
+ * A cross-country alias must beat the top primary by `delta` in log10 population.
86
+ * The function returns the top `limit` rows.
117
87
  *
118
- * The tie it exists for is a DUPLICATE, not a contest. A district and its identically-named seat town are stored as two
119
- * rows carrying the SAME population, so `neg_rank` is equal to the bit and `referential` follows it
120
- * (`referentialFromPopulation` is a pure function of population). Turkey's `Of` is the measured case — locality
121
- * 8114738869649 and its parent county 8837168432019 both hold population 44212 — and 358 locality/parent-county pairs
122
- * across 15 countries share the shape in `admin-global-priority.db` (TR 162, CA 77, US 47, HR 24, DO 14). Without the
123
- * term their order is whatever the scan hands the sorter.
124
- *
125
- * WHERE THE TERM DECIDES, AND WHERE IT CANNOT (#1729). It binds inside `findPlace`, so it orders every row set that
126
- * actually CONTAINS the tie — but the resolver walk's probes all carry a placetype filter, and
127
- * `PLACETYPE_FILTER_GROUPS` (core/resolver) never mixes `locality` with `county`: the `Of`-shape locality/county pair
128
- * is PARTITIONED before this ranker runs, the locality probe fetches one row, and the walk's own `locality` request
129
- * selects the seat by construction — the same winner, decided upstream. The tie that reaches an end-to-end answer
130
- * through this term is the IN-GROUP residue: a locality/localadmin (or borough) duplicate whose `importance` values
131
- * also tie. Downstream the resolver re-sorts by importance (`resolver/toponym-prior.ts`) but is stable on equal keys,
132
- * so the order stamped here is the order that answers — inverting this term moves bare `Pu-cheng-hsien` 1,100 km
133
- * (locality Pucheng over the 浦城县 localadmin, identical population and importance). Where importance separates the pair,
134
- * the fame prior overrides by design; a probe with NO placetype filter (the browser cascade's last resort, the dev
135
- * lookup tools) presents the full tie and this term is all that breaks it.
136
- *
137
- * BOTH CONDITIONS ARE required, and a plain "finer placetype wins" measured wrong before this shape was settled: it
138
- * moved the top slot on 11,377 keys in `candidate.db`, of which only 722 were the seat/district duplicate. The rest
139
- * were contests between genuinely distinct places that merely tie — 2,885 `locality → neighbourhood` (a bare city name
140
- * losing to a same-named hood), 2,973 `region → county`, 2,662 `postalcode → locality` — and 7,179 of the 11,377 sat at
141
- * population 0, where a tie means NO EVIDENCE rather than equal evidence. Requiring a real population keeps the term
142
- * off every no-evidence tie; promoting the populated-place tier specifically, rather than whatever is finer, keeps it
143
- * off the admin-tier and hood contests. It can never reach a pair population separates: it does not override a
144
- * population gap, it replaces an undetermined order with a stated one.
88
+ * When `placetypes` is given, an exact tie prefers a populated `locality`.
89
+ * This places a seat town ahead of a district with the same name and population.
145
90
  */
146
91
  export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
147
92
  rows: readonly R[],
@@ -150,9 +95,7 @@ export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
150
95
  placetypes?: ReadonlyMap<number, string>,
151
96
  exemptVariantAliases = false
152
97
  ): Array<RankedRow<R>> {
153
- // The primary the alias actually competes with for the top slot: highest population (min neg_rank). Undefined
154
- // when the set has no primary → nothing to prefer, penalty is 0, order stays population-first (today's behavior).
155
- let topPrimary: R | undefined
98
+ let topPrimary: R | null = null
156
99
 
157
100
  for (const r of rows) {
158
101
  if (r.is_primary === 1 && (topPrimary == null || r.neg_rank < topPrimary.neg_rank)) {
@@ -162,14 +105,6 @@ export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
162
105
 
163
106
  const topCountry = topPrimary?.country_id
164
107
 
165
- // A cross-country alias (different country than the top primary) is penalized; it is DEMOTED when even after — i.e.
166
- // the penalty leaves its effective rank behind the primary's raw rank (it lost the bounded population contest).
167
- //
168
- // #1882 exemption (opt-in): a `name_role = 'variant'` alias is the holder's OWN primary name in another
169
- // orthography (`Брэст` → `brest`, `George Town` → `georgetown` — the build's own-name detector), so the
170
- // query is naming THAT place, not colliding with it; the penalty exists for the coincidental-collision
171
- // class ("Çançun"/`cancun`), which the detector's measured threshold keeps un-stamped. An artifact
172
- // predating the role column carries no 'variant' rows, so the flag no-ops there by construction.
173
108
  const wouldPenalize = (r: R): boolean =>
174
109
  typeof topCountry === "number" && r.is_primary !== 1 && r.country_id !== topCountry
175
110
 
@@ -180,10 +115,6 @@ export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
180
115
  const penalized = isCrossCountryAlias(r)
181
116
  const effectiveNegRank = r.neg_rank + (penalized ? delta : 0)
182
117
 
183
- // The firing mark (#1893): this row WOULD have taken the cross-country penalty and the exemption
184
- // prevented it — conditions the winner-level receipt needs, computed where the decision is made. A
185
- // variant row that was in-country, primary, or keyed under a set with no foreign primary never
186
- // fired, and stays unmarked.
187
118
  const exempted = !penalized && exemptVariantAliases && r.name_role === "variant" && wouldPenalize(r)
188
119
 
189
120
  return {
@@ -194,12 +125,8 @@ export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
194
125
  }
195
126
  }
196
127
 
197
- // 1 for a populated-place row that can BE a district's seat, 0 for everything else — no code map, no
198
- // placetype on the row, an id the map does not carry, a placetype that is not the seat tier, or no
199
- // recorded population. Every row scoring 0 cancels the term, leaving exactly the
200
- // population-then-scan-order the sort had before it existed.
201
128
  const seatPreference = (r: R): number =>
202
- placetypes != null &&
129
+ placetypes &&
203
130
  typeof r.placetype_id === "number" &&
204
131
  typeof r.population === "number" &&
205
132
  r.population > 0 &&
@@ -207,21 +134,14 @@ export function rankByPrimaryPreference<R extends PrimaryPreferenceRow>(
207
134
  ? 1
208
135
  : 0
209
136
 
210
- // 1 for a PRIMARY-name row whose prominence is unmeasured (population 0), 0 for everything else. Two unmeasured
211
- // rows tie on `neg_rank`, and that tie used to fall through to scan order — the B-tree's `spr_id`, which is no
212
- // evidence about either place. A row that IS named X ranks ahead of one merely also-known-as X when nothing else
213
- // separates them: Taiwan's 溪州鄉 township row (its own name, unmeasured) over the village 溪洲 30 km away that
214
- // carries `溪州鄉` as an alias (unmeasured), which the lower WOF id had been winning. Populated ties are untouched —
215
- // a populated alias ("NYC") never reaches this term, and two populated rows keep the order they had.
216
137
  const unmeasuredPrimary = (r: R): number =>
217
138
  r.is_primary === 1 && typeof r.population === "number" && r.population === 0 ? 1 : 0
218
139
 
219
140
  return (
220
141
  rows
221
142
  .map((r, i) => ({ row: annotate(r), i }))
222
- // Effective rank ASC; ties keep population order, then the seat preference DESC, then the primary name
223
- // among unmeasured rows, then original index (stable).
224
- // oxlint-disable-next-line unicorn/no-array-sort -- sorts a freshly-built array; toSorted would double-allocate on a hot path
143
+
144
+ // oxlint-disable-next-line unicorn/no-array-sort -- sorts a freshly-built array. toSorted would double-allocate on a hot path
225
145
  .sort(
226
146
  (a, b) =>
227
147
  a.row.effectiveNegRank - b.row.effectiveNegRank ||