@mailwoman/resolver-wof-sqlite 10.0.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/README.md +9 -8
  2. package/lib/address/point/interpolation.ts +45 -39
  3. package/lib/address/point/schema.ts +17 -40
  4. package/lib/address/point.ts +316 -0
  5. package/lib/{address/index.ts → address.ts} +1 -1
  6. package/lib/ancestry/backfill.ts +20 -86
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +122 -174
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +44 -114
  11. package/lib/candidate/alias-bags.ts +16 -17
  12. package/lib/candidate/ancestors/schema.ts +77 -61
  13. package/lib/candidate/ancestors/sidecar.ts +15 -22
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +10 -55
  16. package/lib/candidate/fts.ts +5 -23
  17. package/lib/candidate/importance.ts +65 -68
  18. package/lib/candidate/lookup.ts +124 -415
  19. package/lib/candidate/name-roles.ts +15 -33
  20. package/lib/candidate/own-name.ts +35 -32
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +29 -65
  23. package/lib/candidate/scoring.ts +54 -93
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +29 -27
  26. package/lib/coincident-roles.ts +85 -55
  27. package/lib/convention/schema.ts +15 -16
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +40 -45
  30. package/lib/currency-backfill.ts +34 -79
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +32 -21
  33. package/lib/extracts.ts +97 -74
  34. package/lib/fst/autocomplete.ts +54 -37
  35. package/lib/fst/builder.ts +37 -41
  36. package/lib/fst/deserialize-web.ts +16 -17
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +43 -120
  39. package/lib/fst/matcher.ts +7 -5
  40. package/lib/fst/serialize.ts +54 -74
  41. package/lib/fst/types.ts +78 -75
  42. package/lib/{fst/index.ts → fst.ts} +1 -0
  43. package/lib/fts/query.ts +5 -36
  44. package/lib/fts.ts +280 -0
  45. package/lib/geonames/aliases.ts +73 -144
  46. package/lib/geonames/postal.ts +162 -71
  47. package/lib/index.ts +9 -8
  48. package/lib/interpolation.ts +48 -100
  49. package/lib/lookup.ts +155 -270
  50. package/lib/name-score.ts +9 -7
  51. package/lib/nsul/lookup.ts +37 -54
  52. package/lib/nsul/schema.ts +16 -43
  53. package/lib/paths.ts +159 -0
  54. package/lib/place-importance-schema.ts +62 -169
  55. package/lib/poi/lookup.ts +104 -121
  56. package/lib/poi/schema.ts +42 -26
  57. package/lib/polygon-schema.ts +10 -6
  58. package/lib/postal/city/alias/lookup.ts +22 -40
  59. package/lib/postal/city/alias/schema.ts +14 -10
  60. package/lib/postal/city/candidate-schema.ts +26 -20
  61. package/lib/postcode-point-lookup.ts +8 -27
  62. package/lib/primary-preference.ts +46 -126
  63. package/lib/proximity-rerank.ts +44 -33
  64. package/lib/ranking-weights.ts +105 -78
  65. package/lib/region-keys.ts +70 -42
  66. package/lib/reverse.ts +149 -86
  67. package/lib/schema.ts +73 -50
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +15 -23
  70. package/lib/sqlite/utils.ts +19 -22
  71. package/lib/street/centroid/schema.ts +37 -42
  72. package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
  73. package/lib/street/morphology/fst/builder.ts +60 -42
  74. package/lib/street/morphology/fst/loader.ts +40 -32
  75. package/lib/street/name-lookup.ts +45 -41
  76. package/lib/street/normalize.ts +88 -219
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +1 -1
  79. package/lib/types.ts +109 -150
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +29 -68
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +236 -252
  85. package/out/address/point/interpolation.d.ts +20 -23
  86. package/out/address/point/interpolation.d.ts.map +1 -1
  87. package/out/address/point/interpolation.js +42 -39
  88. package/out/address/point/interpolation.js.map +1 -1
  89. package/out/address/point/schema.d.ts +12 -35
  90. package/out/address/point/schema.d.ts.map +1 -1
  91. package/out/address/point/schema.js +3 -19
  92. package/out/address/point/schema.js.map +1 -1
  93. package/out/address/point.d.ts +57 -0
  94. package/out/address/point.d.ts.map +1 -0
  95. package/out/address/point.js +206 -0
  96. package/out/address/point.js.map +1 -0
  97. package/out/{address/index.js → address.d.ts} +2 -2
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +2 -2
  100. package/out/address.js.map +1 -0
  101. package/out/ancestry/backfill.d.ts +11 -64
  102. package/out/ancestry/backfill.d.ts.map +1 -1
  103. package/out/ancestry/backfill.js +14 -74
  104. package/out/ancestry/backfill.js.map +1 -1
  105. package/out/ancestry.d.ts +49 -0
  106. package/out/ancestry.d.ts.map +1 -0
  107. package/out/ancestry.js +59 -0
  108. package/out/ancestry.js.map +1 -0
  109. package/out/build/candidate.d.ts +82 -112
  110. package/out/build/candidate.d.ts.map +1 -1
  111. package/out/build/candidate.js +17 -100
  112. package/out/build/candidate.js.map +1 -1
  113. package/out/build/population-corrections.d.ts +52 -0
  114. package/out/build/population-corrections.d.ts.map +1 -0
  115. package/out/build/population-corrections.js +40 -0
  116. package/out/build/population-corrections.js.map +1 -0
  117. package/out/build/slim.d.ts +8 -44
  118. package/out/build/slim.d.ts.map +1 -1
  119. package/out/build/slim.js +33 -94
  120. package/out/build/slim.js.map +1 -1
  121. package/out/candidate/alias-bags.d.ts +6 -6
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +16 -17
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +73 -57
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -1
  127. package/out/candidate/ancestors/schema.js +56 -48
  128. package/out/candidate/ancestors/schema.js.map +1 -1
  129. package/out/candidate/ancestors/sidecar.d.ts +3 -8
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
  131. package/out/candidate/ancestors/sidecar.js +15 -22
  132. package/out/candidate/ancestors/sidecar.js.map +1 -1
  133. package/out/candidate/country-display-names.d.ts +5 -15
  134. package/out/candidate/country-display-names.d.ts.map +1 -1
  135. package/out/candidate/country-display-names.js +6 -20
  136. package/out/candidate/country-display-names.js.map +1 -1
  137. package/out/candidate/extract-fold.d.ts +7 -15
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +5 -47
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +5 -23
  142. package/out/candidate/fts.d.ts.map +1 -1
  143. package/out/candidate/fts.js +5 -23
  144. package/out/candidate/fts.js.map +1 -1
  145. package/out/candidate/importance.d.ts +60 -65
  146. package/out/candidate/importance.d.ts.map +1 -1
  147. package/out/candidate/importance.js +56 -63
  148. package/out/candidate/importance.js.map +1 -1
  149. package/out/candidate/lookup.d.ts +18 -55
  150. package/out/candidate/lookup.d.ts.map +1 -1
  151. package/out/candidate/lookup.js +82 -385
  152. package/out/candidate/lookup.js.map +1 -1
  153. package/out/candidate/name-roles.d.ts +9 -21
  154. package/out/candidate/name-roles.d.ts.map +1 -1
  155. package/out/candidate/name-roles.js +15 -33
  156. package/out/candidate/name-roles.js.map +1 -1
  157. package/out/candidate/own-name.d.ts +23 -25
  158. package/out/candidate/own-name.d.ts.map +1 -1
  159. package/out/candidate/own-name.js +35 -32
  160. package/out/candidate/own-name.js.map +1 -1
  161. package/out/candidate/place-attrs.d.ts +20 -13
  162. package/out/candidate/place-attrs.d.ts.map +1 -1
  163. package/out/candidate/place-attrs.js +4 -5
  164. package/out/candidate/place-attrs.js.map +1 -1
  165. package/out/candidate/schema.d.ts +27 -62
  166. package/out/candidate/schema.d.ts.map +1 -1
  167. package/out/candidate/schema.js +10 -22
  168. package/out/candidate/schema.js.map +1 -1
  169. package/out/candidate/scoring.d.ts +7 -8
  170. package/out/candidate/scoring.d.ts.map +1 -1
  171. package/out/candidate/scoring.js +52 -89
  172. package/out/candidate/scoring.js.map +1 -1
  173. package/out/capital-schema.d.ts +21 -17
  174. package/out/capital-schema.d.ts.map +1 -1
  175. package/out/capital-schema.js +17 -15
  176. package/out/capital-schema.js.map +1 -1
  177. package/out/capitals.d.ts +27 -25
  178. package/out/capitals.d.ts.map +1 -1
  179. package/out/capitals.js +23 -21
  180. package/out/capitals.js.map +1 -1
  181. package/out/coincident-roles.d.ts +60 -43
  182. package/out/coincident-roles.d.ts.map +1 -1
  183. package/out/coincident-roles.js +72 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +14 -14
  186. package/out/convention/schema.d.ts.map +1 -1
  187. package/out/convention/schema.js +6 -4
  188. package/out/convention/schema.js.map +1 -1
  189. package/out/convention.d.ts +97 -0
  190. package/out/convention.d.ts.map +1 -0
  191. package/out/convention.js +81 -0
  192. package/out/convention.js.map +1 -0
  193. package/out/coverage-manifest-schema.d.ts +34 -39
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +25 -34
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +16 -38
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +19 -46
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +1 -1
  202. package/out/env.js +1 -1
  203. package/out/exact-match.d.ts +19 -10
  204. package/out/exact-match.d.ts.map +1 -1
  205. package/out/exact-match.js +32 -21
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +66 -55
  208. package/out/extracts.d.ts.map +1 -1
  209. package/out/extracts.js +63 -55
  210. package/out/extracts.js.map +1 -1
  211. package/out/fst/autocomplete.d.ts +29 -21
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +39 -28
  214. package/out/fst/autocomplete.js.map +1 -1
  215. package/out/fst/builder.d.ts.map +1 -1
  216. package/out/fst/builder.js +37 -41
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +4 -4
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +15 -16
  221. package/out/fst/deserialize-web.js.map +1 -1
  222. package/out/fst/format.d.ts +41 -23
  223. package/out/fst/format.d.ts.map +1 -1
  224. package/out/fst/format.js +41 -23
  225. package/out/fst/format.js.map +1 -1
  226. package/out/fst/freshness.d.ts +19 -81
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +31 -94
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +5 -4
  231. package/out/fst/matcher.d.ts.map +1 -1
  232. package/out/fst/matcher.js +2 -2
  233. package/out/fst/matcher.js.map +1 -1
  234. package/out/fst/serialize.d.ts +28 -28
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +53 -71
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +78 -74
  239. package/out/fst/types.d.ts.map +1 -1
  240. package/out/fst/types.js +1 -4
  241. package/out/fst/types.js.map +1 -1
  242. package/out/{fst/index.js → fst.d.ts} +2 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +2 -1
  245. package/out/fst.js.map +1 -0
  246. package/out/fts/query.d.ts +2 -27
  247. package/out/fts/query.d.ts.map +1 -1
  248. package/out/fts/query.js +5 -36
  249. package/out/fts/query.js.map +1 -1
  250. package/out/fts.d.ts +126 -0
  251. package/out/fts.d.ts.map +1 -0
  252. package/out/fts.js +188 -0
  253. package/out/fts.js.map +1 -0
  254. package/out/geonames/aliases.d.ts +26 -62
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +45 -117
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -44
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -73
  261. package/out/geonames/postal.js.map +1 -1
  262. package/out/{geonames/index.js → geonames.d.ts} +1 -1
  263. package/out/geonames.d.ts.map +1 -0
  264. package/out/{geonames/index.d.ts → geonames.js} +1 -1
  265. package/out/geonames.js.map +1 -0
  266. package/out/index.d.ts +9 -8
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +6 -5
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +18 -55
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +27 -69
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +23 -51
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +136 -234
  277. package/out/lookup.js.map +1 -1
  278. package/out/name-score.d.ts +2 -2
  279. package/out/name-score.d.ts.map +1 -1
  280. package/out/name-score.js +9 -7
  281. package/out/name-score.js.map +1 -1
  282. package/out/nsul/lookup.d.ts +33 -34
  283. package/out/nsul/lookup.d.ts.map +1 -1
  284. package/out/nsul/lookup.js +26 -40
  285. package/out/nsul/lookup.js.map +1 -1
  286. package/out/nsul/schema.d.ts +16 -43
  287. package/out/nsul/schema.d.ts.map +1 -1
  288. package/out/nsul/schema.js +9 -35
  289. package/out/nsul/schema.js.map +1 -1
  290. package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
  291. package/out/nsul.d.ts.map +1 -0
  292. package/out/{nsul/index.js → nsul.js} +1 -1
  293. package/out/nsul.js.map +1 -0
  294. package/out/paths.d.ts +124 -0
  295. package/out/paths.d.ts.map +1 -0
  296. package/out/paths.js +104 -0
  297. package/out/paths.js.map +1 -0
  298. package/out/place-importance-schema.d.ts +51 -143
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +28 -143
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +40 -47
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +69 -96
  305. package/out/poi/lookup.js.map +1 -1
  306. package/out/poi/schema.d.ts +41 -25
  307. package/out/poi/schema.d.ts.map +1 -1
  308. package/out/poi/schema.js +25 -17
  309. package/out/poi/schema.js.map +1 -1
  310. package/out/{poi/index.js → poi.d.ts} +1 -1
  311. package/out/poi.d.ts.map +1 -0
  312. package/out/{poi/index.d.ts → poi.js} +1 -1
  313. package/out/poi.js.map +1 -0
  314. package/out/polygon-schema.d.ts +10 -6
  315. package/out/polygon-schema.d.ts.map +1 -1
  316. package/out/polygon-schema.js +2 -2
  317. package/out/polygon-schema.js.map +1 -1
  318. package/out/postal/city/alias/lookup.d.ts +20 -23
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -1
  320. package/out/postal/city/alias/lookup.js +17 -30
  321. package/out/postal/city/alias/lookup.js.map +1 -1
  322. package/out/postal/city/alias/schema.d.ts +14 -10
  323. package/out/postal/city/alias/schema.d.ts.map +1 -1
  324. package/out/postal/city/alias/schema.js +10 -8
  325. package/out/postal/city/alias/schema.js.map +1 -1
  326. package/out/postal/city/candidate-schema.d.ts +25 -19
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -1
  328. package/out/postal/city/candidate-schema.js +21 -17
  329. package/out/postal/city/candidate-schema.js.map +1 -1
  330. package/out/{postal/index.d.ts → postal.d.ts} +1 -1
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/{postal/index.js → postal.js} +1 -1
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +6 -24
  335. package/out/postcode-point-lookup.d.ts.map +1 -1
  336. package/out/postcode-point-lookup.js +5 -17
  337. package/out/postcode-point-lookup.js.map +1 -1
  338. package/out/primary-preference.d.ts +38 -92
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +15 -99
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +41 -30
  343. package/out/proximity-rerank.d.ts.map +1 -1
  344. package/out/proximity-rerank.js +38 -29
  345. package/out/proximity-rerank.js.map +1 -1
  346. package/out/ranking-weights.d.ts +98 -64
  347. package/out/ranking-weights.d.ts.map +1 -1
  348. package/out/ranking-weights.js +26 -25
  349. package/out/ranking-weights.js.map +1 -1
  350. package/out/region-keys.d.ts +50 -31
  351. package/out/region-keys.d.ts.map +1 -1
  352. package/out/region-keys.js +68 -40
  353. package/out/region-keys.js.map +1 -1
  354. package/out/reverse.d.ts +78 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +98 -61
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +72 -49
  359. package/out/schema.d.ts.map +1 -1
  360. package/out/schema.js +4 -5
  361. package/out/schema.js.map +1 -1
  362. package/out/search-fetch.d.ts +16 -12
  363. package/out/search-fetch.d.ts.map +1 -1
  364. package/out/search-fetch.js +83 -63
  365. package/out/search-fetch.js.map +1 -1
  366. package/out/sqlite/convention-source.d.ts +12 -16
  367. package/out/sqlite/convention-source.d.ts.map +1 -1
  368. package/out/sqlite/convention-source.js +13 -22
  369. package/out/sqlite/convention-source.js.map +1 -1
  370. package/out/sqlite/utils.d.ts +19 -19
  371. package/out/sqlite/utils.d.ts.map +1 -1
  372. package/out/sqlite/utils.js +14 -15
  373. package/out/sqlite/utils.js.map +1 -1
  374. package/out/street/centroid/schema.d.ts +37 -42
  375. package/out/street/centroid/schema.d.ts.map +1 -1
  376. package/out/street/centroid/schema.js +9 -21
  377. package/out/street/centroid/schema.js.map +1 -1
  378. package/out/street/centroid.d.ts +30 -0
  379. package/out/street/centroid.d.ts.map +1 -0
  380. package/out/street/{centroid/index.js → centroid.js} +13 -24
  381. package/out/street/centroid.js.map +1 -0
  382. package/out/street/morphology/fst/builder.d.ts +20 -10
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -1
  384. package/out/street/morphology/fst/builder.js +44 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -1
  386. package/out/street/morphology/fst/loader.d.ts +33 -26
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -1
  388. package/out/street/morphology/fst/loader.js +23 -22
  389. package/out/street/morphology/fst/loader.js.map +1 -1
  390. package/out/street/name-lookup.d.ts +24 -18
  391. package/out/street/name-lookup.d.ts.map +1 -1
  392. package/out/street/name-lookup.js +34 -35
  393. package/out/street/name-lookup.js.map +1 -1
  394. package/out/street/normalize.d.ts +35 -136
  395. package/out/street/normalize.d.ts.map +1 -1
  396. package/out/street/normalize.js +77 -194
  397. package/out/street/normalize.js.map +1 -1
  398. package/out/street/segment-schema.d.ts +79 -39
  399. package/out/street/segment-schema.d.ts.map +1 -1
  400. package/out/street/segment-schema.js +71 -21
  401. package/out/street/segment-schema.js.map +1 -1
  402. package/out/{street/index.js → street.d.ts} +2 -2
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.d.ts → street.js} +2 -2
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +84 -149
  407. package/out/types.d.ts.map +1 -1
  408. package/out/types.js +0 -6
  409. package/out/types.js.map +1 -1
  410. package/out/unified-schema.d.ts +8 -12
  411. package/out/unified-schema.d.ts.map +1 -1
  412. package/out/unified-schema.js +29 -29
  413. package/out/unified-schema.js.map +1 -1
  414. package/out/uprn/existence.d.ts +28 -17
  415. package/out/uprn/existence.d.ts.map +1 -1
  416. package/out/uprn/existence.js +26 -16
  417. package/out/uprn/existence.js.map +1 -1
  418. package/out/uprn/lookup.d.ts +14 -44
  419. package/out/uprn/lookup.d.ts.map +1 -1
  420. package/out/uprn/lookup.js +21 -57
  421. package/out/uprn/lookup.js.map +1 -1
  422. package/out/uprn/schema.d.ts +18 -40
  423. package/out/uprn/schema.d.ts.map +1 -1
  424. package/out/uprn/schema.js +10 -32
  425. package/out/uprn/schema.js.map +1 -1
  426. package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
  427. package/out/uprn.d.ts.map +1 -0
  428. package/out/{uprn/index.js → uprn.js} +1 -1
  429. package/out/uprn.js.map +1 -0
  430. package/out/weights-overlay-linker.d.ts +138 -144
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +105 -159
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +91 -596
  435. package/lib/address/point/index.ts +0 -328
  436. package/lib/convention/index.ts +0 -154
  437. package/lib/fts/index.ts +0 -361
  438. package/out/address/index.d.ts.map +0 -1
  439. package/out/address/index.js.map +0 -1
  440. package/out/address/point/index.d.ts +0 -53
  441. package/out/address/point/index.d.ts.map +0 -1
  442. package/out/address/point/index.js +0 -222
  443. package/out/address/point/index.js.map +0 -1
  444. package/out/ancestry/index.d.ts +0 -45
  445. package/out/ancestry/index.d.ts.map +0 -1
  446. package/out/ancestry/index.js +0 -55
  447. package/out/ancestry/index.js.map +0 -1
  448. package/out/convention/index.d.ts +0 -106
  449. package/out/convention/index.d.ts.map +0 -1
  450. package/out/convention/index.js +0 -93
  451. package/out/convention/index.js.map +0 -1
  452. package/out/fst/index.d.ts.map +0 -1
  453. package/out/fst/index.js.map +0 -1
  454. package/out/fts/index.d.ts +0 -184
  455. package/out/fts/index.d.ts.map +0 -1
  456. package/out/fts/index.js +0 -277
  457. package/out/fts/index.js.map +0 -1
  458. package/out/geonames/index.d.ts.map +0 -1
  459. package/out/geonames/index.js.map +0 -1
  460. package/out/nsul/index.d.ts.map +0 -1
  461. package/out/nsul/index.js.map +0 -1
  462. package/out/poi/index.d.ts.map +0 -1
  463. package/out/poi/index.js.map +0 -1
  464. package/out/postal/index.d.ts.map +0 -1
  465. package/out/postal/index.js.map +0 -1
  466. package/out/street/centroid/index.d.ts +0 -40
  467. package/out/street/centroid/index.d.ts.map +0 -1
  468. package/out/street/centroid/index.js.map +0 -1
  469. package/out/street/index.d.ts.map +0 -1
  470. package/out/street/index.js.map +0 -1
  471. package/out/uprn/index.d.ts.map +0 -1
  472. package/out/uprn/index.js.map +0 -1
  473. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  474. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  475. /package/lib/{poi/index.ts → poi.ts} +0 -0
  476. /package/lib/{postal/index.ts → postal.ts} +0 -0
  477. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
package/lib/fst/types.ts CHANGED
@@ -2,49 +2,40 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * Types for the FST gazetteer language model. The FST maps token sequences (place names) to
7
- * (placetype, wof_id, parent_chain) entries — pre-computing the valid interpretations for each
8
- * prefix of every place name in the gazetteer.
5
+ * @file Types for the FST gazetteer. These types map place-name token sequences to place entries.
9
6
  */
10
7
 
11
8
  import type { PathBuilderLike } from "path-ts"
12
9
 
10
+ /**
11
+ * One place that a token sequence in the FST accepts.
12
+ */
13
13
  export interface PlaceEntry {
14
14
  wofID: number
15
15
  placetype: PlacetypeID
16
16
  name: string
17
17
  parentChain: number[]
18
18
  /**
19
- * REFERENTIAL likelihood in [0, 1] — population-anchored (`referentialFromPopulation`), and the ONLY score the
20
- * decoder bias is allowed to read (ROAD_TO_V9 §2, ratified 2026-08-06).
21
- *
22
- * This field was called `importance` through format v4, where it carried whichever score the source database happened
23
- * to hold: the population proxy on a database with no `place_importance` table (which is every shipped
24
- * `fst-per-locale` binary), the Wikipedia score where the concordance join landed otherwise. The rename is the point
25
- * — a v4 artifact's value is a CONFLATION that cannot be told apart after the fact, which is why `fst-freshness.ts`
26
- * reports anything below {@link FST_FORMAT_VERSION} as format-stale rather than reading it as referential.
19
+ * The population-based referential likelihood in [0, 1] that the decoder bias uses.
27
20
  */
28
21
  referential: number
29
22
  /**
30
- * Encyclopedic (Wikipedia) importance in [0, 1], fan-out-guarded per #1497. Carried for consumers that want to
31
- * DISPLAY salience; never read by the decoder or by any ranking.
32
- *
33
- * `undefined` = this place has no encyclopedic signal, or the artifact predates format 5. NEVER conflate either with
34
- * 0 — the meaning-of-zero rule; roughly 89% of the 2026-08-05 gazetteer's rows have no Wikipedia article at all.
23
+ * The encyclopedic importance in [0, 1], for display only.
24
+ * It is `null` when unavailable.
35
25
  */
36
- encyclopedic?: number
26
+ encyclopedic: number | null
37
27
  lat: number
38
28
  lon: number
39
29
  /**
40
- * Surface-ambiguity class (survey #4): how many DISTINCT countries carry a place with THIS entry's accepting surface,
41
- * counted over the whole admin DB at build time (clamped to 255). A property of the surface, not the place — the same
42
- * place reached via different alias surfaces reports each surface's own count. `undefined` = built without ambiguity
43
- * data (pre-2026-07-27 artifacts) — NEVER conflate with 1 (the unambiguous case); the meaning-of-zero rule.
30
+ * The number of countries with a place of this surface, clamped to 255.
31
+ * It is `null` when unavailable.
44
32
  */
45
- crossCountryBranches?: number
33
+ crossCountryBranches: number | null
46
34
  }
47
35
 
36
+ /**
37
+ * The placetypes the FST stores.
38
+ */
48
39
  export type PlacetypeID =
49
40
  | "country"
50
41
  | "region"
@@ -58,18 +49,18 @@ export type PlacetypeID =
58
49
  | "dependency"
59
50
  | "street_affix"
60
51
 
61
- export interface FSTMatchResult {
62
- stateID: number
63
- accepted: boolean
64
- depth: number
65
- }
66
-
52
+ /**
53
+ * One outgoing edge from an FST state, with the number of places its target state accepts.
54
+ */
67
55
  export interface FSTContinuation {
68
56
  token: string
69
57
  targetState: number
70
58
  acceptingCount: number
71
59
  }
72
60
 
61
+ /**
62
+ * The places a token path accepts and the tokens that can extend it.
63
+ */
73
64
  export interface FSTQueryResult {
74
65
  path: string[]
75
66
  stateID: number
@@ -77,6 +68,9 @@ export interface FSTQueryResult {
77
68
  continuations: FSTContinuation[]
78
69
  }
79
70
 
71
+ /**
72
+ * The build metadata stored in an FST binary's provenance trailer.
73
+ */
80
74
  export interface FSTProvenance {
81
75
  builtAt: string
82
76
  countries: string[]
@@ -85,93 +79,102 @@ export interface FSTProvenance {
85
79
  edgeCount: number
86
80
  nameInsertions: number
87
81
  /**
88
- * How many places carried a non-zero REFERENTIAL score at build time. Named `importanceMatches` for stamp
89
- * compatibility with every artifact written before the two-score split — renaming the JSON key would make every
90
- * existing stamp unreadable, and the number means the same thing it always did on a population-built artifact.
82
+ * The count of places with a non-zero referential score at build time.
83
+ *
84
+ * The key keeps its older name so that existing provenance stamps stay readable.
91
85
  */
92
86
  importanceMatches: number
93
87
  /**
94
- * How many places carried an ENCYCLOPEDIC score at build time. `undefined` on a pre-split build — which is not the
95
- * same as 0 (a v5 build against a population-only database), so the freshness report says the two in different
96
- * words.
88
+ * The count of places with an encyclopedic score at build time.
89
+ *
90
+ * It is `null` for a build that predates the separate encyclopedic score.
91
+ * The value `0` means a current build used a database without encyclopedic scores.
97
92
  */
98
- encyclopedicMatches?: number
93
+ encyclopedicMatches: number | null
99
94
  /**
100
- * How the builder obtained its two scores — an {@link ImportanceSplitSource}. Recorded so an artifact states, in its
101
- * own provenance, whether its encyclopedic channel is real, reconstructed from a legacy conflated column, or absent.
95
+ * The {@link ImportanceSplitSource} that records whether the encyclopedic score is real,
96
+ * reconstructed from a legacy column, or absent.
102
97
  */
103
- importanceSource?: string
104
- sourceDB?: string
98
+ importanceSource: string | null
99
+ sourceDB: string | null
105
100
  /**
106
- * MD5 of the source database's bytes at build time — the artifact's link to the gazetteer it is a projection of.
101
+ * The MD5 of the source database's bytes at build time.
102
+ * The freshness check compares it.
107
103
  *
108
- * `sourceDB` records the PATH, which is exactly the field that cannot change when the bytes behind it do: the admin
109
- * DB is a sealed readonly artifact that a rebuild REPLACES in place, so every FST built before the 2026-08-04 swap
110
- * still names the current file and none of them was built from it. Compared by `fst-freshness.ts`.
104
+ * `sourceDB` by itself cannot detect staleness.
105
+ * A rebuild replaces the admin database at the same path.
111
106
  *
112
- * `undefined` = built before the stamp existed (every artifact predating 2026-08-05). NEVER conflate that with "built
113
- * from a database whose md5 is unknown" — the freshness check reports the two in different words.
107
+ * The field is `null` for artifacts built before the stamp existed.
108
+ * An unknown digest has a different value.
114
109
  */
115
- sourceDBMD5?: string
110
+ sourceDBMD5: string | null
116
111
  /**
117
- * Byte size of the source database at build time. Not redundant with {@link FSTProvenance.sourceDBMD5}: it survives a
118
- * truncated source and it is what makes a staleness warning legible — "5,273,722,880 → 5,372,076,032" names the
119
- * rebuild, a hex delta does not.
112
+ * The byte size of the source database at build time.
113
+ *
114
+ * It detects a truncated source and makes a staleness warning readable.
120
115
  */
121
- sourceDBBytes?: number
122
- modelCardVersion?: string
116
+ sourceDBBytes: number | null
117
+ modelCardVersion: string | null
123
118
  /**
124
- * Degenerate-surface curation policy applied at build time (absent = uncurated build).
119
+ * The surface-exclusion policy applied at build time.
120
+ * It is absent for an uncurated build.
125
121
  */
126
- exclusionPolicy?: string
122
+ exclusionPolicy: string | null
127
123
  /**
128
- * Name insertions refused by the curation policy.
124
+ * The count of name insertions the exclusion policy rejected.
129
125
  */
130
- excludedInsertions?: number
126
+ excludedInsertions: number | null
131
127
  }
132
128
 
129
+ /**
130
+ * Options for building an FST from a WOF admin database.
131
+ */
133
132
  export interface BuildFSTOpts {
134
133
  dbPath: PathBuilderLike
135
134
  /**
136
- * Pre-computed identity of `dbPath` to stamp into provenance. Omit and the builder reads it via
137
- * `readWOFSourceIdentity` (sidecar-cached). Supply it when the digest is already in hand, or when the caller is
138
- * building from something whose identity it defines itself.
135
+ * The precomputed identity of `dbPath` to record in provenance.
136
+ *
137
+ * When omitted, the builder reads it with `readWOFSourceIdentity`, which caches it in a sidecar file.
139
138
  */
140
139
  sourceIdentity?: { md5: string; bytes: number }
141
140
  countries?: string[]
142
141
  placetypes?: PlacetypeID[]
143
142
  languages?: string[]
144
143
  /**
145
- * Degenerate-surface curation (build-time; the ASR-contextual-biasing "prune the bias list" discipline). A name whose
146
- * FULL normalized token sequence joins to a member of this set is never inserted — the surface carries no
147
- * discriminative value as a bias key (bare function words: "la"; bare street-type words: "boulevard"). The FST is a
148
- * bias list, not the gazetteer of record — the resolver's candidate tables are untouched, so excluded places stay
149
- * findable; they just stop nudging the decoder on degenerate keys. Keys must be `normalizeTokens(...).join(" ")`.
144
+ * Surfaces the builder never inserts, such as the bare function word "la"
145
+ * or the bare street type "boulevard".
146
+ *
147
+ * These surfaces do not discriminate between places when used as decoder bias keys.
148
+ * The resolver's candidate tables still hold the excluded places.
149
+ * Keys must be `normalizeTokens(...).join(" ")`.
150
150
  */
151
151
  excludeSurfaces?: ReadonlySet<string>
152
152
  /**
153
- * Compositional clause of the same policy: refuse a name whose EVERY normalized token is a member (e.g. "de la") — a
154
- * surface made entirely of function words cannot be discriminative. Source this from stopwords only, never
155
- * street-type words ("Avenue Road" is a real name; "de la" is not).
153
+ * Tokens that a name must not consist of entirely, such as "de la".
154
+ *
155
+ * Build this set from stopwords only.
156
+ * A street-type word can be part of a real name, as in "Avenue Road".
156
157
  */
157
158
  excludeAllTokensOf?: ReadonlySet<string>
158
159
  /**
159
- * Recorded verbatim into provenance when either exclusion set is supplied.
160
+ * The policy label recorded in provenance when either exclusion set is supplied.
160
161
  */
161
162
  exclusionPolicy?: string
162
163
  /**
163
- * Surface-ambiguity classes (survey #4, 2026-07-27): normalized-join surface → the number of DISTINCT countries
164
- * (across the WHOLE admin DB, not just this build's country scope) with a place carrying that surface. When supplied,
165
- * every inserted place row records the count for ITS accepting surface (`PlaceEntry.crossCountryBranches`) — an entry
166
- * accessible under several surfaces records each surface's own count. Serialized into the place row's former `_pad`
167
- * byte with presence signaled by header flags bit0, so VERSION stays put and pre-ambiguity artifacts read as "no
168
- * data" (never "0 branches" — the meaning-of-zero rule). No decoder consumes it yet; consumers (FST-prior tempering,
169
- * the Option-A evidence channel) arrive behind their own measured checks.
164
+ * Maps each normalized surface to the number of distinct countries in the whole
165
+ * admin database with a place of that surface.
166
+ *
167
+ * When supplied, each inserted place entry records the count for its accepting
168
+ * surface as `PlaceEntry.crossCountryBranches`.
169
+ * A place reachable under several surfaces records each surface's own count.
170
170
  */
171
171
  surfaceCountryCounts?: ReadonlyMap<string, number>
172
172
  onProgress?: (phase: string, detail?: string) => void
173
173
  }
174
174
 
175
+ /**
176
+ * Counts reported by an FST build.
177
+ */
175
178
  export interface BuildFSTResult {
176
179
  stateCount: number
177
180
  placeCount: number
@@ -11,4 +11,5 @@ export * from "#fst/format"
11
11
  export * from "#fst/freshness"
12
12
  export * from "#fst/matcher"
13
13
  export * from "#fst/serialize"
14
+ export { FST_FORMAT_VERSION } from "#fst/format"
14
15
  export * from "#fst/types"
package/lib/fts/query.ts CHANGED
@@ -3,9 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Query shaping for the FTS5 lookup: placetype normalization and the MATCH-expression sanitizer.
7
- * Both turn a caller's loose input into something SQLite's FTS5 parser accepts without throwing —
8
- * an unescaped quote or a bare `*` is a syntax error, not an empty result.
6
+ * Query shaping for the FTS5 lookup: placetype normalization and the match-expression sanitizer.
9
7
  */
10
8
 
11
9
  import type { FindPlaceQuery, WOFPlacetype } from "#types"
@@ -17,30 +15,7 @@ export function normalizePlacetypes(p: FindPlaceQuery["placetype"]): WOFPlacetyp
17
15
  }
18
16
 
19
17
  /**
20
- * Make an arbitrary user-typed string safe for FTS5 MATCH.
21
- *
22
- * FTS5 has its own query syntax (`"phrase"`, `term1 OR term2`, `prefix*`, NEAR/N, etc.). Letting raw user input through
23
- * means a user typing `Paris's` or `St. (Petersburg)` causes a syntax error.
24
- *
25
- * Per-token rules:
26
- *
27
- * - Strip all punctuation except trailing `*` from each whitespace-separated token.
28
- * - **Trailing `*`** is preserved as FTS5 **prefix syntax** — `627*` becomes the literal `627*` (unquoted). The caller
29
- * signaled they want a prefix; respect that.
30
- * - All other tokens are wrapped in `"..."` as a single-word phrase. Conservative — handles apostrophes, parens, accented
31
- * input, etc. safely.
32
- * - Multiple tokens join with implicit AND.
33
- *
34
- * Examples:
35
- *
36
- * - `"Paris"` → `"Paris"` (phrase)
37
- * - `"627*"` → `627*` (prefix)
38
- * - `"St. (Petersburg)"` → `"St" "Petersburg"` (two phrases, AND-joined)
39
- * - `"Thiron-Gardais"` → `"Thiron" "Gardais"` (intra-token punctuation SPLITS — #945; fusing to `ThironGardais` matched
40
- * nothing because the FTS doc tokenizes the hyphenated name as two terms)
41
- * - `"110 00"` with `fuseTokens` (postcode-typed) → `"110" "00"` per-token fused — the #920 name law
42
- * - `"Pari* TX"` → `Pari* "TX"` (mixed prefix + phrase)
43
- * - `"*"` alone → `""` (no body → drop)
18
+ * Make an arbitrary user-typed string safe for FTS5 match.
44
19
  */
45
20
  export function sanitizeFTSQuery(text: string, opts?: { fuseTokens?: boolean }): string {
46
21
  const out: string[] = []
@@ -51,8 +26,7 @@ export function sanitizeFTSQuery(text: string, opts?: { fuseTokens?: boolean }):
51
26
  if (!trimmed) continue
52
27
  const hasPrefixStar = trimmed.endsWith("*")
53
28
 
54
- // #920 name law (postcode-typed queries ONLY): delete intra-token punctuation and FUSE the
55
- // remainder — postal names are stored in this collapsed shape ("SW1A" stays one term).
29
+ // Fused mode deletes intra-token punctuation because postal names are stored in that collapsed shape.
56
30
  if (opts?.fuseTokens) {
57
31
  const body = trimmed.replaceAll(/[^\p{L}\p{N}]/gu, "")
58
32
 
@@ -62,12 +36,7 @@ export function sanitizeFTSQuery(text: string, opts?: { fuseTokens?: boolean }):
62
36
  continue
63
37
  }
64
38
 
65
- // Everything else SPLITS on intra-token punctuation — the behavior the docstring always
66
- // promised ("St. (Petersburg)" → two phrases). The old code DELETED punctuation instead,
67
- // fusing "Thiron-Gardais" into the unmatchable single term `ThironGardais` while the FTS
68
- // doc holds two terms (#945 — the entire hyphenated-name class missed at the raw lookup;
69
- // masked for years because pre-splice tokenizers never emitted hyphen-preserved values).
70
- const parts = trimmed.split(/[^\p{L}\p{N}]+/u).filter((part) => part.length > 0)
39
+ const parts = trimmed.split(/[^\p{L}\p{N}]+/u).filter((part) => part.length)
71
40
 
72
41
  if (!parts.length) continue
73
42
 
@@ -75,7 +44,7 @@ export function sanitizeFTSQuery(text: string, opts?: { fuseTokens?: boolean }):
75
44
  const body = parts[i]!.replaceAll("*", "")
76
45
 
77
46
  if (!body) continue
78
- // The caller's trailing `*` applies to the FINAL part ("Thiron-Gard*" → "Thiron" Gard*).
47
+ // The caller's trailing `*` applies only to the final part.
79
48
  out.push(hasPrefixStar && i === parts.length - 1 ? `${body}*` : `"${body.replaceAll('"', '""')}"`)
80
49
  }
81
50
  }
package/lib/fts.ts ADDED
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ import type { DatabaseClient } from "@mailwoman/sqlite/client"
8
+ import { tableExists } from "@mailwoman/sqlite/introspection"
9
+
10
+ /**
11
+ * The name of the FTS5 virtual table that indexes WOF place names for `match` queries.
12
+ */
13
+ export const PLACE_SEARCH_TABLE = "place_search"
14
+
15
+ /**
16
+ * The separator between aliases in the `alt_names` bag.
17
+ * It stops a phrase query from matching across two aliases.
18
+ *
19
+ * FTS5 assigns positions only to tokens.
20
+ * Spaces and punctuation are not tokens and would leave the aliases adjacent,
21
+ * but unicode61 indexes the Private Use Area codepoint U+E000 as a token.
22
+ *
23
+ * Query sanitizers strip this character, so no user query can match the separator.
24
+ */
25
+ export const ALIAS_SEPARATOR = "\uE000"
26
+
27
+ const ALIAS_SEPARATOR_CODEPOINT = ALIAS_SEPARATOR.codePointAt(0) as number
28
+
29
+ /**
30
+ * Folds a query or name for exact-tier comparison by lowercasing, trimming
31
+ * and collapsing internal whitespace.
32
+ *
33
+ * Every exact-tier consumer uses this function, so its output compares equal to
34
+ * the aliases that {@link aliasBagExactMatch} parses.
35
+ */
36
+ export function foldQueryText(input: string): string {
37
+ return input.toLowerCase().trim().replaceAll(/\s+/g, " ")
38
+ }
39
+
40
+ /**
41
+ * Returns whether any alias in an `alt_names` bag exactly equals the folded query.
42
+ *
43
+ * A bag without {@link ALIAS_SEPARATOR} is a legacy space-joined bag without alias boundaries.
44
+ * For such a bag, the function checks word-bounded containment only when no candidate matched strictly.
45
+ *
46
+ * Containment by itself would promote fragments such as "York" inside "New York City".
47
+ *
48
+ * @param altNames The `alt_names` bag from `place_search`, or null when the row has no aliases.
49
+ * @param normalizedQuery The query folded by {@link foldQueryText}.
50
+ * @param anyStrictExact Whether any candidate already matched on its canonical name or region abbreviation.
51
+ * Only legacy bags use it.
52
+ */
53
+ export function aliasBagExactMatch(altNames: string | null, normalizedQuery: string, anyStrictExact: boolean): boolean {
54
+ if (!altNames || altNames === "" || !normalizedQuery) return false
55
+
56
+ if (altNames.includes(ALIAS_SEPARATOR)) {
57
+ return altNames.split(ALIAS_SEPARATOR).some((alias) => foldQueryText(alias) === normalizedQuery)
58
+ }
59
+
60
+ if (anyStrictExact) return false
61
+
62
+ return ` ${foldQueryText(altNames)} `.includes(` ${normalizedQuery} `)
63
+ }
64
+
65
+ /**
66
+ * The name of the R*Tree virtual table that indexes WOF place bounding boxes for bbox and proximity lookups.
67
+ */
68
+ export const PLACE_BBOX_TABLE = "place_bbox"
69
+
70
+ /**
71
+ * The name of the sparse table that stores `wof:population` per place.
72
+ *
73
+ * The lookup uses this value to apply the population ranking boost.
74
+ *
75
+ * A place without a row gets no boost and no penalty.
76
+ */
77
+ export const PLACE_POPULATION_TABLE = "place_population"
78
+
79
+ /**
80
+ * The name of the table of per-place `referential` and `encyclopedic` scores,
81
+ * built by `mailwoman gazetteer importance`.
82
+ *
83
+ * The lookup copies `encyclopedic` onto results without ranking by it.
84
+ * It refuses to read an older table with a single `importance` column.
85
+ */
86
+ export const PLACE_IMPORTANCE_TABLE = "place_importance"
87
+
88
+ /**
89
+ * The indexes a {@link buildPlaceSearchFTS} run built and their row counts.
90
+ */
91
+ export interface BuildPlaceSearchFTSResult {
92
+ /**
93
+ * Whether this call built the FTS5 index.
94
+ * It is false when an existing index was kept.
95
+ */
96
+ created: boolean
97
+
98
+ /**
99
+ * The row count of the `place_search` table after the call.
100
+ */
101
+ indexedRows: number
102
+
103
+ /**
104
+ * Whether this call built the R*Tree bbox index.
105
+ * It is false when an existing index was kept.
106
+ */
107
+ bboxCreated: boolean
108
+
109
+ /**
110
+ * The row count of the `place_bbox` R*Tree after the call.
111
+ */
112
+ bboxIndexedRows: number
113
+
114
+ /**
115
+ * The wall-clock duration of the whole build in milliseconds.
116
+ */
117
+ durationMs: number
118
+ }
119
+
120
+ /**
121
+ * Options for {@link buildPlaceSearchFTS}.
122
+ */
123
+ export interface BuildPlaceSearchFTSOpts {
124
+ /**
125
+ * Whether to drop and rebuild existing `place_search` and `place_bbox` tables,
126
+ * such as after an `spr` or `names` update.
127
+ * By default an existing index is kept.
128
+ */
129
+ drop?: boolean
130
+
131
+ /**
132
+ * Receives each build phase as it begins.
133
+ * A planet-scale build takes minutes to populate.
134
+ */
135
+ onProgress?: (
136
+ phase: "checking" | "dropping" | "creating" | "populating" | "creating-bbox" | "populating-bbox" | "done",
137
+ detail?: string
138
+ ) => void
139
+ }
140
+
141
+ /**
142
+ * Builds the `place_search` FTS5 table and the `place_bbox` R*Tree from a WOF
143
+ * database's `spr` and `names` tables.
144
+ *
145
+ * Without `drop: true`, an existing index is kept.
146
+ */
147
+ export function buildPlaceSearchFTS<DB>(
148
+ db: DatabaseClient<DB>,
149
+ opts: BuildPlaceSearchFTSOpts = {}
150
+ ): BuildPlaceSearchFTSResult {
151
+ const start = Date.now()
152
+ const onProgress = opts.onProgress ?? (() => {})
153
+
154
+ onProgress("checking")
155
+ const ftsExisting = tableExists(db, PLACE_SEARCH_TABLE)
156
+ const bboxExisting = tableExists(db, PLACE_BBOX_TABLE)
157
+
158
+ let ftsCreated = false
159
+
160
+ if (ftsExisting && opts.drop) {
161
+ onProgress("dropping", PLACE_SEARCH_TABLE)
162
+ db.exec(`DROP TABLE ${PLACE_SEARCH_TABLE}`)
163
+ }
164
+
165
+ if (!ftsExisting || opts.drop) {
166
+ onProgress("creating")
167
+
168
+ db.exec(`
169
+ CREATE VIRTUAL TABLE ${PLACE_SEARCH_TABLE} USING fts5(
170
+ wof_id UNINDEXED,
171
+ name,
172
+ alt_names,
173
+ tokenize = 'unicode61 remove_diacritics 2'
174
+ );
175
+ `)
176
+
177
+ onProgress("populating")
178
+
179
+ db.exec(`
180
+ INSERT INTO ${PLACE_SEARCH_TABLE} (wof_id, name, alt_names)
181
+ SELECT
182
+ spr.id,
183
+ spr.name,
184
+ COALESCE((
185
+ SELECT GROUP_CONCAT(
186
+ REPLACE(name, char(${ALIAS_SEPARATOR_CODEPOINT}), ' '),
187
+ ' ' || char(${ALIAS_SEPARATOR_CODEPOINT}) || ' '
188
+ ) || ' ' || char(${ALIAS_SEPARATOR_CODEPOINT})
189
+ FROM names WHERE names.id = spr.id
190
+ ), '')
191
+ FROM spr
192
+ WHERE spr.is_current != 0
193
+ AND spr.is_deprecated = 0
194
+ AND spr.name IS NOT NULL;
195
+ `)
196
+
197
+ ftsCreated = true
198
+ }
199
+
200
+ const ftsCountRow = db.prepare(`SELECT COUNT(*) AS n FROM ${PLACE_SEARCH_TABLE}`).get() as { n: number }
201
+
202
+ let bboxCreated = false
203
+
204
+ if (bboxExisting && opts.drop) {
205
+ onProgress("dropping", PLACE_BBOX_TABLE)
206
+ db.exec(`DROP TABLE ${PLACE_BBOX_TABLE}`)
207
+ }
208
+
209
+ if (!bboxExisting || opts.drop) {
210
+ onProgress("creating-bbox")
211
+
212
+ db.exec(`
213
+ CREATE VIRTUAL TABLE ${PLACE_BBOX_TABLE} USING rtree(
214
+ id,
215
+ min_lat, max_lat,
216
+ min_lon, max_lon
217
+ );
218
+ `)
219
+
220
+ onProgress("populating-bbox")
221
+
222
+ db.exec(`
223
+ INSERT INTO ${PLACE_BBOX_TABLE} (id, min_lat, max_lat, min_lon, max_lon)
224
+ SELECT
225
+ spr.id,
226
+ spr.min_latitude,
227
+ spr.max_latitude,
228
+ spr.min_longitude,
229
+ spr.max_longitude
230
+ FROM spr
231
+ WHERE spr.is_current != 0
232
+ AND spr.is_deprecated = 0
233
+ AND spr.min_latitude IS NOT NULL
234
+ AND spr.max_latitude IS NOT NULL
235
+ AND spr.min_longitude IS NOT NULL
236
+ AND spr.max_longitude IS NOT NULL
237
+ AND NOT (spr.min_latitude = 0 AND spr.max_latitude = 0
238
+ AND spr.min_longitude = 0 AND spr.max_longitude = 0);
239
+ `)
240
+
241
+ bboxCreated = true
242
+ }
243
+
244
+ const bboxCountRow = db.prepare(`SELECT COUNT(*) AS n FROM ${PLACE_BBOX_TABLE}`).get() as { n: number }
245
+
246
+ onProgress(
247
+ "done",
248
+ `${ftsCountRow.n} FTS rows + ${bboxCountRow.n} bbox rows ` +
249
+ `(${ftsCreated ? "built" : "preexisting"} / ${bboxCreated ? "built" : "preexisting"})`
250
+ )
251
+
252
+ return {
253
+ created: ftsCreated,
254
+ indexedRows: ftsCountRow.n,
255
+ bboxCreated,
256
+ bboxIndexedRows: bboxCountRow.n,
257
+ durationMs: Date.now() - start,
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Returns whether the connected database has the `place_search` FTS table.
263
+ */
264
+ export function placeSearchFTSExists<DB>(db: DatabaseClient<DB>): boolean {
265
+ return tableExists(db, PLACE_SEARCH_TABLE)
266
+ }
267
+
268
+ /**
269
+ * Returns whether the connected database has the `place_bbox` R*Tree table.
270
+ */
271
+ export function placeBboxExists<DB>(db: DatabaseClient<DB>): boolean {
272
+ return tableExists(db, PLACE_BBOX_TABLE)
273
+ }
274
+
275
+ /**
276
+ * Returns whether the connected database has the `place_population` table.
277
+ */
278
+ export function placePopulationExists<DB>(db: DatabaseClient<DB>): boolean {
279
+ return tableExists(db, PLACE_POPULATION_TABLE)
280
+ }