@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.
- package/candidate-lookup.ts +13 -1
- package/coverage-manifest-schema.ts +210 -0
- package/fst-builder.ts +39 -7
- package/fst-types.ts +20 -0
- package/index.ts +9 -0
- package/interpolation.ts +26 -0
- package/out/candidate-lookup.d.ts +7 -0
- package/out/candidate-lookup.d.ts.map +1 -1
- package/out/candidate-lookup.js +11 -0
- package/out/candidate-lookup.js.map +1 -1
- package/out/coverage-manifest-schema.d.ts +81 -0
- package/out/coverage-manifest-schema.d.ts.map +1 -0
- package/out/coverage-manifest-schema.js +145 -0
- package/out/coverage-manifest-schema.js.map +1 -0
- package/out/fst-builder.d.ts.map +1 -1
- package/out/fst-builder.js +29 -6
- package/out/fst-builder.js.map +1 -1
- package/out/fst-types.d.ts +20 -0
- package/out/fst-types.d.ts.map +1 -1
- package/out/index.d.ts +2 -0
- package/out/index.d.ts.map +1 -1
- package/out/index.js +1 -0
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +6 -0
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +21 -0
- package/out/interpolation.js.map +1 -1
- package/out/street-morphology-fst-loader.d.ts +51 -0
- package/out/street-morphology-fst-loader.d.ts.map +1 -0
- package/out/street-morphology-fst-loader.js +53 -0
- package/out/street-morphology-fst-loader.js.map +1 -0
- package/out/street-segment-schema.d.ts +23 -0
- package/out/street-segment-schema.d.ts.map +1 -1
- package/out/street-segment-schema.js +15 -0
- package/out/street-segment-schema.js.map +1 -1
- package/package.json +13 -5
- package/street-morphology-fst-loader.ts +87 -0
- 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;
|
|
1
|
+
{"version":3,"file":"street-segment-schema.js","sourceRoot":"","sources":["../street-segment-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAyDH;;;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;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,EAAiC,EACjC,GAAyB;IAEzB,MAAM,EAAE,CAAC,MAAM;SACb,WAAW,CAAC,oBAAoB,CAAC;SACjC,SAAS,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC1D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/C,OAAO,EAAE,CAAA;IACX,MAAM,EAAE,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAA;AAChE,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mailwoman/resolver-wof-sqlite",
|
|
3
|
-
"version": "8.
|
|
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.
|
|
238
|
-
"@mailwoman/core": "8.
|
|
239
|
-
"@mailwoman/resolver": "8.
|
|
240
|
-
"@mailwoman/spatial": "8.
|
|
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
|
+
}
|
package/street-segment-schema.ts
CHANGED
|
@@ -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
|