@mailwoman/resolver-wof-sqlite 9.0.0 → 9.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.
- package/README.md +28 -9
- package/address-point-interpolation.ts +18 -8
- package/address-point-schema.ts +18 -6
- package/address-point.ts +111 -18
- package/ancestry.ts +9 -6
- package/build-candidate.ts +287 -157
- package/build-slim.ts +3 -3
- package/candidate/alias-bags.ts +54 -0
- package/candidate/ancestors-sidecar.ts +206 -0
- package/candidate/country-display-names.ts +79 -0
- package/candidate/name-roles.ts +237 -0
- package/candidate/own-name.ts +146 -0
- package/candidate/place-attrs.ts +44 -0
- package/candidate/shard-fold.ts +137 -0
- package/candidate-ancestors-schema.ts +195 -0
- package/candidate-fts.ts +4 -2
- package/candidate-importance.ts +228 -0
- package/candidate-lookup.ts +564 -174
- package/candidate-schema.ts +60 -3
- package/candidate-scoring.ts +268 -0
- package/capital-schema.ts +90 -0
- package/capitals.ts +148 -0
- package/coincident-roles.ts +69 -10
- package/convention-schema.ts +72 -0
- package/convention.ts +2 -2
- package/coverage-manifest-schema.ts +7 -7
- package/currency-backfill.ts +249 -0
- package/exact-match.ts +104 -0
- package/fst-autocomplete.ts +105 -122
- package/fst-builder.ts +39 -47
- package/fst-deserialize-web.ts +43 -7
- package/fst-freshness.ts +2 -2
- package/fst-serialize.ts +68 -12
- package/fst-types.ts +35 -1
- package/fts-query.ts +1 -1
- package/fts.ts +16 -4
- package/geonames-postal.ts +2 -2
- package/index.ts +26 -14
- package/interpolation.ts +113 -19
- package/lookup.ts +118 -560
- package/name-score.ts +6 -4
- package/out/address-point-interpolation.d.ts.map +1 -1
- package/out/address-point-interpolation.js +13 -7
- package/out/address-point-interpolation.js.map +1 -1
- package/out/address-point-schema.d.ts +16 -6
- package/out/address-point-schema.d.ts.map +1 -1
- package/out/address-point-schema.js.map +1 -1
- package/out/address-point.d.ts.map +1 -1
- package/out/address-point.js +70 -14
- package/out/address-point.js.map +1 -1
- package/out/ancestry.d.ts +2 -2
- package/out/ancestry.d.ts.map +1 -1
- package/out/ancestry.js +5 -6
- package/out/ancestry.js.map +1 -1
- package/out/build-candidate.d.ts +108 -0
- package/out/build-candidate.d.ts.map +1 -1
- package/out/build-candidate.js +151 -120
- package/out/build-candidate.js.map +1 -1
- package/out/build-slim.d.ts +1 -1
- package/out/build-slim.js +3 -3
- package/out/build-slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +17 -0
- package/out/candidate/alias-bags.d.ts.map +1 -0
- package/out/candidate/alias-bags.js +39 -0
- package/out/candidate/alias-bags.js.map +1 -0
- package/out/candidate/ancestors-sidecar.d.ts +33 -0
- package/out/candidate/ancestors-sidecar.d.ts.map +1 -0
- package/out/candidate/ancestors-sidecar.js +140 -0
- package/out/candidate/ancestors-sidecar.js.map +1 -0
- package/out/candidate/country-display-names.d.ts +35 -0
- package/out/candidate/country-display-names.d.ts.map +1 -0
- package/out/candidate/country-display-names.js +59 -0
- package/out/candidate/country-display-names.js.map +1 -0
- package/out/candidate/name-roles.d.ts +55 -0
- package/out/candidate/name-roles.d.ts.map +1 -0
- package/out/candidate/name-roles.js +165 -0
- package/out/candidate/name-roles.js.map +1 -0
- package/out/candidate/own-name.d.ts +50 -0
- package/out/candidate/own-name.d.ts.map +1 -0
- package/out/candidate/own-name.js +132 -0
- package/out/candidate/own-name.js.map +1 -0
- package/out/candidate/place-attrs.d.ts +43 -0
- package/out/candidate/place-attrs.d.ts.map +1 -0
- package/out/candidate/place-attrs.js +15 -0
- package/out/candidate/place-attrs.js.map +1 -0
- package/out/candidate/shard-fold.d.ts +31 -0
- package/out/candidate/shard-fold.d.ts.map +1 -0
- package/out/candidate/shard-fold.js +104 -0
- package/out/candidate/shard-fold.js.map +1 -0
- package/out/candidate-ancestors-schema.d.ts +150 -0
- package/out/candidate-ancestors-schema.d.ts.map +1 -0
- package/out/candidate-ancestors-schema.js +123 -0
- package/out/candidate-ancestors-schema.js.map +1 -0
- package/out/candidate-fts.d.ts +4 -2
- package/out/candidate-fts.d.ts.map +1 -1
- package/out/candidate-fts.js +4 -2
- package/out/candidate-fts.js.map +1 -1
- package/out/candidate-importance.d.ts +132 -0
- package/out/candidate-importance.d.ts.map +1 -0
- package/out/candidate-importance.js +174 -0
- package/out/candidate-importance.js.map +1 -0
- package/out/candidate-lookup.d.ts +22 -37
- package/out/candidate-lookup.d.ts.map +1 -1
- package/out/candidate-lookup.js +446 -132
- package/out/candidate-lookup.js.map +1 -1
- package/out/candidate-schema.d.ts +52 -4
- package/out/candidate-schema.d.ts.map +1 -1
- package/out/candidate-schema.js +8 -0
- package/out/candidate-schema.js.map +1 -1
- package/out/candidate-scoring.d.ts +34 -0
- package/out/candidate-scoring.d.ts.map +1 -0
- package/out/candidate-scoring.js +200 -0
- package/out/candidate-scoring.js.map +1 -0
- package/out/capital-schema.d.ts +51 -0
- package/out/capital-schema.d.ts.map +1 -0
- package/out/capital-schema.js +63 -0
- package/out/capital-schema.js.map +1 -0
- package/out/capitals.d.ts +69 -0
- package/out/capitals.d.ts.map +1 -0
- package/out/capitals.js +98 -0
- package/out/capitals.js.map +1 -0
- package/out/coincident-roles.d.ts +7 -0
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +42 -8
- package/out/coincident-roles.js.map +1 -1
- package/out/convention-schema.d.ts +51 -0
- package/out/convention-schema.d.ts.map +1 -0
- package/out/convention-schema.js +34 -0
- package/out/convention-schema.js.map +1 -0
- package/out/convention.d.ts +1 -1
- package/out/convention.js +2 -2
- package/out/coverage-manifest-schema.js +3 -7
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +46 -0
- package/out/currency-backfill.d.ts.map +1 -0
- package/out/currency-backfill.js +180 -0
- package/out/currency-backfill.js.map +1 -0
- package/out/exact-match.d.ts +25 -0
- package/out/exact-match.d.ts.map +1 -0
- package/out/exact-match.js +89 -0
- package/out/exact-match.js.map +1 -0
- package/out/fst-autocomplete.d.ts +24 -14
- package/out/fst-autocomplete.d.ts.map +1 -1
- package/out/fst-autocomplete.js +84 -100
- package/out/fst-autocomplete.js.map +1 -1
- package/out/fst-builder.d.ts.map +1 -1
- package/out/fst-builder.js +32 -40
- package/out/fst-builder.js.map +1 -1
- package/out/fst-deserialize-web.d.ts.map +1 -1
- package/out/fst-deserialize-web.js +36 -7
- package/out/fst-deserialize-web.js.map +1 -1
- package/out/fst-freshness.d.ts +2 -2
- package/out/fst-freshness.js +2 -2
- package/out/fst-serialize.d.ts +14 -4
- package/out/fst-serialize.d.ts.map +1 -1
- package/out/fst-serialize.js +60 -12
- package/out/fst-serialize.js.map +1 -1
- package/out/fst-types.d.ts +35 -1
- package/out/fst-types.d.ts.map +1 -1
- package/out/fts-query.js +1 -1
- package/out/fts-query.js.map +1 -1
- package/out/fts.d.ts +15 -4
- package/out/fts.d.ts.map +1 -1
- package/out/fts.js +15 -4
- package/out/fts.js.map +1 -1
- package/out/geonames-postal.d.ts +2 -2
- package/out/geonames-postal.js +2 -2
- package/out/index.d.ts +4 -2
- package/out/index.d.ts.map +1 -1
- package/out/index.js +3 -2
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +8 -0
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +91 -19
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +4 -5
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +102 -444
- package/out/lookup.js.map +1 -1
- package/out/name-score.d.ts +0 -10
- package/out/name-score.d.ts.map +1 -1
- package/out/name-score.js +6 -4
- package/out/name-score.js.map +1 -1
- package/out/place-importance-schema.d.ts +226 -0
- package/out/place-importance-schema.d.ts.map +1 -0
- package/out/place-importance-schema.js +288 -0
- package/out/place-importance-schema.js.map +1 -0
- package/out/poi-lookup.d.ts +1 -1
- package/out/poi-lookup.d.ts.map +1 -1
- package/out/poi-lookup.js +12 -13
- package/out/poi-lookup.js.map +1 -1
- package/out/poi-schema.d.ts +7 -3
- package/out/poi-schema.d.ts.map +1 -1
- package/out/poi-schema.js.map +1 -1
- package/out/polygon-schema.d.ts +37 -0
- package/out/polygon-schema.d.ts.map +1 -0
- package/out/polygon-schema.js +23 -0
- package/out/polygon-schema.js.map +1 -0
- package/out/postal-city-alias-lookup.d.ts +1 -1
- package/out/postal-city-alias-lookup.js +1 -1
- package/out/postal-city-candidate-schema.d.ts +2 -1
- package/out/postal-city-candidate-schema.d.ts.map +1 -1
- package/out/postal-city-candidate-schema.js.map +1 -1
- package/out/postcode-point-lookup.d.ts +1 -1
- package/out/postcode-point-lookup.js +1 -1
- package/out/primary-preference.d.ts +125 -0
- package/out/primary-preference.d.ts.map +1 -0
- package/out/primary-preference.js +138 -0
- package/out/primary-preference.js.map +1 -0
- package/out/proximity-rerank.d.ts +77 -0
- package/out/proximity-rerank.d.ts.map +1 -0
- package/out/proximity-rerank.js +86 -0
- package/out/proximity-rerank.js.map +1 -0
- package/out/region-keys.d.ts +47 -0
- package/out/region-keys.d.ts.map +1 -0
- package/out/region-keys.js +121 -0
- package/out/region-keys.js.map +1 -0
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +6 -9
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +1 -1
- package/out/search-fetch.d.ts +57 -0
- package/out/search-fetch.d.ts.map +1 -0
- package/out/search-fetch.js +183 -0
- package/out/search-fetch.js.map +1 -0
- package/out/sharding.d.ts +3 -3
- package/out/sharding.js +1 -1
- package/out/sqlite-convention-source.d.ts +1 -1
- package/out/sqlite-convention-source.js +1 -1
- package/out/sqlite-utils.d.ts +31 -1
- package/out/sqlite-utils.d.ts.map +1 -1
- package/out/sqlite-utils.js +38 -0
- package/out/sqlite-utils.js.map +1 -1
- package/out/street-centroid-schema.d.ts +7 -2
- package/out/street-centroid-schema.d.ts.map +1 -1
- package/out/street-centroid-schema.js.map +1 -1
- package/out/street-centroid.d.ts.map +1 -1
- package/out/street-centroid.js +7 -7
- package/out/street-centroid.js.map +1 -1
- package/out/street-morphology-fst-builder.d.ts.map +1 -1
- package/out/street-morphology-fst-builder.js +5 -4
- package/out/street-morphology-fst-builder.js.map +1 -1
- package/out/street-normalize.d.ts +83 -9
- package/out/street-normalize.d.ts.map +1 -1
- package/out/street-normalize.js +177 -10
- package/out/street-normalize.js.map +1 -1
- package/out/street-segment-schema.d.ts +6 -2
- package/out/street-segment-schema.d.ts.map +1 -1
- package/out/street-segment-schema.js.map +1 -1
- package/out/types.d.ts +74 -1
- package/out/types.d.ts.map +1 -1
- package/out/unified-schema.d.ts +1 -1
- package/out/unified-schema.js +1 -1
- package/out/uprn-lookup.d.ts +85 -0
- package/out/uprn-lookup.d.ts.map +1 -0
- package/out/uprn-lookup.js +152 -0
- package/out/uprn-lookup.js.map +1 -0
- package/out/uprn-schema.d.ts +93 -0
- package/out/uprn-schema.d.ts.map +1 -0
- package/out/uprn-schema.js +78 -0
- package/out/uprn-schema.js.map +1 -0
- package/out/weights-overlay-linker.d.ts +141 -0
- package/out/weights-overlay-linker.d.ts.map +1 -0
- package/out/weights-overlay-linker.js +259 -0
- package/out/weights-overlay-linker.js.map +1 -0
- package/package.json +296 -16
- package/place-importance-schema.ts +402 -0
- package/poi-lookup.ts +12 -13
- package/poi-schema.ts +8 -3
- package/polygon-schema.ts +47 -0
- package/postal-city-alias-lookup.ts +1 -1
- package/postal-city-candidate-schema.ts +3 -1
- package/postcode-point-lookup.ts +1 -1
- package/primary-preference.ts +207 -0
- package/proximity-rerank.ts +120 -0
- package/region-keys.ts +144 -0
- package/reverse.ts +17 -16
- package/schema.ts +1 -1
- package/search-fetch.ts +256 -0
- package/sharding.ts +3 -3
- package/sqlite-convention-source.ts +1 -1
- package/sqlite-utils.ts +63 -1
- package/street-centroid-schema.ts +8 -2
- package/street-centroid.ts +13 -8
- package/street-morphology-fst-builder.ts +5 -4
- package/street-normalize.ts +254 -24
- package/street-segment-schema.ts +7 -2
- package/types.ts +74 -1
- package/unified-schema.ts +1 -1
- package/uprn-lookup.ts +210 -0
- package/uprn-schema.ts +124 -0
- package/weights-overlay-linker.ts +377 -0
- package/geo.ts +0 -121
- package/out/geo.d.ts +0 -74
- package/out/geo.d.ts.map +0 -1
- package/out/geo.js +0 -71
- package/out/geo.js.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Typed schema for `uprn.db` — the OS Open UPRN spatial layer: every GB Unique Property Reference
|
|
7
|
+
* Number with its WGS84 point, so mailwoman results can carry UPRN as an interoperability key
|
|
8
|
+
* beside our own `@mailwoman/address-id`. One rowid table keyed `uprn INTEGER PRIMARY KEY` (the
|
|
9
|
+
* rowid alias — the optimal shape for an integer-PK point table; `WITHOUT ROWID` buys nothing
|
|
10
|
+
* here), plus a secondary res-9 `h3_cell` index for the bounded nearest-point probe.
|
|
11
|
+
*
|
|
12
|
+
* ## Coordinates are OS's own WGS84 columns
|
|
13
|
+
*
|
|
14
|
+
* The source CSV publishes BOTH coordinate systems per row — OSGB36 eastings/northings AND WGS84
|
|
15
|
+
* `LATITUDE`/`LONGITUDE`. This layer stores OS's own lat/lon verbatim and never reconverts from
|
|
16
|
+
* eastings: `@mailwoman/spatial`'s `osgb36ToWGS84` is a 7-parameter Helmert with a measured p95 of
|
|
17
|
+
* 4.18 m, and re-deriving what the publisher already computed (with OSTN15, exactly) would replace
|
|
18
|
+
* their answer with a strictly worse one.
|
|
19
|
+
*
|
|
20
|
+
* ## Why `h3_cell` exists at all
|
|
21
|
+
*
|
|
22
|
+
* The layer contract requires every domain row to be addressable by at least one spine key —
|
|
23
|
+
* `writeLayerManifest` throws on a manifest that declares none — and UPRN is its own id space, not
|
|
24
|
+
* H3/WOF/address-id/street. The res-9 short cell (`shortCellToInt`, the same packing as poi.db and
|
|
25
|
+
* the OSM situs shards) is the spine that fits a point table, and its index doubles as the
|
|
26
|
+
* `nearestUPRN` ring probe.
|
|
27
|
+
*
|
|
28
|
+
* The DB also embeds the layer-contract tables from `@mailwoman/core/layers`; the builder
|
|
29
|
+
* (`packages/mailwoman/gazetteer-pipeline/uprn-layer.ts`) writes the manifest and per-res-6-cell
|
|
30
|
+
* coverage.
|
|
31
|
+
*/
|
|
32
|
+
import { shortCellToInt } from "@mailwoman/spatial";
|
|
33
|
+
import { latLngToCell } from "h3-js";
|
|
34
|
+
/**
|
|
35
|
+
* Resolution the `uprn` table's `h3_cell` column is keyed at — the shared layer-spine resolution (poi.db, the OSM situs
|
|
36
|
+
* shards).
|
|
37
|
+
*/
|
|
38
|
+
export const UPRN_H3_RESOLUTION = 9;
|
|
39
|
+
/**
|
|
40
|
+
* Resolution of the layer's `layer_coverage` cells — coarse, per the contract (matches poi.db).
|
|
41
|
+
*/
|
|
42
|
+
export const UPRN_COVERAGE_H3_RESOLUTION = 6;
|
|
43
|
+
/**
|
|
44
|
+
* The full res-9 cell for a UPRN point — the ONE derivation both the builder and every consumer share, so a fixture
|
|
45
|
+
* built by a test and a row built by the real ingest can never disagree on which cell a coordinate keys to.
|
|
46
|
+
*/
|
|
47
|
+
export function uprnFullCell(latitude, longitude) {
|
|
48
|
+
return latLngToCell(latitude, longitude, UPRN_H3_RESOLUTION);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The `h3_cell` column value for a UPRN point: {@link uprnFullCell} packed to the shared 48-bit short-cell integer.
|
|
52
|
+
*/
|
|
53
|
+
export function uprnH3Cell(latitude, longitude) {
|
|
54
|
+
return shortCellToInt(uprnFullCell(latitude, longitude));
|
|
55
|
+
}
|
|
56
|
+
export async function createUPRNTable(db) {
|
|
57
|
+
await db.schema
|
|
58
|
+
.createTable("uprn")
|
|
59
|
+
.addColumn("uprn", "integer", (c) => c.primaryKey())
|
|
60
|
+
.addColumn("lat", "real", (c) => c.notNull())
|
|
61
|
+
.addColumn("lon", "real", (c) => c.notNull())
|
|
62
|
+
.addColumn("h3_cell", "integer", (c) => c.notNull())
|
|
63
|
+
.execute();
|
|
64
|
+
}
|
|
65
|
+
export async function createUPRNMetaTable(db) {
|
|
66
|
+
await db.schema
|
|
67
|
+
.createTable("uprn_meta")
|
|
68
|
+
.addColumn("key", "text", (c) => c.primaryKey())
|
|
69
|
+
.addColumn("value", "text", (c) => c.notNull())
|
|
70
|
+
.execute();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Secondary index for the `nearestUPRN` ring probe. Builders call this AFTER the bulk load (index-after-load).
|
|
74
|
+
*/
|
|
75
|
+
export async function createUPRNIndexes(db) {
|
|
76
|
+
await db.schema.createIndex("uprn_h3_cell").on("uprn").column("h3_cell").execute();
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=uprn-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uprn-schema.js","sourceRoot":"","sources":["../uprn-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAE,cAAc,EAAe,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAGpC;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAEnC;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAA;AAuC5C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,SAAiB;IAC/D,OAAO,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,kBAAkB,CAAW,CAAA;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,SAAiB;IAC7D,OAAO,cAAc,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAwB;IAC7D,MAAM,EAAE,CAAC,MAAM;SACb,WAAW,CAAC,MAAM,CAAC;SACnB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;SACnD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC5C,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC5C,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SACnD,OAAO,EAAE,CAAA;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAwB;IACjE,MAAM,EAAE,CAAC,MAAM;SACb,WAAW,CAAC,WAAW,CAAC;SACxB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;SAC/C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;SAC9C,OAAO,EAAE,CAAA;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EAAwB;IAC/D,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAA;AACnF,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* Shared development tooling for weights overlays: the symlink primitives every overlay linker uses, and the
|
|
6
|
+
* placetype-pair index build.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Replicate `ln -sf SRC DEST` ATOMICALLY: symlink under a temp name, then rename over the destination. A plain
|
|
10
|
+
* unlink-then-symlink leaves a no-file window that concurrent vitest workers can hit mid-suite — bit CI on 2026-07-24.
|
|
11
|
+
* rename(2) replaces the destination atomically.
|
|
12
|
+
*/
|
|
13
|
+
export declare function linkForce(src: string, dest: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Remove a leftover local file/symlink so the #1179 base-weights fallback engages.
|
|
16
|
+
*/
|
|
17
|
+
export declare function removeIfPresent(dest: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Symlink one soft-feed sibling into an overlay, warning rather than failing when the source is absent.
|
|
20
|
+
*
|
|
21
|
+
* Every one of these artifacts is OPTIONAL by design — the runtime has a fallback for each, so a fresh worktree that
|
|
22
|
+
* has not built the gazetteer still geocodes. That is why the miss prints the consequence instead of throwing: the
|
|
23
|
+
* operator needs to know which channel just resolved OFF, not to have the link step abort.
|
|
24
|
+
*/
|
|
25
|
+
export declare function linkSoftFeedSibling(source: string, destination: string, consequenceIfMissing: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* What one pair-index overlay has to say about itself. Everything else is shared.
|
|
28
|
+
*/
|
|
29
|
+
export interface PairIndexOverlay {
|
|
30
|
+
/**
|
|
31
|
+
* Workspace directory name, e.g. `neural-weights-de-de`.
|
|
32
|
+
*/
|
|
33
|
+
packageDir: string;
|
|
34
|
+
/**
|
|
35
|
+
* ISO country code passed to `gazetteer pair-index --country`, and the suffix of the built artifact (`de` →
|
|
36
|
+
* `pair-index-de.bin`). NOT the locale tag: `en-in` builds `pair-index-in.bin`.
|
|
37
|
+
*/
|
|
38
|
+
country: string;
|
|
39
|
+
/**
|
|
40
|
+
* Calibrated magnitudes the locale's bars were measured at. Baked into the artifact's PIX1 header, which is how the
|
|
41
|
+
* freshness check below notices a change to either.
|
|
42
|
+
*/
|
|
43
|
+
delta: number;
|
|
44
|
+
transitionBeta: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The header fields a dev-weights freshness guard reads.
|
|
48
|
+
*/
|
|
49
|
+
export interface PairIndexHeaderFields {
|
|
50
|
+
delta: number;
|
|
51
|
+
transitionBeta: number | undefined;
|
|
52
|
+
parentDelta: number | undefined;
|
|
53
|
+
schemaVersion: number;
|
|
54
|
+
/**
|
|
55
|
+
* One md5 per source the build read, in fold order. Empty on a header that recorded none.
|
|
56
|
+
*/
|
|
57
|
+
sourceMD5s: string[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Minimal PIX1 header reader — magic + header only, reimplemented so a data-only weights package gains no dependency on
|
|
61
|
+
* `@mailwoman/neural` (which pulls onnxruntime-node) to read a few fields. `neural/pair-index-resolver.ts`'s own header
|
|
62
|
+
* parse is the source of truth this must follow.
|
|
63
|
+
*
|
|
64
|
+
* Shared by the overlay build below AND by the four hand-written base linkers
|
|
65
|
+
* (`neural-weights-{en-us,en-gb,en-nz,fr-fr}/scripts/link-dev-weights.ts`), which each carried their own near-copy
|
|
66
|
+
* before 2026-08-04 — the ×5 clone the taste audit named, and the reason three of them were schema-blind while this one
|
|
67
|
+
* was not.
|
|
68
|
+
*/
|
|
69
|
+
export declare function peekPairIndexHeaderFields(path: string): PairIndexHeaderFields;
|
|
70
|
+
/**
|
|
71
|
+
* Md5 of `path`, cached in a standard `md5sum`-format sidecar (`<hash> <filename>`) beside it so a multi-gigabyte
|
|
72
|
+
* source is hashed once per change rather than once per linker run. The sidecar is trusted only when at least as new as
|
|
73
|
+
* the source; a missing or stale sidecar recomputes and rewrites, so the cache self-heals.
|
|
74
|
+
*
|
|
75
|
+
* The shared home for the copies the base linkers (`en-us`, `en-gb`, `en-nz`) each carry — new callers import this one.
|
|
76
|
+
*/
|
|
77
|
+
export declare function md5FileWithSidecar(path: string): Promise<string>;
|
|
78
|
+
/**
|
|
79
|
+
* The calibrated magnitudes a linker bakes into its artifact. `undefined` means the flag is NOT passed and the header
|
|
80
|
+
* carries no such key — a real state, distinct from zero (see `PairIndexHeader.parentDelta`), so the comparison below
|
|
81
|
+
* is `!==` against `undefined` rather than a truthiness test.
|
|
82
|
+
*/
|
|
83
|
+
export interface PairIndexCalibration {
|
|
84
|
+
delta: number;
|
|
85
|
+
transitionBeta?: number;
|
|
86
|
+
parentDelta?: number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Why an existing `pair-index-*.bin` is stale against `expected`, or `undefined` when its header matches. Covers the
|
|
90
|
+
* FORMAT (schemaVersion) and every calibrated magnitude; source-md5 freshness stays with the caller, because each base
|
|
91
|
+
* linker passes a different set of sources and only it knows what they are.
|
|
92
|
+
*
|
|
93
|
+
* One place so a magnitude added to the header cannot be checked by some linkers and not others — which is exactly how
|
|
94
|
+
* three of the four base linkers ended up unable to notice a schema bump.
|
|
95
|
+
*/
|
|
96
|
+
export declare function pairIndexStaleReason(header: PairIndexHeaderFields, expected: PairIndexCalibration): string | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* The PIX1 schema this tree's reader requires. MUST equal `KNOWN_SCHEMA_VERSION` in `neural/pair-index-resolver.ts` —
|
|
99
|
+
* they are two ends of one fact, and this copy exists only because a data-only overlay must not gain a dependency on
|
|
100
|
+
* `@mailwoman/neural` (onnxruntime-node) to read one header field. Bump BOTH in the same commit; a schema bump that
|
|
101
|
+
* leaves this behind makes every dev checkout rebuild-loop or serve an artifact the runtime refuses.
|
|
102
|
+
*
|
|
103
|
+
* The freshness guard must compare it: a guard that checks only delta + source md5 reads a format-obsolete binary as
|
|
104
|
+
* "current" and leaves every dev checkout with artifacts the runtime refuses — the R5 freshness-guard lesson, format
|
|
105
|
+
* edition. Also compared by the four hand-written base linkers (`neural-weights-{en-us,en-gb,en-nz,fr-fr}`), which
|
|
106
|
+
* import this constant rather than re-typing the number.
|
|
107
|
+
*/
|
|
108
|
+
export declare const REQUIRED_PAIR_INDEX_SCHEMA = 3;
|
|
109
|
+
/**
|
|
110
|
+
* Warn when the per-locale FST a linker just symlinked was built from a DIFFERENT admin database than the one on disk
|
|
111
|
+
* now.
|
|
112
|
+
*
|
|
113
|
+
* WHY IT WARNS RATHER THAN REBUILDS, unlike its pair-index sibling above. A pair index is seconds of work and the
|
|
114
|
+
* linker owns its whole recipe. A locale FST is a multi-minute build whose output goes to a STAGING dir on purpose —
|
|
115
|
+
* the swap into `fst-per-locale/` is operator-gated after the battery, because an FST changes decoder behaviour and the
|
|
116
|
+
* D-rule does not let that land unmeasured. So the guard's job is to make the drift impossible to miss, and to name the
|
|
117
|
+
* command that starts fixing it. It is also why a stale FST is never fatal: the artifact is a decode-time bias list,
|
|
118
|
+
* and a dev tree must still run.
|
|
119
|
+
*
|
|
120
|
+
* The source-side half of the comparison lives here rather than in `fst-freshness.ts` for the same reason
|
|
121
|
+
* `pairIndexStaleReason` splits: only the caller knows which database it built against. All three FST-linking base
|
|
122
|
+
* packages build against the same one, so they share this rather than each pinning it.
|
|
123
|
+
*/
|
|
124
|
+
export declare function warnIfFSTStale(fstPath: string, locale: string): void;
|
|
125
|
+
/**
|
|
126
|
+
* Build `pair-index-<country>.bin` into the overlay, skipping the work when the artifact on disk was already built at
|
|
127
|
+
* these magnitudes FROM the admin database on disk.
|
|
128
|
+
*
|
|
129
|
+
* The skip requires both halves (#1734): the header magnitudes (`pairIndexStaleReason`) AND the header's `sourceMD5s`
|
|
130
|
+
* against the current admin DB's md5. Magnitudes alone read a source change as "current" whenever pair counts happen
|
|
131
|
+
* not to move the calibrated numbers — the R5 freshness-guard lesson, which resurfaced in the 2026-08-18 admin swap
|
|
132
|
+
* when all four overlay locales skipped while the md5-checking base linkers rebuilt. This build's ONE source is the
|
|
133
|
+
* admin DB it passes as `--borough-db`, so the comparison is exactly one md5; a linker with a different source list
|
|
134
|
+
* (fr's BAN directory) owns its own guard and must never be pointed at this one.
|
|
135
|
+
*
|
|
136
|
+
* Exits non-zero on a failed build. Missing INPUTS (an unbuilt CLI, an absent WOF database) warn and return instead: a
|
|
137
|
+
* fresh clone has neither, and `yarn test` invokes this to verify auto-resolve, so a hard failure there would be a
|
|
138
|
+
* failure to have run a build yet rather than a real fault.
|
|
139
|
+
*/
|
|
140
|
+
export declare function buildPairIndexOverlay({ packageDir, country, delta, transitionBeta, }: PairIndexOverlay): Promise<void>;
|
|
141
|
+
//# sourceMappingURL=weights-overlay-linker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weights-overlay-linker.d.ts","sourceRoot":"","sources":["../weights-overlay-linker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqBH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUlD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAY9G;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CA2B7E;AAOD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBtE;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,oBAAoB,GAC5B,MAAM,GAAG,SAAS,CAgBpB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAUpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,EAC3C,UAAU,EACV,OAAO,EACP,KAAK,EACL,cAAc,GACd,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFlC"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* Shared development tooling for weights overlays: the symlink primitives every overlay linker uses, and the
|
|
6
|
+
* placetype-pair index build.
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { parseJSONStrict } from "@mailwoman/core/objects";
|
|
12
|
+
import { dataRootPath, md5File, weightsOverlayPath, workspacePath } from "@mailwoman/core/utils";
|
|
13
|
+
import { fstFreshnessWarning } from "./fst-freshness.js";
|
|
14
|
+
/**
|
|
15
|
+
* Replicate `ln -sf SRC DEST` ATOMICALLY: symlink under a temp name, then rename over the destination. A plain
|
|
16
|
+
* unlink-then-symlink leaves a no-file window that concurrent vitest workers can hit mid-suite — bit CI on 2026-07-24.
|
|
17
|
+
* rename(2) replaces the destination atomically.
|
|
18
|
+
*/
|
|
19
|
+
export function linkForce(src, dest) {
|
|
20
|
+
const tmp = `${dest}.tmp-link`;
|
|
21
|
+
if (existsSync(tmp)) {
|
|
22
|
+
unlinkSync(tmp);
|
|
23
|
+
}
|
|
24
|
+
symlinkSync(src, tmp);
|
|
25
|
+
renameSync(tmp, dest);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Remove a leftover local file/symlink so the #1179 base-weights fallback engages.
|
|
29
|
+
*/
|
|
30
|
+
export function removeIfPresent(dest) {
|
|
31
|
+
try {
|
|
32
|
+
lstatSync(dest);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
unlinkSync(dest);
|
|
38
|
+
console.log(`removed stale local ${dest} (base fallback to en-us engages)`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Symlink one soft-feed sibling into an overlay, warning rather than failing when the source is absent.
|
|
42
|
+
*
|
|
43
|
+
* Every one of these artifacts is OPTIONAL by design — the runtime has a fallback for each, so a fresh worktree that
|
|
44
|
+
* has not built the gazetteer still geocodes. That is why the miss prints the consequence instead of throwing: the
|
|
45
|
+
* operator needs to know which channel just resolved OFF, not to have the link step abort.
|
|
46
|
+
*/
|
|
47
|
+
export function linkSoftFeedSibling(source, destination, consequenceIfMissing) {
|
|
48
|
+
if (!existsSync(source)) {
|
|
49
|
+
console.error(`WARNING: missing ${source} — ${consequenceIfMissing}`);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
linkForce(source, destination);
|
|
53
|
+
console.log(`linked ${destination} \u2190 ${source}`);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Minimal PIX1 header reader — magic + header only, reimplemented so a data-only weights package gains no dependency on
|
|
58
|
+
* `@mailwoman/neural` (which pulls onnxruntime-node) to read a few fields. `neural/pair-index-resolver.ts`'s own header
|
|
59
|
+
* parse is the source of truth this must follow.
|
|
60
|
+
*
|
|
61
|
+
* Shared by the overlay build below AND by the four hand-written base linkers
|
|
62
|
+
* (`neural-weights-{en-us,en-gb,en-nz,fr-fr}/scripts/link-dev-weights.ts`), which each carried their own near-copy
|
|
63
|
+
* before 2026-08-04 — the ×5 clone the taste audit named, and the reason three of them were schema-blind while this one
|
|
64
|
+
* was not.
|
|
65
|
+
*/
|
|
66
|
+
export function peekPairIndexHeaderFields(path) {
|
|
67
|
+
const bytes = readFileSync(path);
|
|
68
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
69
|
+
// "PIX1" little-endian.
|
|
70
|
+
const MAGIC = 0x31_58_49_50;
|
|
71
|
+
if (view.getUint32(0, true) !== MAGIC) {
|
|
72
|
+
throw new Error(`pair index: bad magic reading ${path}`);
|
|
73
|
+
}
|
|
74
|
+
const headerLen = view.getUint32(4, true);
|
|
75
|
+
const header = parseJSONStrict(Buffer.from(bytes.subarray(8, 8 + headerLen)).toString("utf8"));
|
|
76
|
+
return {
|
|
77
|
+
delta: header.delta,
|
|
78
|
+
transitionBeta: header.transitionBeta,
|
|
79
|
+
parentDelta: header.parentDelta,
|
|
80
|
+
schemaVersion: header.schemaVersion,
|
|
81
|
+
sourceMD5s: header.sourceMD5s ?? [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Hex characters in an md5 digest.
|
|
86
|
+
*/
|
|
87
|
+
const MD5_HEX_LENGTH = 32;
|
|
88
|
+
/**
|
|
89
|
+
* Md5 of `path`, cached in a standard `md5sum`-format sidecar (`<hash> <filename>`) beside it so a multi-gigabyte
|
|
90
|
+
* source is hashed once per change rather than once per linker run. The sidecar is trusted only when at least as new as
|
|
91
|
+
* the source; a missing or stale sidecar recomputes and rewrites, so the cache self-heals.
|
|
92
|
+
*
|
|
93
|
+
* The shared home for the copies the base linkers (`en-us`, `en-gb`, `en-nz`) each carry — new callers import this one.
|
|
94
|
+
*/
|
|
95
|
+
export async function md5FileWithSidecar(path) {
|
|
96
|
+
const sidecarPath = `${path}.md5`;
|
|
97
|
+
const sourceStats = statSync(path);
|
|
98
|
+
if (existsSync(sidecarPath)) {
|
|
99
|
+
try {
|
|
100
|
+
if (statSync(sidecarPath).mtime >= sourceStats.mtime) {
|
|
101
|
+
const [hash] = readFileSync(sidecarPath, "utf8").trim().split(/\s+/);
|
|
102
|
+
if (hash && hash.length === MD5_HEX_LENGTH)
|
|
103
|
+
return hash;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Unreadable sidecar — recompute below.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const hash = await md5File(path);
|
|
111
|
+
const filename = path.split(/[/\\]/).pop() || path;
|
|
112
|
+
writeFileSync(sidecarPath, `${hash} ${filename}\n`);
|
|
113
|
+
return hash;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Why an existing `pair-index-*.bin` is stale against `expected`, or `undefined` when its header matches. Covers the
|
|
117
|
+
* FORMAT (schemaVersion) and every calibrated magnitude; source-md5 freshness stays with the caller, because each base
|
|
118
|
+
* linker passes a different set of sources and only it knows what they are.
|
|
119
|
+
*
|
|
120
|
+
* One place so a magnitude added to the header cannot be checked by some linkers and not others — which is exactly how
|
|
121
|
+
* three of the four base linkers ended up unable to notice a schema bump.
|
|
122
|
+
*/
|
|
123
|
+
export function pairIndexStaleReason(header, expected) {
|
|
124
|
+
if (header.schemaVersion !== REQUIRED_PAIR_INDEX_SCHEMA) {
|
|
125
|
+
return `schemaVersion ${header.schemaVersion} → ${REQUIRED_PAIR_INDEX_SCHEMA}`;
|
|
126
|
+
}
|
|
127
|
+
if (header.delta !== expected.delta)
|
|
128
|
+
return `delta ${header.delta} → ${expected.delta}`;
|
|
129
|
+
if (header.transitionBeta !== expected.transitionBeta) {
|
|
130
|
+
return `transitionBeta ${header.transitionBeta ?? "(absent)"} → ${expected.transitionBeta ?? "(absent)"}`;
|
|
131
|
+
}
|
|
132
|
+
if (header.parentDelta !== expected.parentDelta) {
|
|
133
|
+
return `parentDelta ${header.parentDelta ?? "(absent)"} → ${expected.parentDelta ?? "(absent)"}`;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The PIX1 schema this tree's reader requires. MUST equal `KNOWN_SCHEMA_VERSION` in `neural/pair-index-resolver.ts` —
|
|
139
|
+
* they are two ends of one fact, and this copy exists only because a data-only overlay must not gain a dependency on
|
|
140
|
+
* `@mailwoman/neural` (onnxruntime-node) to read one header field. Bump BOTH in the same commit; a schema bump that
|
|
141
|
+
* leaves this behind makes every dev checkout rebuild-loop or serve an artifact the runtime refuses.
|
|
142
|
+
*
|
|
143
|
+
* The freshness guard must compare it: a guard that checks only delta + source md5 reads a format-obsolete binary as
|
|
144
|
+
* "current" and leaves every dev checkout with artifacts the runtime refuses — the R5 freshness-guard lesson, format
|
|
145
|
+
* edition. Also compared by the four hand-written base linkers (`neural-weights-{en-us,en-gb,en-nz,fr-fr}`), which
|
|
146
|
+
* import this constant rather than re-typing the number.
|
|
147
|
+
*/
|
|
148
|
+
export const REQUIRED_PAIR_INDEX_SCHEMA = 3;
|
|
149
|
+
/**
|
|
150
|
+
* Warn when the per-locale FST a linker just symlinked was built from a DIFFERENT admin database than the one on disk
|
|
151
|
+
* now.
|
|
152
|
+
*
|
|
153
|
+
* WHY IT WARNS RATHER THAN REBUILDS, unlike its pair-index sibling above. A pair index is seconds of work and the
|
|
154
|
+
* linker owns its whole recipe. A locale FST is a multi-minute build whose output goes to a STAGING dir on purpose —
|
|
155
|
+
* the swap into `fst-per-locale/` is operator-gated after the battery, because an FST changes decoder behaviour and the
|
|
156
|
+
* D-rule does not let that land unmeasured. So the guard's job is to make the drift impossible to miss, and to name the
|
|
157
|
+
* command that starts fixing it. It is also why a stale FST is never fatal: the artifact is a decode-time bias list,
|
|
158
|
+
* and a dev tree must still run.
|
|
159
|
+
*
|
|
160
|
+
* The source-side half of the comparison lives here rather than in `fst-freshness.ts` for the same reason
|
|
161
|
+
* `pairIndexStaleReason` splits: only the caller knows which database it built against. All three FST-linking base
|
|
162
|
+
* packages build against the same one, so they share this rather than each pinning it.
|
|
163
|
+
*/
|
|
164
|
+
export function warnIfFSTStale(fstPath, locale) {
|
|
165
|
+
const warning = fstFreshnessWarning({
|
|
166
|
+
fstPath,
|
|
167
|
+
sourceDBPath: String(dataRootPath("wof", "admin-global-priority.db")),
|
|
168
|
+
rebuildCommand: `node packages/mailwoman/out/cli.js gazetteer build fst --locales ${locale} (writes to a staging dir; swap is operator-gated)`,
|
|
169
|
+
});
|
|
170
|
+
if (warning) {
|
|
171
|
+
console.error(warning);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Build `pair-index-<country>.bin` into the overlay, skipping the work when the artifact on disk was already built at
|
|
176
|
+
* these magnitudes FROM the admin database on disk.
|
|
177
|
+
*
|
|
178
|
+
* The skip requires both halves (#1734): the header magnitudes (`pairIndexStaleReason`) AND the header's `sourceMD5s`
|
|
179
|
+
* against the current admin DB's md5. Magnitudes alone read a source change as "current" whenever pair counts happen
|
|
180
|
+
* not to move the calibrated numbers — the R5 freshness-guard lesson, which resurfaced in the 2026-08-18 admin swap
|
|
181
|
+
* when all four overlay locales skipped while the md5-checking base linkers rebuilt. This build's ONE source is the
|
|
182
|
+
* admin DB it passes as `--borough-db`, so the comparison is exactly one md5; a linker with a different source list
|
|
183
|
+
* (fr's BAN directory) owns its own guard and must never be pointed at this one.
|
|
184
|
+
*
|
|
185
|
+
* Exits non-zero on a failed build. Missing INPUTS (an unbuilt CLI, an absent WOF database) warn and return instead: a
|
|
186
|
+
* fresh clone has neither, and `yarn test` invokes this to verify auto-resolve, so a hard failure there would be a
|
|
187
|
+
* failure to have run a build yet rather than a real fault.
|
|
188
|
+
*/
|
|
189
|
+
export async function buildPairIndexOverlay({ packageDir, country, delta, transitionBeta, }) {
|
|
190
|
+
const CLI = String(workspacePath("mailwoman", "out", "cli.js"));
|
|
191
|
+
const ARTIFACT = `pair-index-${country}.bin`;
|
|
192
|
+
// Built into the data-root OVERLAY, not into the tracked package. The locale is recovered from the
|
|
193
|
+
// workspace name (`neural-weights-en-gb` → `en-gb`) so callers keep passing the one identifier they
|
|
194
|
+
// already had; the alternative was a second parameter every caller would have to keep in step with the
|
|
195
|
+
// first, which is the drift this whole rollout is removing.
|
|
196
|
+
const PKG_DIR = String(weightsOverlayPath(packageDir.replace(/^neural-weights-/, "")));
|
|
197
|
+
const DEST = resolve(PKG_DIR, ARTIFACT);
|
|
198
|
+
mkdirSync(PKG_DIR, { recursive: true });
|
|
199
|
+
/**
|
|
200
|
+
* Checked-in WOF-derived admin pairs — the same posture as the GB secondary sources.
|
|
201
|
+
*/
|
|
202
|
+
const WOF_ADMIN_DB = String(dataRootPath("wof", "admin-global-priority.db"));
|
|
203
|
+
if (!existsSync(CLI)) {
|
|
204
|
+
console.error(`WARNING: ${CLI} not built — run \`yarn compile\` first, then re-run for ${ARTIFACT}.`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!existsSync(WOF_ADMIN_DB)) {
|
|
208
|
+
console.error(`WARNING: missing ${WOF_ADMIN_DB} — ${ARTIFACT} not built.`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (existsSync(DEST)) {
|
|
212
|
+
try {
|
|
213
|
+
// No `parentDelta` in the expectation: the overlay locales (de/in/es/it) ship WITHOUT the whole-edge
|
|
214
|
+
// parent bias — unmeasured there, and the D-rule's answer to an unmeasured locale is a per-locale
|
|
215
|
+
// gate, not an inherited magnitude. `PairIndexOverlay` therefore has no `parentDelta` field to pass;
|
|
216
|
+
// adding one is a deliberate act that should arrive with a board.
|
|
217
|
+
const header = peekPairIndexHeaderFields(DEST);
|
|
218
|
+
const reason = pairIndexStaleReason(header, { delta, transitionBeta });
|
|
219
|
+
if (reason) {
|
|
220
|
+
console.log(`rebuilding ${ARTIFACT} — ${reason}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// The source-md5 half (#1734). This build reads exactly one source, so one md5; an artifact that
|
|
224
|
+
// recorded none, or a different count, predates the stamp and is stale by that fact alone.
|
|
225
|
+
const adminMD5 = await md5FileWithSidecar(WOF_ADMIN_DB);
|
|
226
|
+
if (header.sourceMD5s.length === 1 && header.sourceMD5s[0] === adminMD5) {
|
|
227
|
+
console.log(`skipped ${ARTIFACT} build — ${DEST} is current (magnitudes + source md5 match)`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
console.log(`rebuilding ${ARTIFACT} — header source md5s [${header.sourceMD5s.join(", ") || "(none recorded)"}] != ` +
|
|
231
|
+
`current admin DB [${adminMD5}]`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
console.log(`rebuilding ${ARTIFACT} — header unreadable (${error.message})`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const result = spawnSync(process.execPath, [
|
|
239
|
+
CLI,
|
|
240
|
+
"gazetteer",
|
|
241
|
+
"pair-index",
|
|
242
|
+
"--out",
|
|
243
|
+
PKG_DIR,
|
|
244
|
+
"--country",
|
|
245
|
+
country,
|
|
246
|
+
"--delta",
|
|
247
|
+
String(delta),
|
|
248
|
+
"--transition-beta",
|
|
249
|
+
String(transitionBeta),
|
|
250
|
+
"--borough-db",
|
|
251
|
+
WOF_ADMIN_DB,
|
|
252
|
+
], { stdio: "inherit" });
|
|
253
|
+
if (result.status !== 0 || !existsSync(DEST)) {
|
|
254
|
+
console.error(`FAILED: gazetteer pair-index --country ${country} (exit ${result.status})`);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
console.log(`built ${ARTIFACT} ← ${WOF_ADMIN_DB}`);
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=weights-overlay-linker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weights-overlay-linker.js","sourceRoot":"","sources":["../weights-overlay-linker.ts"],"names":[],"mappings":"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"}
|