@mailwoman/resolver-wof-sqlite 8.0.0 → 8.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 (219) hide show
  1. package/address-point-interpolation.ts +9 -3
  2. package/address-point-schema.ts +32 -10
  3. package/address-point.ts +3 -0
  4. package/ancestry-backfill.ts +18 -5
  5. package/ancestry.ts +7 -2
  6. package/build-candidate.ts +29 -6
  7. package/build-slim.ts +40 -11
  8. package/candidate-fts.ts +1 -0
  9. package/candidate-lookup.ts +49 -15
  10. package/candidate-schema.ts +35 -11
  11. package/coincident-roles.ts +28 -6
  12. package/convention.ts +3 -1
  13. package/coverage-manifest-schema.ts +243 -0
  14. package/fst-autocomplete.ts +15 -9
  15. package/fst-builder.ts +65 -9
  16. package/fst-deserialize-web.ts +46 -9
  17. package/fst-matcher.ts +12 -5
  18. package/fst-serialize.ts +83 -9
  19. package/fst-types.ts +43 -0
  20. package/fts-query.ts +84 -0
  21. package/fts.ts +35 -9
  22. package/geo.ts +9 -3
  23. package/geonames-aliases.ts +116 -79
  24. package/geonames-postal.ts +25 -5
  25. package/index.ts +19 -0
  26. package/interpolation.ts +59 -55
  27. package/lookup.ts +103 -292
  28. package/name-score.ts +76 -0
  29. package/out/address-point-interpolation.d.ts.map +1 -1
  30. package/out/address-point-interpolation.js +4 -2
  31. package/out/address-point-interpolation.js.map +1 -1
  32. package/out/address-point-schema.d.ts +30 -10
  33. package/out/address-point-schema.d.ts.map +1 -1
  34. package/out/address-point-schema.js +6 -2
  35. package/out/address-point-schema.js.map +1 -1
  36. package/out/address-point.d.ts.map +1 -1
  37. package/out/address-point.js.map +1 -1
  38. package/out/ancestry-backfill.d.ts +6 -2
  39. package/out/ancestry-backfill.d.ts.map +1 -1
  40. package/out/ancestry-backfill.js +7 -3
  41. package/out/ancestry-backfill.js.map +1 -1
  42. package/out/ancestry.d.ts +6 -2
  43. package/out/ancestry.d.ts.map +1 -1
  44. package/out/ancestry.js +3 -1
  45. package/out/ancestry.js.map +1 -1
  46. package/out/build-candidate.d.ts +9 -3
  47. package/out/build-candidate.d.ts.map +1 -1
  48. package/out/build-candidate.js +5 -3
  49. package/out/build-candidate.js.map +1 -1
  50. package/out/build-slim.d.ts +15 -5
  51. package/out/build-slim.d.ts.map +1 -1
  52. package/out/build-slim.js +11 -5
  53. package/out/build-slim.js.map +1 -1
  54. package/out/candidate-fts.d.ts.map +1 -1
  55. package/out/candidate-fts.js.map +1 -1
  56. package/out/candidate-lookup.d.ts +16 -3
  57. package/out/candidate-lookup.d.ts.map +1 -1
  58. package/out/candidate-lookup.js +27 -11
  59. package/out/candidate-lookup.js.map +1 -1
  60. package/out/candidate-schema.d.ts +33 -11
  61. package/out/candidate-schema.d.ts.map +1 -1
  62. package/out/candidate-schema.js.map +1 -1
  63. package/out/coincident-roles.d.ts +16 -4
  64. package/out/coincident-roles.d.ts.map +1 -1
  65. package/out/coincident-roles.js +9 -3
  66. package/out/coincident-roles.js.map +1 -1
  67. package/out/convention.d.ts +3 -1
  68. package/out/convention.d.ts.map +1 -1
  69. package/out/convention.js.map +1 -1
  70. package/out/coverage-manifest-schema.d.ts +112 -0
  71. package/out/coverage-manifest-schema.d.ts.map +1 -0
  72. package/out/coverage-manifest-schema.js +154 -0
  73. package/out/coverage-manifest-schema.js.map +1 -0
  74. package/out/fst-autocomplete.d.ts +1 -1
  75. package/out/fst-autocomplete.d.ts.map +1 -1
  76. package/out/fst-autocomplete.js +11 -9
  77. package/out/fst-autocomplete.js.map +1 -1
  78. package/out/fst-builder.d.ts.map +1 -1
  79. package/out/fst-builder.js +42 -9
  80. package/out/fst-builder.js.map +1 -1
  81. package/out/fst-deserialize-web.d.ts.map +1 -1
  82. package/out/fst-deserialize-web.js +34 -9
  83. package/out/fst-deserialize-web.js.map +1 -1
  84. package/out/fst-matcher.d.ts +6 -2
  85. package/out/fst-matcher.d.ts.map +1 -1
  86. package/out/fst-matcher.js +9 -5
  87. package/out/fst-matcher.js.map +1 -1
  88. package/out/fst-serialize.d.ts.map +1 -1
  89. package/out/fst-serialize.js +62 -9
  90. package/out/fst-serialize.js.map +1 -1
  91. package/out/fst-types.d.ts +43 -0
  92. package/out/fst-types.d.ts.map +1 -1
  93. package/out/fts-query.d.ts +41 -0
  94. package/out/fts-query.d.ts.map +1 -0
  95. package/out/fts-query.js +75 -0
  96. package/out/fts-query.js.map +1 -0
  97. package/out/fts.d.ts +21 -7
  98. package/out/fts.d.ts.map +1 -1
  99. package/out/fts.js +10 -4
  100. package/out/fts.js.map +1 -1
  101. package/out/geo.d.ts +6 -2
  102. package/out/geo.d.ts.map +1 -1
  103. package/out/geo.js +3 -1
  104. package/out/geo.js.map +1 -1
  105. package/out/geonames-aliases.d.ts +12 -4
  106. package/out/geonames-aliases.d.ts.map +1 -1
  107. package/out/geonames-aliases.js +72 -67
  108. package/out/geonames-aliases.js.map +1 -1
  109. package/out/geonames-postal.d.ts +9 -3
  110. package/out/geonames-postal.d.ts.map +1 -1
  111. package/out/geonames-postal.js +7 -2
  112. package/out/geonames-postal.js.map +1 -1
  113. package/out/index.d.ts +2 -0
  114. package/out/index.d.ts.map +1 -1
  115. package/out/index.js +1 -0
  116. package/out/index.js.map +1 -1
  117. package/out/interpolation.d.ts +24 -6
  118. package/out/interpolation.d.ts.map +1 -1
  119. package/out/interpolation.js +32 -40
  120. package/out/interpolation.js.map +1 -1
  121. package/out/lookup.d.ts +3 -97
  122. package/out/lookup.d.ts.map +1 -1
  123. package/out/lookup.js +52 -184
  124. package/out/lookup.js.map +1 -1
  125. package/out/name-score.d.ts +28 -0
  126. package/out/name-score.d.ts.map +1 -0
  127. package/out/name-score.js +67 -0
  128. package/out/name-score.js.map +1 -0
  129. package/out/poi-lookup.d.ts +24 -8
  130. package/out/poi-lookup.d.ts.map +1 -1
  131. package/out/poi-lookup.js +27 -13
  132. package/out/poi-lookup.js.map +1 -1
  133. package/out/poi-schema.d.ts +42 -13
  134. package/out/poi-schema.d.ts.map +1 -1
  135. package/out/poi-schema.js +12 -3
  136. package/out/poi-schema.js.map +1 -1
  137. package/out/postal-city-alias-lookup.d.ts +18 -6
  138. package/out/postal-city-alias-lookup.d.ts.map +1 -1
  139. package/out/postal-city-alias-lookup.js.map +1 -1
  140. package/out/postal-city-alias-schema.d.ts +27 -9
  141. package/out/postal-city-alias-schema.d.ts.map +1 -1
  142. package/out/postal-city-alias-schema.js +3 -1
  143. package/out/postal-city-alias-schema.js.map +1 -1
  144. package/out/postal-city-candidate-schema.d.ts +15 -5
  145. package/out/postal-city-candidate-schema.d.ts.map +1 -1
  146. package/out/postal-city-candidate-schema.js +3 -1
  147. package/out/postal-city-candidate-schema.js.map +1 -1
  148. package/out/postcode-point-lookup.d.ts +6 -2
  149. package/out/postcode-point-lookup.d.ts.map +1 -1
  150. package/out/postcode-point-lookup.js +6 -2
  151. package/out/postcode-point-lookup.js.map +1 -1
  152. package/out/ranking-weights.d.ts +118 -0
  153. package/out/ranking-weights.d.ts.map +1 -0
  154. package/out/ranking-weights.js +44 -0
  155. package/out/ranking-weights.js.map +1 -0
  156. package/out/reverse.d.ts +9 -3
  157. package/out/reverse.d.ts.map +1 -1
  158. package/out/reverse.js +20 -6
  159. package/out/reverse.js.map +1 -1
  160. package/out/sharding.d.ts +3 -1
  161. package/out/sharding.d.ts.map +1 -1
  162. package/out/sharding.js +7 -5
  163. package/out/sharding.js.map +1 -1
  164. package/out/sqlite-convention-source.d.ts.map +1 -1
  165. package/out/sqlite-convention-source.js +3 -1
  166. package/out/sqlite-convention-source.js.map +1 -1
  167. package/out/street-centroid-schema.d.ts +33 -11
  168. package/out/street-centroid-schema.d.ts.map +1 -1
  169. package/out/street-centroid-schema.js +3 -1
  170. package/out/street-centroid-schema.js.map +1 -1
  171. package/out/street-centroid.d.ts.map +1 -1
  172. package/out/street-centroid.js +6 -2
  173. package/out/street-centroid.js.map +1 -1
  174. package/out/street-morphology-fst-builder.d.ts +6 -2
  175. package/out/street-morphology-fst-builder.d.ts.map +1 -1
  176. package/out/street-morphology-fst-builder.js +8 -7
  177. package/out/street-morphology-fst-builder.js.map +1 -1
  178. package/out/street-morphology-fst-loader.d.ts +67 -0
  179. package/out/street-morphology-fst-loader.d.ts.map +1 -0
  180. package/out/street-morphology-fst-loader.js +59 -0
  181. package/out/street-morphology-fst-loader.js.map +1 -0
  182. package/out/street-name-lookup.d.ts +9 -3
  183. package/out/street-name-lookup.d.ts.map +1 -1
  184. package/out/street-name-lookup.js +9 -7
  185. package/out/street-name-lookup.js.map +1 -1
  186. package/out/street-normalize.d.ts +3 -1
  187. package/out/street-normalize.d.ts.map +1 -1
  188. package/out/street-normalize.js +23 -13
  189. package/out/street-normalize.js.map +1 -1
  190. package/out/street-segment-schema.d.ts +68 -13
  191. package/out/street-segment-schema.d.ts.map +1 -1
  192. package/out/street-segment-schema.js +21 -2
  193. package/out/street-segment-schema.js.map +1 -1
  194. package/out/types.d.ts +18 -6
  195. package/out/types.d.ts.map +1 -1
  196. package/out/unified-schema.d.ts +1 -1
  197. package/out/unified-schema.d.ts.map +1 -1
  198. package/out/unified-schema.js +2 -2
  199. package/out/unified-schema.js.map +1 -1
  200. package/package.json +13 -5
  201. package/poi-lookup.ts +53 -21
  202. package/poi-schema.ts +43 -13
  203. package/postal-city-alias-lookup.ts +20 -6
  204. package/postal-city-alias-schema.ts +28 -9
  205. package/postal-city-candidate-schema.ts +15 -5
  206. package/postcode-point-lookup.ts +6 -2
  207. package/ranking-weights.ts +148 -0
  208. package/reverse.ts +47 -10
  209. package/sharding.ts +13 -6
  210. package/sqlite-convention-source.ts +4 -1
  211. package/street-centroid-schema.ts +35 -11
  212. package/street-centroid.ts +10 -3
  213. package/street-morphology-fst-builder.ts +25 -9
  214. package/street-morphology-fst-loader.ts +103 -0
  215. package/street-name-lookup.ts +19 -7
  216. package/street-normalize.ts +28 -13
  217. package/street-segment-schema.ts +83 -13
  218. package/types.ts +18 -6
  219. package/unified-schema.ts +11 -2
package/index.ts CHANGED
@@ -23,23 +23,38 @@ export { WOFSqlitePlaceLookup, type RankingWeights, type WOFSqlitePlaceLookupOpt
23
23
 
24
24
  export { CANDIDATE_FTS_TABLE, createCandidateFTS } from "./candidate-fts.ts"
25
25
  export { WOFCandidateTableLookup, type WOFCandidateTableLookupOpts } from "./candidate-lookup.ts"
26
+
27
+ export {
28
+ COUNTRY_BBOX_TABLE,
29
+ COUNTRY_COVERAGE_TABLE,
30
+ createCountryBBoxTable,
31
+ createCountryCoverageTable,
32
+ readGazetteerCoverageManifest,
33
+ writeGazetteerCoverageManifest,
34
+ } from "./coverage-manifest-schema.ts"
35
+
36
+ export type { CountryBBoxTable, CountryCoverageTable, GazetteerCoverageDatabase } from "./coverage-manifest-schema.ts"
26
37
  export { SQLiteStreetNameLookup, type SQLiteStreetNameLookupOpts } from "./street-name-lookup.ts"
27
38
 
28
39
  export { GEONAMES_ID_BASE, ingestGeonamesAliases, type GeonamesIngestProgress } from "./geonames-aliases.ts"
29
40
 
30
41
  export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "./address-point-schema.ts"
31
42
  export type { AddressPointDatabase, AddressPointTable } from "./address-point-schema.ts"
43
+
32
44
  export {
33
45
  WOFPostalCityAliasLookup,
34
46
  type PostalCityAlias,
35
47
  type WOFPostalCityAliasLookupOpts,
36
48
  } from "./postal-city-alias-lookup.ts"
49
+
37
50
  export type { PostalCityAliasDatabase, PostalCityAliasTable } from "./postal-city-alias-schema.ts"
51
+
38
52
  export {
39
53
  POSTAL_CITY_CANDIDATE_COLUMNS,
40
54
  POSTAL_CITY_CANDIDATE_TABLE,
41
55
  createPostalCityCandidateTable,
42
56
  } from "./postal-city-candidate-schema.ts"
57
+
43
58
  export type { PostalCityCandidateDatabase, PostalCityCandidateTable } from "./postal-city-candidate-schema.ts"
44
59
 
45
60
  export {
@@ -95,19 +110,23 @@ export {
95
110
 
96
111
  export { AddressPointInterpolator } from "./address-point-interpolation.ts"
97
112
  export { AddressPointSqliteLookup } from "./address-point.ts"
113
+
98
114
  export {
99
115
  STREET_CENTROID_COLUMNS,
100
116
  createStreetCentroidIndexes,
101
117
  createStreetCentroidTable,
102
118
  } from "./street-centroid-schema.ts"
119
+
103
120
  export type { StreetCentroidDatabase, StreetCentroidTable } from "./street-centroid-schema.ts"
104
121
  export { StreetCentroidSqliteLookup } from "./street-centroid.ts"
122
+
105
123
  export {
106
124
  StreetInterpolator,
107
125
  type InterpolatedHit,
108
126
  type InterpolationMethod,
109
127
  type InterpolationQuery,
110
128
  } from "./interpolation.ts"
129
+
111
130
  export {
112
131
  deriveSchemaName,
113
132
  pickShardForPlacetype,
package/interpolation.ts CHANGED
@@ -29,6 +29,7 @@
29
29
  import { DatabaseSync } from "node:sqlite"
30
30
 
31
31
  import type { InterpolationLookup } from "@mailwoman/resolver"
32
+ import { clampFraction, pointAlong } from "@mailwoman/spatial"
32
33
 
33
34
  import { haversineKm } from "./geo.ts"
34
35
  import { hasTable } from "./sqlite-utils.ts"
@@ -44,13 +45,19 @@ import { canonicalizeRouteKey, normalizeStreetForKey } from "./street-normalize.
44
45
  */
45
46
  export type InterpolationMethod = "address_point" | "tiger_range"
46
47
 
47
- /** One interpolated coordinate estimate. Never an exact situs point — see `uncertaintyM`. */
48
+ /**
49
+ * One interpolated coordinate estimate. Never an exact situs point — see `uncertaintyM`.
50
+ */
48
51
  export interface InterpolatedHit {
49
52
  lat: number
50
53
  lon: number
51
- /** Always true — the tier's honesty flag, mirrored into `resolution_tier` when wired. */
54
+ /**
55
+ * Always true — the tier's honesty flag, mirrored into `resolution_tier` when wired.
56
+ */
52
57
  interpolated: true
53
- /** Which rung answered — see {@link InterpolationMethod}. */
58
+ /**
59
+ * Which rung answered — see {@link InterpolationMethod}.
60
+ */
54
61
  method: InterpolationMethod
55
62
  /**
56
63
  * `tiger_range` only. True when the matched segment side's parity agrees with the house number (or the side is
@@ -67,16 +74,22 @@ export interface InterpolatedHit {
67
74
  * span (`address_point`/`both`), or the explicitly larger extrapolation penalty (`address_point`/`single`).
68
75
  */
69
76
  uncertaintyM: number
70
- /** Provenance, e.g. `"tiger:edges"`. */
77
+ /**
78
+ * Provenance, e.g. `"tiger:edges"`.
79
+ */
71
80
  source: string
72
- /** Pinned data vintage, e.g. `"TIGER2023"`. */
81
+ /**
82
+ * Pinned data vintage, e.g. `"TIGER2023"`.
83
+ */
73
84
  release: string
74
85
  }
75
86
 
76
87
  export interface InterpolationQuery {
77
88
  street: string
78
89
  number: string
79
- /** ZIP scope — strongly preferred; without it common street names abstain (see module doc). */
90
+ /**
91
+ * ZIP scope — strongly preferred; without it common street names abstain (see module doc).
92
+ */
80
93
  postcode?: string
81
94
  }
82
95
 
@@ -97,6 +110,7 @@ export class StreetInterpolator implements InterpolationLookup {
97
110
  readonly #ownsDB: boolean
98
111
  readonly #byPostcode: ReturnType<DatabaseSync["prepare"]> | undefined
99
112
  readonly #byStreet: ReturnType<DatabaseSync["prepare"]> | undefined
113
+ readonly #radiusCalibration: number | undefined
100
114
 
101
115
  constructor(opts: { dbPath?: string; database?: DatabaseSync }) {
102
116
  if (opts.database) {
@@ -113,15 +127,43 @@ export class StreetInterpolator implements InterpolationLookup {
113
127
  // `street_segment` table this interpolator is a no-op miss, not a crash that loses the state (#568).
114
128
  if (hasTable(this.#db, "street_segment")) {
115
129
  const columns = `from_hn, to_hn, min_hn, max_hn, parity, postcode, geometry, source, release`
130
+
116
131
  this.#byPostcode = this.#db.prepare(
117
132
  `SELECT ${columns} FROM street_segment
118
133
  WHERE postcode = ? AND street_norm = ? AND min_hn <= ? AND max_hn >= ?`
119
134
  )
135
+
120
136
  this.#byStreet = this.#db.prepare(
121
137
  `SELECT ${columns} FROM street_segment
122
138
  WHERE street_norm = ? AND min_hn <= ? AND max_hn >= ?`
123
139
  )
124
140
  }
141
+
142
+ // #374 doctrine: the conformal radius multiplier is a property of the calibration set the ARTIFACT was
143
+ // built against, so it ships in the shard's `interp_calibration` metadata table (street-segment-schema.ts)
144
+ // and is read here, once, at open time — sync raw `.prepare()` per the sync-by-interface doctrine
145
+ // (AGENTS.md). Shards predating the table (the pre-2026-07 fleet) yield `undefined`; callers then fall
146
+ // back to their in-code table, byte-identically.
147
+ if (hasTable(this.#db, "interp_calibration")) {
148
+ const row = this.#db.prepare("SELECT radius_multiplier FROM interp_calibration LIMIT 1").get() as
149
+ | { radius_multiplier: unknown }
150
+ | undefined
151
+
152
+ const value = row?.radius_multiplier
153
+
154
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
155
+ this.#radiusCalibration = value
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * The artifact's own conformal radius multiplier (#374), read from the shard's `interp_calibration` metadata table at
162
+ * construction. `undefined` = the shard predates the table (or carries no valid row) — the resolver then applies no
163
+ * artifact default and callers may supply a legacy fallback.
164
+ */
165
+ get radiusCalibration(): number | undefined {
166
+ return this.#radiusCalibration
125
167
  }
126
168
 
127
169
  find(query: InterpolationQuery): InterpolatedHit | null {
@@ -149,23 +191,29 @@ export class StreetInterpolator implements InterpolationLookup {
149
191
  if (postcodes.size > 1) return null
150
192
  }
151
193
 
152
- if (rows.length === 0) return null
194
+ if (!rows.length) return null
153
195
 
154
196
  // Parity preference: exact side first, then 'mixed' (matches either), then the
155
197
  // opposite side as a flagged fallback.
156
198
  const wantOdd = n % 2 === 1
157
199
  const exact = rows.filter((r) => r.parity === (wantOdd ? "odd" : "even"))
158
200
  const mixed = rows.filter((r) => r.parity === "mixed")
159
- const preferred = exact.length > 0 ? exact : mixed
160
- const pool = preferred.length > 0 ? preferred : rows
201
+ const preferred = exact.length ? exact : mixed
202
+ const pool = preferred.length ? preferred : rows
161
203
  const parityMatched = preferred.length > 0
162
204
 
163
205
  // Tightest range wins — the most specific claim about where this number lives.
164
- const best = pool.reduce((a, b) => (b.max_hn - b.min_hn < a.max_hn - a.min_hn ? b : a))
206
+ let best = pool[0]!
207
+
208
+ for (const candidate of pool) {
209
+ if (candidate.max_hn - candidate.min_hn < best.max_hn - best.min_hn) {
210
+ best = candidate
211
+ }
212
+ }
165
213
 
166
214
  const polyline = JSON.parse(best.geometry) as [number, number][]
167
215
  const span = best.to_hn - best.from_hn
168
- const t = span === 0 ? 0.5 : clamp01((n - best.from_hn) / span)
216
+ const t = span === 0 ? 0.5 : clampFraction((n - best.from_hn) / span)
169
217
  const [lon, lat, lengthKm] = pointAlong(polyline, t)
170
218
 
171
219
  return {
@@ -186,47 +234,3 @@ export class StreetInterpolator implements InterpolationLookup {
186
234
  }
187
235
  }
188
236
  }
189
-
190
- function clamp01(t: number): number {
191
- return t < 0 ? 0 : t > 1 ? 1 : t
192
- }
193
-
194
- /**
195
- * Point at fraction `t` of the polyline's total arc length (haversine), plus the total length in km. `t` is assumed
196
- * clamped to [0, 1].
197
- */
198
- function pointAlong(polyline: readonly [number, number][], t: number): [lon: number, lat: number, lengthKm: number] {
199
- const legs: number[] = []
200
- let total = 0
201
-
202
- for (let i = 1; i < polyline.length; i++) {
203
- const [aLon, aLat] = polyline[i - 1]!
204
- const [bLon, bLat] = polyline[i]!
205
- const d = haversineKm(aLat, aLon, bLat, bLon)
206
- legs.push(d)
207
- total += d
208
- }
209
-
210
- if (total === 0) {
211
- const [lon, lat] = polyline[0]!
212
-
213
- return [lon, lat, 0]
214
- }
215
- let remaining = t * total
216
-
217
- for (let i = 0; i < legs.length; i++) {
218
- const leg = legs[i]!
219
-
220
- if (remaining <= leg || i === legs.length - 1) {
221
- const f = leg === 0 ? 0 : clamp01(remaining / leg)
222
- const [aLon, aLat] = polyline[i]!
223
- const [bLon, bLat] = polyline[i + 1]!
224
-
225
- return [aLon + (bLon - aLon) * f, aLat + (bLat - aLat) * f, total]
226
- }
227
- remaining -= leg
228
- }
229
- const [lon, lat] = polyline[polyline.length - 1]!
230
-
231
- return [lon, lat, total]
232
- }