@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
@@ -2,73 +2,31 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * THE street normalizer for the address-point tier (#476). One function, used by BOTH the extract
7
- * builder (`scripts/build-address-point-extract.ts`) and the lookup tier (`address-point.ts`) —
8
- * never two implementations (the PLACETYPE_ORDER lesson: parallel copies silently corrupt).
9
- *
10
- * Normalization contract (deliberately aggressive — both sides apply the same function, so
11
- * collisions only need to be _consistent_, not linguistically perfect):
12
- *
13
- * 1. Lowercase, NFKD-fold diacritics, collapse whitespace, strip punctuation (periods, commas,
14
- * apostrophes).
15
- * 2. Expand USPS directional abbreviations at the FIRST and LAST token position (`n` → `north`, `se` →
16
- * `southeast`) — Overture sources abbreviate inconsistently.
17
- * 3. Canonicalize a trailing USPS street-type token via the codex suffix table to its canonical full
18
- * form (`st`/`str`/`street` → `street`).
19
- *
20
- * Numbered streets are left as digits (`5th` stays `5th`); a SPELLED ordinal before a street suffix
21
- * folds to its digit form (`tenth street` → `10th street`, #723) so the grid-city ordinal
22
- * cross-streets the source data spells with digits become reachable.
23
5
  */
24
6
 
25
7
  import { AbbreviationToDirectional, US_STREET_SUFFIX_LOOKUP } from "@mailwoman/codex/us"
26
8
  import type { Tagged } from "type-fest"
27
9
 
28
10
  /**
29
- * A place name folded by {@link normalizeLocalityForKey} — the value stored in, and required to probe, every
30
- * `name_key`-style column: the candidate gazetteer, the postal-city side-index, the street-centroid extract, an
31
- * ancestor chain's `parent_name_key`, and the address-point locality scope.
32
- *
33
- * The brand is here because the fold is applied at BUILD time and is therefore mandatory at QUERY time, while a
34
- * near-miss approximation of it (`toLowerCase()`, `trim()`) is still a `string`: it binds to the parameter, returns
35
- * fewer rows, and the shortfall reads as a coverage gap in the data rather than a defect in the probe. Requiring the
36
- * brand at the boundary turns that silent under-match into a compile error. Mint one only by calling the fold.
11
+ * A place name folded by {@link normalizeLocalityForKey}, minted only by calling the fold.
37
12
  */
38
13
  export type NameKey = Tagged<string, "NameKey">
39
14
 
40
15
  /**
41
- * A street name folded by {@link normalizeStreetForKey} / {@link normalizeStreetForKeyLocale} — the value stored in a
42
- * `street_norm` address-point column and required to probe it.
43
- *
44
- * Distinct from {@link RouteKey}: the route fold is a FURTHER canonicalization applied to some columns and not others,
45
- * so the two are not interchangeable even though both are folded streets.
16
+ * A street name folded by {@link normalizeStreetForKey} or {@link normalizeStreetForKeyLocale},
17
+ * the form an address-point `street_norm` column stores.
18
+ * Do not substitute {@link RouteKey} for this type.
46
19
  */
47
20
  export type StreetKey = Tagged<string, "StreetKey">
48
21
 
49
22
  /**
50
- * A street key that has additionally passed {@link canonicalizeRouteKey} — the numbered-route canonical form.
51
- *
52
- * A separate brand from {@link StreetKey} because it identifies a DIFFERENT set of columns, and the two folds disagree
53
- * exactly on the rows that motivate the route fold: binding a plain street key to a route-folded column silently misses
54
- * every row whose source spelled the route differently, which is the miss class the fold exists to close.
23
+ * A street key that has also passed {@link canonicalizeRouteKey}; binding a plain
24
+ * {@link StreetKey} to a route-folded column silently misses rows whose route spelling differs.
55
25
  */
56
26
  export type RouteKey = Tagged<string, "RouteKey">
57
27
 
58
- /**
59
- * Token count a street must exceed before its trailing pair is merged. At or below it the pair IS the whole street
60
- * name, and merging would leave nothing to match on.
61
- */
62
28
  const MIN_TOKENS_FOR_TAIL_MERGE = 3
63
29
 
64
- /**
65
- * Spelled ordinal street names → their digit-ordinal form ("tenth" → "10th"), applied ONLY when a street-type suffix
66
- * follows (#723 admin-tail) — so the ordinal cross-streets common in grid cities ("Tenth Street", "Fifth Avenue") match
67
- * the extracts' digit keys, WITHOUT rewriting ordinal-WORD names where the next token is not a suffix ("First National
68
- * Bank Rd" stays "first national …"). Digit-source extracts are unaffected (a digit token isn't in this map), so the
69
- * existing keys need no rebuild; a future rebuild folds any spelled-source key the same way (the one-function
70
- * discipline).
71
- */
72
30
  const SPELLED_ORDINAL_TO_DIGIT = new Map<string, string>([
73
31
  ["first", "1st"],
74
32
  ["second", "2nd"],
@@ -100,9 +58,6 @@ const SPELLED_ORDINAL_TO_DIGIT = new Map<string, string>([
100
58
  ["hundredth", "100th"],
101
59
  ])
102
60
 
103
- /**
104
- * Lowercase + diacritic-fold + punctuation strip + whitespace collapse.
105
- */
106
61
  function fold(input: string): string {
107
62
  return input
108
63
  .normalize("NFKD")
@@ -114,16 +69,15 @@ function fold(input: string): string {
114
69
  }
115
70
 
116
71
  /**
117
- * Normalize a street name for address-point keying. Same function at build time and lookup time — see module docstring
118
- * for the contract.
72
+ * Normalizes a US or English street name into an address-point key, folding directionals,
73
+ * spelled ordinals and the USPS street-type suffix.
74
+ * The extract builder and the probe must both call it.
119
75
  */
120
76
  export function normalizeStreetForKey(street: string): StreetKey {
121
77
  const tokens = fold(street).split(" ")
122
78
 
123
79
  if (!tokens.length) return "" as StreetKey
124
80
 
125
- // Spelled-ordinal street names → digit form when a street suffix follows ("Tenth Street" →
126
- // "10th street", #723). Conditioned on the next token being a suffix so ordinal-WORD names are untouched.
127
81
  for (let i = 0; i < tokens.length - 1; i++) {
128
82
  const digit = SPELLED_ORDINAL_TO_DIGIT.get(tokens[i]!)
129
83
 
@@ -132,10 +86,6 @@ export function normalizeStreetForKey(street: string): StreetKey {
132
86
  }
133
87
  }
134
88
 
135
- // Directional expansion at the edges only ("N Main St" / "Main St N" — never interior
136
- // tokens, where "W" may be an initial in a person-named street). The codex expands
137
- // compounds to two words ("SE" → "SOUTH EAST"); we key on the spaceless form
138
- // ("southeast"), and also merge an already-written two-token pair ("South East …").
139
89
  const edgeDirectional = (raw: string) =>
140
90
  AbbreviationToDirectional.get(raw.toUpperCase())?.toLowerCase().replace(" ", "")
141
91
 
@@ -168,11 +118,8 @@ export function normalizeStreetForKey(street: string): StreetKey {
168
118
  }
169
119
  }
170
120
 
171
- // Street-type canonicalization via the codex table (lowercase keys, UPPER canonical
172
- // values). The suffix is usually the last token, but sits second-to-last when a trailing
173
- // directional follows ("Main St N") — check both positions, canonicalize the first hit.
174
121
  for (const at of [tokens.length - 1, tokens.length - 2]) {
175
- if (at < 1) continue // never canonicalize the only/first token ("Street Road" exists)
122
+ if (at < 1) continue
176
123
  const canonical = US_STREET_SUFFIX_LOOKUP.get(tokens[at]!)
177
124
 
178
125
  if (canonical) {
@@ -186,31 +133,18 @@ export function normalizeStreetForKey(street: string): StreetKey {
186
133
  }
187
134
 
188
135
  /**
189
- * Street-name locale for the address-point key. The US path is the full USPS pipeline ({@link normalizeStreetForKey});
190
- * the international paths fold + apply a SMALL, consistent per-locale type-token canonicalization. Same discipline as
191
- * the US normalizer: build side and probe side call the identical function, so the key only needs to be CONSISTENT, not
192
- * linguistically perfect — a folded "rue du chevaleret" keys the same on both sides whether or not we reorder the
193
- * article, so no salient-token / multi-key index is built yet (deferred until probing shows the normalizer can't absorb
194
- * the false-negatives).
136
+ * Names the rule set {@link normalizeStreetForKeyLocale} uses to fold a street
137
+ * name into an address-point key.
195
138
  */
196
- export type StreetLocale = "us" | "en" | "fr" | "de" | "nl" | "pl" | "vn" | "id" | "zh"
139
+ export type StreetLocale = "us" | "en" | "es" | "fr" | "de" | "it" | "nl" | "pl" | "vn" | "id" | "zh"
197
140
 
198
- /**
199
- * The Han fold shared by every `zh` key: NFKC so a full-width digit or letter keys as its ASCII form (the Taiwanese
200
- * civil-affairs registers write `122`, a person types `122`), 臺 → 台 (the register writes 臺北市, the common hand writes
201
- * 台北市), and no whitespace at all, because a Han surface carries none and a stray space would split one key into two.
202
- * Kanji numerals stay as written: the register spells a road section `一段`, and so does a query.
203
- */
204
141
  function foldHan(input: string): string {
205
142
  return input.normalize("NFKC").replaceAll("臺", "台").replaceAll(/\s+/g, "").toLowerCase()
206
143
  }
207
144
 
208
145
  /**
209
- * Country → street-locale registry surface for the acquisition SDKs (BAN, OSM). Each SDK keeps its own map — membership
210
- * is a per-register decision — and this factory owns the lookup discipline: throw for an unsupported country rather
211
- * than silently folding with the wrong rules, because a extract built with the wrong normalizer keys every street
212
- * incorrectly and looks fine until a probe misses. `label` is the SDK's own registration instructions, appended to the
213
- * error verbatim.
146
+ * Creates a country-to-street-locale lookup for an acquisition SDK, throwing
147
+ * rather than falling back so a misregistered locale cannot key every street wrongly.
214
148
  */
215
149
  export function createStreetLocaleRegistry(
216
150
  registry: ReadonlyMap<string, StreetLocale>,
@@ -230,11 +164,6 @@ export function createStreetLocaleRegistry(
230
164
  }
231
165
  }
232
166
 
233
- /**
234
- * French street-type abbreviations → canonical full form, applied per token after {@link fold}. French address types
235
- * LEAD the name ("Av. de…", "Bd …", "Pl. …") and "St"/"Ste" abbreviate Saint/Sainte inside names ("Rue St-Honoré" →
236
- * "rue saint honore"). fold() has already stripped the trailing period, so the keys are point-free ("av", "bd").
237
- */
238
167
  const FR_STREET_ABBREV = new Map<string, string>([
239
168
  ["av", "avenue"],
240
169
  ["ave", "avenue"],
@@ -257,19 +186,45 @@ const FR_STREET_ABBREV = new Map<string, string>([
257
186
  ["sts", "saints"],
258
187
  ])
259
188
 
189
+ const PL_LEADING_TYPE = new Set(["ul", "ulica", "al", "aleja", "aleje", "pl", "plac", "os", "osiedle"])
190
+
260
191
  /**
261
- * Polish leading street-type tokens, STRIPPED rather than expanded. Measured on the 5.56M-row PL OSM extract
262
- * (2026-08-19): OSM Poland tags `addr:street` BARE — `ulica%` covers 22 rows and `ul.%` eight, so an EXPANSION rule
263
- * makes a typed query ("ul. Świętokrzyska" → "ulica swietokrzyska") miss the 3,846 bare "swietokrzyska" rows. Stripping
264
- * the leading type on BOTH sides keys typed and bare surfaces identically. The full words are stripped too: the
265
- * aleja/plac/osiedle populations (48,597 / 26,815 / 39,552 rows) spell the word out, and "Plac Zamkowy" must key the
266
- * same as a query's "plac zamkowy" or bare "Zamkowy". Never stripped when it is the only token.
192
+ * Italian street types in the abbreviated spellings a person types, mapped to the spelling ANNCSU publishes.
267
193
  */
268
- const PL_LEADING_TYPE = new Set(["ul", "ulica", "al", "aleja", "aleje", "pl", "plac", "os", "osiedle"])
194
+ const IT_STREET_TYPE_ABBREV = new Map<string, string>([
195
+ ["v", "via"],
196
+ ["vle", "viale"],
197
+ ["vl", "viale"],
198
+ ["vic", "vicolo"],
199
+ ["vlo", "vicolo"],
200
+ ["str", "strada"],
201
+ ["pza", "piazza"],
202
+ ["pzza", "piazza"],
203
+ ["p", "piazza"],
204
+ ["ple", "piazzale"],
205
+ ["cso", "corso"],
206
+ ["c", "corso"],
207
+ ["lgo", "largo"],
208
+ ["loc", "localita"],
209
+ ["fraz", "frazione"],
210
+ ["cda", "contrada"],
211
+ ["lungarno", "lungarno"],
212
+ ])
269
213
 
270
214
  /**
271
- * Indonesian street-type abbreviations → canonical full form, leading position ("Jl. Thamrin", "Gg. Waru").
215
+ * Spanish street-type abbreviations whose expansion is the same token in every language
216
+ * the register writes after `fold`; `c/`, `av`, and `pl` are deliberately absent
217
+ * because they expand differently by region.
272
218
  */
219
+ const ES_STREET_TYPE_ABBREV = new Map<string, string>([
220
+ ["ctra", "carretera"],
221
+ ["crta", "carretera"],
222
+ ["rbla", "rambla"],
223
+ ["gta", "glorieta"],
224
+ ["cjon", "callejon"],
225
+ ["rda", "ronda"],
226
+ ])
227
+
273
228
  const ID_STREET_ABBREV = new Map<string, string>([
274
229
  ["jl", "jalan"],
275
230
  ["jln", "jalan"],
@@ -277,42 +232,19 @@ const ID_STREET_ABBREV = new Map<string, string>([
277
232
  ])
278
233
 
279
234
  /**
280
- * Normalize a street name for the address-point key in a non-US locale. Same function build-side and probe-side (the
281
- * one-function discipline). US delegates to {@link normalizeStreetForKey}.
282
- *
283
- * - **en** — the shared English address-key pipeline (directionals + suffix aliases), currently identical to US.
284
- * - **fr** — fold + expand leading type abbreviations and Saint/Sainte (token map).
285
- * - **de** — fold + ß→ss + canonicalize the GLUED `-str(.)` suffix to `-strasse` ("Lindenstr." → "lindenstrasse",
286
- * "Lindenstraße" → "lindenstrasse"); an already-full "-strasse" is left intact.
287
- * - **nl** — fold + canonicalize the glued `-str` suffix to `-straat` ("Kerkstr." → "kerkstraat").
288
- * - **pl** — fold + ł→l (Ł does not NFKD-decompose, so the generic fold keeps it and an undiacritized query would miss;
289
- * every other Polish diacritic is a combining form the fold already strips) + STRIP the leading type token — see
290
- * {@link PL_LEADING_TYPE} for the measured reason expansion was wrong for this source.
291
- * - **vn** — fold + đ→d AND ð→d (both non-decomposing, and OSM mixes the two codepoints inside single values — see the
292
- * branch comment). Deliberately NO type-abbreviation map yet: the common abbreviation is the single letter "Đ." for
293
- * Đường, and expanding a bare folded "d" token would rewrite initials — measure the miss rate on the built extract
294
- * before adding anything.
295
- * - **id** — fold + expand leading type abbreviations (jl/jln→jalan, gg→gang); Indonesian street surfaces are otherwise
296
- * ASCII-clean.
235
+ * Normalizes a street name into an address-point key under the given locale, delegating `us`
236
+ * and `en` to {@link normalizeStreetForKey}; the extract builder and the probe must both call it.
297
237
  */
298
238
  export function normalizeStreetForKeyLocale(street: string, locale: StreetLocale): StreetKey {
299
239
  if (locale === "us" || locale === "en") return normalizeStreetForKey(street)
300
240
 
301
- // A Han street name has no tokens, no type abbreviation and no diacritics; the Latin pipeline below would only
302
- // split it on whitespace it never carries. `zh` keys are the Han fold and nothing else.
303
241
  if (locale === "zh") return foldHan(street) as StreetKey
304
242
 
305
- // Hyphen → space so a compound name keys the same whether the source or the query writes the
306
- // hyphen ("Champs-Élysées", "St-Honoré") or a space — both sides fold identically, so this is pure
307
- // robustness. It also splits a hyphenated abbreviation ("St-Honoré" → "st honore") into tokens the
308
- // per-locale type/Saint map can see. Letter maps for non-decomposing letters (ß, ł, đ) live in the
309
- // per-locale branches, NEVER here: widening the shared pipeline would silently change keys under
310
- // every already-built extract of the other locales.
311
243
  const tokens = fold(street)
312
244
  .replaceAll("ß", "ss")
313
245
  .replaceAll("-", " ")
314
246
  .split(/\s+/)
315
- .filter((value) => value.length > 0)
247
+ .filter((value) => value.length)
316
248
 
317
249
  if (!tokens.length) return "" as StreetKey
318
250
 
@@ -331,6 +263,20 @@ export function normalizeStreetForKeyLocale(street: string, locale: StreetLocale
331
263
  }
332
264
  }
333
265
  break
266
+ case "it":
267
+ // The leading type is expanded and kept, unlike `pl`, because dropping it
268
+ // merges distinct streets that share a name.
269
+ if (tokens.length > 1) {
270
+ tokens[0] = IT_STREET_TYPE_ABBREV.get(tokens[0]!) ?? tokens[0]!
271
+ }
272
+ break
273
+ case "es":
274
+ // Keep the leading type, as `it` does and more strongly, because `Calle Mayor`
275
+ // and `Plaza Mayor` often share a municipio.
276
+ if (tokens.length > 1) {
277
+ tokens[0] = ES_STREET_TYPE_ABBREV.get(tokens[0]!) ?? tokens[0]!
278
+ }
279
+ break
334
280
  case "nl":
335
281
  for (let i = 0; i < tokens.length; i++) {
336
282
  const t = tokens[i]!
@@ -355,11 +301,6 @@ export function normalizeStreetForKeyLocale(street: string, locale: StreetLocale
355
301
  }
356
302
  break
357
303
  case "vn":
358
- // đ (U+0111, d-with-stroke) AND ð (U+00F0, eth): OSM Vietnamese data mixes the two visually
359
- // identical codepoints inside single values — the 2026-08-19 extract measured `Đường Trần Hưng
360
- // Đạo` carrying d-with-stroke at the front and ETH in `Đạo`, splitting the country's most
361
- // common street into two keys with the MAJORITY variant (630 vs 421 rows) unreachable by a
362
- // correctly-typed query. No abbreviation map — see the locale table.
363
304
  for (let i = 0; i < tokens.length; i++) {
364
305
  tokens[i] = tokens[i]!.replaceAll("đ", "d").replaceAll("ð", "d")
365
306
  }
@@ -370,30 +311,27 @@ export function normalizeStreetForKeyLocale(street: string, locale: StreetLocale
370
311
  }
371
312
 
372
313
  /**
373
- * Normalize a locality name for address-point keying (fold only — no street semantics).
314
+ * Normalize a locality name for address-point keying, fold only with no street semantics.
374
315
  */
375
316
  export function normalizeLocalityForKey(locality: string): NameKey {
376
317
  return fold(locality) as NameKey
377
318
  }
378
319
 
379
320
  /**
380
- * The locality key under a street locale: the Han fold for `zh`, {@link normalizeLocalityForKey} for every other.
381
- * Separate from the shared fold on purpose — the candidate gazetteer's `name_key` column is the shared fold, and a Han
382
- * rule inside it would re-key every 臺 name under an extract built without the rule.
321
+ * Folds a locality name into a {@link NameKey} with the Han fold for `zh`
322
+ * and {@link normalizeLocalityForKey} otherwise.
323
+ *
324
+ * The Han rule stays out of the shared fold so existing columns are not re-keyed.
383
325
  */
384
326
  export function normalizeLocalityForKeyLocale(locality: string, locale: StreetLocale): NameKey {
385
327
  return locale === "zh" ? (foldHan(locality) as NameKey) : normalizeLocalityForKey(locality)
386
328
  }
387
329
 
388
330
  /**
389
- * The house-number key under a street locale. Every Latin register stores a lower-cased, trimmed number and the reader
390
- * probes the same; `zh` additionally folds width and drops the trailing 號 the Taiwanese registers write on the number
391
- * itself (`122號`), which a parse also carries on the `house_number` span (`122號`). Both sides key `122`.
331
+ * Folds a house number into an address-point key, additionally width-folding
332
+ * and dropping the trailing 號 for `zh`.
392
333
  */
393
334
  export function normalizeHouseNumberForKey(number: string, locale: StreetLocale): string {
394
- // The sub-number is written two ways — `30之19號` and `30號之19` — and an attached number a third (`30附40號`,
395
- // `55之23附1號`); the register stores the base `30` with the rest in `unit`. Every form keys with the 號 dropped and
396
- // the base number first, which the reader's sub-number rung then falls from.
397
335
  if (locale === "zh") {
398
336
  return foldHan(number)
399
337
  .replace(/^(\d+)號([之附]\d+)$/u, "$1$2")
@@ -403,45 +341,21 @@ export function normalizeHouseNumberForKey(number: string, locale: StreetLocale)
403
341
  return number.trim().toLowerCase()
404
342
  }
405
343
 
406
- /**
407
- * Leading French street-type token. French types LEAD the name ("avenue du Parc", "boul Saint-Laurent") while English
408
- * types TRAIL ("Fifth Avenue", "Grosvenor Place"), so a leading type-token is the discriminating signal — "1 Avenue NE"
409
- * starts with a digit and never matches. The separator lookahead is explicit rather than `\b` because a word boundary
410
- * after an accented final letter ("carré") is not one to an ASCII-word `\b`.
411
- */
412
344
  const FRENCH_LEAD_TYPE =
413
345
  /^(?:rue|ruelle|av|ave|avenue|boul|bd|boulevard|ch|che|chemin|all[ée]e|imp|impasse|mont[ée]e|c[ôo]te|pl|place|prom|promenade|rang|rte|route|autoroute|carr[eé]|croissant|terrasse|sentier)(?=[\s.-]|$)/i
414
346
 
415
347
  /**
416
- * Surface-driven street-locale ROUTER for bilingual extracts — the Québec finishing move on the CA rooftop extract.
417
- *
418
- * A country registers ONE street locale, but Canada's street surfaces are two languages: under the `en` rules a French
419
- * surface passes through mostly unchanged (both sides fold identically, so those rows stay reachable), and what breaks
420
- * is abbreviation variance — the `en` rules cannot fold "boul"/"Ste-" to the full French word, so an abbreviated query
421
- * misses a full-word row and vice versa. Routing on the SURFACE (not the province) also carries bilingual NB and the
422
- * French street names outside Québec for free.
423
- *
424
- * ONE function, called by the extract BUILDER and the query PROBE alike — the #861 discipline: routing is part of the
425
- * fold contract, and two transcriptions of this predicate would diverge exactly where it matters. Only an `en` base
426
- * re-routes: a `fr`/`de`/`nl` extract already speaks its own rules, and the US pipeline stays untouched.
427
- *
428
- * Measured basis (CA extract, 2026-08-19): 183,963 distinct surfaces, 43,762 French-lead; 29,682 fold differently under
429
- * fr-vs-en (888,265 rows), and the non-French half of those are English surfaces this router keeps on `en` unchanged.
348
+ * Routes an `en` street surface leading with a French type word to the `fr` locale
349
+ * and returns any other base unchanged, routing on the surface rather than the province
350
+ * so bilingual Canadian extracts fold consistently.
430
351
  */
431
352
  export function streetLocaleForSurface(street: string, base: StreetLocale): StreetLocale {
432
353
  return base === "en" && FRENCH_LEAD_TYPE.test(street.trimStart()) ? "fr" : base
433
354
  }
434
355
 
435
356
  /**
436
- * Strip a trailing French arrondissement designator from a FOLDED commune key ("paris 8e arrondissement" → "paris",
437
- * "lyon 1er arrondissement" → "lyon", "marseille 10e arrondissement" → "marseille"). Paris, Lyon and Marseille are the
438
- * only French communes subdivided into _arrondissements municipaux_; a national register (BAN) names each row per
439
- * arrondissement, but a query names the base commune ("Place Bellecour, Lyon", never "…, Lyon 2e"). Applied on BOTH
440
- * sides of the #1042 street-centroid key — build-side (deriving the `locality_base` column) and query-side (folding the
441
- * probe commune) — so the two agree by construction (the one-function discipline). The {@link NameKey} parameter is the
442
- * enforcement of "must already be folded": stripping an arrondissement off an unfolded surface yields a key neither
443
- * side stores. A no-op for every other commune, and the strip leaves a fold fixed point, so the result is still a
444
- * {@link NameKey}. Returns the input unchanged if the strip would empty it.
357
+ * Strips a trailing French arrondissement designator from a folded commune key,
358
+ * returning the input unchanged when the strip would leave no text.
445
359
  */
446
360
  export function stripArrondissement(localityNorm: NameKey): NameKey {
447
361
  const stripped = localityNorm.replace(/\s+\d+(?:er|e)\s+arrondissement$/, "").trim() as NameKey
@@ -450,17 +364,10 @@ export function stripArrondissement(localityNorm: NameKey): NameKey {
450
364
  }
451
365
 
452
366
  /**
453
- * Strip a locality QUALIFIER for a query-side fallback — when an OA locality's exact normalized name misses the
454
- * gazetteer's canonical name, retry with the qualifier removed. OA address data carries disambiguating qualifiers the
455
- * gazetteer's canonical name omits: Austrian `Kraubath/Mur` and `Hart b.Graz` → `Hart`; Swiss `Lenk im Simmental` →
456
- * `Lenk`, `Roche VD` → `Roche`; Danish `Odense S`, `Hurup Thy`. A FALLBACK ONLY — the exact name is tried first, and
457
- * the region-bbox disambiguation resolves any base-name ambiguity downstream. The candidate table is unchanged (this is
458
- * purely query-side); feed the result back through {@link normalizeLocalityForKey}. Returns "" when nothing was stripped
459
- * (no point re-probing the identical key).
367
+ * Strips a disambiguating qualifier from a locality name for a query-side retry,
368
+ * returning an empty string when no text was stripped.
460
369
  *
461
- * Measured (`scripts/eval/candidate-recall.ts --strip-fallback`, EU OA holdouts): recovers AT 74.1→88.2% (+14.1pp), DK
462
- * 91.5→96.2%, CH 90.4→92.6%; +1.3pp overall (diluted by the already-100% locales). Conservative by design — only the
463
- * qualifier forms above; FI/PT/SI misses are untouched.
370
+ * The result must be refolded with {@link normalizeLocalityForKey} before probing.
464
371
  */
465
372
  export function stripLocalityQualifier(locality: string): string {
466
373
  let s = locality.trim()
@@ -470,7 +377,7 @@ export function stripLocalityQualifier(locality: string): string {
470
377
  }
471
378
 
472
379
  const words = [...s.matchAll(/\S+/gu)].map((match) => ({ value: match[0], index: match.index }))
473
- let qualifierStart: number | undefined
380
+ let qualifierStart: number | null = null
474
381
 
475
382
  for (let index = 1; index < words.length; index++) {
476
383
  const word = words[index]!
@@ -493,7 +400,7 @@ export function stripLocalityQualifier(locality: string): string {
493
400
  const suffix = words.at(-1)
494
401
 
495
402
  if (
496
- qualifierStart === undefined &&
403
+ qualifierStart === null &&
497
404
  suffix &&
498
405
  words.length > 1 &&
499
406
  (["S", "N", "E", "W", "V", "Ø", "Sø", "Fyn", "Thy", "Sjælland", "Jylland"].includes(suffix.value) ||
@@ -502,7 +409,7 @@ export function stripLocalityQualifier(locality: string): string {
502
409
  qualifierStart = suffix.index
503
410
  }
504
411
 
505
- if (qualifierStart !== undefined) {
412
+ if (qualifierStart !== null) {
506
413
  s = s.slice(0, qualifierStart).trimEnd()
507
414
  }
508
415
 
@@ -512,57 +419,24 @@ export function stripLocalityQualifier(locality: string): string {
512
419
  }
513
420
 
514
421
  /**
515
- * Fold numbered-route designators to a canonical key, applied AFTER {@link normalizeStreetForKey}. Sources disagree
516
- * systematically on how they spell a route: TIGER says `State Rte 100` / `US Hwy 5` where E911/Overture say `VT ROUTE
517
- * 100` / `US ROUTE 5` — the dominant street-name miss class in the #483 interpolation eval (rural addresses live on
518
- * routes). `us <designator> N…` folds to `us route N…`; `state <designator> N…` and `<2-letter-prefix> <designator> N…`
519
- * (the state abbreviation form) fold to `state route N…`. Only digit-leading route numbers fold — `State Street` and
520
- * friends never match.
521
- *
522
- * Used by BOTH the segment-extract builder (`scripts/build-interpolation-extract.ts`) and the interpolation lookup —
523
- * same one-function discipline as {@link normalizeStreetForKey}. The address-point tier (#476) does NOT apply it yet:
524
- * adopting it there requires a extract rebuild (noted on #483).
525
- *
526
- * A same-numbered US and state route stay DISTINCT keys (`us route 5` vs `state route 5`); only the BARE `route N` form
527
- * is ambiguous (designator unknown) and it stays unfolded — a bare-route query therefore misses rather than guessing a
528
- * designator.
422
+ * Folds a numbered-route designator in a street key to `us route N` or `state route N`;
423
+ * only digit-leading numbers after a `us`, `state`, or two-letter prefix fold.
529
424
  */
530
425
  export function canonicalizeRouteKey(streetNorm: StreetKey): RouteKey {
531
426
  const match = /^(us|state|[a-z]{2}) (?:route|rte|rt|highway|hwy) (\d.*)$/.exec(streetNorm)
532
427
 
533
- // A street naming no route is already its own route key. {@link StreetKey} and {@link RouteKey} are
534
- // SIBLING brands rather than nested ones — precisely so neither column accepts the other's key — so
535
- // re-minting here is an explicit hop through the unbranded string.
536
428
  if (!match) return streetNorm as string as RouteKey
537
429
 
538
430
  return `${match[1] === "us" ? "us" : "state"} route ${match[2]}` as RouteKey
539
431
  }
540
432
 
541
- /**
542
- * The canonical street-type words (lowercase) — the VALUE side of the codex suffix table. Membership means "this
543
- * normalized token is a fully-spelled street type" ("place", "street", "road" …), which is how the doubled-type
544
- * collapse below recognizes its shape without any parse-tree knowledge.
545
- */
546
433
  const CANONICAL_TYPE_WORDS: ReadonlySet<string> = new Set(
547
434
  [...US_STREET_SUFFIX_LOOKUP.values()].map((v) => v.toLowerCase())
548
435
  )
549
436
 
550
437
  /**
551
- * Ordered lookup-key variants for a US/EN street span — the primary normalized key first, then the null-only recovery
552
- * forms a reader may probe when the primary misses. Two register mismatches motivate them, both measured on live
553
- * queries (2026-08-14):
554
- *
555
- * - **Doubled type** — a user types the type twice ("Saint Pauls PL St"), and the normalizer canonicalizes only the LAST
556
- * type token, leaving `saint pauls pl street`, which matches nothing anywhere. The signature is visible in the key
557
- * itself: a canonical type word in last position DIRECTLY after an uncanonicalized type abbreviation. The variant
558
- * drops the trailing word and canonicalizes what remains (`saint pauls place`). A street genuinely named with two
559
- * types keys identically on both sides and is caught by the primary probe first.
560
- * - **Saint↔St register split** — the artifacts preserve each SOURCE's spelling (NYC situs keys `st pauls place`, Nassau
561
- * keys `saint pauls place`), and a query arrives in whichever register the user typed. A leading `saint` or `st`
562
- * token is swapped for its sibling; leading position only — a leading `st` is always the hagionym in US street names,
563
- * and interior tokens ("Mount Saint Helens Dr") are out of scope until measured.
564
- *
565
- * Deduplicated and ordered most-literal-first, so probing the list in order preserves the primary key's precedence.
438
+ * Lists the lookup keys to probe for a street span, most literal first, adding doubled-type
439
+ * and Saint/St-swap recovery forms for `us` and `en`.
566
440
  */
567
441
  export function streetKeyVariants(street: string, locale: StreetLocale = "us"): StreetKey[] {
568
442
  const primary = locale === "us" ? normalizeStreetForKey(street) : normalizeStreetForKeyLocale(street, locale)
@@ -589,8 +463,6 @@ export function streetKeyVariants(street: string, locale: StreetLocale = "us"):
589
463
  }
590
464
  }
591
465
 
592
- // Snapshot before the swap pass — it appends while reading, and iterating the live array would
593
- // re-visit its own additions.
594
466
  const preSwap = variants.slice()
595
467
 
596
468
  for (const variant of preSwap) {
@@ -598,9 +470,6 @@ export function streetKeyVariants(street: string, locale: StreetLocale = "us"):
598
470
  const swapped = head === "saint" ? "st" : head === "st" ? "saint" : null
599
471
 
600
472
  if (swapped && rest.length) {
601
- // Substituting a leading hagionym token in an already-normalized key leaves a fold fixed point —
602
- // the suffix pass never rewrites position 0 — so the swap yields a {@link StreetKey} without
603
- // re-folding.
604
473
  const candidate = [swapped, ...rest].join(" ") as StreetKey
605
474
 
606
475
  if (!variants.includes(candidate)) {