@mailwoman/resolver-wof-sqlite 9.4.0 → 10.1.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 (548) hide show
  1. package/README.md +8 -8
  2. package/lib/address/{point-interpolation.ts → point/interpolation.ts} +40 -38
  3. package/lib/address/point/schema.ts +132 -0
  4. package/lib/address/point.ts +68 -81
  5. package/lib/{address/index.ts → address.ts} +2 -2
  6. package/lib/ancestry/backfill.ts +24 -88
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +608 -0
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +338 -0
  11. package/lib/candidate/alias-bags.ts +74 -14
  12. package/lib/candidate/ancestors/schema.ts +211 -0
  13. package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +17 -24
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +12 -57
  16. package/lib/candidate/fts.ts +28 -0
  17. package/lib/candidate/importance.ts +221 -0
  18. package/lib/candidate/lookup.ts +669 -0
  19. package/lib/candidate/name-roles.ts +16 -34
  20. package/lib/candidate/own-name.ts +34 -31
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +211 -0
  23. package/lib/candidate/scoring.ts +222 -0
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +26 -24
  26. package/lib/coincident-roles.ts +74 -55
  27. package/lib/convention/schema.ts +13 -7
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +34 -39
  30. package/lib/currency-backfill.ts +24 -69
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +31 -20
  33. package/lib/extracts.ts +96 -73
  34. package/lib/fst/autocomplete.ts +52 -35
  35. package/lib/fst/builder.ts +30 -36
  36. package/lib/fst/deserialize-web.ts +8 -9
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +17 -95
  39. package/lib/fst/matcher.ts +2 -2
  40. package/lib/fst/serialize.ts +45 -60
  41. package/lib/fst/types.ts +71 -59
  42. package/lib/fts/query.ts +5 -36
  43. package/lib/fts.ts +280 -0
  44. package/lib/geonames/aliases.ts +72 -149
  45. package/lib/geonames/postal.ts +162 -77
  46. package/lib/index.ts +20 -27
  47. package/lib/interpolation.ts +43 -96
  48. package/lib/lookup.ts +137 -257
  49. package/lib/name-score.ts +9 -7
  50. package/lib/nsul/lookup.ts +36 -53
  51. package/lib/nsul/schema.ts +16 -43
  52. package/lib/paths.ts +159 -0
  53. package/lib/place-importance-schema.ts +57 -164
  54. package/lib/poi/lookup.ts +100 -109
  55. package/lib/poi/schema.ts +42 -26
  56. package/lib/polygon-schema.ts +10 -6
  57. package/lib/postal/city/alias/lookup.ts +79 -0
  58. package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +14 -10
  59. package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +26 -20
  60. package/lib/postal.ts +9 -0
  61. package/lib/postcode-point-lookup.ts +9 -19
  62. package/lib/primary-preference.ts +45 -125
  63. package/lib/proximity-rerank.ts +43 -32
  64. package/lib/ranking-weights.ts +98 -76
  65. package/lib/region-keys.ts +68 -40
  66. package/lib/reverse.ts +108 -72
  67. package/lib/schema.ts +74 -42
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +57 -0
  70. package/lib/sqlite/utils.ts +79 -0
  71. package/lib/street/centroid/schema.ts +149 -0
  72. package/lib/street/centroid.ts +16 -25
  73. package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +56 -41
  74. package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +36 -29
  75. package/lib/street/name-lookup.ts +43 -35
  76. package/lib/street/normalize.ts +85 -216
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +3 -3
  79. package/lib/types.ts +94 -140
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +28 -67
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +204 -232
  85. package/out/address/point/interpolation.d.ts +45 -0
  86. package/out/address/point/interpolation.d.ts.map +1 -0
  87. package/out/address/{point-interpolation.js → point/interpolation.js} +38 -38
  88. package/out/address/point/interpolation.js.map +1 -0
  89. package/out/address/point/schema.d.ts +74 -0
  90. package/out/address/point/schema.d.ts.map +1 -0
  91. package/out/address/{point-schema.js → point/schema.js} +4 -20
  92. package/out/address/point/schema.js.map +1 -0
  93. package/out/address/point.d.ts +24 -21
  94. package/out/address/point.d.ts.map +1 -1
  95. package/out/address/point.js +60 -77
  96. package/out/address/point.js.map +1 -1
  97. package/out/{address/index.js → address.d.ts} +3 -3
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +3 -3
  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 +18 -76
  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 +170 -0
  110. package/out/build/candidate.d.ts.map +1 -0
  111. package/out/{build-candidate.js → build/candidate.js} +33 -102
  112. package/out/build/candidate.js.map +1 -0
  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 +44 -0
  118. package/out/build/slim.d.ts.map +1 -0
  119. package/out/{build-slim.js → build/slim.js} +33 -94
  120. package/out/build/slim.js.map +1 -0
  121. package/out/candidate/alias-bags.d.ts +13 -5
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +55 -12
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +166 -0
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -0
  127. package/out/candidate/ancestors/schema.js +131 -0
  128. package/out/candidate/ancestors/schema.js.map +1 -0
  129. package/out/candidate/ancestors/sidecar.d.ts +27 -0
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
  131. package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +17 -24
  132. package/out/candidate/ancestors/sidecar.js.map +1 -0
  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 +8 -16
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +6 -48
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +19 -0
  142. package/out/candidate/fts.d.ts.map +1 -0
  143. package/out/candidate/fts.js +22 -0
  144. package/out/candidate/fts.js.map +1 -0
  145. package/out/candidate/importance.d.ts +127 -0
  146. package/out/candidate/importance.d.ts.map +1 -0
  147. package/out/candidate/importance.js +162 -0
  148. package/out/candidate/importance.js.map +1 -0
  149. package/out/candidate/lookup.d.ts +36 -0
  150. package/out/candidate/lookup.d.ts.map +1 -0
  151. package/out/candidate/lookup.js +456 -0
  152. package/out/candidate/lookup.js.map +1 -0
  153. package/out/candidate/name-roles.d.ts +10 -22
  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 +34 -31
  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 +120 -0
  166. package/out/candidate/schema.d.ts.map +1 -0
  167. package/out/{candidate-schema.js → candidate/schema.js} +11 -23
  168. package/out/candidate/schema.js.map +1 -0
  169. package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +8 -9
  170. package/out/candidate/scoring.d.ts.map +1 -0
  171. package/out/candidate/scoring.js +157 -0
  172. package/out/candidate/scoring.js.map +1 -0
  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 +26 -24
  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 +61 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +12 -6
  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 +33 -38
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +20 -29
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +13 -35
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +12 -39
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +2 -2
  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 +31 -20
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +65 -54
  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 +28 -20
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +38 -27
  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 +30 -36
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +3 -3
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +8 -9
  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 +11 -73
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +12 -76
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +1 -1
  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 +30 -26
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +44 -58
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +71 -59
  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} +1 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +1 -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 -67
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +44 -121
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -50
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -78
  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 +20 -21
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +14 -15
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +15 -52
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +25 -68
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +24 -53
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +117 -223
  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 +49 -141
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +25 -140
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +38 -42
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +67 -90
  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 → city/alias/lookup.d.ts} +22 -25
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -0
  320. package/out/postal/city/alias/lookup.js +49 -0
  321. package/out/postal/city/alias/lookup.js.map +1 -0
  322. package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +15 -11
  323. package/out/postal/city/alias/schema.d.ts.map +1 -0
  324. package/out/postal/{city-alias-schema.js → city/alias/schema.js} +11 -9
  325. package/out/postal/city/alias/schema.js.map +1 -0
  326. package/out/postal/city/candidate-schema.d.ts +72 -0
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -0
  328. package/out/postal/city/candidate-schema.js +60 -0
  329. package/out/postal/city/candidate-schema.js.map +1 -0
  330. package/out/postal.d.ts +9 -0
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/postal.js +9 -0
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +9 -19
  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 +39 -93
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +13 -97
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +40 -29
  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 +91 -62
  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 +48 -29
  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 +75 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +61 -49
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +74 -42
  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 +25 -0
  367. package/out/sqlite/convention-source.d.ts.map +1 -0
  368. package/out/sqlite/convention-source.js +48 -0
  369. package/out/sqlite/convention-source.js.map +1 -0
  370. package/out/sqlite/utils.d.ts +45 -0
  371. package/out/sqlite/utils.d.ts.map +1 -0
  372. package/out/sqlite/utils.js +56 -0
  373. package/out/sqlite/utils.js.map +1 -0
  374. package/out/street/centroid/schema.d.ts +100 -0
  375. package/out/street/centroid/schema.d.ts.map +1 -0
  376. package/out/street/centroid/schema.js +66 -0
  377. package/out/street/centroid/schema.js.map +1 -0
  378. package/out/street/centroid.d.ts +9 -19
  379. package/out/street/centroid.d.ts.map +1 -1
  380. package/out/street/centroid.js +11 -22
  381. package/out/street/centroid.js.map +1 -1
  382. package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +21 -11
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -0
  384. package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +40 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -0
  386. package/out/street/morphology/fst/loader.d.ts +74 -0
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -0
  388. package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +21 -21
  389. package/out/street/morphology/fst/loader.js.map +1 -0
  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 +32 -31
  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 +74 -191
  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.d.ts → street.d.ts} +4 -4
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.js → street.js} +4 -4
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +69 -138
  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 +20 -56
  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 +132 -141
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +86 -154
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +94 -599
  435. package/lib/address/point-schema.ts +0 -155
  436. package/lib/build-candidate.ts +0 -632
  437. package/lib/build-slim.ts +0 -408
  438. package/lib/candidate-ancestors-schema.ts +0 -195
  439. package/lib/candidate-fts.ts +0 -46
  440. package/lib/candidate-importance.ts +0 -224
  441. package/lib/candidate-lookup.ts +0 -977
  442. package/lib/candidate-schema.ts +0 -247
  443. package/lib/candidate-scoring.ts +0 -261
  444. package/lib/convention/index.ts +0 -154
  445. package/lib/fts/index.ts +0 -361
  446. package/lib/postal/city-alias-lookup.ts +0 -97
  447. package/lib/postal/index.ts +0 -9
  448. package/lib/sqlite-convention-source.ts +0 -65
  449. package/lib/sqlite-utils.ts +0 -82
  450. package/lib/street/centroid-schema.ts +0 -154
  451. package/out/address/index.d.ts.map +0 -1
  452. package/out/address/index.js.map +0 -1
  453. package/out/address/point-interpolation.d.ts +0 -49
  454. package/out/address/point-interpolation.d.ts.map +0 -1
  455. package/out/address/point-interpolation.js.map +0 -1
  456. package/out/address/point-schema.d.ts +0 -97
  457. package/out/address/point-schema.d.ts.map +0 -1
  458. package/out/address/point-schema.js.map +0 -1
  459. package/out/ancestry/index.d.ts +0 -45
  460. package/out/ancestry/index.d.ts.map +0 -1
  461. package/out/ancestry/index.js +0 -55
  462. package/out/ancestry/index.js.map +0 -1
  463. package/out/build-candidate.d.ts +0 -184
  464. package/out/build-candidate.d.ts.map +0 -1
  465. package/out/build-candidate.js.map +0 -1
  466. package/out/build-slim.d.ts +0 -80
  467. package/out/build-slim.d.ts.map +0 -1
  468. package/out/build-slim.js.map +0 -1
  469. package/out/candidate/ancestors-sidecar.d.ts +0 -32
  470. package/out/candidate/ancestors-sidecar.d.ts.map +0 -1
  471. package/out/candidate/ancestors-sidecar.js.map +0 -1
  472. package/out/candidate-ancestors-schema.d.ts +0 -150
  473. package/out/candidate-ancestors-schema.d.ts.map +0 -1
  474. package/out/candidate-ancestors-schema.js +0 -123
  475. package/out/candidate-ancestors-schema.js.map +0 -1
  476. package/out/candidate-fts.d.ts +0 -37
  477. package/out/candidate-fts.d.ts.map +0 -1
  478. package/out/candidate-fts.js +0 -40
  479. package/out/candidate-fts.js.map +0 -1
  480. package/out/candidate-importance.d.ts +0 -132
  481. package/out/candidate-importance.d.ts.map +0 -1
  482. package/out/candidate-importance.js +0 -169
  483. package/out/candidate-importance.js.map +0 -1
  484. package/out/candidate-lookup.d.ts +0 -76
  485. package/out/candidate-lookup.d.ts.map +0 -1
  486. package/out/candidate-lookup.js +0 -768
  487. package/out/candidate-lookup.js.map +0 -1
  488. package/out/candidate-schema.d.ts +0 -155
  489. package/out/candidate-schema.d.ts.map +0 -1
  490. package/out/candidate-schema.js.map +0 -1
  491. package/out/candidate-scoring.d.ts.map +0 -1
  492. package/out/candidate-scoring.js +0 -196
  493. package/out/candidate-scoring.js.map +0 -1
  494. package/out/convention/index.d.ts +0 -106
  495. package/out/convention/index.d.ts.map +0 -1
  496. package/out/convention/index.js +0 -93
  497. package/out/convention/index.js.map +0 -1
  498. package/out/fst/index.d.ts.map +0 -1
  499. package/out/fst/index.js.map +0 -1
  500. package/out/fts/index.d.ts +0 -184
  501. package/out/fts/index.d.ts.map +0 -1
  502. package/out/fts/index.js +0 -277
  503. package/out/fts/index.js.map +0 -1
  504. package/out/geonames/index.d.ts.map +0 -1
  505. package/out/geonames/index.js.map +0 -1
  506. package/out/nsul/index.d.ts.map +0 -1
  507. package/out/nsul/index.js.map +0 -1
  508. package/out/poi/index.d.ts.map +0 -1
  509. package/out/poi/index.js.map +0 -1
  510. package/out/postal/city-alias-lookup.d.ts.map +0 -1
  511. package/out/postal/city-alias-lookup.js +0 -62
  512. package/out/postal/city-alias-lookup.js.map +0 -1
  513. package/out/postal/city-alias-schema.d.ts.map +0 -1
  514. package/out/postal/city-alias-schema.js.map +0 -1
  515. package/out/postal/city-candidate-schema.d.ts +0 -66
  516. package/out/postal/city-candidate-schema.d.ts.map +0 -1
  517. package/out/postal/city-candidate-schema.js +0 -56
  518. package/out/postal/city-candidate-schema.js.map +0 -1
  519. package/out/postal/index.d.ts +0 -9
  520. package/out/postal/index.d.ts.map +0 -1
  521. package/out/postal/index.js +0 -9
  522. package/out/postal/index.js.map +0 -1
  523. package/out/sqlite-convention-source.d.ts +0 -29
  524. package/out/sqlite-convention-source.d.ts.map +0 -1
  525. package/out/sqlite-convention-source.js +0 -57
  526. package/out/sqlite-convention-source.js.map +0 -1
  527. package/out/sqlite-utils.d.ts +0 -45
  528. package/out/sqlite-utils.d.ts.map +0 -1
  529. package/out/sqlite-utils.js +0 -57
  530. package/out/sqlite-utils.js.map +0 -1
  531. package/out/street/centroid-schema.d.ts +0 -105
  532. package/out/street/centroid-schema.d.ts.map +0 -1
  533. package/out/street/centroid-schema.js +0 -78
  534. package/out/street/centroid-schema.js.map +0 -1
  535. package/out/street/index.d.ts.map +0 -1
  536. package/out/street/index.js.map +0 -1
  537. package/out/street/morphology-fst-builder.d.ts.map +0 -1
  538. package/out/street/morphology-fst-builder.js.map +0 -1
  539. package/out/street/morphology-fst-loader.d.ts +0 -67
  540. package/out/street/morphology-fst-loader.d.ts.map +0 -1
  541. package/out/street/morphology-fst-loader.js.map +0 -1
  542. package/out/uprn/index.d.ts.map +0 -1
  543. package/out/uprn/index.js.map +0 -1
  544. /package/lib/{fst/index.ts → fst.ts} +0 -0
  545. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  546. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  547. /package/lib/{poi/index.ts → poi.ts} +0 -0
  548. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
@@ -0,0 +1,222 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ import { compareReferential, referentialFromPopulation } from "@mailwoman/core/resolver"
8
+ import { haversineKm } from "@mailwoman/spatial"
9
+ import type { DatabaseClient } from "@mailwoman/sqlite/client"
10
+
11
+ import { exactMatchIDs, officialNameIDs } from "#exact-match"
12
+ import { foldQueryText } from "#fts"
13
+ import { populationBoostTerm, type RankingWeights } from "#ranking-weights"
14
+ import type { RawSearchRow } from "#search-fetch"
15
+ import type { FindPlaceQuery, PlaceCandidate, WOFPlacetype } from "#types"
16
+
17
+ /**
18
+ * Score one raw FTS row into a `PlaceCandidate`: the weighted sum over the negated
19
+ * BM25 baseline plus placetype, country and parent boosts.
20
+ *
21
+ * It also applies the length penalty, proximity and population terms,
22
+ * then returns the fields consumers read.
23
+ */
24
+ export function candidateFromSearchRow(
25
+ row: RawSearchRow,
26
+ context: {
27
+ query: FindPlaceQuery
28
+ placetypes: WOFPlacetype[] | null
29
+ queryLen: number
30
+ weights: RankingWeights
31
+ }
32
+ ): PlaceCandidate {
33
+ const { query, placetypes, queryLen, weights } = context
34
+
35
+ // SQLite's bm25() is lower-is-better, so negate it to get a higher-is-better baseline.
36
+ let score = -row.rank
37
+
38
+ if (placetypes && placetypes.length && placetypes.includes(row.placetype as WOFPlacetype)) {
39
+ score += weights.placetypeMatchBoost
40
+ }
41
+
42
+ if (!placetypes && row.placetype === "locality") {
43
+ score += weights.localityImplicitBoost
44
+ }
45
+
46
+ if (query.country && row.country === query.country) {
47
+ score += weights.countryMatchBoost
48
+ }
49
+
50
+ if (query.parentID !== undefined) {
51
+ score += row.parent_id === query.parentID ? weights.directChildBoost : weights.descendantBoost
52
+ }
53
+
54
+ const extraLen = Math.max(0, row.name.length - queryLen - 3)
55
+ score -= (weights.lengthPenaltyWeight * extraLen) / 10
56
+
57
+ // Proximity boost applies only when the query includes `near` and the candidate has real coordinates.
58
+ // The decay is tunable via proximityBoost + proximityScaleKm.
59
+ let distanceKm: number | undefined
60
+ // The best decayed-distance term over `near` and every `bias` point wins, each scaled by its weight.
61
+ let proximityTerm = 0
62
+
63
+ if (row.lat !== null && row.lon !== null && !(row.lat === 0 && row.lon === 0)) {
64
+ const hints: Array<{ lat: number; lon: number; weight: number }> = []
65
+
66
+ if (query.near) {
67
+ hints.push({ lat: query.near.lat, lon: query.near.lon, weight: 1 })
68
+ }
69
+
70
+ for (const b of query.bias ?? []) {
71
+ hints.push({ lat: b.lat, lon: b.lon, weight: b.weight ?? 1 })
72
+ }
73
+
74
+ let scoreTerm = 0
75
+
76
+ for (const h of hints) {
77
+ const d = haversineKm(h.lat, h.lon, row.lat, row.lon)
78
+ const decay = h.weight / (1 + d / weights.proximityScaleKm)
79
+ const prom = decay * weights.biasBoost
80
+
81
+ if (prom > proximityTerm) {
82
+ proximityTerm = prom
83
+ distanceKm = d
84
+ scoreTerm = decay * weights.proximityBoost
85
+ }
86
+ }
87
+
88
+ score += scoreTerm
89
+ }
90
+
91
+ // Population boost caps at `populationBoost` magnitude at `10^populationScaleLog10` people and never penalizes.
92
+ const popTerm = populationBoostTerm(row.population, weights)
93
+ score += popTerm
94
+
95
+ // Combined prominence for the exact-tier sort: population and nearness in the same
96
+ // additive units, so hints can win a cross-country postcode tie without a hard filter.
97
+ const prominence = popTerm + proximityTerm
98
+
99
+ const candidate: PlaceCandidate = {
100
+ id: row.id,
101
+ prominence,
102
+ name: row.name,
103
+ placetype: row.placetype as WOFPlacetype,
104
+ country: row.country ?? "",
105
+ lat: row.lat ?? 0,
106
+ lon: row.lon ?? 0,
107
+ parent_id: row.parent_id ?? undefined,
108
+ score,
109
+ }
110
+
111
+ if (distanceKm !== undefined) {
112
+ candidate.distanceKm = distanceKm
113
+ }
114
+
115
+ if (row.population !== null && row.population > 0) {
116
+ candidate.population = row.population
117
+ // The referential ranking key is derived as a pure function of this row's population,
118
+ // so it cannot drift from the ordering.
119
+ candidate.referential = referentialFromPopulation(row.population)
120
+ }
121
+
122
+ // Returned for consumers (annotations / API surfaces); no ranking site reads it.
123
+ if (row.encyclopedic !== null) {
124
+ candidate.encyclopedic = row.encyclopedic
125
+ }
126
+
127
+ // Candidate bbox for parity with the wasm lookup. Without it the Node backend's
128
+ // region→bbox constraint is dead and disambiguation falls to population ranking.
129
+ if (row.min_latitude != null && row.max_latitude != null && row.min_longitude != null && row.max_longitude != null) {
130
+ candidate.bbox = {
131
+ minLat: row.min_latitude,
132
+ maxLat: row.max_latitude,
133
+ minLon: row.min_longitude,
134
+ maxLon: row.max_longitude,
135
+ }
136
+ }
137
+
138
+ return candidate
139
+ }
140
+
141
+ /**
142
+ * Order `candidates` in place with the exact-match tier first when the extract can answer name probes.
143
+ * Stamp every candidate's `exactMatch` flag.
144
+ */
145
+ export function rankCandidates<DB>(
146
+ candidates: PlaceCandidate[],
147
+ options: {
148
+ db: DatabaseClient<DB>
149
+ schemaName: string
150
+ query: FindPlaceQuery
151
+ weights: RankingWeights
152
+ }
153
+ ): void {
154
+ const { db, schemaName, query, weights } = options
155
+
156
+ // Exact-match tiering ranks a case-folded full match above a partial one.
157
+ // It also runs for a single candidate so `exactMatch` is stamped consistently.
158
+ if (weights.exactMatchTiering && candidates.length) {
159
+ const exactIDs = exactMatchIDs(
160
+ db,
161
+ schemaName,
162
+ candidates.map((c) => c.id as number),
163
+ query.text
164
+ )
165
+
166
+ // Stamp the tier onto every candidate so a downstream re-rank's country pin
167
+ // cannot cross the exact/partial boundary.
168
+ for (const c of candidates) {
169
+ c.exactMatch = exactIDs.has(c.id as number)
170
+ }
171
+
172
+ if (exactIDs.size) {
173
+ // Within the exact tier, population is the primary key and the score only breaks
174
+ // ties. a name-exact candidate outranks an alias-exact one.
175
+ const needle = foldQueryText(query.text)
176
+
177
+ // An official name counts as the place's own name for the sub-tier, floor-conditioned on the holder's population.
178
+ const officialIDs = weights.officialNameExact
179
+ ? officialNameIDs(
180
+ db,
181
+ schemaName,
182
+ candidates
183
+ .filter((c) => exactIDs.has(c.id as number) && (c.population ?? 0) >= weights.officialNameExactFloor)
184
+ .map((c) => c.id as number),
185
+ query.text
186
+ )
187
+ : undefined
188
+
189
+ const kind = (c: PlaceCandidate): number => {
190
+ if (!exactIDs.has(c.id as number)) return 0
191
+
192
+ if (foldQueryText(String(c.name ?? "")) === needle) return 2
193
+
194
+ return officialIDs?.has(c.id as number) ? 2 : 1
195
+ }
196
+
197
+ // With proximity hints, prominence replaces raw population as the within-tier key.
198
+ // Without them, referential ordering decides.
199
+ // Encyclopedic importance does not affect this order.
200
+ const hasHints = !!query.near || (query.bias?.length ?? 0) > 0
201
+
202
+ candidates.sort((a, b) => {
203
+ const ax = kind(a)
204
+ const bx = kind(b)
205
+
206
+ if (bx !== ax) return bx - ax
207
+
208
+ if (ax >= 1) {
209
+ if (hasHints) return (b.prominence ?? 0) - (a.prominence ?? 0) || b.score - a.score
210
+
211
+ return compareReferential(a, b) || b.score - a.score
212
+ }
213
+
214
+ return b.score - a.score
215
+ })
216
+
217
+ return
218
+ }
219
+ }
220
+
221
+ candidates.sort((a, b) => b.score - a.score)
222
+ }
@@ -3,15 +3,14 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * The `capital` table (#1880's distribution home): the capital-status reference carried INSIDE
7
- * `candidate.db`, so an npm consumer who pulled the artifact can run `capital_tier` without the
8
- * repo's `data/gazetteer/capitals-v1.json` — which published packages do not ship. One row per
9
- * reference entry (241 national capitals + 3,463 admin-1 seats at the 2026-08-24 build); the
10
- * loader reads the WHOLE table once into a `CapitalIndex` at session construction, so there is no
11
- * per-probe query and no index beyond the rowid.
6
+ * The `capital` table: the capital-status reference included inside `candidate.db`, so an npm
7
+ * consumer who pulled the artifact can run `capital_tier` without the repo's
8
+ * `data/gazetteer/capitals-v1.json`, which published packages do not ship. One row per reference
9
+ * entry. The loader reads the whole table once into a `CapitalIndex` at session construction, so
10
+ * there is no per-probe query and no index beyond the rowid.
12
11
  *
13
- * `keys` holds the entry's folded name set as a JSON array — the name-membership conjunct that
14
- * keeps the coordinate radius from promoting a capital's same-name neighbours (`capitals.ts`).
12
+ * `keys` holds the entry's folded name set as a JSON array, the name-membership conjunct that keeps
13
+ * the coordinate radius from promoting a capital's same-name neighbors (`capitals.ts`).
15
14
  */
16
15
 
17
16
  import { tryParsingJSON } from "@mailwoman/core/json"
@@ -22,14 +21,16 @@ import type { CapitalPoint } from "#capitals"
22
21
  import { hasTable } from "#sqlite-utils"
23
22
 
24
23
  /**
25
- * The table name the builder writes and the reader probes — one word, singular, matching the artifact's other reference
26
- * tables (`candidate`, `country_codes`).
24
+ * The table name the builder writes and the reader probes, one word and singular,
25
+ * matching the artifact's other reference tables (`candidate`, `country_codes`).
27
26
  */
28
27
  export const CAPITAL_TABLE = "capital"
29
28
 
30
29
  /**
31
- * One reference entry as the artifact stores it. `level` is the reference vocabulary (`national` | `admin1`) kept as
32
- * text — the reader validates on load rather than trusting bytes.
30
+ * One reference entry as the artifact stores it.
31
+ *
32
+ * `level` is the reference vocabulary (`national` | `admin1`) kept as text.
33
+ * and the reader validates on load rather than trusting bytes.
33
34
  */
34
35
  export interface CapitalTable {
35
36
  country: string
@@ -43,8 +44,10 @@ export interface CapitalTable {
43
44
  }
44
45
 
45
46
  /**
46
- * Create the table on a build in progress. Async because Kysely's schema-builder is; called from the candidate build's
47
- * DDL phase alongside the other typed builders.
47
+ * Create the table on a build in progress.
48
+ *
49
+ * Async because Kysely's schema-builder is.
50
+ * Called from the candidate build's DDL phase alongside the other typed builders.
48
51
  */
49
52
  export async function createCapitalTable<DB extends { capital: CapitalTable }>(db: Kysely<DB>): Promise<void> {
50
53
  await db.schema
@@ -58,9 +61,10 @@ export async function createCapitalTable<DB extends { capital: CapitalTable }>(d
58
61
  }
59
62
 
60
63
  /**
61
- * Read the whole reference out of an artifact, or `null` when the artifact predates the table — the caller then falls
62
- * back to its next source rather than treating an old artifact as "no capitals" (the meaning-of-zero rule: a missing
63
- * table is UNMEASURED, an empty one is a finding).
64
+ * Read the whole reference out of an artifact, or `null` when the artifact predates the table.
65
+ *
66
+ * The caller then falls back to its next source rather than treating an old artifact as "no
67
+ * capitals" (the meaning-of-zero rule: a missing table is unmeasured, an empty one is a finding).
64
68
  */
65
69
  export function readCapitalPoints<DB>(db: DatabaseClient<DB>): CapitalPoint[] | null {
66
70
  if (!hasTable(db, CAPITAL_TABLE)) return null
package/lib/capitals.ts CHANGED
@@ -3,19 +3,15 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * The CAPITAL-STATUS reference index (#1880) — answers, for one resolved candidate, "is this
7
- * place the national capital or an admin-1 seat of its country?". The consumer is the resolver's
6
+ * The capital-status reference index answers whether a resolved candidate is its country's national capital or admin-1 seat.
7
+ * The consumer is the resolver's
8
8
  * bounded capital promotion (`@mailwoman/resolver`'s `promoteCapitals`, applied after the fame
9
- * key on the bare-toponym class); this module only matches, it never ranks. PURE and
10
- * platform-free (the #861 parity discipline).
9
+ * key on the bare-toponym class). This module only matches and never ranks. It is pure and
10
+ * platform-free.
11
11
  *
12
- * Matching is an IDENTITY test with three conjuncts: same country, within
13
- * {@link CAPITAL_MATCH_RADIUS_KM} of the reference point, and the candidate's own folded name a
14
- * member of the reference entry's folded name set (name + romanization + the source's alternate
15
- * names, so exonym rows — "Vienna" for Wien — still match). All three are required. The
16
- * iteration-1 board run matched on country + coordinate alone, and the 25 km radius promoted
17
- * capital-ADJACENT namesakes instead of capitals: North Salt Lake beside the Utah seat, a Gujarat
18
- * Indiranagar beside Gandhinagar, Via delle Parti beside Perugia. The name set is what makes the
12
+ * A match requires the same country and a distance within {@link CAPITAL_MATCH_RADIUS_KM} of the reference point.
13
+ * The candidate's folded name must also appear in the reference entry's folded name set (name + romanization + the source's alternate
14
+ * names, so exonym rows such as "Vienna" for Wien still match). The name set is what makes the
19
15
  * radius a centroid-drift allowance rather than a catchment.
20
16
  */
21
17
 
@@ -24,8 +20,9 @@ import { haversineKm } from "@mailwoman/spatial"
24
20
  import { normalizeLocalityForKey } from "#street/normalize"
25
21
 
26
22
  /**
27
- * Capital status of one candidate: national capital, admin-1 seat, or neither. Numeric so the resolver's promotion can
28
- * compare levels.
23
+ * Capital status of one candidate: national capital, admin-1 seat, or neither.
24
+ *
25
+ * Numeric so the resolver's promotion can compare levels.
29
26
  */
30
27
  export const CAPITAL_LEVEL = {
31
28
  none: 0,
@@ -36,14 +33,15 @@ export const CAPITAL_LEVEL = {
36
33
  export type CapitalLevel = (typeof CAPITAL_LEVEL)[keyof typeof CAPITAL_LEVEL]
37
34
 
38
35
  /**
39
- * How far a candidate row may sit from the reference point and still read as the same place — a centroid-convention
40
- * allowance (GeoNames point vs WOF centroid on a metro-scale city), not a catchment: the name-membership conjunct is
41
- * what excludes neighbours inside the radius.
36
+ * How far a candidate row may sit from the reference point and still read as the same place.
37
+ *
38
+ * A centroid-convention allowance for a GeoNames point against a WOF centroid on a metro-scale city.
39
+ * The name-membership conjunct excludes neighbors inside the radius.
42
40
  */
43
41
  export const CAPITAL_MATCH_RADIUS_KM = 25
44
42
 
45
43
  /**
46
- * One reference entry, as `data/gazetteer/capitals-v1.json` carries it (`entries[]`).
44
+ * One reference entry, as recorded in `data/gazetteer/capitals-v1.json` (`entries[]`).
47
45
  */
48
46
  export interface CapitalPoint {
49
47
  /**
@@ -54,8 +52,8 @@ export interface CapitalPoint {
54
52
  longitude: number
55
53
  level: "national" | "admin1"
56
54
  /**
57
- * Folded name keys (name + romanization + alternate names, `normalizeLocalityForKey` fold) — the membership set for
58
- * the name conjunct.
55
+ * Folded name keys (name + romanization + alternate names, `normalizeLocalityForKey` fold).
56
+ * The membership set for the name conjunct.
59
57
  */
60
58
  k: string[]
61
59
  }
@@ -73,9 +71,11 @@ interface IndexedPoint {
73
71
  }
74
72
 
75
73
  /**
76
- * Country-bucketed capital points with the three-conjunct identity probe. Construct from the parsed reference file's
77
- * `entries` — the loader that reads the file off disk lives with the path owners (`mailwoman`'s resolver backend),
78
- * keeping this module platform-free.
74
+ * Country-bucketed capital points with the three-conjunct identity probe.
75
+ *
76
+ * Construct from the parsed reference file's `entries`.
77
+ * The loader that reads the file off disk lives with the path owners
78
+ * (`mailwoman`'s resolver backend), keeping this module platform-free.
79
79
  */
80
80
  export class CapitalIndex {
81
81
  readonly #byCountry = new Map<string, IndexedPoint[]>()
@@ -102,8 +102,10 @@ export class CapitalIndex {
102
102
  }
103
103
 
104
104
  /**
105
- * The highest capital level whose entry passes all three conjuncts for this place. `none` — never a throw — for a
106
- * missing name, country, or coordinate, an unknown country, or no matching entry.
105
+ * The highest capital level whose entry passes all three conjuncts for this place.
106
+ *
107
+ * `none`, never a throw, for a missing name, country, or coordinate,
108
+ * an unknown country, or no matching entry.
107
109
  */
108
110
  levelOfPlace(
109
111
  name: string | null | undefined,
@@ -3,39 +3,37 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * `buildCoincidentRoles` — derives the **coincident-roles relation** (#403, epic #402) into the
7
- * unified gazetteer.
6
+ * `buildCoincidentRoles` derives the coincident-roles relation into the unified gazetteer.
8
7
  *
9
- * Many places occupy MULTIPLE admin tiers under one name: German city-states (Berlin/Hamburg/Bremen
10
- * = city == state), Italian provinces named after their capital (Milano, Varese…), Spanish
11
- * provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals
12
- * (Utrecht/Groningen), Shanghai. When an address surfaces only the admin role (the parser drops
13
- * the locality span), the resolver has no locality to place. The hierarchy-completion step (#405)
14
- * repairs that by consulting THIS relation; the table replaces #387's hardcoded 15 km constant
15
- * with the gazetteer's own structure, so the runtime is an O(1) membership lookup with no
16
- * distance math.
8
+ * Many places occupy multiple admin tiers under one name: German city-states (Berlin, Hamburg,
9
+ * Bremen), Italian provinces whose names match their capital (Milano, Varese), Spanish
10
+ * provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals (Utrecht,
11
+ * Groningen), and Shanghai. When an address surfaces only the admin role because the parser
12
+ * dropped the locality span, the resolver has no locality to place. The hierarchy-completion step
13
+ * consults this relation. The table preserves the gazetteer's own structure, so the runtime is an
14
+ * O(1) membership lookup with no distance math.
17
15
  *
18
- * V1 is REGION-tier only (admin.placetype = `region`): the ~124 places matching the census across 9
19
- * countries (IT/ES/GB/JP/KR/FR/DE/NL/CN). County-tier same-name coincidences are deliberately
20
- * excluded — they're dominated by French cantons and JP counties (admin subdivisions named after
21
- * a seat town, not dual-role cities) that don't hit the parser-drops-locality failure; genuine
22
- * consolidated city-counties (US SF/Denver) are a separate follow-up needing a relative-size
23
- * filter.
16
+ * V1 is region-tier only (admin.placetype = `region`), the ~124 places matching the census across
17
+ * nine countries. County-tier same-name coincidences are excluded because they are dominated by
18
+ * French cantons and JP counties, admin subdivisions whose names match a seat town that do not hit the
19
+ * parser-drops-locality failure. Consolidated city-counties (US SF, Denver) are a separate
20
+ * follow-up needing a relative-size filter.
24
21
  *
25
- * A pair `(admin, locality)` is recorded when all hold: same `name` (case-insensitive), the
26
- * locality is a `descendant` of the admin (via the `ancestors` table), and their centroids are
27
- * within a RELATIVE tolerance — `toleranceFraction × admin-bbox-diagonal`, floored at
28
- * `minToleranceKm`. The relative term lets a large Italian province admit a city ~tens of km from
29
- * its centroid while a tiny city-state stays tight; the floor catches city-states whose bbox is
30
- * small (Bremen's centroids sit 9.3 km apart). The tolerance lives ONLY here at build time — it
31
- * never enters the resolver hot path.
22
+ * A pair `(admin, locality)` is recorded when the names match case-insensitively, the locality descends from the admin in `ancestors`,
23
+ * and their centroids fall within a
24
+ * relative tolerance of `toleranceFraction × admin-bbox-diagonal`, floored at `minToleranceKm`.
25
+ * The relative term lets a large Italian province admit a city tens of km from its centroid while
26
+ * a tiny city-state stays tight. The floor catches city-states whose bbox is small (Bremen's
27
+ * centroids sit 9.3 km apart). The tolerance lives only here at build time and never enters the
28
+ * resolver hot path.
32
29
  *
33
- * `relationship_type` is recorded for debuggability / deferred per-type behavior; v1 completion is
34
- * uniform (see #405). It's a coarse classification, not critical.
30
+ * `relationship_type` is recorded for debuggability and deferred per-type behavior. V1 completion
31
+ * is uniform. It is a coarse classification.
35
32
  *
36
- * Mirrors the derived-table builder pattern in `fts.ts` (`buildPlaceSearchFTS`). Run incrementally
37
- * against an existing `admin-global-priority.db` via `build-coincident-roles-cli.ts`; should also
38
- * be wired as a post-step of the main `scripts/build-unified-wof.ts`.
33
+ * This mirrors the derived-table builder pattern in `fts.ts` (`buildPlaceSearchFTS`). It runs
34
+ * incrementally against an existing `admin-global-priority.db` via
35
+ * `build-coincident-roles-cli.ts`, and it should also be wired as a post-step of the main
36
+ * `scripts/build-unified-wof.ts`.
39
37
  */
40
38
 
41
39
  import type { CoincidentLocality } from "@mailwoman/core/resolver"
@@ -46,13 +44,15 @@ import { tableExists } from "@mailwoman/sqlite/introspection"
46
44
 
47
45
  import type { WOFDatabase } from "#schema"
48
46
  /**
49
- * Table of places that hold more than one admin role — a locality that is also its county seat. Written by the
50
- * gazetteer build, read by the resolver when a coincident locality has to be chosen.
47
+ * Table of places that hold more than one admin role.
48
+ * A locality that is also its county seat.
49
+ *
50
+ * Written by the gazetteer build, read by the resolver when a coincident locality has to be chosen.
51
51
  */
52
52
  export const COINCIDENT_ROLES_TABLE = "coincident_roles"
53
53
 
54
54
  /**
55
- * A place that plays multiple admin roles — one row of the relation, keyed by `admin_id`.
55
+ * A place that plays multiple admin roles, one row of the relation keyed by `admin_id`.
56
56
  */
57
57
  export interface CoincidentRole {
58
58
  localityID: number
@@ -64,19 +64,27 @@ export interface CoincidentRole {
64
64
 
65
65
  export interface BuildCoincidentRolesOpts {
66
66
  /**
67
- * Drop + rebuild the table if it already exists. Default true (the build is cheap + idempotent).
67
+ * Drop + rebuild the table if it already exists.
68
+ *
69
+ * Default true (the build is cheap + idempotent).
68
70
  */
69
71
  drop?: boolean
70
72
  /**
71
- * Relative tolerance: a pair is kept when centroid distance ≤ `toleranceFraction × bbox-diagonal`. Default 0.15.
73
+ * Relative tolerance: a pair is kept when centroid distance ≤ `toleranceFraction × bbox-diagonal`.
74
+ *
75
+ * Default 0.15.
72
76
  */
73
77
  toleranceFraction?: number
74
78
  /**
75
- * Floor (km) under the relative tolerance, so small-bbox city-states still qualify. Default 12.
79
+ * Floor (km) under the relative tolerance, so small-bbox city-states still qualify.
80
+ *
81
+ * Default 12.
76
82
  */
77
83
  minToleranceKm?: number
78
84
  /**
79
- * Centroid distance (km) below which a region-tier pair is classed `city-state` (metadata only). Default 2.
85
+ * Centroid distance (km) below which a region-tier pair is classed `city-state` (metadata only).
86
+ *
87
+ * Default 2.
80
88
  */
81
89
  cityStateMaxKm?: number
82
90
  onProgress?: (phase: string, detail?: string) => void
@@ -106,8 +114,11 @@ interface CandidateRow {
106
114
  }
107
115
 
108
116
  /**
109
- * Derive the coincident-roles relation into `db`. Additive — only creates/replaces the `coincident_roles` table; never
110
- * touches `spr`/`names`/`ancestors`. Idempotent.
117
+ * Derive the coincident-roles relation into `db`.
118
+ *
119
+ * Additive, only creating or replacing the `coincident_roles` table.
120
+ * It never touches `spr`, `names`, or `ancestors`.
121
+ * Idempotent.
111
122
  */
112
123
  export function buildCoincidentRoles(
113
124
  db: DatabaseClient<WOFDatabase>,
@@ -127,10 +138,9 @@ export function buildCoincidentRoles(
127
138
 
128
139
  onProgress("creating", COINCIDENT_ROLES_TABLE)
129
140
 
130
- // Raw DDL by design: this is a sync builder consumed by a sync CLI (build-coincident-roles-cli) and
131
- // 6 sync unit tests, so routing one table through async Kysely would cascade async through all of
132
- // them for no real gain. See AGENTS.md "Database / inline SQL". (The SELECT + INSERT loop below are
133
- // likewise the raw hot path.)
141
+ // Raw DDL by design: this is a sync builder consumed by a sync CLI and six sync unit tests,
142
+ // so routing one table through async Kysely would cascade async through all of them.
143
+ // See agents.md "Database / inline SQL".
134
144
  db.exec(`
135
145
  CREATE TABLE IF NOT EXISTS ${COINCIDENT_ROLES_TABLE} (
136
146
  admin_id INTEGER NOT NULL,
@@ -145,9 +155,11 @@ export function buildCoincidentRoles(
145
155
 
146
156
  onProgress("scanning")
147
157
 
148
- // Admin (region/county tier) ⋈ same-name DESCENDANT locality. `place_population` is optional (LEFT
149
- // JOIN → 0 when absent). The relative-tolerance filter + relationship classification happen in JS so
150
- // the SQL stays a plain join. `spr` exposes the bbox columns we need for the diagonal.
158
+ // Admin (region or county tier) joined to a same-name descendant locality.
159
+ // `place_population` is optional, so the left join yields 0 when absent.
160
+ // JavaScript applies the relative-tolerance filter and classifies the relationship.
161
+ // SQL stays a plain join.
162
+ // `spr` exposes the bbox columns needed for the diagonal.
151
163
  const candidates = allRows<CandidateRow>(
152
164
  db.prepare(
153
165
  `SELECT r.id AS admin_id, r.placetype AS admin_placetype, r.country AS country, l.id AS locality_id,
@@ -183,11 +195,12 @@ export function buildCoincidentRoles(
183
195
  const tolerance = Math.max(toleranceFraction * diag, minToleranceKm)
184
196
 
185
197
  if (dist > tolerance) continue
186
- // v1 is region-tier only: a place is a `city-state` when its centroid coincides with the
187
- // region's (Berlin/Hamburg), else `capital-seat` (a region named after its principal city, e.g.
188
- // Milano province → Milano comune). `consolidated-county` is reserved for a future county-tier
189
- // pass (US SF/Denver) — excluded from v1 because county-tier same-name coincidences are
190
- // dominated by French cantons / JP counties that don't hit the parser-drops-locality failure.
198
+ // v1 is region-tier only: a place is `city-state` when its centroid coincides with the
199
+ // region's (Berlin, Hamburg), otherwise `capital-seat` (a region whose name matches its
200
+ // principal city, such as Milano province and Milano comune). `consolidated-county` is
201
+ // reserved for a future county-tier pass (US SF, Denver), excluded from v1 because
202
+ // county-tier same-name coincidences are dominated by French cantons and JP counties
203
+ // that do not hit the parser-drops-locality failure.
191
204
  const relationshipType = dist <= cityStateMaxKm ? "city-state" : "capital-seat"
192
205
  insert.run(c.admin_id, c.locality_id, relationshipType, c.admin_placetype, dist, c.pop)
193
206
 
@@ -209,16 +222,20 @@ export function buildCoincidentRoles(
209
222
  }
210
223
 
211
224
  /**
212
- * True iff the relation table exists. Used by the resolver to decide whether completion can run.
225
+ * True iff the relation table exists.
226
+ *
227
+ * Used by the resolver to decide whether completion can run.
213
228
  */
214
229
  export function coincidentRolesExists<DB>(db: DatabaseClient<DB>): boolean {
215
230
  return tableExists(db, COINCIDENT_ROLES_TABLE)
216
231
  }
217
232
 
218
233
  /**
219
- * Load the relation into an in-memory map keyed by `admin_id` for O(1) runtime lookup (#405). Each admin may map to
220
- * MULTIPLE same-name descendants; the consumer disambiguates (min distance → population → abstain). Returns an empty
221
- * map when the table is absent.
234
+ * Load the relation into an in-memory map keyed by `admin_id` for an O(1) runtime lookup.
235
+ *
236
+ * Each admin may map to multiple same-name descendants.
237
+ * The consumer disambiguates by minimum distance, then population, then abstains.
238
+ * Returns an empty map when the table is absent.
222
239
  */
223
240
  export function loadCoincidentRoles<DB>(db: DatabaseClient<DB>): Map<number, CoincidentRole[]> {
224
241
  const map = new Map<number, CoincidentRole[]>()
@@ -261,8 +278,10 @@ export function loadCoincidentRoles<DB>(db: DatabaseClient<DB>): Map<number, Coi
261
278
  }
262
279
 
263
280
  /**
264
- * The relation joined with `spr`, as an in-memory map keyed by `admin_id` — the resolver-facing view of
265
- * {@link loadCoincidentRoles}, carrying the canonical name and coordinates each coincident locality resolves to.
281
+ * The relation joined with `spr`, as an in-memory map keyed by `admin_id`.
282
+ *
283
+ * The resolver-facing view of {@link loadCoincidentRoles}, carrying the canonical name
284
+ * and coordinates each coincident locality resolves to.
266
285
  * Returns an empty map when the relation table is absent.
267
286
  */
268
287
  export function loadCoincidentLocalities<DB>(db: DatabaseClient<DB>): Map<number, CoincidentLocality[]> {
@@ -4,14 +4,14 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Typed schema for the convention asset — the `address_convention` table `SqliteConventionSource` probes, plus the
7
- * `meta` provenance row every sealed artifact here carries. The interface is the read/write contract and
7
+ * `meta` provenance row every sealed artifact here includes. The interface is the read/write interface and
8
8
  * {@link createAddressConventionTable} creates the table, so a column added to one is a compile error against the
9
9
  * other.
10
10
  */
11
11
 
12
12
  import type { Kysely } from "kysely"
13
13
 
14
- import { ADDRESS_CONVENTION_TABLE } from "#convention/index"
14
+ import { ADDRESS_CONVENTION_TABLE } from "#convention"
15
15
 
16
16
  /**
17
17
  * One convention profile, keyed by the WOF admin polygon it attaches to.
@@ -19,7 +19,9 @@ import { ADDRESS_CONVENTION_TABLE } from "#convention/index"
19
19
  export interface AddressConventionTable {
20
20
  wof_id: number
21
21
  /**
22
- * The `Convention` record, JSON-encoded. Parsed at read time by `SqliteConventionSource`.
22
+ * The `Convention` record, JSON-encoded.
23
+ *
24
+ * Parsed at read time by `SqliteConventionSource`.
23
25
  */
24
26
  convention: string
25
27
  /**
@@ -42,14 +44,18 @@ export interface ConventionDatabase {
42
44
  }
43
45
 
44
46
  /**
45
- * The slice of a Kysely handle the convention DDL touches. Kysely is invariant in its schema parameter, so naming only
46
- * `schema` lets a builder holding a wider handle pass it without a cast.
47
+ * The subset of a Kysely handle the convention DDL touches.
48
+ *
49
+ * Kysely is invariant in its schema parameter, so naming only `schema` lets a
50
+ * builder holding a wider handle pass it without a cast.
47
51
  */
48
52
  export type ConventionSchemaHandle = Pick<Kysely<ConventionDatabase>, "schema">
49
53
 
50
54
  /**
51
- * Create `address_convention`. The table name comes from {@link ADDRESS_CONVENTION_TABLE} so the build script, the
52
- * runtime source, and the extract auto-detect cannot drift apart.
55
+ * Create `address_convention`.
56
+ *
57
+ * The table name comes from {@link ADDRESS_CONVENTION_TABLE} so the build script, the runtime source.
58
+ * The extract auto-detect stays aligned with it.
53
59
  */
54
60
  export async function createAddressConventionTable(db: ConventionSchemaHandle): Promise<void> {
55
61
  await db.schema