@mailwoman/resolver-wof-sqlite 2.1.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 (158) hide show
  1. package/README.md +250 -0
  2. package/out/address-point-interpolation.d.ts +48 -0
  3. package/out/address-point-interpolation.d.ts.map +1 -0
  4. package/out/address-point-interpolation.js +164 -0
  5. package/out/address-point-interpolation.js.map +1 -0
  6. package/out/address-point-schema.d.ts +58 -0
  7. package/out/address-point-schema.d.ts.map +1 -0
  8. package/out/address-point-schema.js +67 -0
  9. package/out/address-point-schema.js.map +1 -0
  10. package/out/address-point.d.ts +29 -0
  11. package/out/address-point.d.ts.map +1 -0
  12. package/out/address-point.js +62 -0
  13. package/out/address-point.js.map +1 -0
  14. package/out/ancestry.d.ts +40 -0
  15. package/out/ancestry.d.ts.map +1 -0
  16. package/out/ancestry.js +53 -0
  17. package/out/ancestry.js.map +1 -0
  18. package/out/build-candidate-cli.d.ts +16 -0
  19. package/out/build-candidate-cli.d.ts.map +1 -0
  20. package/out/build-candidate-cli.js +80 -0
  21. package/out/build-candidate-cli.js.map +1 -0
  22. package/out/build-candidate.d.ts +54 -0
  23. package/out/build-candidate.d.ts.map +1 -0
  24. package/out/build-candidate.js +230 -0
  25. package/out/build-candidate.js.map +1 -0
  26. package/out/build-coincident-roles-cli.d.ts +16 -0
  27. package/out/build-coincident-roles-cli.d.ts.map +1 -0
  28. package/out/build-coincident-roles-cli.js +94 -0
  29. package/out/build-coincident-roles-cli.js.map +1 -0
  30. package/out/build-fts-cli.d.ts +23 -0
  31. package/out/build-fts-cli.d.ts.map +1 -0
  32. package/out/build-fts-cli.js +117 -0
  33. package/out/build-fts-cli.js.map +1 -0
  34. package/out/build-slim-cli.d.ts +14 -0
  35. package/out/build-slim-cli.d.ts.map +1 -0
  36. package/out/build-slim-cli.js +130 -0
  37. package/out/build-slim-cli.js.map +1 -0
  38. package/out/build-slim.d.ts +71 -0
  39. package/out/build-slim.d.ts.map +1 -0
  40. package/out/build-slim.js +267 -0
  41. package/out/build-slim.js.map +1 -0
  42. package/out/candidate-lookup.d.ts +43 -0
  43. package/out/candidate-lookup.d.ts.map +1 -0
  44. package/out/candidate-lookup.js +191 -0
  45. package/out/candidate-lookup.js.map +1 -0
  46. package/out/candidate-schema.d.ts +86 -0
  47. package/out/candidate-schema.d.ts.map +1 -0
  48. package/out/candidate-schema.js +109 -0
  49. package/out/candidate-schema.js.map +1 -0
  50. package/out/coincident-roles.d.ts +86 -0
  51. package/out/coincident-roles.d.ts.map +1 -0
  52. package/out/coincident-roles.js +160 -0
  53. package/out/coincident-roles.js.map +1 -0
  54. package/out/convention.d.ts +109 -0
  55. package/out/convention.d.ts.map +1 -0
  56. package/out/convention.js +94 -0
  57. package/out/convention.js.map +1 -0
  58. package/out/fst-autocomplete.d.ts +49 -0
  59. package/out/fst-autocomplete.d.ts.map +1 -0
  60. package/out/fst-autocomplete.js +124 -0
  61. package/out/fst-autocomplete.js.map +1 -0
  62. package/out/fst-builder.d.ts +20 -0
  63. package/out/fst-builder.d.ts.map +1 -0
  64. package/out/fst-builder.js +219 -0
  65. package/out/fst-builder.js.map +1 -0
  66. package/out/fst-deserialize-web.d.ts +16 -0
  67. package/out/fst-deserialize-web.d.ts.map +1 -0
  68. package/out/fst-deserialize-web.js +133 -0
  69. package/out/fst-deserialize-web.js.map +1 -0
  70. package/out/fst-matcher.d.ts +33 -0
  71. package/out/fst-matcher.d.ts.map +1 -0
  72. package/out/fst-matcher.js +117 -0
  73. package/out/fst-matcher.js.map +1 -0
  74. package/out/fst-serialize.d.ts +30 -0
  75. package/out/fst-serialize.d.ts.map +1 -0
  76. package/out/fst-serialize.js +261 -0
  77. package/out/fst-serialize.js.map +1 -0
  78. package/out/fst-types.d.ts +60 -0
  79. package/out/fst-types.d.ts.map +1 -0
  80. package/out/fst-types.js +11 -0
  81. package/out/fst-types.js.map +1 -0
  82. package/out/fts.d.ts +158 -0
  83. package/out/fts.d.ts.map +1 -0
  84. package/out/fts.js +261 -0
  85. package/out/fts.js.map +1 -0
  86. package/out/geo.d.ts +74 -0
  87. package/out/geo.d.ts.map +1 -0
  88. package/out/geo.js +88 -0
  89. package/out/geo.js.map +1 -0
  90. package/out/index.d.ts +27 -0
  91. package/out/index.d.ts.map +1 -0
  92. package/out/index.js +22 -0
  93. package/out/index.js.map +1 -0
  94. package/out/interpolation.d.ts +84 -0
  95. package/out/interpolation.d.ts.map +1 -0
  96. package/out/interpolation.js +150 -0
  97. package/out/interpolation.js.map +1 -0
  98. package/out/lookup.d.ts +156 -0
  99. package/out/lookup.d.ts.map +1 -0
  100. package/out/lookup.js +876 -0
  101. package/out/lookup.js.map +1 -0
  102. package/out/postal-city-alias-lookup.d.ts +50 -0
  103. package/out/postal-city-alias-lookup.d.ts.map +1 -0
  104. package/out/postal-city-alias-lookup.js +66 -0
  105. package/out/postal-city-alias-lookup.js.map +1 -0
  106. package/out/postal-city-alias-schema.d.ts +51 -0
  107. package/out/postal-city-alias-schema.d.ts.map +1 -0
  108. package/out/postal-city-alias-schema.js +47 -0
  109. package/out/postal-city-alias-schema.js.map +1 -0
  110. package/out/postal-city-candidate-schema.d.ts +58 -0
  111. package/out/postal-city-candidate-schema.d.ts.map +1 -0
  112. package/out/postal-city-candidate-schema.js +56 -0
  113. package/out/postal-city-candidate-schema.js.map +1 -0
  114. package/out/postcode-point-lookup.d.ts +38 -0
  115. package/out/postcode-point-lookup.d.ts.map +1 -0
  116. package/out/postcode-point-lookup.js +46 -0
  117. package/out/postcode-point-lookup.js.map +1 -0
  118. package/out/reverse.d.ts +99 -0
  119. package/out/reverse.d.ts.map +1 -0
  120. package/out/reverse.js +290 -0
  121. package/out/reverse.js.map +1 -0
  122. package/out/schema.d.ts +163 -0
  123. package/out/schema.d.ts.map +1 -0
  124. package/out/schema.js +18 -0
  125. package/out/schema.js.map +1 -0
  126. package/out/sharding.d.ts +96 -0
  127. package/out/sharding.d.ts.map +1 -0
  128. package/out/sharding.js +129 -0
  129. package/out/sharding.js.map +1 -0
  130. package/out/sqlite-convention-source.d.ts +29 -0
  131. package/out/sqlite-convention-source.d.ts.map +1 -0
  132. package/out/sqlite-convention-source.js +53 -0
  133. package/out/sqlite-convention-source.js.map +1 -0
  134. package/out/sqlite-utils.d.ts +17 -0
  135. package/out/sqlite-utils.d.ts.map +1 -0
  136. package/out/sqlite-utils.js +24 -0
  137. package/out/sqlite-utils.js.map +1 -0
  138. package/out/street-morphology-fst-builder.d.ts +59 -0
  139. package/out/street-morphology-fst-builder.d.ts.map +1 -0
  140. package/out/street-morphology-fst-builder.js +174 -0
  141. package/out/street-morphology-fst-builder.js.map +1 -0
  142. package/out/street-normalize.d.ts +66 -0
  143. package/out/street-normalize.d.ts.map +1 -0
  144. package/out/street-normalize.js +176 -0
  145. package/out/street-normalize.js.map +1 -0
  146. package/out/street-segment-schema.d.ts +61 -0
  147. package/out/street-segment-schema.d.ts.map +1 -0
  148. package/out/street-segment-schema.js +64 -0
  149. package/out/street-segment-schema.js.map +1 -0
  150. package/out/types.d.ts +137 -0
  151. package/out/types.d.ts.map +1 -0
  152. package/out/types.js +13 -0
  153. package/out/types.js.map +1 -0
  154. package/out/unified-schema.d.ts +25 -0
  155. package/out/unified-schema.d.ts.map +1 -0
  156. package/out/unified-schema.js +142 -0
  157. package/out/unified-schema.js.map +1 -0
  158. package/package.json +54 -0
package/out/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ export { WofSqlitePlaceLookup } from "./lookup.js";
7
+ export { WofCandidateTableLookup } from "./candidate-lookup.js";
8
+ export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "./address-point-schema.js";
9
+ export { WofPostalCityAliasLookup, } from "./postal-city-alias-lookup.js";
10
+ export { POSTAL_CITY_CANDIDATE_COLUMNS, POSTAL_CITY_CANDIDATE_TABLE, createPostalCityCandidateTable, } from "./postal-city-candidate-schema.js";
11
+ export { ADDRESS_CONVENTION_TABLE, BUILTIN_STRATEGY_NAMES, SeedConventionSource, WORLD_DEFAULT, mergeConventions, resolveConvention, } from "./convention.js";
12
+ export { SqliteConventionSource } from "./sqlite-convention-source.js";
13
+ export { WofPostcodeLookup } from "./postcode-point-lookup.js";
14
+ export { PLACE_BBOX_TABLE, PLACE_SEARCH_TABLE, buildPlaceSearchFts, placeBboxExists, placeSearchFtsExists, } from "./fts.js";
15
+ export { bboxAround, geometryContains, haversineKm, pointInPolygonRings, pointInRing, } from "./geo.js";
16
+ export { PLACETYPE_DEPTH, ancestorLineage, placetypeDepth } from "./ancestry.js";
17
+ export { WofReverseGeocoder, } from "./reverse.js";
18
+ export { AddressPointInterpolator } from "./address-point-interpolation.js";
19
+ export { AddressPointSqliteLookup } from "./address-point.js";
20
+ export { StreetInterpolator, } from "./interpolation.js";
21
+ export { deriveSchemaName, pickShardForPlacetype, resolveShards, } from "./sharding.js";
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiBH,OAAO,EAAE,oBAAoB,EAAsD,MAAM,aAAa,CAAA;AAEtG,OAAO,EAAE,uBAAuB,EAAoC,MAAM,uBAAuB,CAAA;AAEjG,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AAErH,OAAO,EACN,wBAAwB,GAGxB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACN,6BAA6B,EAC7B,2BAA2B,EAC3B,8BAA8B,GAC9B,MAAM,mCAAmC,CAAA;AAG1C,OAAO,EACN,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAMjB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AAEtE,OAAO,EAAE,iBAAiB,EAAsB,MAAM,4BAA4B,CAAA;AAElF,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,GAGpB,MAAM,UAAU,CAAA;AAEjB,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,mBAAmB,EACnB,WAAW,GAMX,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAyB,MAAM,eAAe,CAAA;AAEvG,OAAO,EACN,kBAAkB,GAKlB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EACN,kBAAkB,GAIlB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACN,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,GAGb,MAAM,eAAe,CAAA"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * House-number interpolation (#483): when the exact address-point tier (#476, `address-point.ts`)
7
+ * misses, estimate the coordinate from TIGER street-segment ranges — parity-aware range match,
8
+ * then linear interpolation along the segment polyline. Design:
9
+ * `docs/articles/plan/2026-06-11-interpolation-design.md`.
10
+ *
11
+ * Reads the per-state shard built by `scripts/build-interpolation-shard.ts` (`street_segment`: one
12
+ * row per TIGER edge SIDE — independent left/right ranges, ZIPs, parity). Query-side
13
+ * normalization is THE shared normalizer (`street-normalize.ts`) — identical to build-side, by
14
+ * construction.
15
+ *
16
+ * Every answer is honest about being an estimate: `interpolated: true`, `parityMatched` (false when
17
+ * only the opposite side's range contained the number — usually the right block, wrong side of
18
+ * the street), and `uncertaintyM` (half the matched segment's length — the #483 issue's honest
19
+ * default). Scoping is postcode-first (a given ZIP that scopes to nothing is a MISS — the
20
+ * statewide retry was measured and rejected, see `find()`); without a postcode the statewide name
21
+ * match must agree on a single postcode or the lookup ABSTAINS (a common street name spanning
22
+ * towns is ambiguity, not an answer).
23
+ *
24
+ * Standalone in this slice — core tier wiring (`resolution_tier: "interpolated"` after the
25
+ * exact-point fall-through) is a noted follow-up on #483, so the `find()` shape mirrors
26
+ * `AddressPointLookup.find()` to keep that wiring mechanical.
27
+ */
28
+ import { DatabaseSync } from "node:sqlite";
29
+ import type { InterpolationLookup } from "@mailwoman/resolver";
30
+ /**
31
+ * How an interpolated answer was computed (#483 Method 2):
32
+ *
33
+ * - `address_point` — bracketed/extrapolated between REAL neighbor points from the #476 shard
34
+ * (`AddressPointInterpolator`), replacing TIGER's uniform-spacing assumption with occupancy.
35
+ * - `tiger_range` — linear position within a TIGER segment's theoretical house-number range
36
+ * (`StreetInterpolator`), the fallback for streets too sparse to bracket.
37
+ */
38
+ export type InterpolationMethod = "address_point" | "tiger_range";
39
+ /** One interpolated coordinate estimate. Never an exact situs point — see `uncertaintyM`. */
40
+ export interface InterpolatedHit {
41
+ lat: number;
42
+ lon: number;
43
+ /** Always true — the tier's honesty flag, mirrored into `resolution_tier` when wired. */
44
+ interpolated: true;
45
+ /** Which rung answered — see {@link InterpolationMethod}. */
46
+ method: InterpolationMethod;
47
+ /**
48
+ * `tiger_range` only. True when the matched segment side's parity agrees with the house number
49
+ * (or the side is `mixed`). False = opposite-side fallback: usually the right block, wrong side
50
+ * of the street.
51
+ */
52
+ parityMatched?: boolean;
53
+ /**
54
+ * `address_point` only. `both` = the query number sits between two known neighbor numbers;
55
+ * `single` = neighbors exist on one side only (extrapolated, larger `uncertaintyM`).
56
+ */
57
+ bracket?: "both" | "single";
58
+ /**
59
+ * Honest uncertainty radius in meters: half the matched segment's polyline length
60
+ * (`tiger_range`), half the bracket span (`address_point`/`both`), or the explicitly larger
61
+ * extrapolation penalty (`address_point`/`single`).
62
+ */
63
+ uncertaintyM: number;
64
+ /** Provenance, e.g. `"tiger:edges"`. */
65
+ source: string;
66
+ /** Pinned data vintage, e.g. `"TIGER2023"`. */
67
+ release: string;
68
+ }
69
+ export interface InterpolationQuery {
70
+ street: string;
71
+ number: string;
72
+ /** ZIP scope — strongly preferred; without it common street names abstain (see module doc). */
73
+ postcode?: string;
74
+ }
75
+ export declare class StreetInterpolator implements InterpolationLookup {
76
+ #private;
77
+ constructor(opts: {
78
+ dbPath?: string;
79
+ database?: DatabaseSync;
80
+ });
81
+ find(query: InterpolationQuery): InterpolatedHit | null;
82
+ close(): void;
83
+ }
84
+ //# sourceMappingURL=interpolation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpolation.d.ts","sourceRoot":"","sources":["../interpolation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAM9D;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,aAAa,CAAA;AAEjE,6FAA6F;AAC7F,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,yFAAyF;IACzF,YAAY,EAAE,IAAI,CAAA;IAClB,6DAA6D;IAC7D,MAAM,EAAE,mBAAmB,CAAA;IAC3B;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAcD,qBAAa,kBAAmB,YAAW,mBAAmB;;gBAMjD,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;KAAE;IAyB9D,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,eAAe,GAAG,IAAI;IAmDvD,KAAK,IAAI,IAAI;CAGb"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * House-number interpolation (#483): when the exact address-point tier (#476, `address-point.ts`)
7
+ * misses, estimate the coordinate from TIGER street-segment ranges — parity-aware range match,
8
+ * then linear interpolation along the segment polyline. Design:
9
+ * `docs/articles/plan/2026-06-11-interpolation-design.md`.
10
+ *
11
+ * Reads the per-state shard built by `scripts/build-interpolation-shard.ts` (`street_segment`: one
12
+ * row per TIGER edge SIDE — independent left/right ranges, ZIPs, parity). Query-side
13
+ * normalization is THE shared normalizer (`street-normalize.ts`) — identical to build-side, by
14
+ * construction.
15
+ *
16
+ * Every answer is honest about being an estimate: `interpolated: true`, `parityMatched` (false when
17
+ * only the opposite side's range contained the number — usually the right block, wrong side of
18
+ * the street), and `uncertaintyM` (half the matched segment's length — the #483 issue's honest
19
+ * default). Scoping is postcode-first (a given ZIP that scopes to nothing is a MISS — the
20
+ * statewide retry was measured and rejected, see `find()`); without a postcode the statewide name
21
+ * match must agree on a single postcode or the lookup ABSTAINS (a common street name spanning
22
+ * towns is ambiguity, not an answer).
23
+ *
24
+ * Standalone in this slice — core tier wiring (`resolution_tier: "interpolated"` after the
25
+ * exact-point fall-through) is a noted follow-up on #483, so the `find()` shape mirrors
26
+ * `AddressPointLookup.find()` to keep that wiring mechanical.
27
+ */
28
+ import { DatabaseSync } from "node:sqlite";
29
+ import { haversineKm } from "./geo.js";
30
+ import { hasTable } from "./sqlite-utils.js";
31
+ import { canonicalizeRouteKey, normalizeStreetForKey } from "./street-normalize.js";
32
+ export class StreetInterpolator {
33
+ #db;
34
+ #ownsDb;
35
+ #byPostcode;
36
+ #byStreet;
37
+ constructor(opts) {
38
+ if (opts.database) {
39
+ this.#db = opts.database;
40
+ this.#ownsDb = false;
41
+ }
42
+ else if (opts.dbPath) {
43
+ this.#db = new DatabaseSync(opts.dbPath, { readOnly: true });
44
+ this.#ownsDb = true;
45
+ }
46
+ else {
47
+ throw new Error("StreetInterpolator: one of dbPath or database is required");
48
+ }
49
+ // Degrade gracefully on an empty/tableless shard (interrupted build, stray 0-byte file): with no
50
+ // `street_segment` table this interpolator is a no-op miss, not a crash that loses the state (#568).
51
+ if (hasTable(this.#db, "street_segment")) {
52
+ const columns = `from_hn, to_hn, min_hn, max_hn, parity, postcode, geometry, source, release`;
53
+ this.#byPostcode = this.#db.prepare(`SELECT ${columns} FROM street_segment
54
+ WHERE postcode = ? AND street_norm = ? AND min_hn <= ? AND max_hn >= ?`);
55
+ this.#byStreet = this.#db.prepare(`SELECT ${columns} FROM street_segment
56
+ WHERE street_norm = ? AND min_hn <= ? AND max_hn >= ?`);
57
+ }
58
+ }
59
+ find(query) {
60
+ if (!this.#byPostcode || !this.#byStreet)
61
+ return null;
62
+ const streetNorm = canonicalizeRouteKey(normalizeStreetForKey(query.street));
63
+ const numberRaw = query.number.trim();
64
+ // Strictly-numeric house numbers only — this tier estimates, it doesn't guess at
65
+ // hyphenated/alphanumeric schemes the ranges don't model.
66
+ if (!streetNorm || !/^\d+$/.test(numberRaw))
67
+ return null;
68
+ const n = Number(numberRaw);
69
+ let rows;
70
+ if (query.postcode) {
71
+ // A given ZIP that scopes to nothing is a MISS, not a statewide guess: the retry was
72
+ // measured (2026-06-11 VT eval) at +2.3pp coverage for a poisoned tail (p99 1.0 → 20.8
73
+ // km, max 204 km — a unique name statewide can live in a far-away town).
74
+ rows = this.#byPostcode.all(query.postcode.trim(), streetNorm, n, n);
75
+ }
76
+ else {
77
+ // No scope given: a name matching ranges across several ZIPs is ambiguous — abstain.
78
+ rows = this.#byStreet.all(streetNorm, n, n);
79
+ const postcodes = new Set(rows.map((r) => r.postcode ?? ""));
80
+ if (postcodes.size > 1)
81
+ return null;
82
+ }
83
+ if (rows.length === 0)
84
+ return null;
85
+ // Parity preference: exact side first, then 'mixed' (matches either), then the
86
+ // opposite side as a flagged fallback.
87
+ const wantOdd = n % 2 === 1;
88
+ const exact = rows.filter((r) => r.parity === (wantOdd ? "odd" : "even"));
89
+ const mixed = rows.filter((r) => r.parity === "mixed");
90
+ const preferred = exact.length > 0 ? exact : mixed;
91
+ const pool = preferred.length > 0 ? preferred : rows;
92
+ const parityMatched = preferred.length > 0;
93
+ // Tightest range wins — the most specific claim about where this number lives.
94
+ const best = pool.reduce((a, b) => (b.max_hn - b.min_hn < a.max_hn - a.min_hn ? b : a));
95
+ const polyline = JSON.parse(best.geometry);
96
+ const span = best.to_hn - best.from_hn;
97
+ const t = span === 0 ? 0.5 : clamp01((n - best.from_hn) / span);
98
+ const [lon, lat, lengthKm] = pointAlong(polyline, t);
99
+ return {
100
+ lat,
101
+ lon,
102
+ interpolated: true,
103
+ method: "tiger_range",
104
+ parityMatched,
105
+ uncertaintyM: Math.round((lengthKm * 1000) / 2),
106
+ source: best.source,
107
+ release: best.release,
108
+ };
109
+ }
110
+ close() {
111
+ if (this.#ownsDb)
112
+ this.#db.close();
113
+ }
114
+ }
115
+ function clamp01(t) {
116
+ return t < 0 ? 0 : t > 1 ? 1 : t;
117
+ }
118
+ /**
119
+ * Point at fraction `t` of the polyline's total arc length (haversine), plus the total length in
120
+ * km. `t` is assumed clamped to [0, 1].
121
+ */
122
+ function pointAlong(polyline, t) {
123
+ const legs = [];
124
+ let total = 0;
125
+ for (let i = 1; i < polyline.length; i++) {
126
+ const [aLon, aLat] = polyline[i - 1];
127
+ const [bLon, bLat] = polyline[i];
128
+ const d = haversineKm(aLat, aLon, bLat, bLon);
129
+ legs.push(d);
130
+ total += d;
131
+ }
132
+ if (total === 0) {
133
+ const [lon, lat] = polyline[0];
134
+ return [lon, lat, 0];
135
+ }
136
+ let remaining = t * total;
137
+ for (let i = 0; i < legs.length; i++) {
138
+ const leg = legs[i];
139
+ if (remaining <= leg || i === legs.length - 1) {
140
+ const f = leg === 0 ? 0 : clamp01(remaining / leg);
141
+ const [aLon, aLat] = polyline[i];
142
+ const [bLon, bLat] = polyline[i + 1];
143
+ return [aLon + (bLon - aLon) * f, aLat + (bLat - aLat) * f, total];
144
+ }
145
+ remaining -= leg;
146
+ }
147
+ const [lon, lat] = polyline[polyline.length - 1];
148
+ return [lon, lat, total];
149
+ }
150
+ //# sourceMappingURL=interpolation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpolation.js","sourceRoot":"","sources":["../interpolation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `WofSqlitePlaceLookup` — the resolver implementation backed by `node:sqlite` + a Kysely-typed
7
+ * query layer where the queries are non-trivial, and raw SQL where they aren't (FTS5 MATCH, the
8
+ * FTS index build).
9
+ *
10
+ * See `docs/plan/phases/PHASE_4_2_wof_sqlite.md` for the design rationale.
11
+ */
12
+ import { DatabaseSync } from "node:sqlite";
13
+ import { type Ancestor, type CoincidentLocality } from "@mailwoman/resolver";
14
+ import { type Convention, type ConventionSource } from "./convention.js";
15
+ import type { WofPostalCityAliasLookup } from "./postal-city-alias-lookup.js";
16
+ import { type ShardConfig } from "./sharding.js";
17
+ import type { FindPlaceQuery, PlaceCandidate, PlaceLookup } from "./types.js";
18
+ export interface WofSqlitePlaceLookupOpts {
19
+ /**
20
+ * Path to the WOF SQLite distribution on disk. Mutually exclusive with `database`.
21
+ *
22
+ * **Single string** — opens that one DB as the main shard.
23
+ *
24
+ * **Array** — opens the first entry as main, then ATTACHes each subsequent entry as a separate
25
+ * SQLite schema. Schema names are derived from the filename (`whosonfirst-data-postalcode-
26
+ * us-latest.db` → `postalcode_us`); override with `ShardConfig.schemaName` when the filename
27
+ * doesn't follow WOF convention. See `sharding.ts` for the derivation rules.
28
+ *
29
+ * Routing: queries with a `placetype` matching a shard's name (or explicit `placetypes` hint) are
30
+ * sent to that shard; everything else hits main. Cross-shard UNION is NOT done — BM25 isn't
31
+ * comparable across separately-indexed corpora.
32
+ */
33
+ databasePath?: string | ReadonlyArray<string | ShardConfig>;
34
+ /**
35
+ * Pre-opened DatabaseSync — primarily for tests against an inline fixture DB. Mutually exclusive
36
+ * with `databasePath`. Multi-shard requires `databasePath` (so the lookup owns the ATTACH).
37
+ */
38
+ database?: DatabaseSync;
39
+ /**
40
+ * If true, build the FTS5 `place_search` virtual table on construction if it doesn't already
41
+ * exist. The upstream WOF distribution does NOT ship FTS5, so callers either set this once on
42
+ * first open or pre-build it via the operator-side CLI documented in the README. Default false —
43
+ * the resolver assumes the index already exists and errors loudly if it doesn't.
44
+ *
45
+ * With multi-shard, `buildFts: true` builds the index on the **main** shard only. Other shards
46
+ * must be pre-built via `mailwoman-wof-build-fts` — operator script for predictable cost.
47
+ */
48
+ buildFts?: boolean;
49
+ /**
50
+ * Geographic Rule Engine convention source (Direction E, #289). Per-WOF-polygon resolution
51
+ * profiles, either as a ready `ConventionSource` or a plain `{ wofId: Convention }` seed map.
52
+ * Default empty — every query rides `WORLD_DEFAULT` (the EU coordinate-first behavior). JP/KR/TW
53
+ * add rows; #290 wires a build-from-source sqlite-backed source here.
54
+ */
55
+ conventions?: ConventionSource | Record<number, Convention>;
56
+ /**
57
+ * Opt-in postal-city alias reader (#475). When supplied, the coordinate-first locality scorer
58
+ * treats an observed `postal_city` ("Antioch", postcode 37013) as a name-match alias for the
59
+ * geographic locality the postcode sits in ("Nashville"), recovering the chronic postal-vs-
60
+ * geographic-city mismatch. Absent (the default), the resolver is byte-identical — every alias
61
+ * code path is gated on this being non-null, so an unprovided reader changes no score.
62
+ */
63
+ postalCityAliases?: WofPostalCityAliasLookup;
64
+ }
65
+ /**
66
+ * Ranking weights for `findPlace`. Tweakable per-instance but defaults match the values declared in
67
+ * the Phase 4.2 plan doc.
68
+ */
69
+ export interface RankingWeights {
70
+ /** Boost when the candidate's placetype matches an explicit `placetype` filter. */
71
+ placetypeMatchBoost: number;
72
+ /** Boost when the candidate is a locality and no explicit placetype was requested. */
73
+ localityImplicitBoost: number;
74
+ /** Boost when the candidate's country matches an explicit `country` filter. */
75
+ countryMatchBoost: number;
76
+ /** Boost when the candidate is a direct child of the requested `parentId`. */
77
+ directChildBoost: number;
78
+ /** Boost when the candidate is a transitive descendant of the requested `parentId`. */
79
+ descendantBoost: number;
80
+ /** Multiplier on the length-penalty term (penalizes much-longer-than-query names). */
81
+ lengthPenaltyWeight: number;
82
+ /**
83
+ * Magnitude of the proximity boost when the query carries `near`. The contribution is
84
+ * `proximityBoost / (1 + distanceKm / proximityScaleKm)` — at distance 0 the boost is full
85
+ * magnitude, at `proximityScaleKm` it's half, decaying further with distance. Default tuned so
86
+ * proximity can overcome a typical FTS rank tie but not dominate a strong text match.
87
+ */
88
+ proximityBoost: number;
89
+ /** Distance (km) at which the proximity boost halves. Tune to the typical query radius. */
90
+ proximityScaleKm: number;
91
+ /**
92
+ * Magnitude of the population boost when the candidate has a known `wof:population`. The
93
+ * contribution is `populationBoost * log10(1 + population) / populationScaleLog10`, capped at
94
+ * `populationBoost`. WOF only carries population for ~15% of localities (mostly larger ones);
95
+ * places without it get +0 (never a penalty). Default tuned so the famous Springfield, IL (pop
96
+ * ~112k) gets ~0.42 boost — enough to nudge past tiny same-name peers.
97
+ */
98
+ populationBoost: number;
99
+ /**
100
+ * Population (in log10) at which the boost reaches its full magnitude. Default 6 — i.e. a
101
+ * population of 1,000,000 gives `populationBoost` exactly. Larger populations cap at the same
102
+ * value (no compounding effect for megacities).
103
+ */
104
+ populationScaleLog10: number;
105
+ /**
106
+ * Tier candidates with an EXACT name/alias match above candidates that only match partially,
107
+ * BEFORE the weighted-sum score is consulted. Default true.
108
+ *
109
+ * Why this is needed (and why it ALIGNS with — rather than overrides — the population/importance
110
+ * signal): the weighted sum adds population as a large additive boost (`populationBoost`, up to
111
+ * +4) so that famous places surface for unambiguous full-name queries. But population is a
112
+ * _prominence prior_ — its job is to break ties among candidates that match the query EQUALLY
113
+ * WELL (e.g. "Springfield" → Springfield IL over Springfield MA, both exact name matches). It was
114
+ * never meant to promote a place that matches the query WORSE. For a 2-letter region abbreviation
115
+ * that backfires: querying "ME" returns Maine (which has the exact alias `ME`) AND Missouri/
116
+ * Michigan/etc. (which do not), and Missouri's larger population (+4) overcomes Maine's bm25 edge
117
+ * — so "Portland, ME" resolves its region to Missouri and the locality then cascades to the wrong
118
+ * state. Tiering restores the intended ordering: **match quality is the primary key, prominence
119
+ * (population) the secondary key WITHIN a tier.** Springfield-IL-over-MA still works (both exact
120
+ * → same tier → population decides); ME→Maine now works (only Maine is exact → higher tier →
121
+ * population never gets to override it). See
122
+ * docs/articles/evals/2026-05-30-resolver-exact-match.md.
123
+ *
124
+ * Note: tiering re-ranks within the over-fetched candidate window (`limit * 4`); a pathological
125
+ * exact match that falls outside that window is not rescued. For the region-abbrev case the
126
+ * window is comfortably sufficient (a handful of states match a 2-letter query).
127
+ */
128
+ exactMatchTiering: boolean;
129
+ }
130
+ export declare class WofSqlitePlaceLookup implements PlaceLookup, Disposable {
131
+ #private;
132
+ constructor(opts: WofSqlitePlaceLookupOpts, weights?: Partial<RankingWeights>);
133
+ findPlace(query: FindPlaceQuery): Promise<PlaceCandidate[]>;
134
+ /**
135
+ * Dual-role localities coincident with an admin id, from the precomputed `coincident_roles`
136
+ * relation (#403). Backs {@link ResolveOpts.hierarchyCompletion} (#405): O(1) once the relation is
137
+ * loaded. Returns `[]` when the relation table is absent (older DB) or the admin isn't a
138
+ * dual-role place, so completion degrades gracefully. The relation + `spr` join is loaded once
139
+ * and memoized.
140
+ */
141
+ coincidentLocalitiesFor(adminId: number | string): CoincidentLocality[];
142
+ /**
143
+ * The ancestor lineage of a place — its containment chain joined with `spr` for canonical names,
144
+ * ordered NEAREST-FIRST (localadmin → county → region → … → country). Backs
145
+ * {@link ResolveOpts.includeAncestors} (#404). Self is excluded; memoized per id. Returns `[]`
146
+ * when the place has no recorded ancestry.
147
+ *
148
+ * The walk itself lives in `ancestry.ts` (shared with the reverse geocoder, #484); the ordering
149
+ * is its `PLACETYPE_DEPTH` table — same ranking as the previous inline SQL CASE, extended below
150
+ * `localadmin` so locality/neighbourhood ancestors order correctly instead of sorting last.
151
+ */
152
+ ancestors(id: number | string): Ancestor[];
153
+ close(): void;
154
+ [Symbol.dispose](): void;
155
+ }
156
+ //# sourceMappingURL=lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAA;AAI9D,OAAO,EAAyB,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACnG,OAAO,EAIN,KAAK,UAAU,EACf,KAAK,gBAAgB,EAGrB,MAAM,iBAAiB,CAAA;AAcxB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAE7E,OAAO,EAA4D,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAE1G,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAgB,MAAM,YAAY,CAAA;AAE3F,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,GAAG,WAAW,CAAC,CAAA;IAC3D;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC3D;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAA;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,mFAAmF;IACnF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,sFAAsF;IACtF,qBAAqB,EAAE,MAAM,CAAA;IAC7B,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAA;IACxB,uFAAuF;IACvF,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,mBAAmB,EAAE,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB,2FAA2F;IAC3F,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,iBAAiB,EAAE,OAAO,CAAA;CAC1B;AA4HD,qBAAa,oBAAqB,YAAW,WAAW,EAAE,UAAU;;gBAwDvD,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC;IAyFvE,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoBjE;;;;;;OAMG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,kBAAkB,EAAE;IAgDvE;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,EAAE;IA8e1C,KAAK,IAAI,IAAI;IASb,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAgBxB"}