@mailwoman/resolver-wof-sqlite 9.3.0 → 10.0.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 (286) hide show
  1. package/lib/address/index.ts +3 -3
  2. package/lib/address/{point.ts → point/index.ts} +55 -7
  3. package/lib/address/{point-interpolation.ts → point/interpolation.ts} +2 -3
  4. package/lib/ancestry/backfill.ts +4 -2
  5. package/lib/ancestry/index.ts +1 -2
  6. package/lib/{build-candidate.ts → build/candidate.ts} +62 -31
  7. package/lib/candidate/alias-bags.ts +68 -7
  8. package/lib/{candidate-ancestors-schema.ts → candidate/ancestors/schema.ts} +1 -1
  9. package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +2 -2
  10. package/lib/candidate/extract-fold.ts +91 -10
  11. package/lib/{candidate-importance.ts → candidate/importance.ts} +1 -1
  12. package/lib/{candidate-lookup.ts → candidate/lookup.ts} +13 -16
  13. package/lib/candidate/name-roles.ts +2 -2
  14. package/lib/candidate/own-name.ts +1 -0
  15. package/lib/{candidate-schema.ts → candidate/schema.ts} +2 -2
  16. package/lib/{candidate-scoring.ts → candidate/scoring.ts} +1 -1
  17. package/lib/coincident-roles.ts +2 -3
  18. package/lib/coverage-manifest-schema.ts +2 -1
  19. package/lib/currency-backfill.ts +2 -2
  20. package/lib/fst/builder.ts +1 -1
  21. package/lib/fst/deserialize-web.ts +2 -2
  22. package/lib/fst/freshness.ts +0 -1
  23. package/lib/fst/serialize.ts +9 -9
  24. package/lib/geonames/aliases.ts +1 -7
  25. package/lib/geonames/postal.ts +1 -7
  26. package/lib/index.ts +18 -25
  27. package/lib/interpolation.ts +1 -1
  28. package/lib/lookup.ts +6 -5
  29. package/lib/place-importance-schema.ts +1 -9
  30. package/lib/poi/lookup.ts +1 -2
  31. package/lib/postal/{city-alias-lookup.ts → city/alias/lookup.ts} +1 -1
  32. package/lib/postal/index.ts +3 -3
  33. package/lib/primary-preference.ts +15 -5
  34. package/lib/reverse.ts +1 -2
  35. package/lib/search-fetch.ts +1 -2
  36. package/lib/{sqlite-utils.ts → sqlite/utils.ts} +0 -1
  37. package/lib/street/{centroid.ts → centroid/index.ts} +2 -2
  38. package/lib/street/index.ts +4 -4
  39. package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +8 -2
  40. package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +1 -1
  41. package/lib/street/normalize.ts +42 -1
  42. package/lib/uprn/lookup.ts +1 -2
  43. package/lib/weights-overlay-linker.ts +5 -4
  44. package/out/address/index.d.ts +3 -3
  45. package/out/address/index.d.ts.map +1 -1
  46. package/out/address/index.js +3 -3
  47. package/out/address/index.js.map +1 -1
  48. package/out/address/{point.d.ts → point/index.d.ts} +5 -3
  49. package/out/address/point/index.d.ts.map +1 -0
  50. package/out/address/{point.js → point/index.js} +38 -7
  51. package/out/address/point/index.js.map +1 -0
  52. package/out/address/{point-interpolation.d.ts → point/interpolation.d.ts} +3 -3
  53. package/out/address/point/interpolation.d.ts.map +1 -0
  54. package/out/address/{point-interpolation.js → point/interpolation.js} +1 -1
  55. package/out/address/point/interpolation.js.map +1 -0
  56. package/out/address/{point-schema.d.ts → point/schema.d.ts} +1 -1
  57. package/out/address/point/schema.d.ts.map +1 -0
  58. package/out/address/{point-schema.js → point/schema.js} +1 -1
  59. package/out/address/point/schema.js.map +1 -0
  60. package/out/ancestry/backfill.d.ts.map +1 -1
  61. package/out/ancestry/backfill.js +4 -2
  62. package/out/ancestry/backfill.js.map +1 -1
  63. package/out/ancestry/index.d.ts.map +1 -1
  64. package/out/ancestry/index.js +1 -1
  65. package/out/ancestry/index.js.map +1 -1
  66. package/out/{build-candidate.d.ts → build/candidate.d.ts} +12 -4
  67. package/out/build/candidate.d.ts.map +1 -0
  68. package/out/{build-candidate.js → build/candidate.js} +39 -24
  69. package/out/build/candidate.js.map +1 -0
  70. package/out/{build-slim.d.ts → build/slim.d.ts} +1 -1
  71. package/out/build/slim.d.ts.map +1 -0
  72. package/out/{build-slim.js → build/slim.js} +1 -1
  73. package/out/build/slim.js.map +1 -0
  74. package/out/candidate/alias-bags.d.ts +10 -2
  75. package/out/candidate/alias-bags.d.ts.map +1 -1
  76. package/out/candidate/alias-bags.js +49 -5
  77. package/out/candidate/alias-bags.js.map +1 -1
  78. package/out/{candidate-ancestors-schema.d.ts → candidate/ancestors/schema.d.ts} +2 -2
  79. package/out/candidate/ancestors/schema.d.ts.map +1 -0
  80. package/out/{candidate-ancestors-schema.js → candidate/ancestors/schema.js} +1 -1
  81. package/out/candidate/ancestors/schema.js.map +1 -0
  82. package/out/candidate/{ancestors-sidecar.d.ts → ancestors/sidecar.d.ts} +2 -2
  83. package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
  84. package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +2 -2
  85. package/out/candidate/ancestors/sidecar.js.map +1 -0
  86. package/out/candidate/extract-fold.d.ts +12 -3
  87. package/out/candidate/extract-fold.d.ts.map +1 -1
  88. package/out/candidate/extract-fold.js +57 -8
  89. package/out/candidate/extract-fold.js.map +1 -1
  90. package/out/{candidate-fts.d.ts → candidate/fts.d.ts} +1 -1
  91. package/out/candidate/fts.d.ts.map +1 -0
  92. package/out/{candidate-fts.js → candidate/fts.js} +1 -1
  93. package/out/candidate/fts.js.map +1 -0
  94. package/out/{candidate-importance.d.ts → candidate/importance.d.ts} +1 -1
  95. package/out/candidate/importance.d.ts.map +1 -0
  96. package/out/{candidate-importance.js → candidate/importance.js} +1 -1
  97. package/out/candidate/importance.js.map +1 -0
  98. package/out/{candidate-lookup.d.ts → candidate/lookup.d.ts} +7 -7
  99. package/out/candidate/lookup.d.ts.map +1 -0
  100. package/out/{candidate-lookup.js → candidate/lookup.js} +13 -11
  101. package/out/candidate/lookup.js.map +1 -0
  102. package/out/candidate/name-roles.d.ts +1 -1
  103. package/out/candidate/name-roles.d.ts.map +1 -1
  104. package/out/candidate/name-roles.js +1 -1
  105. package/out/candidate/name-roles.js.map +1 -1
  106. package/out/candidate/own-name.d.ts.map +1 -1
  107. package/out/candidate/own-name.js +1 -0
  108. package/out/candidate/own-name.js.map +1 -1
  109. package/out/{candidate-schema.d.ts → candidate/schema.d.ts} +3 -3
  110. package/out/candidate/schema.d.ts.map +1 -0
  111. package/out/{candidate-schema.js → candidate/schema.js} +2 -2
  112. package/out/candidate/schema.js.map +1 -0
  113. package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +1 -1
  114. package/out/candidate/scoring.d.ts.map +1 -0
  115. package/out/{candidate-scoring.js → candidate/scoring.js} +2 -2
  116. package/out/candidate/scoring.js.map +1 -0
  117. package/out/capitals.d.ts.map +1 -1
  118. package/out/coincident-roles.d.ts +1 -1
  119. package/out/coincident-roles.d.ts.map +1 -1
  120. package/out/coincident-roles.js +1 -1
  121. package/out/coincident-roles.js.map +1 -1
  122. package/out/convention/index.d.ts.map +1 -1
  123. package/out/convention/index.js.map +1 -1
  124. package/out/coverage-manifest-schema.d.ts.map +1 -1
  125. package/out/coverage-manifest-schema.js +2 -1
  126. package/out/coverage-manifest-schema.js.map +1 -1
  127. package/out/currency-backfill.d.ts +2 -2
  128. package/out/currency-backfill.d.ts.map +1 -1
  129. package/out/env.d.ts +1 -1
  130. package/out/fst/autocomplete.js.map +1 -1
  131. package/out/fst/builder.d.ts.map +1 -1
  132. package/out/fst/builder.js +1 -1
  133. package/out/fst/builder.js.map +1 -1
  134. package/out/fst/deserialize-web.js +2 -2
  135. package/out/fst/deserialize-web.js.map +1 -1
  136. package/out/fst/freshness.d.ts +0 -1
  137. package/out/fst/freshness.d.ts.map +1 -1
  138. package/out/fst/freshness.js +0 -1
  139. package/out/fst/freshness.js.map +1 -1
  140. package/out/fst/matcher.d.ts.map +1 -1
  141. package/out/fst/serialize.js +9 -9
  142. package/out/fst/serialize.js.map +1 -1
  143. package/out/fts/index.js.map +1 -1
  144. package/out/geonames/aliases.d.ts +0 -5
  145. package/out/geonames/aliases.d.ts.map +1 -1
  146. package/out/geonames/aliases.js +1 -6
  147. package/out/geonames/aliases.js.map +1 -1
  148. package/out/geonames/postal.d.ts +0 -6
  149. package/out/geonames/postal.d.ts.map +1 -1
  150. package/out/geonames/postal.js +1 -6
  151. package/out/geonames/postal.js.map +1 -1
  152. package/out/index.d.ts +18 -19
  153. package/out/index.d.ts.map +1 -1
  154. package/out/index.js +13 -14
  155. package/out/index.js.map +1 -1
  156. package/out/interpolation.d.ts +1 -1
  157. package/out/interpolation.d.ts.map +1 -1
  158. package/out/lookup.d.ts +2 -2
  159. package/out/lookup.d.ts.map +1 -1
  160. package/out/lookup.js +4 -4
  161. package/out/lookup.js.map +1 -1
  162. package/out/nsul/lookup.d.ts.map +1 -1
  163. package/out/place-importance-schema.d.ts +0 -1
  164. package/out/place-importance-schema.d.ts.map +1 -1
  165. package/out/place-importance-schema.js +1 -2
  166. package/out/place-importance-schema.js.map +1 -1
  167. package/out/poi/lookup.d.ts.map +1 -1
  168. package/out/poi/lookup.js +1 -1
  169. package/out/poi/lookup.js.map +1 -1
  170. package/out/poi/schema.d.ts.map +1 -1
  171. package/out/postal/{city-alias-lookup.d.ts → city/alias/lookup.d.ts} +2 -2
  172. package/out/postal/city/alias/lookup.d.ts.map +1 -0
  173. package/out/postal/{city-alias-lookup.js → city/alias/lookup.js} +1 -1
  174. package/out/postal/city/alias/lookup.js.map +1 -0
  175. package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +1 -1
  176. package/out/postal/city/alias/schema.d.ts.map +1 -0
  177. package/out/postal/{city-alias-schema.js → city/alias/schema.js} +1 -1
  178. package/out/postal/city/alias/schema.js.map +1 -0
  179. package/out/postal/{city-candidate-schema.d.ts → city/candidate-schema.d.ts} +1 -1
  180. package/out/postal/city/candidate-schema.d.ts.map +1 -0
  181. package/out/postal/{city-candidate-schema.js → city/candidate-schema.js} +1 -1
  182. package/out/postal/city/candidate-schema.js.map +1 -0
  183. package/out/postal/index.d.ts +3 -3
  184. package/out/postal/index.js +3 -3
  185. package/out/postcode-point-lookup.d.ts.map +1 -1
  186. package/out/primary-preference.d.ts +3 -3
  187. package/out/primary-preference.d.ts.map +1 -1
  188. package/out/primary-preference.js +12 -4
  189. package/out/primary-preference.js.map +1 -1
  190. package/out/reverse.d.ts.map +1 -1
  191. package/out/reverse.js +1 -1
  192. package/out/reverse.js.map +1 -1
  193. package/out/search-fetch.d.ts.map +1 -1
  194. package/out/search-fetch.js +1 -1
  195. package/out/search-fetch.js.map +1 -1
  196. package/out/{sqlite-convention-source.d.ts → sqlite/convention-source.d.ts} +1 -1
  197. package/out/sqlite/convention-source.d.ts.map +1 -0
  198. package/out/{sqlite-convention-source.js → sqlite/convention-source.js} +1 -1
  199. package/out/sqlite/convention-source.js.map +1 -0
  200. package/out/{sqlite-utils.d.ts → sqlite/utils.d.ts} +1 -2
  201. package/out/sqlite/utils.d.ts.map +1 -0
  202. package/out/{sqlite-utils.js → sqlite/utils.js} +1 -4
  203. package/out/sqlite/utils.js.map +1 -0
  204. package/out/street/{centroid.d.ts → centroid/index.d.ts} +2 -2
  205. package/out/street/centroid/index.d.ts.map +1 -0
  206. package/out/street/{centroid.js → centroid/index.js} +1 -1
  207. package/out/street/centroid/index.js.map +1 -0
  208. package/out/street/{centroid-schema.d.ts → centroid/schema.d.ts} +1 -1
  209. package/out/street/centroid/schema.d.ts.map +1 -0
  210. package/out/street/{centroid-schema.js → centroid/schema.js} +1 -1
  211. package/out/street/centroid/schema.js.map +1 -0
  212. package/out/street/index.d.ts +4 -4
  213. package/out/street/index.d.ts.map +1 -1
  214. package/out/street/index.js +4 -4
  215. package/out/street/index.js.map +1 -1
  216. package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +1 -1
  217. package/out/street/morphology/fst/builder.d.ts.map +1 -0
  218. package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +8 -3
  219. package/out/street/morphology/fst/builder.js.map +1 -0
  220. package/out/street/{morphology-fst-loader.d.ts → morphology/fst/loader.d.ts} +1 -1
  221. package/out/street/morphology/fst/loader.d.ts.map +1 -0
  222. package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +2 -2
  223. package/out/street/morphology/fst/loader.js.map +1 -0
  224. package/out/street/name-lookup.d.ts.map +1 -1
  225. package/out/street/name-lookup.js.map +1 -1
  226. package/out/street/normalize.d.ts +13 -1
  227. package/out/street/normalize.d.ts.map +1 -1
  228. package/out/street/normalize.js +37 -0
  229. package/out/street/normalize.js.map +1 -1
  230. package/out/street/segment-schema.d.ts.map +1 -1
  231. package/out/uprn/lookup.d.ts.map +1 -1
  232. package/out/uprn/lookup.js +1 -1
  233. package/out/uprn/lookup.js.map +1 -1
  234. package/out/weights-overlay-linker.d.ts.map +1 -1
  235. package/out/weights-overlay-linker.js +4 -3
  236. package/out/weights-overlay-linker.js.map +1 -1
  237. package/package.json +64 -64
  238. package/out/address/point-interpolation.d.ts.map +0 -1
  239. package/out/address/point-interpolation.js.map +0 -1
  240. package/out/address/point-schema.d.ts.map +0 -1
  241. package/out/address/point-schema.js.map +0 -1
  242. package/out/address/point.d.ts.map +0 -1
  243. package/out/address/point.js.map +0 -1
  244. package/out/build-candidate.d.ts.map +0 -1
  245. package/out/build-candidate.js.map +0 -1
  246. package/out/build-slim.d.ts.map +0 -1
  247. package/out/build-slim.js.map +0 -1
  248. package/out/candidate/ancestors-sidecar.d.ts.map +0 -1
  249. package/out/candidate/ancestors-sidecar.js.map +0 -1
  250. package/out/candidate-ancestors-schema.d.ts.map +0 -1
  251. package/out/candidate-ancestors-schema.js.map +0 -1
  252. package/out/candidate-fts.d.ts.map +0 -1
  253. package/out/candidate-fts.js.map +0 -1
  254. package/out/candidate-importance.d.ts.map +0 -1
  255. package/out/candidate-importance.js.map +0 -1
  256. package/out/candidate-lookup.d.ts.map +0 -1
  257. package/out/candidate-lookup.js.map +0 -1
  258. package/out/candidate-schema.d.ts.map +0 -1
  259. package/out/candidate-schema.js.map +0 -1
  260. package/out/candidate-scoring.d.ts.map +0 -1
  261. package/out/candidate-scoring.js.map +0 -1
  262. package/out/postal/city-alias-lookup.d.ts.map +0 -1
  263. package/out/postal/city-alias-lookup.js.map +0 -1
  264. package/out/postal/city-alias-schema.d.ts.map +0 -1
  265. package/out/postal/city-alias-schema.js.map +0 -1
  266. package/out/postal/city-candidate-schema.d.ts.map +0 -1
  267. package/out/postal/city-candidate-schema.js.map +0 -1
  268. package/out/sqlite-convention-source.d.ts.map +0 -1
  269. package/out/sqlite-convention-source.js.map +0 -1
  270. package/out/sqlite-utils.d.ts.map +0 -1
  271. package/out/sqlite-utils.js.map +0 -1
  272. package/out/street/centroid-schema.d.ts.map +0 -1
  273. package/out/street/centroid-schema.js.map +0 -1
  274. package/out/street/centroid.d.ts.map +0 -1
  275. package/out/street/centroid.js.map +0 -1
  276. package/out/street/morphology-fst-builder.d.ts.map +0 -1
  277. package/out/street/morphology-fst-builder.js.map +0 -1
  278. package/out/street/morphology-fst-loader.d.ts.map +0 -1
  279. package/out/street/morphology-fst-loader.js.map +0 -1
  280. /package/lib/address/{point-schema.ts → point/schema.ts} +0 -0
  281. /package/lib/{build-slim.ts → build/slim.ts} +0 -0
  282. /package/lib/{candidate-fts.ts → candidate/fts.ts} +0 -0
  283. /package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +0 -0
  284. /package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +0 -0
  285. /package/lib/{sqlite-convention-source.ts → sqlite/convention-source.ts} +0 -0
  286. /package/lib/street/{centroid-schema.ts → centroid/schema.ts} +0 -0
@@ -8,17 +8,51 @@
8
8
  import { DatabaseClient } from "@mailwoman/sqlite/client"
9
9
  import { tableExists } from "@mailwoman/sqlite/introspection"
10
10
 
11
- import type { CandidateDatabase } from "#candidate-schema"
11
+ import { CANDIDATE_ANCESTOR_COLUMNS, CANDIDATE_ANCESTOR_TABLE } from "#candidate/ancestors/schema"
12
12
  import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
13
+ import type { CandidateDatabase } from "#candidate/schema"
13
14
  import type { WOFDatabase } from "#schema"
14
15
  import { normalizeLocalityForKey } from "#street/normalize"
15
16
 
17
+ /**
18
+ * The extract's own region-tier ancestry, when it carries any: extract id → the admin region's id. A locality extract
19
+ * derived from a national register (the Taiwanese 鄉鎮市區 from the civil-affairs address points) knows which 縣市 each row
20
+ * belongs to, and writes that as an `ancestors` row against the admin region's WOF id. Without it a folded row has no
21
+ * region scope, so a `parentID`-scoped probe misses it and the cascade widens to whatever namesake carries the key.
22
+ *
23
+ * Only a region the admin build staged (`attrs`) counts: an id the admin does not know cannot be denormalized into a
24
+ * closure row, and a scope nothing can match would be a silent zero rather than an absence.
25
+ */
26
+ function extractRegionAncestry(pc: DatabaseClient<WOFDatabase>, attrs: Map<number, PlaceAttrs>): Map<number, number> {
27
+ const regionOf = new Map<number, number>()
28
+
29
+ if (!tableExists(pc, "ancestors")) {
30
+ return regionOf
31
+ }
32
+
33
+ for (const r of pc
34
+ .prepare(
35
+ "SELECT id, MIN(ancestor_id) AS ancestor_id FROM ancestors WHERE ancestor_placetype = 'region' GROUP BY id"
36
+ )
37
+ .iterate()) {
38
+ const rid = Number(r.ancestor_id)
39
+
40
+ if (attrs.has(rid)) {
41
+ regionOf.set(Number(r.id), rid)
42
+ }
43
+ }
44
+
45
+ return regionOf
46
+ }
47
+
16
48
  /**
17
49
  * Fold ONE extract (`spr` rows at `extractPlacetype` carrying real coordinates) in, then pass 4b: the alias names
18
50
  * hanging off that same extract's `names` table.
19
51
  *
20
52
  * Self-contained by construction — it shares only the staging statement and the code dictionaries with the admin passes
21
- * above it, and nothing downstream reads anything it produces except the two counters it returns.
53
+ * above it, and nothing downstream reads anything it produces except the counters it returns. The one thing it writes
54
+ * beyond the staging table is a depth-1 closure row for a row whose extract names its region (see
55
+ * {@link extractRegionAncestry}), so the pick's stamped ancestors agree with the scope it was found under.
22
56
  */
23
57
  export function foldExtract(ctx: {
24
58
  /**
@@ -30,8 +64,13 @@ export function foldExtract(ctx: {
30
64
  ccID: (code: string | null) => number
31
65
  ptID: (pt: string | null) => number
32
66
  stageRow: StageRow
67
+ /**
68
+ * The admin places pass 1 staged — what an extract's region ancestry is resolved against. Absent, no extract row
69
+ * takes a region scope (the postcode extracts, which carry none).
70
+ */
71
+ attrs?: Map<number, PlaceAttrs>
33
72
  progress: (phase: string, message: string) => void
34
- }): { primaries: number; aliases: number } {
73
+ }): { primaries: number; aliases: number; scoped: number; ancestorRows: number } {
35
74
  const { out, extractPath, extractPlacetype, ccID, ptID, stageRow, progress } = ctx
36
75
 
37
76
  progress(extractPlacetype === "postalcode" ? "postcodes" : "localities", `reading ${extractPath}`)
@@ -41,8 +80,24 @@ export function foldExtract(ctx: {
41
80
  // Per-extract, not the admin `attrs` map: pass 1 only ever sees the admin DB, so the alias pass
42
81
  // below has nothing to join against unless this primary loop records what it staged.
43
82
  const pcAttrs = new Map<number, PlaceAttrs>()
83
+ const regionOf = ctx.attrs ? extractRegionAncestry(pc, ctx.attrs) : new Map<number, number>()
84
+
85
+ const insAncestor = out.prepare(
86
+ `INSERT OR IGNORE INTO ${CANDIDATE_ANCESTOR_TABLE} VALUES (${CANDIDATE_ANCESTOR_COLUMNS.map(() => "?").join(", ")})`
87
+ )
88
+
89
+ // The region's own closure rows, as the sidecar pass wrote them. A scoped row inherits the whole chain above its
90
+ // region — macroregion, country — not the region alone: the widened-scope refusal reads a pick's stamped ancestors
91
+ // against the parent the walk resolved, and a 縣市 that resolves to its macroregion record (`桃園市`, which WOF names
92
+ // only at that tier) would otherwise contradict a lineage that stops at the region.
93
+ const regionChain = out.prepare(
94
+ `SELECT depth, parent_spr_id, parent_placetype_id, parent_name, parent_name_key FROM ${CANDIDATE_ANCESTOR_TABLE} WHERE spr_id = ? ORDER BY depth`
95
+ )
96
+
44
97
  let primaries = 0
45
98
  let aliases = 0
99
+ let scoped = 0
100
+ let ancestorRows = 0
46
101
 
47
102
  out.exec("BEGIN")
48
103
 
@@ -60,12 +115,15 @@ export function foldExtract(ctx: {
60
115
 
61
116
  const lat = r.latitude as number
62
117
  const lon = r.longitude as number
118
+ const id = Number(r.id)
119
+ const rid = regionOf.get(id) ?? 0
63
120
 
64
- // region_id 0 (a postcode is unique by name+country — no same-name disambiguation); neg_rank 0
65
- // (no population). bbox = the postcode's own min/max (falls back to the centroid point).
121
+ // region_id from the extract's own ancestry when it names one, else 0 (a postcode is unique by
122
+ // name+country — no same-name disambiguation); neg_rank 0 (no population). bbox = the row's own
123
+ // min/max (falls back to the centroid point).
66
124
  const a: PlaceAttrs = {
67
125
  cid: ccID(r.country as string | null),
68
- rid: 0,
126
+ rid,
69
127
  ptid: pcPtid,
70
128
  name,
71
129
  lat,
@@ -83,15 +141,38 @@ export function foldExtract(ctx: {
83
141
  imp: null,
84
142
  }
85
143
 
86
- pcAttrs.set(Number(r.id), a)
87
- stageRow(key, a, Number(r.id), 1)
144
+ pcAttrs.set(id, a)
145
+ stageRow(key, a, id, 1)
146
+
147
+ if (rid !== 0) {
148
+ const region = ctx.attrs!.get(rid)!
149
+
150
+ insAncestor.run(id, 1, rid, region.ptid, region.name, region.pkey)
151
+
152
+ scoped++
153
+
154
+ ancestorRows++
155
+
156
+ for (const link of regionChain.iterate(rid)) {
157
+ insAncestor.run(
158
+ id,
159
+ Number(link.depth) + 1,
160
+ Number(link.parent_spr_id),
161
+ Number(link.parent_placetype_id),
162
+ String(link.parent_name ?? ""),
163
+ String(link.parent_name_key ?? "")
164
+ )
165
+
166
+ ancestorRows++
167
+ }
168
+ }
88
169
 
89
170
  primaries++
90
171
  }
91
172
 
92
173
  out.exec("COMMIT")
93
174
 
94
- // --- pass 4b: postcode ALIAS names (#1495) ---
175
+ // Fold postcode aliases into the staged candidate names.
95
176
  //
96
177
  // The delivery-city names GeoNames supplies for a ZIP ("Brooklyn" for 11201) are written into
97
178
  // the extract's `names` table by `postcode/centroid-fills.ts`'s `geonamesNameFill`. Everything
@@ -134,5 +215,5 @@ export function foldExtract(ctx: {
134
215
  progress("postcode-aliases", `${extractPath} has no \`names\` table — no delivery-city aliases to fold`)
135
216
  }
136
217
 
137
- return { primaries, aliases }
218
+ return { primaries, aliases, scoped, ancestorRows }
138
219
  }
@@ -44,7 +44,7 @@
44
44
  import { haversineKm } from "@mailwoman/spatial"
45
45
  import { DatabaseClient } from "@mailwoman/sqlite/client"
46
46
 
47
- import type { CandidateDatabase } from "#candidate-schema"
47
+ import type { CandidateDatabase } from "#candidate/schema"
48
48
  import { normalizeLocalityForKey } from "#street/normalize"
49
49
 
50
50
  /**
@@ -4,10 +4,10 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * Node-side {@link PlaceLookup} over the byte-range CANDIDATE table (`build-candidate.ts`) — the
7
- * SAME gazetteer the browser demo resolves against ({@link WOFCandidateTableLookup} in
8
- * `docs/src/shared/httpvfs-resolver.ts`), but reading a LOCAL `candidate.db` via `node:sqlite`
9
- * instead of sql.js-httpvfs. This is what makes the server/CLI resolver match the demo: one
10
- * lookup surface, one artifact, one ranking.
7
+ * SAME gazetteer the browser runtime resolves against ({@link WOFCandidateTableLookup} in
8
+ * `packages/resolver-wof-wasm/lib/httpvfs/resolver.ts`), but reading a LOCAL `candidate.db` via
9
+ * `node:sqlite` instead of sql.js-httpvfs. This is what makes the server/CLI resolver match the
10
+ * browser: one lookup surface, one artifact, one ranking.
11
11
  *
12
12
  * The query is a single contiguous probe on the `WITHOUT ROWID` B-tree keyed `(name_key,
13
13
  * country_id, region_id, placetype_id, neg_rank, spr_id)`. `name_key` is the SHARED
@@ -23,13 +23,11 @@
23
23
  * `bbox` field on {@link FindPlaceQuery}).
24
24
  */
25
25
 
26
+ import { expandPlacetypeFilter } from "@mailwoman/codex/placetype-map"
27
+ import { type Ancestor, type GazetteerArtifactCoverage, referentialFromPopulation } from "@mailwoman/core/resolver"
28
+ import { allRows } from "@mailwoman/core/utils"
26
29
  import { jaroWinkler, levenshteinSimilarity } from "@mailwoman/match/comparators"
27
- import {
28
- expandPlacetypeFilter,
29
- partitionByContainment,
30
- type Ancestor,
31
- type GazetteerArtifactCoverage,
32
- } from "@mailwoman/resolver"
30
+ import { partitionByContainment } from "@mailwoman/resolver"
33
31
  import { haversineKm } from "@mailwoman/spatial"
34
32
  import { DatabaseClient } from "@mailwoman/sqlite/client"
35
33
  import type { PathBuilderLike } from "path-ts"
@@ -40,16 +38,15 @@ import {
40
38
  intervalContains,
41
39
  type CandidateAncestorTable,
42
40
  type IntervalLabel,
43
- } from "#candidate-ancestors-schema"
44
- import { CANDIDATE_FTS_TABLE } from "#candidate-fts"
45
- import type { CandidateDatabase, CandidateTable, CountryCodeTable, PlacetypeCodeTable } from "#candidate-schema"
41
+ } from "#candidate/ancestors/schema"
42
+ import { CANDIDATE_FTS_TABLE } from "#candidate/fts"
43
+ import type { CandidateDatabase, CandidateTable, CountryCodeTable, PlacetypeCodeTable } from "#candidate/schema"
46
44
  import { readGazetteerCoverageManifest } from "#coverage-manifest-schema"
47
- import { referentialFromPopulation } from "#place-importance-schema"
48
- import { POSTAL_CITY_CANDIDATE_TABLE, type PostalCityCandidateTable } from "#postal/city-candidate-schema"
45
+ import { POSTAL_CITY_CANDIDATE_TABLE, type PostalCityCandidateTable } from "#postal/city/candidate-schema"
49
46
  import { rankByPrimaryPreference, type RankedRow, RERANK_FETCH } from "#primary-preference"
50
47
  import { applyProximityRerank } from "#proximity-rerank"
51
48
  import { REGION_CLASS_PLACETYPES, regionQualifierProbeKeys } from "#region-keys"
52
- import { allRows, hasColumn, hasTable } from "#sqlite-utils"
49
+ import { hasColumn, hasTable } from "#sqlite-utils"
53
50
  import { type NameKey, normalizeLocalityForKey, stripLocalityQualifier } from "#street/normalize"
54
51
  import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "#types"
55
52
 
@@ -9,9 +9,9 @@ import { isOfficialLanguage } from "@mailwoman/codex/country"
9
9
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
10
10
  import { tableExists } from "@mailwoman/sqlite/introspection"
11
11
 
12
- import type { CandidateDatabase } from "#candidate-schema"
13
12
  import { isOwnNameVariant } from "#candidate/own-name"
14
13
  import type { PlaceAttrs } from "#candidate/place-attrs"
14
+ import type { CandidateDatabase } from "#candidate/schema"
15
15
  import type { WOFDatabase } from "#schema"
16
16
  import { normalizeLocalityForKey } from "#street/normalize"
17
17
 
@@ -141,7 +141,7 @@ export function stampNameRoles(ctx: {
141
141
  .run().changes
142
142
  )
143
143
 
144
- // --- variant detector (#1882): the alias surface is the holder's OWN primary name in another
144
+ // Detect aliases that are the holder's own primary name in another variant.
145
145
  // orthography — romanization, spacing/diacritic variant, or abbreviation expansion. The verdict
146
146
  // is per (alias key, primary key) pair, so it runs in JS over the still-unstamped alias rows;
147
147
  // an uncovered script answers no-verdict and stamps nothing (own-name.ts owns the predicate and
@@ -127,6 +127,7 @@ export function romanizeNameKey(key: string): string | null {
127
127
  * Edit similarity between a holder's primary name key and one of its alias keys, both romanized and
128
128
  * abbreviation-expanded. `null` when either side's script is uncovered.
129
129
  */
130
+ // repo-health-ignore export-name-affix -- romanizes and expands abbreviations first; `nameSimilarity` compares as given.
130
131
  export function ownNameSimilarity(primaryKey: string, aliasKey: string): number | null {
131
132
  const primary = romanizeNameKey(primaryKey)
132
133
  const alias = romanizeNameKey(aliasKey)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Typed schema for the byte-range CANDIDATE gazetteer (`candidate.db`) — the single source of truth
7
7
  * for the columns shared by the BUILDER ({@link buildCandidateTable}) and the READERS (the Node
8
- * {@link WOFCandidateTableLookup} + the browser `httpvfs-resolver.ts`). Before this module each
8
+ * {@link WOFCandidateTableLookup} + the browser `@mailwoman/resolver-wof-wasm/httpvfs/resolver`). Before this module each
9
9
  * side hand-wrote the column list; a rename in one place broke the other at runtime. Now the
10
10
  * contract is a Kysely `Database` interface (`new DatabaseClient<CandidateDatabase>(...)` for
11
11
  * typed inserts) plus the table DDL as strings — so a column change is a compile error on every
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { sql, type Kysely } from "kysely"
19
19
 
20
- import type { CandidateAncestorsDatabase } from "#candidate-ancestors-schema"
20
+ import type { CandidateAncestorsDatabase } from "#candidate/ancestors/schema"
21
21
  import type { CapitalTable } from "#capital-schema"
22
22
  import type { NameKey } from "#street/normalize"
23
23
 
@@ -7,12 +7,12 @@
7
7
  * ordered — the weighted-sum score and the exact-match tiering that ranks over it.
8
8
  */
9
9
 
10
+ import { compareReferential, referentialFromPopulation } from "@mailwoman/core/resolver"
10
11
  import { haversineKm } from "@mailwoman/spatial"
11
12
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
12
13
 
13
14
  import { exactMatchIDs, officialNameIDs } from "#exact-match"
14
15
  import { foldQueryText } from "#fts/index"
15
- import { compareReferential, referentialFromPopulation } from "#place-importance-schema"
16
16
  import { populationBoostTerm, type RankingWeights } from "#ranking-weights"
17
17
  import type { RawSearchRow } from "#search-fetch"
18
18
  import type { FindPlaceQuery, PlaceCandidate, WOFPlacetype } from "#types"
@@ -38,14 +38,13 @@
38
38
  * be wired as a post-step of the main `scripts/build-unified-wof.ts`.
39
39
  */
40
40
 
41
- import type { CoincidentLocality } from "@mailwoman/resolver"
41
+ import type { CoincidentLocality } from "@mailwoman/core/resolver"
42
+ import { allRows } from "@mailwoman/core/utils"
42
43
  import { haversineKm } from "@mailwoman/spatial"
43
44
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
44
45
  import { tableExists } from "@mailwoman/sqlite/introspection"
45
46
 
46
47
  import type { WOFDatabase } from "#schema"
47
- import { allRows } from "#sqlite-utils"
48
-
49
48
  /**
50
49
  * Table of places that hold more than one admin role — a locality that is also its county seat. Written by the
51
50
  * gazetteer build, read by the resolver when a coincident locality has to be chosen.
@@ -29,10 +29,11 @@ import {
29
29
  type CountryCoverageFact,
30
30
  type GazetteerArtifactCoverage,
31
31
  } from "@mailwoman/core/resolver"
32
+ import { allRows } from "@mailwoman/core/utils"
32
33
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
33
34
  import { sql, type Kysely } from "kysely"
34
35
 
35
- import { allRows, hasTable } from "#sqlite-utils"
36
+ import { hasTable } from "#sqlite-utils"
36
37
 
37
38
  /**
38
39
  * One country's hard-filter coverage measurement — the storage form of {@link CountryCoverageFact}.
@@ -16,9 +16,9 @@ import type { DatabaseClient } from "@mailwoman/sqlite/client"
16
16
  import { resolvePath } from "path-ts"
17
17
  import { TSVSpliterator } from "spliterator"
18
18
 
19
- import type { loadImportanceIndex } from "#candidate-importance"
20
- import type { CandidateDatabase } from "#candidate-schema"
19
+ import type { loadImportanceIndex } from "#candidate/importance"
21
20
  import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
21
+ import type { CandidateDatabase } from "#candidate/schema"
22
22
  import type { WOFDatabase } from "#schema"
23
23
  import { normalizeLocalityForKey } from "#street/normalize"
24
24
 
@@ -11,6 +11,7 @@
11
11
  * PlaceEntry at terminals → return FSTMatcher.
12
12
  */
13
13
 
14
+ import { allRows, getRow } from "@mailwoman/core/utils"
14
15
  import { DatabaseClient } from "@mailwoman/sqlite/client"
15
16
  import { resolvePath } from "path-ts"
16
17
 
@@ -20,7 +21,6 @@ import { FSTMatcher, normalizeTokens } from "#fst/matcher"
20
21
  import type { BuildFSTOpts, BuildFSTResult, FSTProvenance, PlaceEntry, PlacetypeID } from "#fst/types"
21
22
  import { loadImportanceSplit } from "#place-importance-schema"
22
23
  import type { WOFDatabase } from "#schema"
23
- import { allRows, getRow } from "#sqlite-utils"
24
24
 
25
25
  const DEFAULT_PLACETYPES: PlacetypeID[] = [
26
26
  "country",
@@ -67,7 +67,7 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
67
67
 
68
68
  let pos = HEADER_SIZE
69
69
 
70
- // --- String table ---
70
+ // Decode the interned string table.
71
71
  const strOffsets = new Uint32Array(stringCount + 1)
72
72
 
73
73
  for (let i = 0; i <= stringCount; i++) {
@@ -86,7 +86,7 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
86
86
 
87
87
  pos += stringBytes
88
88
 
89
- // --- State table ---
89
+ // Decode the state table and its transitions.
90
90
  const stateEntrySize = version >= VERSION_WIDE_STATE_COUNTERS ? WIDE_STATE_ENTRY_SIZE : NARROW_STATE_ENTRY_SIZE
91
91
  // v5 grew the place entry by the encyclopedic float; v4-and-below files are read at the old stride.
92
92
  const placeEntrySize = isSplit ? SPLIT_PLACE_ENTRY_SIZE : LEGACY_PLACE_ENTRY_SIZE
@@ -146,7 +146,6 @@ export async function peekFSTStampFields(path: string): Promise<FSTStampFields |
146
146
  * Re-exported from `@mailwoman/core/hash`. This package's own test imports it from here, and the sidecar convention
147
147
  * below is what it is for.
148
148
  */
149
- export { md5File } from "@mailwoman/core/hash"
150
149
 
151
150
  /**
152
151
  * The source identity an FST build should stamp, or a check should compare against.
@@ -77,7 +77,7 @@ for (let i = 0; i < PLACETYPE_ORDER.length; i++) {
77
77
  export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): Buffer {
78
78
  const nodes = matcher.toNodes() as FSTNode[]
79
79
 
80
- // --- String interning ---
80
+ // Intern state-edge strings before serializing their table.
81
81
  const stringMap = new Map<string, number>()
82
82
  const strings: string[] = []
83
83
 
@@ -106,7 +106,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
106
106
  const encodedStrings = strings.map((s) => Buffer.from(s, "utf8"))
107
107
  const stringBytes = encodedStrings.reduce((sum, b) => sum + b.length, 0)
108
108
 
109
- // --- Counts ---
109
+ // Count each serialized record class.
110
110
  let totalEdges = 0
111
111
  let totalPlaces = 0
112
112
 
@@ -115,7 +115,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
115
115
  totalPlaces += node.places.length
116
116
  }
117
117
 
118
- // --- Allocate ---
118
+ // Allocate the complete binary buffer.
119
119
  const stringTableSize = (strings.length + 1) * 4 + stringBytes
120
120
  const stateTableSize = nodes.length * WIDE_STATE_ENTRY_SIZE
121
121
  const edgeTableSize = totalEdges * EDGE_ENTRY_SIZE
@@ -127,7 +127,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
127
127
  const buf = Buffer.alloc(totalSize)
128
128
  let pos = 0
129
129
 
130
- // --- Header ---
130
+ // Read the versioned binary header.
131
131
  // flags bit0 (survey #4, 2026-07-27): place rows carry surface-ambiguity data in the former _pad
132
132
  // byte (pp+6 = crossCountryBranches u8, pp+7 reserved). Presence-signaled here so VERSION stays
133
133
  // put: pre-ambiguity artifacts read flags=0 → readers expose `undefined`, never a fake 0.
@@ -151,7 +151,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
151
151
  buf.writeUInt32LE(provenanceJson ? binarySize : 0, pos)
152
152
  pos += 4
153
153
 
154
- // --- String table ---
154
+ // Read the interned string table.
155
155
  let strOffset = 0
156
156
 
157
157
  for (const encoded of encodedStrings) {
@@ -170,7 +170,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
170
170
  pos += encoded.length
171
171
  }
172
172
 
173
- // --- State, edge, and place tables ---
173
+ // Read state, edge, and place tables.
174
174
  const stateTableStart = pos
175
175
  const edgeTableStart = stateTableStart + stateTableSize
176
176
  const placeTableStart = edgeTableStart + edgeTableSize
@@ -234,7 +234,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
234
234
  }
235
235
 
236
236
  export function deserializeFST(buf: Buffer): FSTMatcher {
237
- // --- Header ---
237
+ // Verify the versioned binary header.
238
238
  if (buf.length < HEADER_SIZE) throw new Error("FST buffer too small for header")
239
239
 
240
240
  if (!buf.subarray(0, 4).equals(MAGIC)) throw new Error("FST magic mismatch")
@@ -257,7 +257,7 @@ export function deserializeFST(buf: Buffer): FSTMatcher {
257
257
 
258
258
  let pos = HEADER_SIZE
259
259
 
260
- // --- String table ---
260
+ // Verify string-table offsets and contents.
261
261
  const strOffsets = new Uint32Array(stringCount + 1)
262
262
 
263
263
  for (let i = 0; i <= stringCount; i++) {
@@ -276,7 +276,7 @@ export function deserializeFST(buf: Buffer): FSTMatcher {
276
276
 
277
277
  pos += stringBytes
278
278
 
279
- // --- State table ---
279
+ // Verify state-table offsets and transitions.
280
280
  const stateEntrySize = version >= VERSION_WIDE_STATE_COUNTERS ? WIDE_STATE_ENTRY_SIZE : NARROW_STATE_ENTRY_SIZE
281
281
  // v5 grew the place entry by the encyclopedic float; v4-and-below files are read at the old stride.
282
282
  const placeEntrySize = version >= VERSION_TWO_SCORE_SPLIT ? SPLIT_PLACE_ENTRY_SIZE : LEGACY_PLACE_ENTRY_SIZE
@@ -26,19 +26,13 @@
26
26
 
27
27
  import { isOfficialLanguage } from "@mailwoman/codex/country"
28
28
  import { pathExists } from "@mailwoman/core/fs/readers"
29
+ import { GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE } from "@mailwoman/core/resolver/synthetic-id-ranges"
29
30
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
30
31
  import { join, type PathBuilderLike } from "path-ts"
31
32
  import { TSVSpliterator } from "spliterator"
32
33
 
33
- import { GEONAMES_POSTAL_ID_BASE } from "#geonames/postal"
34
34
  import type { WOFDatabase } from "#schema"
35
35
 
36
- /**
37
- * Synthetic id base for GeoNames-sourced rows (#743/#193) — above Overture's 8e12 so the three sources (WOF real ids,
38
- * Overture, GeoNames) never collide in a combined DB.
39
- */
40
- export const GEONAMES_ID_BASE = 9_000_000_000_000
41
-
42
36
  /**
43
37
  * The four tables the alias fold writes into the range it owns. The purge and the ingest must agree on this list — a
44
38
  * table written but not purged is exactly the #1514 defect.
@@ -35,6 +35,7 @@
35
35
  */
36
36
 
37
37
  import { pathExists } from "@mailwoman/core/fs/readers"
38
+ import { GEONAMES_POSTAL_ID_BASE } from "@mailwoman/core/resolver/synthetic-id-ranges"
38
39
  import type { DatabaseClient } from "@mailwoman/sqlite/client"
39
40
  import { join, type PathBuilderLike } from "path-ts"
40
41
  import { TSVSpliterator } from "spliterator"
@@ -47,13 +48,6 @@ import type { WOFDatabase } from "#schema"
47
48
  */
48
49
  const GEONAMES_POSTAL_COLUMNS = 11
49
50
 
50
- /**
51
- * Synthetic id base for GeoNames-POSTAL rows — its own namespace above the alias fold's {@link GEONAMES_ID_BASE} (9e12)
52
- * allocation so all four sources (WOF, Overture, GeoNames-alias, GeoNames-postal) coexist collision-free in a combined
53
- * DB.
54
- */
55
- export const GEONAMES_POSTAL_ID_BASE = 9_500_000_000_000
56
-
57
51
  /**
58
52
  * The #920 name law: reduce a postcode to the sanitized-query token shape — strip every non-letter/number — so the
59
53
  * stored name matches what `sanitizeFTSQuery` produces from the parsed postcode token. `"110 00"` → `"11000"`,
package/lib/index.ts CHANGED
@@ -29,25 +29,25 @@ export {
29
29
  createCandidateIntervalTable,
30
30
  intervalContains,
31
31
  MAX_ANCESTOR_DEPTH,
32
- } from "#candidate-ancestors-schema"
32
+ } from "#candidate/ancestors/schema"
33
33
 
34
34
  export type {
35
35
  CandidateAncestorsDatabase,
36
36
  CandidateAncestorTable,
37
37
  CandidateIntervalTable,
38
38
  IntervalLabel,
39
- } from "#candidate-ancestors-schema"
39
+ } from "#candidate/ancestors/schema"
40
40
 
41
- export { CANDIDATE_FTS_TABLE, createCandidateFTS } from "#candidate-fts"
41
+ export { CANDIDATE_FTS_TABLE, createCandidateFTS } from "#candidate/fts"
42
42
 
43
43
  export {
44
44
  ImportanceIndex,
45
45
  IMPORTANCE_JOIN_RADIUS_KM,
46
46
  type ImportanceIndexStats,
47
47
  loadImportanceIndex,
48
- } from "#candidate-importance"
48
+ } from "#candidate/importance"
49
49
 
50
- export { WOFCandidateTableLookup, type WOFCandidateTableLookupOpts } from "#candidate-lookup"
50
+ export { WOFCandidateTableLookup, type WOFCandidateTableLookupOpts } from "#candidate/lookup"
51
51
 
52
52
  export {
53
53
  COUNTRY_BBOX_TABLE,
@@ -61,33 +61,26 @@ export {
61
61
  export type { CountryBBoxTable, CountryCoverageTable, GazetteerCoverageDatabase } from "#coverage-manifest-schema"
62
62
  export { SQLiteStreetNameLookup, type SQLiteStreetNameLookupOpts } from "#street/name-lookup"
63
63
 
64
- export {
65
- GEONAMES_ID_BASE,
66
- type GeonamesIngestProgress,
67
- ingestGeonamesAliases,
68
- purgeGeonamesAliasRange,
69
- } from "#geonames/aliases"
70
-
71
- export { GEONAMES_POSTAL_ID_BASE } from "#geonames/postal"
64
+ export { type GeonamesIngestProgress, ingestGeonamesAliases, purgeGeonamesAliasRange } from "#geonames/aliases"
72
65
 
73
- export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "#address/point-schema"
74
- export type { AddressPointDatabase, AddressPointTable } from "#address/point-schema"
66
+ export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "#address/point/schema"
67
+ export type { AddressPointDatabase, AddressPointTable } from "#address/point/schema"
75
68
 
76
69
  export {
77
70
  WOFPostalCityAliasLookup,
78
71
  type PostalCityAlias,
79
72
  type WOFPostalCityAliasLookupOpts,
80
- } from "#postal/city-alias-lookup"
73
+ } from "#postal/city/alias/lookup"
81
74
 
82
- export type { PostalCityAliasDatabase, PostalCityAliasTable } from "#postal/city-alias-schema"
75
+ export type { PostalCityAliasDatabase, PostalCityAliasTable } from "#postal/city/alias/schema"
83
76
 
84
77
  export {
85
78
  POSTAL_CITY_CANDIDATE_COLUMNS,
86
79
  POSTAL_CITY_CANDIDATE_TABLE,
87
80
  createPostalCityCandidateTable,
88
- } from "#postal/city-candidate-schema"
81
+ } from "#postal/city/candidate-schema"
89
82
 
90
- export type { PostalCityCandidateDatabase, PostalCityCandidateTable } from "#postal/city-candidate-schema"
83
+ export type { PostalCityCandidateDatabase, PostalCityCandidateTable } from "#postal/city/candidate-schema"
91
84
 
92
85
  export {
93
86
  ADDRESS_CONVENTION_TABLE,
@@ -103,7 +96,7 @@ export {
103
96
  type Strategy,
104
97
  } from "#convention/index"
105
98
 
106
- export { SqliteConventionSource } from "#sqlite-convention-source"
99
+ export { SqliteConventionSource } from "#sqlite/convention-source"
107
100
 
108
101
  export { WOFPostcodeLookup, type PostcodePlace } from "#postcode-point-lookup"
109
102
 
@@ -127,17 +120,17 @@ export {
127
120
  type WOFReverseGeocoderOpts,
128
121
  } from "#reverse"
129
122
 
130
- export { AddressPointInterpolator } from "#address/point-interpolation"
131
- export { AddressPointSqliteLookup } from "#address/point"
123
+ export { AddressPointInterpolator } from "#address/point/interpolation"
124
+ export { AddressPointSqliteLookup } from "#address/point/index"
132
125
 
133
126
  export {
134
127
  STREET_CENTROID_COLUMNS,
135
128
  createStreetCentroidIndexes,
136
129
  createStreetCentroidTable,
137
- } from "#street/centroid-schema"
130
+ } from "#street/centroid/schema"
138
131
 
139
- export type { StreetCentroidDatabase, StreetCentroidTable } from "#street/centroid-schema"
140
- export { StreetCentroidSqliteLookup } from "#street/centroid"
132
+ export type { StreetCentroidDatabase, StreetCentroidTable } from "#street/centroid/schema"
133
+ export { StreetCentroidSqliteLookup } from "#street/centroid/index"
141
134
 
142
135
  export {
143
136
  StreetInterpolator,
@@ -27,7 +27,7 @@
27
27
  */
28
28
 
29
29
  import { parseJSONStrict } from "@mailwoman/core/json"
30
- import type { InterpolationLookup } from "@mailwoman/resolver"
30
+ import type { InterpolationLookup } from "@mailwoman/core/resolver"
31
31
  import { clampFraction, haversineKm, pointAlong } from "@mailwoman/spatial"
32
32
  import { DatabaseClient } from "@mailwoman/sqlite/client"
33
33
 
package/lib/lookup.ts CHANGED
@@ -10,13 +10,15 @@
10
10
  * See `docs/plan/phases/PHASE_4_2_wof_sqlite.md` for the design rationale.
11
11
  */
12
12
 
13
- import { expandPlacetypeFilter, type Ancestor, type CoincidentLocality } from "@mailwoman/resolver"
13
+ import { expandPlacetypeFilter } from "@mailwoman/codex/placetype-map"
14
+ import type { Ancestor, CoincidentLocality } from "@mailwoman/core/resolver"
15
+ import { allRows } from "@mailwoman/core/utils"
14
16
  import { haversineKm } from "@mailwoman/spatial"
15
17
  import type { SQLInputValue } from "@mailwoman/sqlite/client"
16
18
  import { DatabaseClient } from "@mailwoman/sqlite/client"
17
19
 
18
20
  import { ancestorLineage } from "#ancestry/index"
19
- import { candidateFromSearchRow, rankCandidates } from "#candidate-scoring"
21
+ import { candidateFromSearchRow, rankCandidates } from "#candidate/scoring"
20
22
  import { loadCoincidentLocalities } from "#coincident-roles"
21
23
  import {
22
24
  ADDRESS_CONVENTION_TABLE,
@@ -46,12 +48,11 @@ import {
46
48
  import { normalizePlacetypes, sanitizeFTSQuery } from "#fts/query"
47
49
  import { cfNormalize, softNameScore } from "#name-score"
48
50
  import { encyclopedicClauses } from "#place-importance-schema"
49
- import type { WOFPostalCityAliasLookup } from "#postal/city-alias-lookup"
51
+ import type { WOFPostalCityAliasLookup } from "#postal/city/alias/lookup"
50
52
  import { DEFAULT_WEIGHTS, populationScaleTerm, type RankingWeights } from "#ranking-weights"
51
53
  import type { WOFDatabase } from "#schema"
52
54
  import { fetchSearchRows, type RawSearchRow } from "#search-fetch"
53
- import { SqliteConventionSource } from "#sqlite-convention-source"
54
- import { allRows } from "#sqlite-utils"
55
+ import { SqliteConventionSource } from "#sqlite/convention-source"
55
56
  import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "#types"
56
57
 
57
58
  export interface WOFSQLitePlaceLookupOpts {