@mailwoman/resolver-wof-sqlite 10.1.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 (212) hide show
  1. package/README.md +1 -0
  2. package/lib/address/point/interpolation.ts +6 -2
  3. package/lib/address/point.ts +15 -14
  4. package/lib/build/candidate.ts +11 -21
  5. package/lib/build/slim.ts +1 -1
  6. package/lib/candidate/importance.ts +1 -1
  7. package/lib/candidate/lookup.ts +47 -34
  8. package/lib/candidate/own-name.ts +1 -1
  9. package/lib/candidate/scoring.ts +29 -29
  10. package/lib/capitals.ts +3 -3
  11. package/lib/coincident-roles.ts +15 -4
  12. package/lib/convention/schema.ts +2 -9
  13. package/lib/convention.ts +7 -7
  14. package/lib/coverage-manifest-schema.ts +6 -6
  15. package/lib/currency-backfill.ts +13 -13
  16. package/lib/exact-match.ts +1 -1
  17. package/lib/extracts.ts +1 -1
  18. package/lib/fst/autocomplete.ts +2 -2
  19. package/lib/fst/builder.ts +9 -7
  20. package/lib/fst/deserialize-web.ts +8 -8
  21. package/lib/fst/freshness.ts +27 -26
  22. package/lib/fst/matcher.ts +5 -3
  23. package/lib/fst/serialize.ts +12 -17
  24. package/lib/fst/types.ts +14 -23
  25. package/lib/fst.ts +1 -0
  26. package/lib/fts.ts +1 -1
  27. package/lib/geonames/aliases.ts +3 -3
  28. package/lib/geonames/postal.ts +1 -1
  29. package/lib/index.ts +3 -2
  30. package/lib/interpolation.ts +7 -6
  31. package/lib/lookup.ts +26 -20
  32. package/lib/nsul/lookup.ts +1 -1
  33. package/lib/place-importance-schema.ts +5 -5
  34. package/lib/poi/lookup.ts +13 -21
  35. package/lib/postcode-point-lookup.ts +1 -10
  36. package/lib/primary-preference.ts +2 -2
  37. package/lib/proximity-rerank.ts +1 -1
  38. package/lib/ranking-weights.ts +15 -10
  39. package/lib/region-keys.ts +2 -2
  40. package/lib/reverse.ts +45 -18
  41. package/lib/schema.ts +3 -12
  42. package/lib/sqlite/convention-source.ts +3 -3
  43. package/lib/sqlite/utils.ts +1 -1
  44. package/lib/street/centroid.ts +1 -1
  45. package/lib/street/morphology/fst/builder.ts +16 -7
  46. package/lib/street/morphology/fst/loader.ts +8 -7
  47. package/lib/street/name-lookup.ts +4 -8
  48. package/lib/street/normalize.ts +3 -3
  49. package/lib/street/segment-schema.ts +2 -2
  50. package/lib/types.ts +17 -12
  51. package/lib/uprn/lookup.ts +1 -1
  52. package/lib/weights-overlay-linker.ts +38 -26
  53. package/out/address/point/interpolation.d.ts +1 -0
  54. package/out/address/point/interpolation.d.ts.map +1 -1
  55. package/out/address/point/interpolation.js +5 -2
  56. package/out/address/point/interpolation.js.map +1 -1
  57. package/out/address/point.d.ts +5 -4
  58. package/out/address/point.d.ts.map +1 -1
  59. package/out/address/point.js +11 -10
  60. package/out/address/point.js.map +1 -1
  61. package/out/build/candidate.d.ts +7 -17
  62. package/out/build/candidate.d.ts.map +1 -1
  63. package/out/build/candidate.js +4 -11
  64. package/out/build/candidate.js.map +1 -1
  65. package/out/build/slim.js +1 -1
  66. package/out/build/slim.js.map +1 -1
  67. package/out/candidate/importance.js +1 -1
  68. package/out/candidate/importance.js.map +1 -1
  69. package/out/candidate/lookup.d.ts +8 -5
  70. package/out/candidate/lookup.d.ts.map +1 -1
  71. package/out/candidate/lookup.js +35 -25
  72. package/out/candidate/lookup.js.map +1 -1
  73. package/out/candidate/own-name.js +1 -1
  74. package/out/candidate/own-name.js.map +1 -1
  75. package/out/candidate/scoring.js +27 -25
  76. package/out/candidate/scoring.js.map +1 -1
  77. package/out/capitals.d.ts +1 -1
  78. package/out/capitals.d.ts.map +1 -1
  79. package/out/capitals.js.map +1 -1
  80. package/out/coincident-roles.d.ts +4 -4
  81. package/out/coincident-roles.d.ts.map +1 -1
  82. package/out/coincident-roles.js +11 -0
  83. package/out/coincident-roles.js.map +1 -1
  84. package/out/convention/schema.d.ts +2 -8
  85. package/out/convention/schema.d.ts.map +1 -1
  86. package/out/convention/schema.js.map +1 -1
  87. package/out/convention.d.ts +4 -4
  88. package/out/convention.d.ts.map +1 -1
  89. package/out/convention.js +3 -3
  90. package/out/convention.js.map +1 -1
  91. package/out/coverage-manifest-schema.d.ts +1 -1
  92. package/out/coverage-manifest-schema.d.ts.map +1 -1
  93. package/out/coverage-manifest-schema.js +5 -5
  94. package/out/coverage-manifest-schema.js.map +1 -1
  95. package/out/currency-backfill.d.ts +6 -6
  96. package/out/currency-backfill.d.ts.map +1 -1
  97. package/out/currency-backfill.js +7 -7
  98. package/out/currency-backfill.js.map +1 -1
  99. package/out/exact-match.js +1 -1
  100. package/out/exact-match.js.map +1 -1
  101. package/out/extracts.d.ts +1 -1
  102. package/out/extracts.d.ts.map +1 -1
  103. package/out/extracts.js.map +1 -1
  104. package/out/fst/autocomplete.d.ts +1 -1
  105. package/out/fst/autocomplete.d.ts.map +1 -1
  106. package/out/fst/autocomplete.js +1 -1
  107. package/out/fst/autocomplete.js.map +1 -1
  108. package/out/fst/builder.d.ts.map +1 -1
  109. package/out/fst/builder.js +9 -7
  110. package/out/fst/builder.js.map +1 -1
  111. package/out/fst/deserialize-web.d.ts +1 -1
  112. package/out/fst/deserialize-web.d.ts.map +1 -1
  113. package/out/fst/deserialize-web.js +7 -7
  114. package/out/fst/deserialize-web.js.map +1 -1
  115. package/out/fst/freshness.d.ts +9 -9
  116. package/out/fst/freshness.d.ts.map +1 -1
  117. package/out/fst/freshness.js +19 -18
  118. package/out/fst/freshness.js.map +1 -1
  119. package/out/fst/matcher.d.ts +4 -3
  120. package/out/fst/matcher.d.ts.map +1 -1
  121. package/out/fst/matcher.js.map +1 -1
  122. package/out/fst/serialize.d.ts +1 -5
  123. package/out/fst/serialize.d.ts.map +1 -1
  124. package/out/fst/serialize.js +11 -15
  125. package/out/fst/serialize.js.map +1 -1
  126. package/out/fst/types.d.ts +14 -22
  127. package/out/fst/types.d.ts.map +1 -1
  128. package/out/fst.d.ts +1 -0
  129. package/out/fst.d.ts.map +1 -1
  130. package/out/fst.js +1 -0
  131. package/out/fst.js.map +1 -1
  132. package/out/fts.js +1 -1
  133. package/out/fts.js.map +1 -1
  134. package/out/geonames/aliases.d.ts +1 -1
  135. package/out/geonames/aliases.d.ts.map +1 -1
  136. package/out/geonames/aliases.js +2 -2
  137. package/out/geonames/aliases.js.map +1 -1
  138. package/out/geonames/postal.js +1 -1
  139. package/out/geonames/postal.js.map +1 -1
  140. package/out/index.d.ts +3 -2
  141. package/out/index.d.ts.map +1 -1
  142. package/out/index.js +1 -0
  143. package/out/index.js.map +1 -1
  144. package/out/interpolation.d.ts +4 -4
  145. package/out/interpolation.d.ts.map +1 -1
  146. package/out/interpolation.js +4 -3
  147. package/out/interpolation.js.map +1 -1
  148. package/out/lookup.d.ts +4 -3
  149. package/out/lookup.d.ts.map +1 -1
  150. package/out/lookup.js +22 -14
  151. package/out/lookup.js.map +1 -1
  152. package/out/nsul/lookup.js.map +1 -1
  153. package/out/place-importance-schema.d.ts +2 -2
  154. package/out/place-importance-schema.d.ts.map +1 -1
  155. package/out/place-importance-schema.js +3 -3
  156. package/out/place-importance-schema.js.map +1 -1
  157. package/out/poi/lookup.d.ts +10 -13
  158. package/out/poi/lookup.d.ts.map +1 -1
  159. package/out/poi/lookup.js +3 -7
  160. package/out/poi/lookup.js.map +1 -1
  161. package/out/postcode-point-lookup.d.ts +1 -9
  162. package/out/postcode-point-lookup.d.ts.map +1 -1
  163. package/out/postcode-point-lookup.js.map +1 -1
  164. package/out/primary-preference.js +2 -2
  165. package/out/primary-preference.js.map +1 -1
  166. package/out/proximity-rerank.d.ts +1 -1
  167. package/out/proximity-rerank.d.ts.map +1 -1
  168. package/out/ranking-weights.d.ts +15 -10
  169. package/out/ranking-weights.d.ts.map +1 -1
  170. package/out/ranking-weights.js.map +1 -1
  171. package/out/region-keys.d.ts +2 -2
  172. package/out/region-keys.d.ts.map +1 -1
  173. package/out/region-keys.js.map +1 -1
  174. package/out/reverse.d.ts +7 -4
  175. package/out/reverse.d.ts.map +1 -1
  176. package/out/reverse.js +38 -13
  177. package/out/reverse.js.map +1 -1
  178. package/out/schema.d.ts +2 -11
  179. package/out/schema.d.ts.map +1 -1
  180. package/out/sqlite/convention-source.d.ts +1 -1
  181. package/out/sqlite/convention-source.d.ts.map +1 -1
  182. package/out/sqlite/convention-source.js +2 -2
  183. package/out/sqlite/convention-source.js.map +1 -1
  184. package/out/sqlite/utils.d.ts +1 -1
  185. package/out/sqlite/utils.d.ts.map +1 -1
  186. package/out/street/centroid.js +1 -1
  187. package/out/street/centroid.js.map +1 -1
  188. package/out/street/morphology/fst/builder.d.ts +3 -3
  189. package/out/street/morphology/fst/builder.d.ts.map +1 -1
  190. package/out/street/morphology/fst/builder.js +13 -4
  191. package/out/street/morphology/fst/builder.js.map +1 -1
  192. package/out/street/morphology/fst/loader.d.ts +4 -4
  193. package/out/street/morphology/fst/loader.d.ts.map +1 -1
  194. package/out/street/morphology/fst/loader.js +4 -3
  195. package/out/street/morphology/fst/loader.js.map +1 -1
  196. package/out/street/name-lookup.d.ts +2 -2
  197. package/out/street/name-lookup.d.ts.map +1 -1
  198. package/out/street/name-lookup.js +2 -4
  199. package/out/street/name-lookup.js.map +1 -1
  200. package/out/street/normalize.js +3 -3
  201. package/out/street/normalize.js.map +1 -1
  202. package/out/street/segment-schema.d.ts +2 -2
  203. package/out/street/segment-schema.js +2 -2
  204. package/out/types.d.ts +16 -12
  205. package/out/types.d.ts.map +1 -1
  206. package/out/uprn/lookup.js +1 -1
  207. package/out/uprn/lookup.js.map +1 -1
  208. package/out/weights-overlay-linker.d.ts +9 -6
  209. package/out/weights-overlay-linker.d.ts.map +1 -1
  210. package/out/weights-overlay-linker.js +21 -7
  211. package/out/weights-overlay-linker.js.map +1 -1
  212. package/package.json +13 -13
@@ -24,7 +24,8 @@
24
24
  * `fst-street-morphology.bin`.
25
25
  */
26
26
 
27
- import { isDirectory, isFile, readLocalTextFile } from "@mailwoman/core/fs/readers"
27
+ import { readLocalTextFile } from "@mailwoman/core/fs/readers"
28
+ import { isDirectory, isFile } from "@mailwoman/core/fs/readers/stat"
28
29
  import { PathBuilder, type PathBuilderLike } from "path-ts"
29
30
  import { TextSpliterator } from "spliterator"
30
31
  import { Globerator } from "spliterator/node/fs"
@@ -52,14 +53,12 @@ export interface BuildStreetMorphologyFSTOpts {
52
53
  /**
53
54
  * Optional locale filter — only ingest these locale subfolders.
54
55
  *
55
- * Defaults to all that have a `street_types.txt`.
56
+ * @defaultValue all that have a `street_types.txt`.
56
57
  */
57
58
  locales?: string[]
58
59
  /**
59
60
  * Minimum length (in characters, post-normalization) of variant surface forms to insert into the trie.
60
61
  *
61
- * Defaults to 3.
62
- *
63
62
  * Rationale: libpostal's street_types dictionaries contain 1-2 character abbreviations
64
63
  * (`a`, `b`, `av`, `bd`, `br`, ...) that collide with non-affix tokens at parse time,
65
64
  * including US state abbreviations (`or`, `CA`, `ND`, `NY`).
@@ -69,6 +68,8 @@ export interface BuildStreetMorphologyFSTOpts {
69
68
  * abbreviations as `street_suffix`.
70
69
  *
71
70
  * A minimum length of 3 retains useful forms (`ave`, `blvd`, `rue`, `str`) while filtering out the noise.
71
+ *
72
+ * @defaultValue `3`
72
73
  */
73
74
  minVariantLength?: number
74
75
  /**
@@ -97,7 +98,7 @@ export interface BuildStreetMorphologyFSTResult {
97
98
  function parseLine(line: string): { canonical: string; variants: string[] } | null {
98
99
  const trimmed = line.trim()
99
100
 
100
- if (!trimmed.length || trimmed.startsWith("#")) return null
101
+ if (!trimmed || trimmed.startsWith("#")) return null
101
102
 
102
103
  const parts = TextSpliterator.from(trimmed, { delimiter: "|" }).toArray()
103
104
 
@@ -223,11 +224,12 @@ export async function buildStreetMorphologyFST(
223
224
  // (Avenue is almost never anything but street-typing).
224
225
  // The morphology prior caps bias separately.
225
226
  // This value just feeds the cap formula `referential * cap`.
226
- // No encyclopedic field — a street affix is not a place and has no article.
227
- // Absence here is the correct statement.
227
+ // No encyclopedic score — a street affix is not a place and has no article.
228
228
  referential: 1,
229
+ encyclopedic: null,
229
230
  lat: 0,
230
231
  lon: 0,
232
+ crossCountryBranches: null,
231
233
  }
232
234
 
233
235
  for (const variant of variants) {
@@ -260,7 +262,14 @@ export async function buildStreetMorphologyFST(
260
262
  edgeCount,
261
263
  nameInsertions: insertCount,
262
264
  importanceMatches: 0, // No importance scoring for morphology — fixed at 1.0.
265
+ encyclopedicMatches: null,
266
+ importanceSource: null,
263
267
  sourceDB: dictionariesDir.toString(),
268
+ sourceDBMD5: null,
269
+ sourceDBBytes: null,
270
+ modelCardVersion: null,
271
+ exclusionPolicy: null,
272
+ excludedInsertions: null,
264
273
  }
265
274
 
266
275
  return {
@@ -19,7 +19,8 @@
19
19
  * artifact through `fst-deserialize-web.ts` (see the docs demo loader).
20
20
  */
21
21
 
22
- import { pathExists, readLocalBuffer } from "@mailwoman/core/fs/readers"
22
+ import { readLocalBuffer } from "@mailwoman/core/fs/readers"
23
+ import { pathExists } from "@mailwoman/core/fs/readers/stat"
23
24
  import { resourceDictionaryPath } from "@mailwoman/core/paths"
24
25
  import type { PathBuilder, PathBuilderLike } from "path-ts"
25
26
 
@@ -53,13 +54,13 @@ export interface LoadStreetMorphologyFSTOpts {
53
54
  /**
54
55
  * Dictionaries dir for the build fallback.
55
56
  *
56
- * Defaults to core's bundled libpostal dictionaries.
57
+ * @defaultValue core's bundled libpostal dictionaries.
57
58
  */
58
59
  dictionariesDir?: PathBuilderLike
59
60
  /**
60
61
  * Unreadable-artifact diagnostics.
61
62
  *
62
- * Defaults to silent (the caller owns its warn channel).
63
+ * @defaultValue silent (the caller owns its warn channel).
63
64
  */
64
65
  onWarn?: (message: string) => void
65
66
  }
@@ -73,11 +74,11 @@ export interface LoadedStreetMorphologyFST {
73
74
  /**
74
75
  * The artifact path when `source === "artifact"`.
75
76
  */
76
- path?: PathBuilderLike
77
+ path: PathBuilderLike | null
77
78
  /**
78
79
  * Build provenance, read from the artifact trailer or copied from the fallback build.
79
80
  */
80
- provenance?: FSTProvenance
81
+ provenance: FSTProvenance | null
81
82
  }
82
83
 
83
84
  /**
@@ -96,7 +97,7 @@ export async function loadStreetMorphologyFST(
96
97
  const matcher = deserializeFST(buf)
97
98
  const provenance = readFSTProvenance(buf)
98
99
 
99
- return { matcher, source: "artifact", path: artifactPath, ...(provenance ? { provenance } : {}) }
100
+ return { matcher, source: "artifact", path: artifactPath, provenance }
100
101
  } catch (error) {
101
102
  warn(
102
103
  `street-morphology artifact at ${artifactPath} unreadable (${(error as Error).message}) — falling back to the dictionary build`
@@ -108,5 +109,5 @@ export async function loadStreetMorphologyFST(
108
109
  dictionariesDir: (opts.dictionariesDir ?? resourceDictionaryPath("libpostal")).toString(),
109
110
  })
110
111
 
111
- return { matcher: built.matcher, source: "built", provenance: built.provenance }
112
+ return { matcher: built.matcher, source: "built", path: null, provenance: built.provenance }
112
113
  }
@@ -29,13 +29,13 @@ export interface SQLiteStreetNameLookupOpts {
29
29
  /**
30
30
  * ISO-2 (upper-case) countries this index answers for.
31
31
  *
32
- * Default `["FR"]` (the BAN street-centroids instance).
32
+ * @defaultValue `["FR"]` (the BAN street-centroids instance).
33
33
  */
34
34
  countries?: Iterable<string>
35
35
  /**
36
36
  * Table name.
37
37
  *
38
- * Default `street_centroid`.
38
+ * @defaultValue `street_centroid`
39
39
  */
40
40
  table?: string
41
41
  }
@@ -88,15 +88,11 @@ export class SQLiteStreetNameLookup extends SQLiteLookup<WOFDatabase> implements
88
88
  // Scoped lookups tighten precision when the hypothesis includes a locality/postcode.
89
89
  // A scoped miss falls back to the unscoped probe.
90
90
  // The scope column may be incomplete, so a miss there does not establish absence.
91
- if (
92
- scope?.locality &&
93
- this.#byNameLocality &&
94
- this.#byNameLocality.get(norm, foldStreetSurface(scope.locality)) !== undefined
95
- ) {
91
+ if (scope?.locality && this.#byNameLocality && this.#byNameLocality.get(norm, foldStreetSurface(scope.locality))) {
96
92
  return true
97
93
  }
98
94
 
99
- if (scope?.postcode && this.#byNamePostcode && this.#byNamePostcode.get(norm, scope.postcode) !== undefined) {
95
+ if (scope?.postcode && this.#byNamePostcode && this.#byNamePostcode.get(norm, scope.postcode)) {
100
96
  return true
101
97
  }
102
98
 
@@ -377,7 +377,7 @@ export function stripLocalityQualifier(locality: string): string {
377
377
  }
378
378
 
379
379
  const words = [...s.matchAll(/\S+/gu)].map((match) => ({ value: match[0], index: match.index }))
380
- let qualifierStart: number | undefined
380
+ let qualifierStart: number | null = null
381
381
 
382
382
  for (let index = 1; index < words.length; index++) {
383
383
  const word = words[index]!
@@ -400,7 +400,7 @@ export function stripLocalityQualifier(locality: string): string {
400
400
  const suffix = words.at(-1)
401
401
 
402
402
  if (
403
- qualifierStart === undefined &&
403
+ qualifierStart === null &&
404
404
  suffix &&
405
405
  words.length > 1 &&
406
406
  (["S", "N", "E", "W", "V", "Ø", "Sø", "Fyn", "Thy", "Sjælland", "Jylland"].includes(suffix.value) ||
@@ -409,7 +409,7 @@ export function stripLocalityQualifier(locality: string): string {
409
409
  qualifierStart = suffix.index
410
410
  }
411
411
 
412
- if (qualifierStart !== undefined) {
412
+ if (qualifierStart !== null) {
413
413
  s = s.slice(0, qualifierStart).trimEnd()
414
414
  }
415
415
 
@@ -214,7 +214,7 @@ const STREET_SEGMENT_CLUSTER_ORDER = "street_norm, postcode, min_hn, max_hn, row
214
214
  *
215
215
  * `street_segment` stays a rowid table, because `postcode` is nullable.
216
216
  * A `WITHOUT ROWID` primary key forbids a null column.
217
- * Loading in key order gives the same page locality.
217
+ * A load in key order gives the same page locality.
218
218
  *
219
219
  * Run {@link createStreetSegmentIndexes} and `VACUUM` afterwards.
220
220
  */
@@ -233,7 +233,7 @@ export async function clusterStreetSegments(db: Kysely<StreetSegmentDatabase>):
233
233
  /**
234
234
  * Create the two probe indexes the readers use (postcode-scope, street-scope).
235
235
  *
236
- * Each index carries `max_hn`.
236
+ * Each index holds `max_hn`.
237
237
  * SQLite therefore rejects a range ending below the house number from the index entry.
238
238
  * The rejected row's table page is never fetched.
239
239
  */
package/lib/types.ts CHANGED
@@ -45,69 +45,74 @@ export interface PlaceCandidate {
45
45
  * The place's depth-1 ancestor id from the ancestors sidecar, absent when the
46
46
  * artifact has no sidecar even for a place with a parent.
47
47
  */
48
- parent_id?: number
48
+ parent_id: number | null
49
49
  score: number
50
- distanceKm?: number
50
+ distanceKm: number | null
51
51
 
52
52
  /**
53
53
  * Whether the candidate's name or an alias exactly equals the query, used by a
54
54
  * later re-rank to stay within the exact-match tier.
55
55
  */
56
- exactMatch?: boolean
56
+ exactMatch: boolean | null
57
57
 
58
58
  /**
59
59
  * The population term plus the best proximity-bias term.
60
60
  *
61
61
  * The exact tier sorts by this value instead of population when the query has `near` or `bias`.
62
62
  */
63
- prominence?: number
63
+ prominence: number | null
64
64
 
65
65
  /**
66
66
  * The WOF `wof:population` value, absent when the population is unknown.
67
67
  */
68
- population?: number
68
+ population: number | null
69
69
 
70
70
  /**
71
71
  * The referential likelihood in [0, 1], derived from {@link PlaceCandidate.population}
72
72
  * and absent whenever it is.
73
73
  */
74
- referential?: number
74
+ referential: number | null
75
75
 
76
76
  /**
77
77
  * The encyclopedia-evidence importance in [0, 1], used for display only and present only
78
78
  * when the extract's `place_importance` table has the split columns.
79
79
  */
80
- encyclopedic?: number
80
+ encyclopedic: number | null
81
81
 
82
82
  /**
83
83
  * The blended toponym prior in [0, 1] that `rankByImportance` reads for bare toponyms,
84
84
  * set only by the candidate-table backend.
85
85
  */
86
- importance?: number
86
+ importance: number | null
87
87
 
88
88
  /**
89
89
  * The bounding box from the WOF `spr` extent columns, omitted when the schema lacks them.
90
90
  */
91
- bbox?: LatLonBounds
91
+ bbox: LatLonBounds | null
92
92
 
93
93
  /**
94
94
  * Set by the postcode path when the chosen locality is far from the postcode's own
95
95
  * locality. the candidate is still returned so callers can lower their confidence.
96
96
  */
97
- mismatch?: boolean
97
+ mismatch: boolean | null
98
98
 
99
99
  /**
100
100
  * Whether the ancestors sidecar places this candidate under the query's
101
101
  * {@link FindPlaceQuery.regionQualifier}, with `false` meaning the check ran
102
102
  * and an absent value meaning it did not.
103
103
  */
104
- containedByQualifier?: boolean
104
+ containedByQualifier: boolean | null
105
+
106
+ /**
107
+ * Set when the candidate lies outside the query's parent scope, so callers can demote it.
108
+ */
109
+ regionScopeMiss: boolean | null
105
110
 
106
111
  /**
107
112
  * Set when the variant exemption spared the candidate from the cross-country
108
113
  * alias penalty, by the candidate-table backend only.
109
114
  */
110
- variantAliasExempted?: true
115
+ variantAliasExempted: true | null
111
116
  }
112
117
 
113
118
  /**
@@ -150,7 +150,7 @@ export class UPRNLookup implements Disposable {
150
150
  for (const row of rows) {
151
151
  const distanceM = haversineKm(latitude, longitude, row.lat, row.lon) * 1000
152
152
 
153
- if (distanceM <= radiusM && (best === null || distanceM < best.distanceM)) {
153
+ if (distanceM <= radiusM && (!best || distanceM < best.distanceM)) {
154
154
  best = { uprn: row.uprn, latitude: row.lat, longitude: row.lon, distanceM }
155
155
  }
156
156
  }
@@ -5,14 +5,8 @@
5
5
  */
6
6
 
7
7
  import { dataRootPath, weightsOverlayPath } from "@mailwoman/core/data-root"
8
- import {
9
- pathExists,
10
- readLocalJSONFile,
11
- readLocalTextFile,
12
- statPath,
13
- readLocalBuffer,
14
- statLink,
15
- } from "@mailwoman/core/fs/readers"
8
+ import { readLocalJSONFile, readLocalTextFile, readLocalBuffer } from "@mailwoman/core/fs/readers"
9
+ import { pathExists, statPath, statLink } from "@mailwoman/core/fs/readers/stat"
16
10
  import {
17
11
  createSymbolicLink,
18
12
  makeDirectories,
@@ -140,24 +134,27 @@ export interface PairIndexOverlay {
140
134
 
141
135
  /**
142
136
  * The source files whose MD5s the build records, in the order that `gazetteer pair-index` records them.
143
- * The default is the WOF admin database.
144
137
  *
145
138
  * The freshness check compares each MD5 with the header.
146
139
  * An empty list makes the check compare the magnitudes only.
140
+ *
141
+ * @defaultValue the WOF admin database.
147
142
  */
148
143
  sources?: PathBuilder[]
149
144
 
150
145
  /**
151
146
  * Files that must exist before a build runs.
152
147
  *
153
- * The default is `sources`.
154
148
  * A missing file prints a warning and skips the build.
149
+ *
150
+ * @defaultValue `sources`
155
151
  */
156
152
  inputs?: PathBuilder[]
157
153
 
158
154
  /**
159
155
  * Extra CLI arguments that pass the build's sources.
160
- * The default is `--borough-db <admin db>`.
156
+ *
157
+ * @defaultValue `--borough-db <admin db>`
161
158
  */
162
159
  extraArgs?: PathBuilderLike[]
163
160
 
@@ -268,10 +265,7 @@ export interface PairIndexCalibration {
268
265
  *
269
266
  * The caller checks the source MD5s because only the caller knows its sources.
270
267
  */
271
- export function pairIndexStaleReason(
272
- header: PairIndexHeaderFields,
273
- expected: PairIndexCalibration
274
- ): string | undefined {
268
+ export function pairIndexStaleReason(header: PairIndexHeaderFields, expected: PairIndexCalibration): string | null {
275
269
  if (header.schemaVersion !== REQUIRED_PAIR_INDEX_SCHEMA) {
276
270
  return `schemaVersion ${header.schemaVersion} → ${REQUIRED_PAIR_INDEX_SCHEMA}`
277
271
  }
@@ -286,7 +280,7 @@ export function pairIndexStaleReason(
286
280
  return `parentDelta ${header.parentDelta ?? "(absent)"} → ${expected.parentDelta ?? "(absent)"}`
287
281
  }
288
282
 
289
- return undefined
283
+ return null
290
284
  }
291
285
 
292
286
  /**
@@ -518,15 +512,15 @@ export interface SoftFeedLink {
518
512
  export async function committedSoftFeedLinks(): Promise<{
519
513
  anchor: SoftFeedLink
520
514
  country: SoftFeedLink
521
- streetType?: SoftFeedLink
515
+ streetType: SoftFeedLink | null
522
516
  }> {
523
517
  const config = await readReleaseConfig()
524
518
  const sources = repoCommittedSoftFeedSources(repoRootPath(), config.softFeed ?? {})
525
519
 
526
- const link = (name: string, consequenceIfMissing: string): SoftFeedLink | undefined => {
520
+ const link = (name: string, consequenceIfMissing: string): SoftFeedLink | null => {
527
521
  const source = sources.get(name)
528
522
 
529
- return source ? { source, name, consequenceIfMissing } : undefined
523
+ return source ? { source, name, consequenceIfMissing } : null
530
524
  }
531
525
 
532
526
  const anchor = link("anchor-lexicon-v1.json", "gazetteer channel will resolve OFF in this worktree.")
@@ -540,7 +534,7 @@ export async function committedSoftFeedLinks(): Promise<{
540
534
 
541
535
  const streetType = link("street-type-lexicon-v3.json", "the street_type channel will resolve OFF in this worktree.")
542
536
 
543
- return { anchor, country, ...(streetType ? { streetType } : {}) }
537
+ return { anchor, country, streetType }
544
538
  }
545
539
 
546
540
  /**
@@ -631,7 +625,7 @@ export interface DevOverlayManifest {
631
625
  export interface DevOverlay {
632
626
  destDir: PathBuilder
633
627
  cli: PathBuilder
634
- card: WeightsCard | undefined
628
+ card: WeightsCard | null
635
629
  }
636
630
 
637
631
  const EVIDENCE_LEXICON_SOURCES: ReadonlyArray<{
@@ -642,10 +636,23 @@ const EVIDENCE_LEXICON_SOURCES: ReadonlyArray<{
642
636
  { channel: "locality_surface", source: (name) => dataRootPath("gazetteer", name) },
643
637
  ]
644
638
 
645
- async function readWeightsCard(workspace: string): Promise<WeightsCard | undefined> {
639
+ /**
640
+ * Whether the locale's workspace package declares `mailwoman.baseWeights`.
641
+ */
642
+ async function declaresBaseWeights(locale: string): Promise<boolean> {
643
+ const path = workspacePathBuilder(`neural-weights-${locale}`, "package.json")
644
+
645
+ if (!(await pathExists(path))) return false
646
+
647
+ const { mailwoman } = await readLocalJSONFile<{ mailwoman?: { baseWeights?: unknown } }>(path)
648
+
649
+ return typeof mailwoman?.baseWeights === "string" && mailwoman.baseWeights.length > 0
650
+ }
651
+
652
+ async function readWeightsCard(workspace: string): Promise<WeightsCard | null> {
646
653
  const path = workspacePathBuilder(workspace, "model-card.json")
647
654
 
648
- if (!(await pathExists(path))) return undefined
655
+ if (!(await pathExists(path))) return null
649
656
 
650
657
  return readLocalJSONFile<WeightsCard>(path)
651
658
  }
@@ -680,7 +687,7 @@ async function linkCharModel(destDir: PathBuilder, family: string): Promise<void
680
687
  await removeIfPresent(destDir("tokenizer.model"))
681
688
  }
682
689
 
683
- async function linkBaseModelPair(destDir: PathBuilder, digestCard: string | undefined): Promise<void> {
690
+ async function linkBaseModelPair(destDir: PathBuilder, digestCard: string | null): Promise<void> {
684
691
  const recipe = await readReleaseConfig()
685
692
 
686
693
  const dataRoot = dataRootPath()
@@ -800,11 +807,16 @@ export async function materializeDevOverlay(manifest: DevOverlayManifest): Promi
800
807
 
801
808
  await makeDirectories(destDir)
802
809
 
803
- if (manifest.model?.kind === "inherit") {
810
+ // A package that declares `mailwoman.baseWeights` inherits its graph whatever the manifest says,
811
+ // because `resolveWeights` takes the base's graph over a file in the overlay directory.
812
+ const inherits =
813
+ manifest.model?.kind === "inherit" || (!manifest.model && (await declaresBaseWeights(manifest.locale)))
814
+
815
+ if (inherits) {
804
816
  await removeIfPresent(destDir("model.onnx"))
805
817
  await removeIfPresent(destDir("tokenizer.model"))
806
818
  } else if (manifest.model?.kind === "link") {
807
- await linkBaseModelPair(destDir, manifest.model.digestCard)
819
+ await linkBaseModelPair(destDir, manifest.model.digestCard ?? null)
808
820
  } else if (manifest.model?.kind === "char") {
809
821
  await linkCharModel(destDir, manifest.model.family)
810
822
  }
@@ -39,6 +39,7 @@ export declare class AddressPointInterpolator<DB extends AddressPointDatabase =
39
39
  database?: DatabaseClient<DB>;
40
40
  fallback?: StreetInterpolator;
41
41
  });
42
+ readonly radiusCalibration: null;
42
43
  find(query: InterpolationQuery): InterpolatedHit | null;
43
44
  [Symbol.dispose](): void;
44
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"interpolation.d.ts","sourceRoot":"","sources":["../../../lib/address/point/interpolation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AA8B7F,qBAAa,wBAAwB,CACpC,EAAE,SAAS,oBAAoB,GAAG,oBAAoB,CACrD,YAAW,mBAAmB;;IAY/B,YAAY,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;KAAE,EA2BlG;IAED,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,eAAe,GAAG,IAAI,CAsBtD;IAED,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAEvB;CACD"}
1
+ {"version":3,"file":"interpolation.d.ts","sourceRoot":"","sources":["../../../lib/address/point/interpolation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AA8B7F,qBAAa,wBAAwB,CACpC,EAAE,SAAS,oBAAoB,GAAG,oBAAoB,CACrD,YAAW,mBAAmB;;IAY/B,YAAY,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;KAAE,EA2BlG;IAED,QAAQ,CAAC,iBAAiB,OAAO;IAEjC,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,eAAe,GAAG,IAAI,CAsBtD;IAED,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAEvB;CACD"}
@@ -73,6 +73,7 @@ export class AddressPointInterpolator {
73
73
  }
74
74
  this.#resources = resources.move();
75
75
  }
76
+ radiusCalibration = null;
76
77
  find(query) {
77
78
  const numberRaw = query.number.trim();
78
79
  if (!/^\d+$/.test(numberRaw))
@@ -123,8 +124,8 @@ function interpolateFromNeighbors(rows, n) {
123
124
  const anchors = anchorsByNumber(rows);
124
125
  // Nearest known number below and above the query.
125
126
  // The rows never contain n itself.
126
- let below;
127
- let above;
127
+ let below = null;
128
+ let above = null;
128
129
  for (const anchor of anchors) {
129
130
  if (anchor.n < n) {
130
131
  below = anchor;
@@ -142,6 +143,7 @@ function interpolateFromNeighbors(rows, n) {
142
143
  lon: below.lon + (above.lon - below.lon) * t,
143
144
  interpolated: true,
144
145
  method: "address_point",
146
+ parityMatched: null,
145
147
  bracket: "both",
146
148
  uncertaintyM: Math.round(spanM / 2),
147
149
  source: below.source,
@@ -166,6 +168,7 @@ function interpolateFromNeighbors(rows, n) {
166
168
  lon,
167
169
  interpolated: true,
168
170
  method: "address_point",
171
+ parityMatched: null,
169
172
  bracket: "single",
170
173
  // Explicitly larger than the both-sided radius: the whole pair span plus the overshoot.
171
174
  uncertaintyM: Math.round(pairM + overshootM),
@@ -1 +1 @@
1
- {"version":3,"file":"interpolation.js","sourceRoot":"","sources":["../../../lib/address/point/interpolation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interpolation.js","sourceRoot":"","sources":["../../../lib/address/point/interpolation.ts"],"names":[],"mappings":"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"}
@@ -25,13 +25,14 @@ export declare class AddressPointSqliteLookup<DB extends AddressPointDatabase =
25
25
  * @param dbPath Extract path.
26
26
  * @param opts.streetLocale The street-normalization locale the extract was built with.
27
27
  * A mismatch makes every key miss.
28
- * It defaults to `"us"`.
29
28
  * @param opts.localityKeys Whether the extract's `locality_norm` holds full place names.
30
29
  * With `"abbreviated"`, a row's locality can select a row but never rejects one.
31
30
  * The US situs extract uses NAD city fields.
32
31
  * Some counties abbreviate them (`addi` for Addison).
33
32
  * Other counties replace them with the parent town.
34
- * It defaults to `"abbreviated"` for `"us"` and `"full"` for every other locale.
33
+ *
34
+ * @defaultValue `"us"`
35
+ * @defaultValue `"abbreviated"` for `"us"` and `"full"` for every other locale.
35
36
  */
36
37
  constructor(dbPath: PathBuilderLike, opts?: {
37
38
  streetLocale?: StreetLocale;
@@ -44,12 +45,12 @@ export declare class AddressPointSqliteLookup<DB extends AddressPointDatabase =
44
45
  locality?: string;
45
46
  region?: string;
46
47
  subregion?: string;
47
- bbox?: {
48
+ bbox: {
48
49
  minLat: number;
49
50
  maxLat: number;
50
51
  minLon: number;
51
52
  maxLon: number;
52
- };
53
+ } | null;
53
54
  }): AddressPointHit | null;
54
55
  [Symbol.dispose](): void;
55
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../lib/address/point.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAEnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,oBAAoB,EAAqB,MAAM,uBAAuB,CAAA;AAEpF,OAAO,EASN,KAAK,YAAY,EACjB,MAAM,mBAAmB,CAAA;AAc1B;;;;;;;;;;GAUG;AACH,qBAAa,wBAAwB,CAAC,EAAE,SAAS,oBAAoB,GAAG,oBAAoB,CAC3F,YAAW,kBAAkB,EAAE,UAAU;;IAyBzC;;;;;;;;;;;OAWG;IACH,YACC,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE;QAAE,YAAY,CAAC,EAAE,YAAY,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;KAAO,EAsCjF;IAED,IAAI,CAAC,KAAK,EAAE;QACX,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,IAAI,CAAC,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KACzE,GAAG,eAAe,GAAG,IAAI,CAsCzB;IAyID,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAEvB;CACD"}
1
+ {"version":3,"file":"point.d.ts","sourceRoot":"","sources":["../../lib/address/point.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAEnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,KAAK,EAAE,oBAAoB,EAAqB,MAAM,uBAAuB,CAAA;AAEpF,OAAO,EASN,KAAK,YAAY,EACjB,MAAM,mBAAmB,CAAA;AAc1B;;;;;;;;;;GAUG;AACH,qBAAa,wBAAwB,CAAC,EAAE,SAAS,oBAAoB,GAAG,oBAAoB,CAC3F,YAAW,kBAAkB,EAAE,UAAU;;IAyBzC;;;;;;;;;;;;OAYG;IACH,YACC,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE;QAAE,YAAY,CAAC,EAAE,YAAY,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;KAAO,EAsCjF;IAED,IAAI,CAAC,KAAK,EAAE;QACX,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;KAC/E,GAAG,eAAe,GAAG,IAAI,CAsCzB;IAyID,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAEvB;CACD"}
@@ -41,13 +41,14 @@ export class AddressPointSqliteLookup {
41
41
  * @param dbPath Extract path.
42
42
  * @param opts.streetLocale The street-normalization locale the extract was built with.
43
43
  * A mismatch makes every key miss.
44
- * It defaults to `"us"`.
45
44
  * @param opts.localityKeys Whether the extract's `locality_norm` holds full place names.
46
45
  * With `"abbreviated"`, a row's locality can select a row but never rejects one.
47
46
  * The US situs extract uses NAD city fields.
48
47
  * Some counties abbreviate them (`addi` for Addison).
49
48
  * Other counties replace them with the parent town.
50
- * It defaults to `"abbreviated"` for `"us"` and `"full"` for every other locale.
49
+ *
50
+ * @defaultValue `"us"`
51
+ * @defaultValue `"abbreviated"` for `"us"` and `"full"` for every other locale.
51
52
  */
52
53
  constructor(dbPath, opts = {}) {
53
54
  this.#db = new DatabaseClient(dbPath, { readOnly: true });
@@ -85,7 +86,7 @@ export class AddressPointSqliteLookup {
85
86
  // `streetKeyVariants` yields the literal key first.
86
87
  // Each variant runs the full number fallback sequence.
87
88
  // The first variant with a match wins.
88
- let row;
89
+ let row = null;
89
90
  for (const streetNorm of streetKeyVariants(query.street, streetLocaleForSurface(query.street, this.#locale))) {
90
91
  row = this.#findForKey(streetNorm, number, scoped);
91
92
  if (row)
@@ -98,8 +99,8 @@ export class AddressPointSqliteLookup {
98
99
  lon: row.lon,
99
100
  source: row.source,
100
101
  release: row.release,
101
- ...(row.locality_norm ? { localityNorm: row.locality_norm } : {}),
102
- ...(row.postcode ? { postcode: row.postcode } : {}),
102
+ localityNorm: row.locality_norm || null,
103
+ postcode: row.postcode || null,
103
104
  };
104
105
  }
105
106
  /**
@@ -145,16 +146,16 @@ export class AddressPointSqliteLookup {
145
146
  * Each scope runs only when the previous ones missed.
146
147
  */
147
148
  #probe(streetNorm, number, query) {
148
- let row;
149
+ let row = null;
149
150
  if (query.postcode) {
150
151
  const postcode = query.postcode.trim();
151
- const localityKey = query.locality ? this.#localityKey(query.locality) : undefined;
152
+ const localityKey = query.locality ? this.#localityKey(query.locality) : null;
152
153
  // One postcode can cover several places with the same street and number,
153
154
  // so a row whose locality agrees with the query is tried first.
154
155
  // The postcode-only row answers only if `#scopeContradicts` accepts it.
155
- const agreeing = localityKey ? this.#byPostcodeLocality(postcode, localityKey, streetNorm, number) : undefined;
156
+ const agreeing = localityKey ? this.#byPostcodeLocality(postcode, localityKey, streetNorm, number) : null;
156
157
  const candidate = agreeing ?? this.#byPostcode(postcode, streetNorm, number);
157
- row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : undefined;
158
+ row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : null;
158
159
  }
159
160
  if (!row && query.locality) {
160
161
  row = this.#byLocality(this.#localityKey(query.locality), streetNorm, number);
@@ -168,7 +169,7 @@ export class AddressPointSqliteLookup {
168
169
  const b = query.bbox;
169
170
  const candidate = this.#byBbox(streetNorm, number, b.minLat, b.maxLat, b.minLon, b.maxLon);
170
171
  // A row with its own scope that the scoped probes missed is a different address in the same box.
171
- row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : undefined;
172
+ row = candidate && !this.#scopeContradicts(candidate, query) ? candidate : null;
172
173
  }
173
174
  return row;
174
175
  }