@mailwoman/resolver-wof-sqlite 8.0.0 → 8.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 (38) hide show
  1. package/candidate-lookup.ts +13 -1
  2. package/coverage-manifest-schema.ts +210 -0
  3. package/fst-builder.ts +39 -7
  4. package/fst-types.ts +20 -0
  5. package/index.ts +9 -0
  6. package/interpolation.ts +26 -0
  7. package/out/candidate-lookup.d.ts +7 -0
  8. package/out/candidate-lookup.d.ts.map +1 -1
  9. package/out/candidate-lookup.js +11 -0
  10. package/out/candidate-lookup.js.map +1 -1
  11. package/out/coverage-manifest-schema.d.ts +81 -0
  12. package/out/coverage-manifest-schema.d.ts.map +1 -0
  13. package/out/coverage-manifest-schema.js +145 -0
  14. package/out/coverage-manifest-schema.js.map +1 -0
  15. package/out/fst-builder.d.ts.map +1 -1
  16. package/out/fst-builder.js +29 -6
  17. package/out/fst-builder.js.map +1 -1
  18. package/out/fst-types.d.ts +20 -0
  19. package/out/fst-types.d.ts.map +1 -1
  20. package/out/index.d.ts +2 -0
  21. package/out/index.d.ts.map +1 -1
  22. package/out/index.js +1 -0
  23. package/out/index.js.map +1 -1
  24. package/out/interpolation.d.ts +6 -0
  25. package/out/interpolation.d.ts.map +1 -1
  26. package/out/interpolation.js +21 -0
  27. package/out/interpolation.js.map +1 -1
  28. package/out/street-morphology-fst-loader.d.ts +51 -0
  29. package/out/street-morphology-fst-loader.d.ts.map +1 -0
  30. package/out/street-morphology-fst-loader.js +53 -0
  31. package/out/street-morphology-fst-loader.js.map +1 -0
  32. package/out/street-segment-schema.d.ts +23 -0
  33. package/out/street-segment-schema.d.ts.map +1 -1
  34. package/out/street-segment-schema.js +15 -0
  35. package/out/street-segment-schema.js.map +1 -1
  36. package/package.json +13 -5
  37. package/street-morphology-fst-loader.ts +87 -0
  38. package/street-segment-schema.ts +36 -0
@@ -52,6 +52,21 @@ export async function createStreetSegmentTable(db) {
52
52
  .addColumn("release", "text", (c) => c.notNull())
53
53
  .execute();
54
54
  }
55
+ /**
56
+ * Create + populate the single-row `interp_calibration` metadata table (see {@link InterpCalibrationRow}) — called once
57
+ * by the shard builder, after the value is selected from the calibration source of record. Build-time only (async
58
+ * Kysely is fine here); the READ side is the raw sync probe in {@link StreetInterpolator}'s constructor, per the
59
+ * sync-by-interface doctrine.
60
+ */
61
+ export async function writeInterpCalibration(db, row) {
62
+ await db.schema
63
+ .createTable("interp_calibration")
64
+ .addColumn("radius_multiplier", "real", (c) => c.notNull())
65
+ .addColumn("method", "text", (c) => c.notNull())
66
+ .addColumn("region", "text", (c) => c.notNull())
67
+ .execute();
68
+ await db.insertInto("interp_calibration").values(row).execute();
69
+ }
55
70
  /** Create the two probe indexes the reader relies on (postcode-scope, street-scope). */
56
71
  export async function createStreetSegmentIndexes(db) {
57
72
  await db.schema
@@ -1 +1 @@
1
- {"version":3,"file":"street-segment-schema.js","sourceRoot":"","sources":["../street-segment-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwCH;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,aAAa;IACb,MAAM;IACN,SAAS;IACT,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,aAAa;IACb,YAAY;IACZ,UAAU;IACV,QAAQ;IACR,SAAS;CACA,CAAA;AAEV,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,EAAiC;IAC/E,MAAM,EAAE,CAAC,MAAM;SACb,WAAW,CAAC,gBAAgB,CAAC;SAC7B,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACpD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC7C,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACnD,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAClD,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAClD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/C,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC;SAC7B,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACpD,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACnD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/C,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAChD,OAAO,EAAE,CAAA;AACZ,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,EAAiC;IACjF,MAAM,EAAE,CAAC,MAAM;SACb,WAAW,CAAC,kBAAkB,CAAC;SAC/B,EAAE,CAAC,gBAAgB,CAAC;SACpB,OAAO,CAAC,CAAC,UAAU,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;SAC9C,OAAO,EAAE,CAAA;IACX,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAA;AAChH,CAAC"}
1
+ {"version":3,"file":"street-segment-schema.js","sourceRoot":"","sources":["../street-segment-schema.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/resolver-wof-sqlite",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "description": "Who's On First SQLite resolver — FTS5-backed PlaceLookup that resolves text queries to WOF place IDs + coordinates. Optional dependency for callers who want geocoding.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -46,6 +46,10 @@
46
46
  "types": "./out/street-morphology-fst-builder.d.ts",
47
47
  "default": "./out/street-morphology-fst-builder.js"
48
48
  },
49
+ "./street-morphology-fst-loader": {
50
+ "types": "./out/street-morphology-fst-loader.d.ts",
51
+ "default": "./out/street-morphology-fst-loader.js"
52
+ },
49
53
  "./fst-matcher": {
50
54
  "types": "./out/fst-matcher.d.ts",
51
55
  "default": "./out/fst-matcher.js"
@@ -151,6 +155,10 @@
151
155
  "types": "./out/street-morphology-fst-builder.d.ts",
152
156
  "default": "./out/street-morphology-fst-builder.js"
153
157
  },
158
+ "./street-morphology-fst-loader": {
159
+ "types": "./out/street-morphology-fst-loader.d.ts",
160
+ "default": "./out/street-morphology-fst-loader.js"
161
+ },
154
162
  "./fst-matcher": {
155
163
  "types": "./out/fst-matcher.d.ts",
156
164
  "default": "./out/fst-matcher.js"
@@ -234,10 +242,10 @@
234
242
  }
235
243
  },
236
244
  "dependencies": {
237
- "@mailwoman/codex": "8.0.0",
238
- "@mailwoman/core": "8.0.0",
239
- "@mailwoman/resolver": "8.0.0",
240
- "@mailwoman/spatial": "8.0.0",
245
+ "@mailwoman/codex": "8.1.0",
246
+ "@mailwoman/core": "8.1.0",
247
+ "@mailwoman/resolver": "8.1.0",
248
+ "@mailwoman/spatial": "8.1.0",
241
249
  "h3-js": "^4.5.0",
242
250
  "kysely": "^0.29.4"
243
251
  }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Unified runtime loader for the street-morphology FST — the #1315 street-context gate's signal
7
+ * source, previously rebuilt from the libpostal dictionaries per process at three duplicate call
8
+ * sites (runtime pipeline / parity eval / neural harness). The sealed artifact
9
+ * (`fst-street-morphology.bin`, built by `mailwoman gazetteer build street-morphology`) replaces
10
+ * those per-process builds; this loader is the ONE resolution ladder every node call site shares:
11
+ *
12
+ * 1. An explicit `artifactPath` (e.g. the weights-package sibling surfaced by
13
+ * `NeuralAddressClassifier.streetMorphologyPath`). When given, it is the only artifact probed.
14
+ * 2. Otherwise the staged sealed artifact at `$MAILWOMAN_DATA_ROOT/wof/fst-street-morphology.bin`.
15
+ * 3. Build-from-dictionaries fallback: `buildStreetMorphologyFST` over core's bundled libpostal
16
+ * `street_types.txt` files — the pre-artifact behavior, kept so a missing artifact degrades to
17
+ * a per-process build rather than a crash.
18
+ *
19
+ * A present-but-unreadable artifact reports through `onWarn` and falls through to the build rung.
20
+ * Browsers can't take the fallback (no fs) — they deserialize the same artifact via
21
+ * `fst-deserialize-web.ts` (see the docs demo loader).
22
+ */
23
+
24
+ import { existsSync, readFileSync } from "node:fs"
25
+
26
+ import { dataRootPath, resourceDictionaryPath } from "@mailwoman/core/utils"
27
+
28
+ import type { FSTMatcher } from "./fst-matcher.ts"
29
+ import { deserializeFST, readFSTProvenance } from "./fst-serialize.ts"
30
+ import type { FSTProvenance } from "./fst-types.ts"
31
+ import { buildStreetMorphologyFST } from "./street-morphology-fst-builder.ts"
32
+
33
+ /** The sealed artifact's canonical filename — identical in the data-root staging dir and as a weights-package sibling. */
34
+ export const STREET_MORPHOLOGY_ARTIFACT_FILENAME = "fst-street-morphology.bin"
35
+
36
+ /** The staged artifact's default location: `$MAILWOMAN_DATA_ROOT/wof/fst-street-morphology.bin`. */
37
+ export function defaultStreetMorphologyArtifactPath(): string {
38
+ return String(dataRootPath("wof", STREET_MORPHOLOGY_ARTIFACT_FILENAME))
39
+ }
40
+
41
+ export interface LoadStreetMorphologyFSTOpts {
42
+ /**
43
+ * Explicit artifact path (e.g. a weights-package sibling). When given it is the ONLY artifact probed — missing or
44
+ * unreadable degrades straight to the dictionary build, never a throw.
45
+ */
46
+ artifactPath?: string
47
+ /** Dictionaries dir for the build fallback. Defaults to core's bundled libpostal dictionaries. */
48
+ dictionariesDir?: string
49
+ /** Unreadable-artifact diagnostics. Defaults to silent (the caller owns its warn channel). */
50
+ onWarn?: (message: string) => void
51
+ }
52
+
53
+ export interface LoadedStreetMorphologyFST {
54
+ matcher: FSTMatcher
55
+ /** Which rung produced the matcher: a sealed-artifact deserialize, or the per-process dictionary build. */
56
+ source: "artifact" | "built"
57
+ /** The artifact path when `source === "artifact"`. */
58
+ path?: string
59
+ /** Build provenance — read from the artifact trailer, or carried fresh off the fallback build. */
60
+ provenance?: FSTProvenance
61
+ }
62
+
63
+ /** Load the street-morphology matcher: sealed artifact first, per-process dictionary build as the degrade path. */
64
+ export function loadStreetMorphologyFST(opts: LoadStreetMorphologyFSTOpts = {}): LoadedStreetMorphologyFST {
65
+ const warn = opts.onWarn ?? (() => {})
66
+ const artifactPath = opts.artifactPath ?? defaultStreetMorphologyArtifactPath()
67
+
68
+ if (existsSync(artifactPath)) {
69
+ try {
70
+ const buf = readFileSync(artifactPath)
71
+ const matcher = deserializeFST(buf)
72
+ const provenance = readFSTProvenance(buf)
73
+
74
+ return { matcher, source: "artifact", path: artifactPath, ...(provenance ? { provenance } : {}) }
75
+ } catch (err) {
76
+ warn(
77
+ `street-morphology artifact at ${artifactPath} unreadable (${(err as Error).message}) — falling back to the dictionary build`
78
+ )
79
+ }
80
+ }
81
+
82
+ const built = buildStreetMorphologyFST({
83
+ dictionariesDir: opts.dictionariesDir ?? resourceDictionaryPath("libpostal"),
84
+ })
85
+
86
+ return { matcher: built.matcher, source: "built", provenance: built.provenance }
87
+ }
@@ -48,9 +48,26 @@ export interface StreetSegmentTable {
48
48
  release: string
49
49
  }
50
50
 
51
+ /**
52
+ * The shard's single-row calibration metadata (#374 doctrine, 2026-07-26): the conformal radius multiplier is a
53
+ * property of the CALIBRATION SET the artifact was built against — so it ships IN the artifact (the pair-index δ
54
+ * precedent, `neural/pair-index-resolver.ts`), not in caller code. Written once by the builder; read at open time by
55
+ * {@link StreetInterpolator}. Shards built before this table exists simply lack it — the reader degrades to `undefined`
56
+ * and callers fall back to the in-code per-region table (never patch shipped DBs — rebuild).
57
+ */
58
+ export interface InterpCalibrationRow {
59
+ /** Conformal multiplier for the raw half-segment `uncertainty_m` radius (#374/#584) — ×Q̂ for a ~90% bound. */
60
+ radius_multiplier: number
61
+ /** Provenance of the multiplier (e.g. `split-conformal:2026-06-14`). */
62
+ method: string
63
+ /** The calibration-table key the multiplier was selected by (a USPS region code, or `default` for unmeasured). */
64
+ region: string
65
+ }
66
+
51
67
  /** The street-segment database schema for `new DatabaseClient<StreetSegmentDatabase>(...)`. */
52
68
  export interface StreetSegmentDatabase {
53
69
  street_segment: StreetSegmentTable
70
+ interp_calibration: InterpCalibrationRow
54
71
  }
55
72
 
56
73
  /**
@@ -93,6 +110,25 @@ export async function createStreetSegmentTable(db: Kysely<StreetSegmentDatabase>
93
110
  .execute()
94
111
  }
95
112
 
113
+ /**
114
+ * Create + populate the single-row `interp_calibration` metadata table (see {@link InterpCalibrationRow}) — called once
115
+ * by the shard builder, after the value is selected from the calibration source of record. Build-time only (async
116
+ * Kysely is fine here); the READ side is the raw sync probe in {@link StreetInterpolator}'s constructor, per the
117
+ * sync-by-interface doctrine.
118
+ */
119
+ export async function writeInterpCalibration(
120
+ db: Kysely<StreetSegmentDatabase>,
121
+ row: InterpCalibrationRow
122
+ ): Promise<void> {
123
+ await db.schema
124
+ .createTable("interp_calibration")
125
+ .addColumn("radius_multiplier", "real", (c) => c.notNull())
126
+ .addColumn("method", "text", (c) => c.notNull())
127
+ .addColumn("region", "text", (c) => c.notNull())
128
+ .execute()
129
+ await db.insertInto("interp_calibration").values(row).execute()
130
+ }
131
+
96
132
  /** Create the two probe indexes the reader relies on (postcode-scope, street-scope). */
97
133
  export async function createStreetSegmentIndexes(db: Kysely<StreetSegmentDatabase>): Promise<void> {
98
134
  await db.schema