@mailwoman/resolver-wof-sqlite 10.0.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/README.md +9 -8
  2. package/lib/address/point/interpolation.ts +45 -39
  3. package/lib/address/point/schema.ts +17 -40
  4. package/lib/address/point.ts +316 -0
  5. package/lib/{address/index.ts → address.ts} +1 -1
  6. package/lib/ancestry/backfill.ts +20 -86
  7. package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
  8. package/lib/build/candidate.ts +122 -174
  9. package/lib/build/population-corrections.ts +64 -0
  10. package/lib/build/slim.ts +44 -114
  11. package/lib/candidate/alias-bags.ts +16 -17
  12. package/lib/candidate/ancestors/schema.ts +77 -61
  13. package/lib/candidate/ancestors/sidecar.ts +15 -22
  14. package/lib/candidate/country-display-names.ts +6 -20
  15. package/lib/candidate/extract-fold.ts +10 -55
  16. package/lib/candidate/fts.ts +5 -23
  17. package/lib/candidate/importance.ts +65 -68
  18. package/lib/candidate/lookup.ts +124 -415
  19. package/lib/candidate/name-roles.ts +15 -33
  20. package/lib/candidate/own-name.ts +35 -32
  21. package/lib/candidate/place-attrs.ts +20 -13
  22. package/lib/candidate/schema.ts +29 -65
  23. package/lib/candidate/scoring.ts +54 -93
  24. package/lib/capital-schema.ts +21 -17
  25. package/lib/capitals.ts +29 -27
  26. package/lib/coincident-roles.ts +85 -55
  27. package/lib/convention/schema.ts +15 -16
  28. package/lib/convention.ts +144 -0
  29. package/lib/coverage-manifest-schema.ts +40 -45
  30. package/lib/currency-backfill.ts +34 -79
  31. package/lib/env.ts +1 -1
  32. package/lib/exact-match.ts +32 -21
  33. package/lib/extracts.ts +97 -74
  34. package/lib/fst/autocomplete.ts +54 -37
  35. package/lib/fst/builder.ts +37 -41
  36. package/lib/fst/deserialize-web.ts +16 -17
  37. package/lib/fst/format.ts +41 -23
  38. package/lib/fst/freshness.ts +43 -120
  39. package/lib/fst/matcher.ts +7 -5
  40. package/lib/fst/serialize.ts +54 -74
  41. package/lib/fst/types.ts +78 -75
  42. package/lib/{fst/index.ts → fst.ts} +1 -0
  43. package/lib/fts/query.ts +5 -36
  44. package/lib/fts.ts +280 -0
  45. package/lib/geonames/aliases.ts +73 -144
  46. package/lib/geonames/postal.ts +162 -71
  47. package/lib/index.ts +9 -8
  48. package/lib/interpolation.ts +48 -100
  49. package/lib/lookup.ts +155 -270
  50. package/lib/name-score.ts +9 -7
  51. package/lib/nsul/lookup.ts +37 -54
  52. package/lib/nsul/schema.ts +16 -43
  53. package/lib/paths.ts +159 -0
  54. package/lib/place-importance-schema.ts +62 -169
  55. package/lib/poi/lookup.ts +104 -121
  56. package/lib/poi/schema.ts +42 -26
  57. package/lib/polygon-schema.ts +10 -6
  58. package/lib/postal/city/alias/lookup.ts +22 -40
  59. package/lib/postal/city/alias/schema.ts +14 -10
  60. package/lib/postal/city/candidate-schema.ts +26 -20
  61. package/lib/postcode-point-lookup.ts +8 -27
  62. package/lib/primary-preference.ts +46 -126
  63. package/lib/proximity-rerank.ts +44 -33
  64. package/lib/ranking-weights.ts +105 -78
  65. package/lib/region-keys.ts +70 -42
  66. package/lib/reverse.ts +149 -86
  67. package/lib/schema.ts +73 -50
  68. package/lib/search-fetch.ts +90 -68
  69. package/lib/sqlite/convention-source.ts +15 -23
  70. package/lib/sqlite/utils.ts +19 -22
  71. package/lib/street/centroid/schema.ts +37 -42
  72. package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
  73. package/lib/street/morphology/fst/builder.ts +60 -42
  74. package/lib/street/morphology/fst/loader.ts +40 -32
  75. package/lib/street/name-lookup.ts +45 -41
  76. package/lib/street/normalize.ts +88 -219
  77. package/lib/street/segment-schema.ts +116 -43
  78. package/lib/{street/index.ts → street.ts} +1 -1
  79. package/lib/types.ts +109 -150
  80. package/lib/unified-schema.ts +29 -29
  81. package/lib/uprn/existence.ts +30 -19
  82. package/lib/uprn/lookup.ts +29 -68
  83. package/lib/uprn/schema.ts +18 -40
  84. package/lib/weights-overlay-linker.ts +236 -252
  85. package/out/address/point/interpolation.d.ts +20 -23
  86. package/out/address/point/interpolation.d.ts.map +1 -1
  87. package/out/address/point/interpolation.js +42 -39
  88. package/out/address/point/interpolation.js.map +1 -1
  89. package/out/address/point/schema.d.ts +12 -35
  90. package/out/address/point/schema.d.ts.map +1 -1
  91. package/out/address/point/schema.js +3 -19
  92. package/out/address/point/schema.js.map +1 -1
  93. package/out/address/point.d.ts +57 -0
  94. package/out/address/point.d.ts.map +1 -0
  95. package/out/address/point.js +206 -0
  96. package/out/address/point.js.map +1 -0
  97. package/out/{address/index.js → address.d.ts} +2 -2
  98. package/out/address.d.ts.map +1 -0
  99. package/out/{address/index.d.ts → address.js} +2 -2
  100. package/out/address.js.map +1 -0
  101. package/out/ancestry/backfill.d.ts +11 -64
  102. package/out/ancestry/backfill.d.ts.map +1 -1
  103. package/out/ancestry/backfill.js +14 -74
  104. package/out/ancestry/backfill.js.map +1 -1
  105. package/out/ancestry.d.ts +49 -0
  106. package/out/ancestry.d.ts.map +1 -0
  107. package/out/ancestry.js +59 -0
  108. package/out/ancestry.js.map +1 -0
  109. package/out/build/candidate.d.ts +82 -112
  110. package/out/build/candidate.d.ts.map +1 -1
  111. package/out/build/candidate.js +17 -100
  112. package/out/build/candidate.js.map +1 -1
  113. package/out/build/population-corrections.d.ts +52 -0
  114. package/out/build/population-corrections.d.ts.map +1 -0
  115. package/out/build/population-corrections.js +40 -0
  116. package/out/build/population-corrections.js.map +1 -0
  117. package/out/build/slim.d.ts +8 -44
  118. package/out/build/slim.d.ts.map +1 -1
  119. package/out/build/slim.js +33 -94
  120. package/out/build/slim.js.map +1 -1
  121. package/out/candidate/alias-bags.d.ts +6 -6
  122. package/out/candidate/alias-bags.d.ts.map +1 -1
  123. package/out/candidate/alias-bags.js +16 -17
  124. package/out/candidate/alias-bags.js.map +1 -1
  125. package/out/candidate/ancestors/schema.d.ts +73 -57
  126. package/out/candidate/ancestors/schema.d.ts.map +1 -1
  127. package/out/candidate/ancestors/schema.js +56 -48
  128. package/out/candidate/ancestors/schema.js.map +1 -1
  129. package/out/candidate/ancestors/sidecar.d.ts +3 -8
  130. package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
  131. package/out/candidate/ancestors/sidecar.js +15 -22
  132. package/out/candidate/ancestors/sidecar.js.map +1 -1
  133. package/out/candidate/country-display-names.d.ts +5 -15
  134. package/out/candidate/country-display-names.d.ts.map +1 -1
  135. package/out/candidate/country-display-names.js +6 -20
  136. package/out/candidate/country-display-names.js.map +1 -1
  137. package/out/candidate/extract-fold.d.ts +7 -15
  138. package/out/candidate/extract-fold.d.ts.map +1 -1
  139. package/out/candidate/extract-fold.js +5 -47
  140. package/out/candidate/extract-fold.js.map +1 -1
  141. package/out/candidate/fts.d.ts +5 -23
  142. package/out/candidate/fts.d.ts.map +1 -1
  143. package/out/candidate/fts.js +5 -23
  144. package/out/candidate/fts.js.map +1 -1
  145. package/out/candidate/importance.d.ts +60 -65
  146. package/out/candidate/importance.d.ts.map +1 -1
  147. package/out/candidate/importance.js +56 -63
  148. package/out/candidate/importance.js.map +1 -1
  149. package/out/candidate/lookup.d.ts +18 -55
  150. package/out/candidate/lookup.d.ts.map +1 -1
  151. package/out/candidate/lookup.js +82 -385
  152. package/out/candidate/lookup.js.map +1 -1
  153. package/out/candidate/name-roles.d.ts +9 -21
  154. package/out/candidate/name-roles.d.ts.map +1 -1
  155. package/out/candidate/name-roles.js +15 -33
  156. package/out/candidate/name-roles.js.map +1 -1
  157. package/out/candidate/own-name.d.ts +23 -25
  158. package/out/candidate/own-name.d.ts.map +1 -1
  159. package/out/candidate/own-name.js +35 -32
  160. package/out/candidate/own-name.js.map +1 -1
  161. package/out/candidate/place-attrs.d.ts +20 -13
  162. package/out/candidate/place-attrs.d.ts.map +1 -1
  163. package/out/candidate/place-attrs.js +4 -5
  164. package/out/candidate/place-attrs.js.map +1 -1
  165. package/out/candidate/schema.d.ts +27 -62
  166. package/out/candidate/schema.d.ts.map +1 -1
  167. package/out/candidate/schema.js +10 -22
  168. package/out/candidate/schema.js.map +1 -1
  169. package/out/candidate/scoring.d.ts +7 -8
  170. package/out/candidate/scoring.d.ts.map +1 -1
  171. package/out/candidate/scoring.js +52 -89
  172. package/out/candidate/scoring.js.map +1 -1
  173. package/out/capital-schema.d.ts +21 -17
  174. package/out/capital-schema.d.ts.map +1 -1
  175. package/out/capital-schema.js +17 -15
  176. package/out/capital-schema.js.map +1 -1
  177. package/out/capitals.d.ts +27 -25
  178. package/out/capitals.d.ts.map +1 -1
  179. package/out/capitals.js +23 -21
  180. package/out/capitals.js.map +1 -1
  181. package/out/coincident-roles.d.ts +60 -43
  182. package/out/coincident-roles.d.ts.map +1 -1
  183. package/out/coincident-roles.js +72 -50
  184. package/out/coincident-roles.js.map +1 -1
  185. package/out/convention/schema.d.ts +14 -14
  186. package/out/convention/schema.d.ts.map +1 -1
  187. package/out/convention/schema.js +6 -4
  188. package/out/convention/schema.js.map +1 -1
  189. package/out/convention.d.ts +97 -0
  190. package/out/convention.d.ts.map +1 -0
  191. package/out/convention.js +81 -0
  192. package/out/convention.js.map +1 -0
  193. package/out/coverage-manifest-schema.d.ts +34 -39
  194. package/out/coverage-manifest-schema.d.ts.map +1 -1
  195. package/out/coverage-manifest-schema.js +25 -34
  196. package/out/coverage-manifest-schema.js.map +1 -1
  197. package/out/currency-backfill.d.ts +16 -38
  198. package/out/currency-backfill.d.ts.map +1 -1
  199. package/out/currency-backfill.js +19 -46
  200. package/out/currency-backfill.js.map +1 -1
  201. package/out/env.d.ts +1 -1
  202. package/out/env.js +1 -1
  203. package/out/exact-match.d.ts +19 -10
  204. package/out/exact-match.d.ts.map +1 -1
  205. package/out/exact-match.js +32 -21
  206. package/out/exact-match.js.map +1 -1
  207. package/out/extracts.d.ts +66 -55
  208. package/out/extracts.d.ts.map +1 -1
  209. package/out/extracts.js +63 -55
  210. package/out/extracts.js.map +1 -1
  211. package/out/fst/autocomplete.d.ts +29 -21
  212. package/out/fst/autocomplete.d.ts.map +1 -1
  213. package/out/fst/autocomplete.js +39 -28
  214. package/out/fst/autocomplete.js.map +1 -1
  215. package/out/fst/builder.d.ts.map +1 -1
  216. package/out/fst/builder.js +37 -41
  217. package/out/fst/builder.js.map +1 -1
  218. package/out/fst/deserialize-web.d.ts +4 -4
  219. package/out/fst/deserialize-web.d.ts.map +1 -1
  220. package/out/fst/deserialize-web.js +15 -16
  221. package/out/fst/deserialize-web.js.map +1 -1
  222. package/out/fst/format.d.ts +41 -23
  223. package/out/fst/format.d.ts.map +1 -1
  224. package/out/fst/format.js +41 -23
  225. package/out/fst/format.js.map +1 -1
  226. package/out/fst/freshness.d.ts +19 -81
  227. package/out/fst/freshness.d.ts.map +1 -1
  228. package/out/fst/freshness.js +31 -94
  229. package/out/fst/freshness.js.map +1 -1
  230. package/out/fst/matcher.d.ts +5 -4
  231. package/out/fst/matcher.d.ts.map +1 -1
  232. package/out/fst/matcher.js +2 -2
  233. package/out/fst/matcher.js.map +1 -1
  234. package/out/fst/serialize.d.ts +28 -28
  235. package/out/fst/serialize.d.ts.map +1 -1
  236. package/out/fst/serialize.js +53 -71
  237. package/out/fst/serialize.js.map +1 -1
  238. package/out/fst/types.d.ts +78 -74
  239. package/out/fst/types.d.ts.map +1 -1
  240. package/out/fst/types.js +1 -4
  241. package/out/fst/types.js.map +1 -1
  242. package/out/{fst/index.js → fst.d.ts} +2 -1
  243. package/out/fst.d.ts.map +1 -0
  244. package/out/{fst/index.d.ts → fst.js} +2 -1
  245. package/out/fst.js.map +1 -0
  246. package/out/fts/query.d.ts +2 -27
  247. package/out/fts/query.d.ts.map +1 -1
  248. package/out/fts/query.js +5 -36
  249. package/out/fts/query.js.map +1 -1
  250. package/out/fts.d.ts +126 -0
  251. package/out/fts.d.ts.map +1 -0
  252. package/out/fts.js +188 -0
  253. package/out/fts.js.map +1 -0
  254. package/out/geonames/aliases.d.ts +26 -62
  255. package/out/geonames/aliases.d.ts.map +1 -1
  256. package/out/geonames/aliases.js +45 -117
  257. package/out/geonames/aliases.js.map +1 -1
  258. package/out/geonames/postal.d.ts +84 -44
  259. package/out/geonames/postal.d.ts.map +1 -1
  260. package/out/geonames/postal.js +115 -73
  261. package/out/geonames/postal.js.map +1 -1
  262. package/out/{geonames/index.js → geonames.d.ts} +1 -1
  263. package/out/geonames.d.ts.map +1 -0
  264. package/out/{geonames/index.d.ts → geonames.js} +1 -1
  265. package/out/geonames.js.map +1 -0
  266. package/out/index.d.ts +9 -8
  267. package/out/index.d.ts.map +1 -1
  268. package/out/index.js +6 -5
  269. package/out/index.js.map +1 -1
  270. package/out/interpolation.d.ts +18 -55
  271. package/out/interpolation.d.ts.map +1 -1
  272. package/out/interpolation.js +27 -69
  273. package/out/interpolation.js.map +1 -1
  274. package/out/lookup.d.ts +23 -51
  275. package/out/lookup.d.ts.map +1 -1
  276. package/out/lookup.js +136 -234
  277. package/out/lookup.js.map +1 -1
  278. package/out/name-score.d.ts +2 -2
  279. package/out/name-score.d.ts.map +1 -1
  280. package/out/name-score.js +9 -7
  281. package/out/name-score.js.map +1 -1
  282. package/out/nsul/lookup.d.ts +33 -34
  283. package/out/nsul/lookup.d.ts.map +1 -1
  284. package/out/nsul/lookup.js +26 -40
  285. package/out/nsul/lookup.js.map +1 -1
  286. package/out/nsul/schema.d.ts +16 -43
  287. package/out/nsul/schema.d.ts.map +1 -1
  288. package/out/nsul/schema.js +9 -35
  289. package/out/nsul/schema.js.map +1 -1
  290. package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
  291. package/out/nsul.d.ts.map +1 -0
  292. package/out/{nsul/index.js → nsul.js} +1 -1
  293. package/out/nsul.js.map +1 -0
  294. package/out/paths.d.ts +124 -0
  295. package/out/paths.d.ts.map +1 -0
  296. package/out/paths.js +104 -0
  297. package/out/paths.js.map +1 -0
  298. package/out/place-importance-schema.d.ts +51 -143
  299. package/out/place-importance-schema.d.ts.map +1 -1
  300. package/out/place-importance-schema.js +28 -143
  301. package/out/place-importance-schema.js.map +1 -1
  302. package/out/poi/lookup.d.ts +40 -47
  303. package/out/poi/lookup.d.ts.map +1 -1
  304. package/out/poi/lookup.js +69 -96
  305. package/out/poi/lookup.js.map +1 -1
  306. package/out/poi/schema.d.ts +41 -25
  307. package/out/poi/schema.d.ts.map +1 -1
  308. package/out/poi/schema.js +25 -17
  309. package/out/poi/schema.js.map +1 -1
  310. package/out/{poi/index.js → poi.d.ts} +1 -1
  311. package/out/poi.d.ts.map +1 -0
  312. package/out/{poi/index.d.ts → poi.js} +1 -1
  313. package/out/poi.js.map +1 -0
  314. package/out/polygon-schema.d.ts +10 -6
  315. package/out/polygon-schema.d.ts.map +1 -1
  316. package/out/polygon-schema.js +2 -2
  317. package/out/polygon-schema.js.map +1 -1
  318. package/out/postal/city/alias/lookup.d.ts +20 -23
  319. package/out/postal/city/alias/lookup.d.ts.map +1 -1
  320. package/out/postal/city/alias/lookup.js +17 -30
  321. package/out/postal/city/alias/lookup.js.map +1 -1
  322. package/out/postal/city/alias/schema.d.ts +14 -10
  323. package/out/postal/city/alias/schema.d.ts.map +1 -1
  324. package/out/postal/city/alias/schema.js +10 -8
  325. package/out/postal/city/alias/schema.js.map +1 -1
  326. package/out/postal/city/candidate-schema.d.ts +25 -19
  327. package/out/postal/city/candidate-schema.d.ts.map +1 -1
  328. package/out/postal/city/candidate-schema.js +21 -17
  329. package/out/postal/city/candidate-schema.js.map +1 -1
  330. package/out/{postal/index.d.ts → postal.d.ts} +1 -1
  331. package/out/postal.d.ts.map +1 -0
  332. package/out/{postal/index.js → postal.js} +1 -1
  333. package/out/postal.js.map +1 -0
  334. package/out/postcode-point-lookup.d.ts +6 -24
  335. package/out/postcode-point-lookup.d.ts.map +1 -1
  336. package/out/postcode-point-lookup.js +5 -17
  337. package/out/postcode-point-lookup.js.map +1 -1
  338. package/out/primary-preference.d.ts +38 -92
  339. package/out/primary-preference.d.ts.map +1 -1
  340. package/out/primary-preference.js +15 -99
  341. package/out/primary-preference.js.map +1 -1
  342. package/out/proximity-rerank.d.ts +41 -30
  343. package/out/proximity-rerank.d.ts.map +1 -1
  344. package/out/proximity-rerank.js +38 -29
  345. package/out/proximity-rerank.js.map +1 -1
  346. package/out/ranking-weights.d.ts +98 -64
  347. package/out/ranking-weights.d.ts.map +1 -1
  348. package/out/ranking-weights.js +26 -25
  349. package/out/ranking-weights.js.map +1 -1
  350. package/out/region-keys.d.ts +50 -31
  351. package/out/region-keys.d.ts.map +1 -1
  352. package/out/region-keys.js +68 -40
  353. package/out/region-keys.js.map +1 -1
  354. package/out/reverse.d.ts +78 -50
  355. package/out/reverse.d.ts.map +1 -1
  356. package/out/reverse.js +98 -61
  357. package/out/reverse.js.map +1 -1
  358. package/out/schema.d.ts +72 -49
  359. package/out/schema.d.ts.map +1 -1
  360. package/out/schema.js +4 -5
  361. package/out/schema.js.map +1 -1
  362. package/out/search-fetch.d.ts +16 -12
  363. package/out/search-fetch.d.ts.map +1 -1
  364. package/out/search-fetch.js +83 -63
  365. package/out/search-fetch.js.map +1 -1
  366. package/out/sqlite/convention-source.d.ts +12 -16
  367. package/out/sqlite/convention-source.d.ts.map +1 -1
  368. package/out/sqlite/convention-source.js +13 -22
  369. package/out/sqlite/convention-source.js.map +1 -1
  370. package/out/sqlite/utils.d.ts +19 -19
  371. package/out/sqlite/utils.d.ts.map +1 -1
  372. package/out/sqlite/utils.js +14 -15
  373. package/out/sqlite/utils.js.map +1 -1
  374. package/out/street/centroid/schema.d.ts +37 -42
  375. package/out/street/centroid/schema.d.ts.map +1 -1
  376. package/out/street/centroid/schema.js +9 -21
  377. package/out/street/centroid/schema.js.map +1 -1
  378. package/out/street/centroid.d.ts +30 -0
  379. package/out/street/centroid.d.ts.map +1 -0
  380. package/out/street/{centroid/index.js → centroid.js} +13 -24
  381. package/out/street/centroid.js.map +1 -0
  382. package/out/street/morphology/fst/builder.d.ts +20 -10
  383. package/out/street/morphology/fst/builder.d.ts.map +1 -1
  384. package/out/street/morphology/fst/builder.js +44 -34
  385. package/out/street/morphology/fst/builder.js.map +1 -1
  386. package/out/street/morphology/fst/loader.d.ts +33 -26
  387. package/out/street/morphology/fst/loader.d.ts.map +1 -1
  388. package/out/street/morphology/fst/loader.js +23 -22
  389. package/out/street/morphology/fst/loader.js.map +1 -1
  390. package/out/street/name-lookup.d.ts +24 -18
  391. package/out/street/name-lookup.d.ts.map +1 -1
  392. package/out/street/name-lookup.js +34 -35
  393. package/out/street/name-lookup.js.map +1 -1
  394. package/out/street/normalize.d.ts +35 -136
  395. package/out/street/normalize.d.ts.map +1 -1
  396. package/out/street/normalize.js +77 -194
  397. package/out/street/normalize.js.map +1 -1
  398. package/out/street/segment-schema.d.ts +79 -39
  399. package/out/street/segment-schema.d.ts.map +1 -1
  400. package/out/street/segment-schema.js +71 -21
  401. package/out/street/segment-schema.js.map +1 -1
  402. package/out/{street/index.js → street.d.ts} +2 -2
  403. package/out/street.d.ts.map +1 -0
  404. package/out/{street/index.d.ts → street.js} +2 -2
  405. package/out/street.js.map +1 -0
  406. package/out/types.d.ts +84 -149
  407. package/out/types.d.ts.map +1 -1
  408. package/out/types.js +0 -6
  409. package/out/types.js.map +1 -1
  410. package/out/unified-schema.d.ts +8 -12
  411. package/out/unified-schema.d.ts.map +1 -1
  412. package/out/unified-schema.js +29 -29
  413. package/out/unified-schema.js.map +1 -1
  414. package/out/uprn/existence.d.ts +28 -17
  415. package/out/uprn/existence.d.ts.map +1 -1
  416. package/out/uprn/existence.js +26 -16
  417. package/out/uprn/existence.js.map +1 -1
  418. package/out/uprn/lookup.d.ts +14 -44
  419. package/out/uprn/lookup.d.ts.map +1 -1
  420. package/out/uprn/lookup.js +21 -57
  421. package/out/uprn/lookup.js.map +1 -1
  422. package/out/uprn/schema.d.ts +18 -40
  423. package/out/uprn/schema.d.ts.map +1 -1
  424. package/out/uprn/schema.js +10 -32
  425. package/out/uprn/schema.js.map +1 -1
  426. package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
  427. package/out/uprn.d.ts.map +1 -0
  428. package/out/{uprn/index.js → uprn.js} +1 -1
  429. package/out/uprn.js.map +1 -0
  430. package/out/weights-overlay-linker.d.ts +138 -144
  431. package/out/weights-overlay-linker.d.ts.map +1 -1
  432. package/out/weights-overlay-linker.js +105 -159
  433. package/out/weights-overlay-linker.js.map +1 -1
  434. package/package.json +91 -596
  435. package/lib/address/point/index.ts +0 -328
  436. package/lib/convention/index.ts +0 -154
  437. package/lib/fts/index.ts +0 -361
  438. package/out/address/index.d.ts.map +0 -1
  439. package/out/address/index.js.map +0 -1
  440. package/out/address/point/index.d.ts +0 -53
  441. package/out/address/point/index.d.ts.map +0 -1
  442. package/out/address/point/index.js +0 -222
  443. package/out/address/point/index.js.map +0 -1
  444. package/out/ancestry/index.d.ts +0 -45
  445. package/out/ancestry/index.d.ts.map +0 -1
  446. package/out/ancestry/index.js +0 -55
  447. package/out/ancestry/index.js.map +0 -1
  448. package/out/convention/index.d.ts +0 -106
  449. package/out/convention/index.d.ts.map +0 -1
  450. package/out/convention/index.js +0 -93
  451. package/out/convention/index.js.map +0 -1
  452. package/out/fst/index.d.ts.map +0 -1
  453. package/out/fst/index.js.map +0 -1
  454. package/out/fts/index.d.ts +0 -184
  455. package/out/fts/index.d.ts.map +0 -1
  456. package/out/fts/index.js +0 -277
  457. package/out/fts/index.js.map +0 -1
  458. package/out/geonames/index.d.ts.map +0 -1
  459. package/out/geonames/index.js.map +0 -1
  460. package/out/nsul/index.d.ts.map +0 -1
  461. package/out/nsul/index.js.map +0 -1
  462. package/out/poi/index.d.ts.map +0 -1
  463. package/out/poi/index.js.map +0 -1
  464. package/out/postal/index.d.ts.map +0 -1
  465. package/out/postal/index.js.map +0 -1
  466. package/out/street/centroid/index.d.ts +0 -40
  467. package/out/street/centroid/index.d.ts.map +0 -1
  468. package/out/street/centroid/index.js.map +0 -1
  469. package/out/street/index.d.ts.map +0 -1
  470. package/out/street/index.js.map +0 -1
  471. package/out/uprn/index.d.ts.map +0 -1
  472. package/out/uprn/index.js.map +0 -1
  473. /package/lib/{geonames/index.ts → geonames.ts} +0 -0
  474. /package/lib/{nsul/index.ts → nsul.ts} +0 -0
  475. /package/lib/{poi/index.ts → poi.ts} +0 -0
  476. /package/lib/{postal/index.ts → postal.ts} +0 -0
  477. /package/lib/{uprn/index.ts → uprn.ts} +0 -0
@@ -3,18 +3,18 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * FST-based autocomplete — mailwoman vocabulary over `@mailwoman/ancestrie`'s generic algorithm
7
- * (#1728 phase 2). The #587 behavior — prefix walk + BFS expansion, partial-last-token completion,
8
- * per-branch capping, dedupe — lives in ancestrie's `autocomplete`; this module contributes only
9
- * the storage adapter ({@link FSTMatcher} → `AncestrieReaderLike`) and the mapping back to
10
- * mailwoman's suggestion shape (name, placetype, referential/encyclopedic, WOF ids).
6
+ * FST-based autocomplete, mailwoman vocabulary over `@mailwoman/ancestrie`'s generic algorithm.
7
+ * The prefix walk, BFS expansion, partial-last-token completion, per-branch capping and dedupe
8
+ * live in ancestrie's `autocomplete`. This module contributes only the storage adapter
9
+ * ({@link FSTMatcher} → `AncestrieReaderLike`) and the mapping back to mailwoman's suggestion
10
+ * shape (name, placetype, referential and encyclopedic scores, WOF ids).
11
11
  *
12
- * THE BYTES DO NOT MIGRATE. The shipped artifacts are `FST\0` v1–v5 (`fst-serialize.ts`), not
13
- * ancestrie's `ANCT`: ancestrie entries are id-keyed with one record per id, while an FST place row
14
- * is per-(surface, place) — `crossCountryBranches` is a property of the SURFACE, so the same wofID
15
- * legitimately carries different values under different aliases and cannot be represented id-keyed.
16
- * The matcher, both deserializers, and the serializer therefore stay here; what migrated is the
17
- * ALGORITHM, which is the half that drifts (the #861 share-the-function rule).
12
+ * The bytes do not migrate. The shipped artifacts are `FST\0` v1 to v5 (`fst-serialize.ts`), and
13
+ * ancestrie's entries are id-keyed with one record per id. An FST place row is per-(surface,
14
+ * place), because `crossCountryBranches` is a property of the surface, so the same wofID
15
+ * legitimately has different values under different aliases, so it cannot use IDs as keys.
16
+ * The matcher, both deserializers and serializer stay here. The algorithm is the
17
+ * half that migrated.
18
18
  */
19
19
 
20
20
  import type {
@@ -41,16 +41,20 @@ export interface AutocompleteSuggestion {
41
41
  name: string
42
42
  placetype: string
43
43
  /**
44
- * The REFERENTIAL likelihood the suggestion is ranked by (ROAD_TO_V9 §2). Autocomplete answers "which place does the
45
- * user mean", so it ranks referentially like everything else; encyclopedic importance rides along on
46
- * {@link AutocompleteSuggestion.encyclopedic} for display and never enters the order.
44
+ * The referential likelihood the suggestion is ranked by (ROAD_TO_V9 §2).
45
+ *
46
+ * Autocomplete answers "which place does the user mean", so it ranks referentially like everything else.
47
+ * Encyclopedic importance appears in {@link AutocompleteSuggestion.encyclopedic}
48
+ * for display and never enters the order.
47
49
  */
48
50
  referential: number
49
51
  /**
50
- * Encyclopedic (Wikipedia) importance, when the FST artifact carries one for this place. `undefined` = no article, or
51
- * a pre-v5 binary — never 0.
52
+ * Encyclopedic (Wikipedia) importance, when the FST artifact stores one for this place.
53
+ *
54
+ * `undefined` means no article, or a pre-v5 binary.
55
+ * It is never 0.
52
56
  */
53
- encyclopedic?: number
57
+ encyclopedic: number | null
54
58
  wofID: number
55
59
  parentChain: number[]
56
60
  matchDepth: number
@@ -61,22 +65,28 @@ export interface AutocompleteOpts {
61
65
  maxSuggestions?: number
62
66
  maxExpansionDepth?: number
63
67
  /**
64
- * Collapse same-name suggestions to the single highest-referential one. Off by default (the CLI surfaces distinct
65
- * same-name places — New York the city vs the county); a typeahead wants it ON so the dropdown isn't four "New
66
- * London"s. (#587)
68
+ * Collapse same-name suggestions to the single highest-referential one.
69
+ *
70
+ * Off by default, because the CLI surfaces distinct same-name places such as
71
+ * New York the city and New York the county.
72
+ * A typeahead wants it on so the dropdown does not show four "New London"s.
67
73
  */
68
74
  dedupeByName?: boolean
69
75
  }
70
76
 
71
77
  /**
72
- * Max accepting entries collected per BFS branch — keeps one dense branch from starving the search.
78
+ * Max accepting entries collected per BFS branch, keeping one dense branch from starving the search.
73
79
  */
74
80
  const PER_BRANCH = 4
75
81
 
76
82
  /**
77
- * The top-`k` entries by REFERENTIAL likelihood (descending). Avoids sorting/allocating when `entries` is small — and
78
- * that shortcut is part of the observable contract: at or under `k` the INSERTION order is served, which decides
79
- * suggestion order among referential ties.
83
+ * The top-`k` entries by referential likelihood (descending).
84
+ *
85
+ * Avoids sorting or allocating when `entries` is small.
86
+ * That shortcut is part of the observable interface.
87
+ *
88
+ * At or under `k`, suggestions retain insertion order.
89
+ * That order decides suggestions among referential ties.
80
90
  */
81
91
  function topByReferential(entries: readonly PlaceEntry[], k: number): PlaceEntry[] {
82
92
  if (entries.length <= k) return [...entries]
@@ -85,19 +95,23 @@ function topByReferential(entries: readonly PlaceEntry[], k: number): PlaceEntry
85
95
  }
86
96
 
87
97
  /**
88
- * {@link FSTMatcher} presented through ancestrie's storage interface. Records carry the {@link PlaceEntry} itself as the
89
- * payload, so the entry that WINS the algorithm's shallowest-depth rule is the entry whose fields the suggestion
90
- * reports — a side lookup keyed on id could pick a different surface's row (`crossCountryBranches` differs per
91
- * surface).
98
+ * {@link FSTMatcher} presented through ancestrie's storage interface.
99
+ *
100
+ * Records store the {@link PlaceEntry} itself as the payload, so the entry that wins the
101
+ * algorithm's shallowest-depth rule is the entry whose fields the suggestion reports.
102
+ * A side lookup keyed on id could pick a different surface's row
103
+ * (`crossCountryBranches` differs per surface).
92
104
  */
93
105
  class FSTReader implements AncestrieReaderLike<PlaceEntry> {
94
106
  readonly #fst: FSTMatcher
95
107
 
96
108
  /**
97
- * Parent chains of the entries this reader has served, id-keyed. A place's chain is identical across its surfaces (it
98
- * is place-row data), so last-write-wins is safe. The algorithm asks {@link FSTReader.ancestorsOf} only for ids it
99
- * just received from {@link FSTReader.entriesAt}, so serving from this memo answers every real call without an
100
- * artifact-wide id index.
109
+ * Parent chains of the entries this reader has served, id-keyed.
110
+ *
111
+ * A place's chain is identical across its surfaces (it is place-row data), so last-write-wins is safe.
112
+ * The algorithm asks {@link FSTReader.ancestorsOf} only for ids it just received
113
+ * from {@link FSTReader.entriesAt}, so serving from this memo answers every
114
+ * real call without an artifact-wide id index.
101
115
  */
102
116
  readonly #chains = new Map<number, number[]>()
103
117
 
@@ -110,7 +124,8 @@ class FSTReader implements AncestrieReaderLike<PlaceEntry> {
110
124
  }
111
125
 
112
126
  continuations(stateID: number): AncestrieContinuation[] {
113
- // Insertion order, verbatim — BFS visit order under the suggestion budget depends on it.
127
+ // Insertion order, verbatim.
128
+ // BFS visit order under the suggestion budget depends on it.
114
129
  return this.#fst.continuations(stateID).map((c) => ({
115
130
  token: c.token,
116
131
  targetState: c.targetState,
@@ -140,7 +155,9 @@ class FSTReader implements AncestrieReaderLike<PlaceEntry> {
140
155
  }
141
156
 
142
157
  /**
143
- * Autocomplete from the current prefix. Returns suggestions ranked referential-descending.
158
+ * Autocomplete from the current prefix.
159
+ *
160
+ * @returns Suggestions ranked referential-descending.
144
161
  */
145
162
  export function autocomplete(fst: FSTMatcher, query: string, opts: AutocompleteOpts = {}): AutocompleteResult {
146
163
  const normalizedTokens = normalizeTokens(query)
@@ -149,7 +166,7 @@ export function autocomplete(fst: FSTMatcher, query: string, opts: AutocompleteO
149
166
  ...(opts.maxSuggestions === undefined ? {} : { maxSuggestions: opts.maxSuggestions }),
150
167
  ...(opts.maxExpansionDepth === undefined ? {} : { maxExpansionDepth: opts.maxExpansionDepth }),
151
168
  perBranchLimit: PER_BRANCH,
152
- // The dedupe key is the DISPLAY name, not the token path: two surfaces of one name must still collapse. (#587)
169
+ // The dedupe key is the display name, so two surfaces of one name still collapse.
153
170
  ...(opts.dedupeByName ? { dedupe: (s: AncestrieSuggestion<PlaceEntry>) => s.payload!.name.toLowerCase() } : {}),
154
171
  })
155
172
 
@@ -158,14 +175,14 @@ export function autocomplete(fst: FSTMatcher, query: string, opts: AutocompleteO
158
175
  normalizedTokens,
159
176
  depth: result.depth,
160
177
  suggestions: result.suggestions.map((s) => {
161
- // Every record this adapter serves carries its entry; the assertion documents the invariant.
178
+ // Every record this adapter serves includes its entry, so the non-null assertion holds.
162
179
  const entry = s.payload!
163
180
 
164
181
  return {
165
182
  name: entry.name,
166
183
  placetype: entry.placetype,
167
184
  referential: entry.referential,
168
- ...(entry.encyclopedic === undefined ? {} : { encyclopedic: entry.encyclopedic }),
185
+ encyclopedic: entry.encyclopedic,
169
186
  wofID: s.id,
170
187
  parentChain: s.parentIDs,
171
188
  matchDepth: s.matchDepth,
@@ -35,8 +35,10 @@ const DEFAULT_PLACETYPES: PlacetypeID[] = [
35
35
  const DEFAULT_COUNTRIES = ["US"]
36
36
  const DEFAULT_LANGUAGES = ["eng", ""]
37
37
  /**
38
- * Ids per `IN (…)` batch. SQLITE_MAX_VARIABLE_NUMBER defaults to 32,766; 500 matches the name-load batch a few phases
39
- * down, so both read paths bind the same shape.
38
+ * Ids per `IN (…)` batch.
39
+ *
40
+ * SQLITE_MAX_VARIABLE_NUMBER defaults to 32,766. 500 matches the name-load batch a
41
+ * few phases down, so both read paths bind the same shape.
40
42
  */
41
43
  const ANCESTOR_CHUNK = 500
42
44
 
@@ -70,7 +72,6 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
70
72
  progress("open", dbPath)
71
73
  using db = new DatabaseClient<WOFDatabase>(dbPath, { open: true })
72
74
 
73
- // Phase 1: Load all matching SPR rows.
74
75
  progress("spr", `Loading places for countries=[${countries}], placetypes=[${placetypes}]`)
75
76
  const placeholders = (arr: string[]) => arr.map(() => "?").join(",")
76
77
 
@@ -85,20 +86,18 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
85
86
  const sprRows = allRows<SprRow>(sprStmt, ...countries, ...placetypes)
86
87
  progress("spr", `Loaded ${sprRows.length} places`)
87
88
 
88
- // Phase 2: Build a lookup for parent chain resolution.
89
89
  const sprByID = new Map<number, SprRow>()
90
90
 
91
91
  for (const row of sprRows) {
92
92
  sprByID.set(row.id, row)
93
93
  }
94
94
 
95
- // Also load parent rows that might be outside our placetype filter (e.g., country for region).
95
+ // Load parent rows that might be outside the placetype filter, such as a country for a region.
96
96
  const parentStmt = db.prepare("SELECT id, name, placetype, parent_id, latitude, longitude FROM spr WHERE id = ?")
97
97
 
98
- // Fallback for a sentinel parent_id (-1, -4, …): the ancestors table. Read in chunked `IN (…)`
99
- // batches ONCE — the point-query version fired per orphan row, and on a global build the orphans
100
- // run to six figures. Ordering is county → region → country, preserved by the same CASE the
101
- // per-row query used, with `id` leading so one pass groups the rows.
98
+ // Fallback for a sentinel parent_id (-1, -4, …): the ancestors table,
99
+ // read in chunked `IN (…)` batches once.
100
+ // Sort IDs in county → region → country order so one pass groups the rows.
102
101
  const ancestorsByID = new Map<number, number[]>()
103
102
 
104
103
  try {
@@ -139,12 +138,10 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
139
138
 
140
139
  if (!row) return []
141
140
 
142
- // If parent_id is a sentinel (≤ 0), use ancestors table.
143
141
  if (row.parent_id <= 0) {
144
142
  return (ancestorsByID.get(id) ?? []).filter((ancestorID) => ancestorID !== id)
145
143
  }
146
144
 
147
- // Normal case: walk parent_id chain.
148
145
  const chain: number[] = []
149
146
  let current = row.parent_id
150
147
  const seen = new Set<number>([id])
@@ -172,13 +169,13 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
172
169
  return chain
173
170
  }
174
171
 
175
- // Phase 3: Load BOTH scores (ROAD_TO_V9 §2 R1, the two-score split).
176
- //
177
- // Referential is ALWAYS population-anchored and never read out of a legacy `place_importance`
178
- // column, because a legacy row that got a Wikipedia score overwrote whatever population would have
179
- // said and the two are indistinguishable afterwards. Encyclopedic rides along for consumers and is
180
- // never handed to the decoder. `loadImportanceSplit` handles all four schema generations; the
181
- // source it reports is stamped into provenance so an artifact says which one it read.
172
+ // Load both scores (the two-score split).
173
+ // Referential is always population-anchored and never read out of a legacy
174
+ // `place_importance` column, because a legacy row that got a Wikipedia score overwrote
175
+ // whatever population would have said and the two are indistinguishable afterwards.
176
+ // Encyclopedic importance remains available to consumers and is never handed to the decoder.
177
+ // `loadImportanceSplit` handles all four schema generations.
178
+ // Provenance records the source so the artifact identifies the schema it read.
182
179
  progress("importance", "Loading referential + encyclopedic scores")
183
180
  const split = loadImportanceSplit(db)
184
181
 
@@ -189,7 +186,6 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
189
186
  ")"
190
187
  )
191
188
 
192
- // Phase 4: Load names for matching places.
193
189
  progress("names", "Loading name variants")
194
190
  const placeIDs = sprRows.map((r) => r.id)
195
191
  const namesByPlace = new Map<number, string[]>()
@@ -223,12 +219,12 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
223
219
 
224
220
  progress("names", `Loaded names for ${namesByPlace.size} places`)
225
221
 
226
- // Phase 5: Build the trie.
227
222
  progress("trie", "Building trie")
228
223
  const nodes: FSTNode[] = [{ edges: new Map(), places: [] }]
229
224
 
230
- // Degenerate-surface curation (see BuildFSTOpts.excludeSurfaces). Applied to the WHOLE normalized
231
- // surface only — a multi-token name containing a function word ("los angeles") is never affected.
225
+ // Degenerate-surface curation (see BuildFSTOpts.excludeSurfaces).
226
+ // Applied to the whole normalized surface only.
227
+ // A multi-token name containing a function word ("los angeles") is never affected.
232
228
  const excludeSurfaces = opts.excludeSurfaces
233
229
  const excludeAllTokensOf = opts.excludeAllTokensOf
234
230
  let excludedCount = 0
@@ -238,14 +234,15 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
238
234
 
239
235
  if (excludeSurfaces?.has(tokens.join(" "))) return true
240
236
 
241
- if (excludeAllTokensOf !== undefined && tokens.every((t) => excludeAllTokensOf.has(t))) return true
237
+ if (excludeAllTokensOf && tokens.every((t) => excludeAllTokensOf.has(t))) return true
242
238
 
243
239
  return false
244
240
  }
245
241
 
246
- // Surface-ambiguity classes (survey #4): a per-SURFACE fact, so the entry is cloned per insertion
247
- // with its accepting surface's count attached (the same place under "nyc" and "new york city"
248
- // records each surface's own ambiguity). Absent map → entries carry no count (back-compat bytes).
242
+ // Surface-ambiguity classes are per-surface facts, so clone the entry for each
243
+ // insertion and attach that surface's count.
244
+ // The same place under "nyc" and "new york city" records a separate ambiguity count for each surface.
245
+ // An absent map means entries have no count for back-compat bytes.
249
246
  const surfaceCountryCounts = opts.surfaceCountryCounts
250
247
 
251
248
  function insertName(tokens: string[], entry: PlaceEntry): boolean {
@@ -272,11 +269,10 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
272
269
  stateID = next
273
270
  }
274
271
 
275
- // Deduplicate: don't add the same wofID twice at the same state.
276
272
  const existing = nodes[stateID]!.places
277
273
 
278
274
  if (!existing.some((p) => p.wofID === entry.wofID && p.placetype === entry.placetype)) {
279
- if (surfaceCountryCounts !== undefined) {
275
+ if (surfaceCountryCounts) {
280
276
  const count = surfaceCountryCounts.get(tokens.join(" "))
281
277
  existing.push({ ...entry, crossCountryBranches: Math.min(count ?? 1, 255) })
282
278
  } else {
@@ -300,21 +296,20 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
300
296
  name: row.name,
301
297
  parentChain,
302
298
  referential: split.referential.get(row.id) ?? 0,
303
- // Spread rather than assigned: a place with no Wikipedia article must carry NO field, not a
304
- // zero. The serializer's per-place presence bit reads `!== undefined`.
305
- ...(encyclopedic === undefined ? {} : { encyclopedic }),
299
+ // A place with no Wikipedia article is `null` rather than a zero.
300
+ // The serializer's per-place presence bit reads `!== null`.
301
+ encyclopedic: encyclopedic ?? null,
306
302
  lat: row.latitude,
307
303
  lon: row.longitude,
304
+ crossCountryBranches: null,
308
305
  }
309
306
 
310
- // Insert the primary name from spr.
311
307
  const primaryTokens = normalizeTokens(row.name)
312
308
 
313
309
  if (insertName(primaryTokens, entry)) {
314
310
  insertCount++
315
311
  }
316
312
 
317
- // Insert alt names from the names table.
318
313
  const altNames = namesByPlace.get(row.id) ?? []
319
314
 
320
315
  for (const altName of altNames) {
@@ -336,12 +331,12 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
336
331
  const edgeCount = nodes.reduce((sum, n) => sum + n.edges.size, 0)
337
332
  const matcher = FSTMatcher.fromNodes(nodes)
338
333
 
339
- // The build stamp (2026-08-05). `sourceDB` alone was never enough to tell a reader whether this
340
- // artifact matches the database at that path — the admin DB is sealed and REPLACED by a rebuild, so
341
- // the path is constant across every generation of it. Hashing costs 7.3 s for the 5.27 GB admin DB
342
- // and is free whenever the `.md5` sidecar is current, which the admin build already writes.
343
- // `sourceIdentity` lets a caller that already knows the digest (or is building from something that
344
- // is not a file at all) supply it instead.
334
+ // The build stamp.
335
+ // `sourceDB` by itself cannot tell a reader whether this artifact matches the database
336
+ // at that path, because the admin database is sealed and replaced by a rebuild
337
+ // so the path is constant across every generation.
338
+ // `sourceIdentity` lets a caller that already knows the digest, or is building
339
+ // from something that is not a file at all, supply it instead.
345
340
  progress("stamp", `Reading source identity for ${dbPath}`)
346
341
  const source = opts.sourceIdentity ?? (await readWOFSourceIdentity(dbPath))
347
342
 
@@ -358,9 +353,10 @@ export async function buildFSTFromWOF(opts: BuildFSTOpts): Promise<{
358
353
  sourceDB: dbPath,
359
354
  sourceDBMD5: source.md5,
360
355
  sourceDBBytes: source.bytes,
361
- ...(excludeSurfaces !== undefined || excludeAllTokensOf !== undefined
356
+ modelCardVersion: null,
357
+ ...(excludeSurfaces || excludeAllTokensOf
362
358
  ? { exclusionPolicy: opts.exclusionPolicy ?? "unspecified", excludedInsertions: excludedCount }
363
- : {}),
359
+ : { exclusionPolicy: null, excludedInsertions: null }),
364
360
  }
365
361
 
366
362
  return {
@@ -4,10 +4,10 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Browser-compatible FST deserializer. Uses DataView + TextDecoder instead of Node's Buffer so the
7
- * same binary format can be loaded in the browser via fetch(url).then(r => r.arrayBuffer()).
7
+ * same binary format loads in the browser through fetch(url).then(r => r.arrayBuffer()).
8
8
  *
9
- * This is a read-only counterpart to fst-serialize.ts — serialization stays Node-only (it's a
10
- * build-time operation).
9
+ * This is a read-only counterpart to fst-serialize.ts. Serialization stays Node-only because it is
10
+ * a build-time operation.
11
11
  */
12
12
 
13
13
  import { tryParsingJSON } from "@mailwoman/core/json"
@@ -56,7 +56,7 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
56
56
 
57
57
  const isV2 = version >= 2
58
58
  const isSplit = version >= VERSION_TWO_SCORE_SPLIT
59
- // flags bit0 (survey #4, mirrors fst-serialize.ts): place rows carry surface-ambiguity data.
59
+ // flags bit0 (mirrors fst-serialize.ts): place rows include surface-ambiguity data.
60
60
  const hasAmbiguity = (view.getUint16(6, true) & 1) === 1
61
61
 
62
62
  const stateCount = view.getUint32(8, true)
@@ -67,7 +67,6 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
67
67
 
68
68
  let pos = HEADER_SIZE
69
69
 
70
- // Decode the interned string table.
71
70
  const strOffsets = new Uint32Array(stringCount + 1)
72
71
 
73
72
  for (let i = 0; i <= stringCount; i++) {
@@ -86,9 +85,8 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
86
85
 
87
86
  pos += stringBytes
88
87
 
89
- // Decode the state table and its transitions.
90
88
  const stateEntrySize = version >= VERSION_WIDE_STATE_COUNTERS ? WIDE_STATE_ENTRY_SIZE : NARROW_STATE_ENTRY_SIZE
91
- // v5 grew the place entry by the encyclopedic float; v4-and-below files are read at the old stride.
89
+ // v5 grew the place entry by the encyclopedic float. v4-and-below files are read at the old stride.
92
90
  const placeEntrySize = isSplit ? SPLIT_PLACE_ENTRY_SIZE : LEGACY_PLACE_ENTRY_SIZE
93
91
  const stateTableStart = pos
94
92
  const edgeTableStart = stateTableStart + stateCount * stateEntrySize
@@ -127,8 +125,9 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
127
125
  parentChain.push(view.getUint32(pp + 24 + ci * 4, true))
128
126
  }
129
127
 
130
- // v1 stored a raw population u32 here; v2-v4 the conflated `importance` float; v5 the
131
- // referential score. See the Node deserializer for why a v1 value is genuinely referential.
128
+ // v1 stored a raw population u32 here. v2-v4 stored the conflated `importance`
129
+ // float. v5 stored the referential score.
130
+ // The Node deserializer explains why a v1 value is genuinely referential.
132
131
  const referential = isV2
133
132
  ? view.getFloat32(pp + 12, true)
134
133
  : Math.min(1, Math.log2(1 + view.getUint32(pp + 12, true) / 1000) / 14)
@@ -144,8 +143,8 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
144
143
  lat: view.getFloat32(pp + 16, true),
145
144
  lon: view.getFloat32(pp + 20, true),
146
145
  parentChain,
147
- ...(hasAmbiguity ? { crossCountryBranches: view.getUint8(pp + 6) } : {}),
148
- ...(hasEncyclopedic ? { encyclopedic: view.getFloat32(pp + ENCYCLOPEDIC_OFFSET, true) } : {}),
146
+ crossCountryBranches: hasAmbiguity ? view.getUint8(pp + 6) : null,
147
+ encyclopedic: hasEncyclopedic ? view.getFloat32(pp + ENCYCLOPEDIC_OFFSET, true) : null,
149
148
  }
150
149
  }
151
150
 
@@ -155,25 +154,25 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
155
154
  return FSTMatcher.fromNodes(nodes)
156
155
  }
157
156
 
158
- export function readFSTProvenanceWeb(input: ArrayBuffer | Uint8Array): FSTProvenance | undefined {
157
+ export function readFSTProvenanceWeb(input: ArrayBuffer | Uint8Array): FSTProvenance | null {
159
158
  const bytes = input instanceof ArrayBuffer ? new Uint8Array(input) : input
160
159
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
161
160
  const decoder = new TextDecoder("utf-8")
162
161
 
163
- if (bytes.byteLength < HEADER_SIZE) return undefined
162
+ if (bytes.byteLength < HEADER_SIZE) return null
164
163
  const version = view.getUint16(4, true)
165
164
 
166
- if (version < VERSION_WITH_METADATA) return undefined
165
+ if (version < VERSION_WITH_METADATA) return null
167
166
  const provenanceOffset = view.getUint32(28, true)
168
167
 
169
- if (provenanceOffset === 0 || provenanceOffset >= bytes.byteLength) return undefined
168
+ if (provenanceOffset === 0 || provenanceOffset >= bytes.byteLength) return null
170
169
 
171
170
  try {
172
171
  const jsonLen = view.getUint32(provenanceOffset, true)
173
172
  const jsonStr = decoder.decode(bytes.subarray(provenanceOffset + 4, provenanceOffset + 4 + jsonLen))
174
173
 
175
- return tryParsingJSON<FSTProvenance>(jsonStr) ?? undefined
174
+ return tryParsingJSON<FSTProvenance>(jsonStr) ?? null
176
175
  } catch {
177
- return undefined
176
+ return null
178
177
  }
179
178
  }
package/lib/fst/format.ts CHANGED
@@ -4,26 +4,33 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * The FST binary-format constants, shared by the Node serializer/deserializer (`fst-serialize.ts`)
7
- * and the browser deserializer (`fst-deserialize-web.ts`). Platform-free by design — no Buffer at
8
- * module scope; both runtimes convert {@link FST_MAGIC_BYTES} themselves.
7
+ * and the browser deserializer (`fst-deserialize-web.ts`). This module stays platform-free.
8
+ * It uses no Buffer at module scope. Both runtimes convert {@link FST_MAGIC_BYTES} themselves.
9
9
  */
10
10
 
11
11
  import type { PlacetypeID } from "#fst/types"
12
12
 
13
13
  /**
14
- * Format version this tree WRITES and the maximum either deserializer accepts, published so a freshness guard can call
15
- * an older artifact format-stale without re-typing the number (mirrors `REQUIRED_PAIR_INDEX_SCHEMA`'s role for PIX1 —
16
- * see `fst-freshness.ts`).
14
+ * Format version this tree writes and the maximum either deserializer accepts, published
15
+ * so a freshness guard can call an older artifact format-stale without re-typing the number
16
+ * (mirrors `REQUIRED_PAIR_INDEX_SCHEMA`'s role for PIX1 — see `fst-freshness.ts`).
17
17
  *
18
- * ONE constant for the writer and both readers, deliberately. The browser reader's acceptance criterion was a SEPARATE
19
- * `MAX_VERSION` number from the layout branches, and it drifted twice: left stale at 2 when the v4 wide-state layout
20
- * shipped (rejecting every real artifact), and stale again at 4 through the v5 two-score split until the line moved by
21
- * hand. A check that is the writer's version cannot drift from it.
18
+ * One constant for the writer and both readers, deliberately.
19
+ * The browser reader once kept a separate `MAX_VERSION` from the layout branches.
20
+ *
21
+ * It drifted twice.
22
+ * It stayed at 2 after the v4 wide-state layout shipped and rejected every real artifact.
23
+ *
24
+ * It later stayed at 4 through the v5 two-score split until someone updated it by hand.
25
+ *
26
+ * A check that is the writer's version cannot drift from it.
22
27
  */
23
28
  export const FST_FORMAT_VERSION = 5
24
29
 
25
30
  /**
26
- * Fixed header size in bytes: magic, version, and the section offsets that follow it.
31
+ * Fixed header size in bytes.
32
+ *
33
+ * It stores the magic and version, followed by section offsets.
27
34
  */
28
35
  export const HEADER_SIZE = 32
29
36
 
@@ -33,8 +40,10 @@ export const HEADER_SIZE = 32
33
40
  export const EDGE_ENTRY_SIZE = 8
34
41
 
35
42
  /**
36
- * Format version that widened the per-state edge and place counters from 16 to 32 bits, growing the state entry from 12
37
- * to 16 bytes. Readers branch on it to stay backward-compatible with v2/v3 files.
43
+ * Format version that widened the per-state edge and place counters from 16 to 32 bits,
44
+ * growing the state entry from 12 to 16 bytes.
45
+ *
46
+ * Readers branch on it to stay backward-compatible with v2/v3 files.
38
47
  */
39
48
  export const VERSION_WIDE_STATE_COUNTERS = 4
40
49
 
@@ -49,14 +58,15 @@ export const WIDE_STATE_ENTRY_SIZE = 16
49
58
  export const NARROW_STATE_ENTRY_SIZE = 12
50
59
 
51
60
  /**
52
- * First format version carrying the trailing metadata block; older files simply have none.
61
+ * First format version carrying the trailing metadata block.
62
+ * Older files simply have none.
53
63
  */
54
64
  export const VERSION_WITH_METADATA = 3
55
65
 
56
66
  /**
57
- * Format version that split the single `importance` float into `referential` + `encyclopedic` (ROAD_TO_V9 §2 R1),
58
- * growing the place entry from 56 to 60 bytes and claiming the previously-reserved `pp+7` byte as
59
- * {@link PLACE_FLAG_HAS_ENCYCLOPEDIC}.
67
+ * Format version that split the single `importance` float into `referential` +
68
+ * `encyclopedic` (ROAD_TO_V9 §2 R1), growing the place entry from 56 to 60 bytes
69
+ * and claiming the previously-reserved `pp+7` byte as {@link PLACE_FLAG_HAS_ENCYCLOPEDIC}.
60
70
  */
61
71
  export const VERSION_TWO_SCORE_SPLIT = 5
62
72
 
@@ -71,25 +81,33 @@ export const SPLIT_PLACE_ENTRY_SIZE = 60
71
81
  export const LEGACY_PLACE_ENTRY_SIZE = 56
72
82
 
73
83
  /**
74
- * Byte offset of the encyclopedic float inside a v5 place entry — immediately after the 8-slot parent chain.
84
+ * Byte offset of the encyclopedic float inside a v5 place entry — immediately
85
+ * after the 8-slot parent chain.
75
86
  */
76
87
  export const ENCYCLOPEDIC_OFFSET = 56
77
88
 
78
89
  /**
79
- * `placeFlags` bit 0 (byte `pp+7`, v5+): this place carries an encyclopedic score. Per-PLACE rather than per-file
80
- * because absence is the common case — roughly 89% of the 2026-08-05 gazetteer has no Wikipedia article — and a
81
- * file-level flag would force every one of those rows to claim a 0 it never had.
90
+ * `placeFlags` bit 0 (byte `pp+7`, v5+): this place has an encyclopedic score.
91
+ *
92
+ * Per-place rather than per-file because absence is the common case — roughly 89% of
93
+ * the 2026-08-05 gazetteer has no Wikipedia article — and a file-level flag would
94
+ * force every one of those rows to claim a 0 it never had.
82
95
  */
83
96
  export const PLACE_FLAG_HAS_ENCYCLOPEDIC = 1
84
97
 
85
98
  /**
86
- * File magic, "FST\0" as bytes. A reader rejects anything not starting with these four bytes before parsing further.
99
+ * File magic, "FST\0" as bytes.
100
+ *
101
+ * A reader rejects anything not starting with these four bytes before parsing further.
87
102
  */
88
103
  export const FST_MAGIC_BYTES: readonly number[] = [0x46, 0x53, 0x54, 0x00]
89
104
 
90
105
  /**
91
- * Placetypes in hierarchy order, largest first. The index into this array is what gets written into a place entry, so
92
- * REORDERING IT BREAKS EVERY EXISTING FILE — append instead, and bump the version.
106
+ * Placetypes in hierarchy order, largest first.
107
+ *
108
+ * The writer stores each placetype's index in a place entry.
109
+ * A different array order invalidates every existing file.
110
+ * Append entries and bump the version.
93
111
  */
94
112
  export const PLACETYPE_ORDER: readonly PlacetypeID[] = [
95
113
  "country",