@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.
- package/README.md +250 -0
- package/out/address-point-interpolation.d.ts +48 -0
- package/out/address-point-interpolation.d.ts.map +1 -0
- package/out/address-point-interpolation.js +164 -0
- package/out/address-point-interpolation.js.map +1 -0
- package/out/address-point-schema.d.ts +58 -0
- package/out/address-point-schema.d.ts.map +1 -0
- package/out/address-point-schema.js +67 -0
- package/out/address-point-schema.js.map +1 -0
- package/out/address-point.d.ts +29 -0
- package/out/address-point.d.ts.map +1 -0
- package/out/address-point.js +62 -0
- package/out/address-point.js.map +1 -0
- package/out/ancestry.d.ts +40 -0
- package/out/ancestry.d.ts.map +1 -0
- package/out/ancestry.js +53 -0
- package/out/ancestry.js.map +1 -0
- package/out/build-candidate-cli.d.ts +16 -0
- package/out/build-candidate-cli.d.ts.map +1 -0
- package/out/build-candidate-cli.js +80 -0
- package/out/build-candidate-cli.js.map +1 -0
- package/out/build-candidate.d.ts +54 -0
- package/out/build-candidate.d.ts.map +1 -0
- package/out/build-candidate.js +230 -0
- package/out/build-candidate.js.map +1 -0
- package/out/build-coincident-roles-cli.d.ts +16 -0
- package/out/build-coincident-roles-cli.d.ts.map +1 -0
- package/out/build-coincident-roles-cli.js +94 -0
- package/out/build-coincident-roles-cli.js.map +1 -0
- package/out/build-fts-cli.d.ts +23 -0
- package/out/build-fts-cli.d.ts.map +1 -0
- package/out/build-fts-cli.js +117 -0
- package/out/build-fts-cli.js.map +1 -0
- package/out/build-slim-cli.d.ts +14 -0
- package/out/build-slim-cli.d.ts.map +1 -0
- package/out/build-slim-cli.js +130 -0
- package/out/build-slim-cli.js.map +1 -0
- package/out/build-slim.d.ts +71 -0
- package/out/build-slim.d.ts.map +1 -0
- package/out/build-slim.js +267 -0
- package/out/build-slim.js.map +1 -0
- package/out/candidate-lookup.d.ts +43 -0
- package/out/candidate-lookup.d.ts.map +1 -0
- package/out/candidate-lookup.js +191 -0
- package/out/candidate-lookup.js.map +1 -0
- package/out/candidate-schema.d.ts +86 -0
- package/out/candidate-schema.d.ts.map +1 -0
- package/out/candidate-schema.js +109 -0
- package/out/candidate-schema.js.map +1 -0
- package/out/coincident-roles.d.ts +86 -0
- package/out/coincident-roles.d.ts.map +1 -0
- package/out/coincident-roles.js +160 -0
- package/out/coincident-roles.js.map +1 -0
- package/out/convention.d.ts +109 -0
- package/out/convention.d.ts.map +1 -0
- package/out/convention.js +94 -0
- package/out/convention.js.map +1 -0
- package/out/fst-autocomplete.d.ts +49 -0
- package/out/fst-autocomplete.d.ts.map +1 -0
- package/out/fst-autocomplete.js +124 -0
- package/out/fst-autocomplete.js.map +1 -0
- package/out/fst-builder.d.ts +20 -0
- package/out/fst-builder.d.ts.map +1 -0
- package/out/fst-builder.js +219 -0
- package/out/fst-builder.js.map +1 -0
- package/out/fst-deserialize-web.d.ts +16 -0
- package/out/fst-deserialize-web.d.ts.map +1 -0
- package/out/fst-deserialize-web.js +133 -0
- package/out/fst-deserialize-web.js.map +1 -0
- package/out/fst-matcher.d.ts +33 -0
- package/out/fst-matcher.d.ts.map +1 -0
- package/out/fst-matcher.js +117 -0
- package/out/fst-matcher.js.map +1 -0
- package/out/fst-serialize.d.ts +30 -0
- package/out/fst-serialize.d.ts.map +1 -0
- package/out/fst-serialize.js +261 -0
- package/out/fst-serialize.js.map +1 -0
- package/out/fst-types.d.ts +60 -0
- package/out/fst-types.d.ts.map +1 -0
- package/out/fst-types.js +11 -0
- package/out/fst-types.js.map +1 -0
- package/out/fts.d.ts +158 -0
- package/out/fts.d.ts.map +1 -0
- package/out/fts.js +261 -0
- package/out/fts.js.map +1 -0
- package/out/geo.d.ts +74 -0
- package/out/geo.d.ts.map +1 -0
- package/out/geo.js +88 -0
- package/out/geo.js.map +1 -0
- package/out/index.d.ts +27 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +22 -0
- package/out/index.js.map +1 -0
- package/out/interpolation.d.ts +84 -0
- package/out/interpolation.d.ts.map +1 -0
- package/out/interpolation.js +150 -0
- package/out/interpolation.js.map +1 -0
- package/out/lookup.d.ts +156 -0
- package/out/lookup.d.ts.map +1 -0
- package/out/lookup.js +876 -0
- package/out/lookup.js.map +1 -0
- package/out/postal-city-alias-lookup.d.ts +50 -0
- package/out/postal-city-alias-lookup.d.ts.map +1 -0
- package/out/postal-city-alias-lookup.js +66 -0
- package/out/postal-city-alias-lookup.js.map +1 -0
- package/out/postal-city-alias-schema.d.ts +51 -0
- package/out/postal-city-alias-schema.d.ts.map +1 -0
- package/out/postal-city-alias-schema.js +47 -0
- package/out/postal-city-alias-schema.js.map +1 -0
- package/out/postal-city-candidate-schema.d.ts +58 -0
- package/out/postal-city-candidate-schema.d.ts.map +1 -0
- package/out/postal-city-candidate-schema.js +56 -0
- package/out/postal-city-candidate-schema.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +38 -0
- package/out/postcode-point-lookup.d.ts.map +1 -0
- package/out/postcode-point-lookup.js +46 -0
- package/out/postcode-point-lookup.js.map +1 -0
- package/out/reverse.d.ts +99 -0
- package/out/reverse.d.ts.map +1 -0
- package/out/reverse.js +290 -0
- package/out/reverse.js.map +1 -0
- package/out/schema.d.ts +163 -0
- package/out/schema.d.ts.map +1 -0
- package/out/schema.js +18 -0
- package/out/schema.js.map +1 -0
- package/out/sharding.d.ts +96 -0
- package/out/sharding.d.ts.map +1 -0
- package/out/sharding.js +129 -0
- package/out/sharding.js.map +1 -0
- package/out/sqlite-convention-source.d.ts +29 -0
- package/out/sqlite-convention-source.d.ts.map +1 -0
- package/out/sqlite-convention-source.js +53 -0
- package/out/sqlite-convention-source.js.map +1 -0
- package/out/sqlite-utils.d.ts +17 -0
- package/out/sqlite-utils.d.ts.map +1 -0
- package/out/sqlite-utils.js +24 -0
- package/out/sqlite-utils.js.map +1 -0
- package/out/street-morphology-fst-builder.d.ts +59 -0
- package/out/street-morphology-fst-builder.d.ts.map +1 -0
- package/out/street-morphology-fst-builder.js +174 -0
- package/out/street-morphology-fst-builder.js.map +1 -0
- package/out/street-normalize.d.ts +66 -0
- package/out/street-normalize.d.ts.map +1 -0
- package/out/street-normalize.js +176 -0
- package/out/street-normalize.js.map +1 -0
- package/out/street-segment-schema.d.ts +61 -0
- package/out/street-segment-schema.d.ts.map +1 -0
- package/out/street-segment-schema.js +64 -0
- package/out/street-segment-schema.js.map +1 -0
- package/out/types.d.ts +137 -0
- package/out/types.d.ts.map +1 -0
- package/out/types.js +13 -0
- package/out/types.js.map +1 -0
- package/out/unified-schema.d.ts +25 -0
- package/out/unified-schema.d.ts.map +1 -0
- package/out/unified-schema.js +142 -0
- package/out/unified-schema.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Typed schema for the byte-range CANDIDATE gazetteer (`candidate.db`) — the single source of truth
|
|
7
|
+
* for the columns shared by the BUILDER ({@link buildCandidateTable}) and the READERS (the Node
|
|
8
|
+
* {@link WofCandidateTableLookup} + the browser `httpvfs-resolver.ts`). Before this module each
|
|
9
|
+
* side hand-wrote the column list; a rename in one place broke the other at runtime. Now the
|
|
10
|
+
* contract is a Kysely `Database` interface (`new DatabaseClient<CandidateDatabase>(...)` for
|
|
11
|
+
* typed inserts) plus the table DDL as strings — so a column change is a compile error on every
|
|
12
|
+
* consumer.
|
|
13
|
+
*
|
|
14
|
+
* `cand_stage` is the transient staging table the builder bulk-loads; `candidate` is the clustered
|
|
15
|
+
* `WITHOUT ROWID` B-tree it's materialized into (same columns). The reader queries `candidate`.
|
|
16
|
+
*/
|
|
17
|
+
import { sql } from "kysely";
|
|
18
|
+
/**
|
|
19
|
+
* The `candidate`/`cand_stage` columns in clustered-key order. The materialization `INSERT INTO
|
|
20
|
+
* candidate SELECT … FROM cand_stage` derives its column list from this, so the two tables can't
|
|
21
|
+
* drift. Keep in sync with {@link CandidateTable}.
|
|
22
|
+
*/
|
|
23
|
+
export const CANDIDATE_COLUMNS = [
|
|
24
|
+
"name_key",
|
|
25
|
+
"country_id",
|
|
26
|
+
"region_id",
|
|
27
|
+
"placetype_id",
|
|
28
|
+
"neg_rank",
|
|
29
|
+
"spr_id",
|
|
30
|
+
"name",
|
|
31
|
+
"latitude",
|
|
32
|
+
"longitude",
|
|
33
|
+
"min_lat",
|
|
34
|
+
"min_lon",
|
|
35
|
+
"max_lat",
|
|
36
|
+
"max_lon",
|
|
37
|
+
"population",
|
|
38
|
+
"is_primary",
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Create the code dictionaries + the transient staging table — called before the build's load
|
|
42
|
+
* passes. `cand_stage` mirrors {@link CandidateTable} but every column is nullable (the loader fills
|
|
43
|
+
* them positionally). Pass a {@link DatabaseClient} (or any `Kysely`) over the candidate DB.
|
|
44
|
+
*/
|
|
45
|
+
export async function createCandidateStagingTables(db) {
|
|
46
|
+
await db.schema
|
|
47
|
+
.createTable("country_codes")
|
|
48
|
+
.addColumn("id", "integer", (c) => c.primaryKey())
|
|
49
|
+
.addColumn("code", "text", (c) => c.unique())
|
|
50
|
+
.execute();
|
|
51
|
+
await db.schema
|
|
52
|
+
.createTable("placetype_codes")
|
|
53
|
+
.addColumn("id", "integer", (c) => c.primaryKey())
|
|
54
|
+
.addColumn("placetype", "text", (c) => c.unique())
|
|
55
|
+
.execute();
|
|
56
|
+
await db.schema
|
|
57
|
+
.createTable("cand_stage")
|
|
58
|
+
.addColumn("name_key", "text")
|
|
59
|
+
.addColumn("country_id", "integer")
|
|
60
|
+
.addColumn("region_id", "integer")
|
|
61
|
+
.addColumn("placetype_id", "integer")
|
|
62
|
+
.addColumn("neg_rank", "real")
|
|
63
|
+
.addColumn("spr_id", "integer")
|
|
64
|
+
.addColumn("name", "text")
|
|
65
|
+
.addColumn("latitude", "real")
|
|
66
|
+
.addColumn("longitude", "real")
|
|
67
|
+
.addColumn("min_lat", "real")
|
|
68
|
+
.addColumn("min_lon", "real")
|
|
69
|
+
.addColumn("max_lat", "real")
|
|
70
|
+
.addColumn("max_lon", "real")
|
|
71
|
+
.addColumn("population", "integer")
|
|
72
|
+
.addColumn("is_primary", "integer")
|
|
73
|
+
.execute();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create the clustered `WITHOUT ROWID` lookup table — called after staging, before the VACUUM. The
|
|
77
|
+
* first six columns form the clustered primary key (population-ranked via `neg_rank`).
|
|
78
|
+
*/
|
|
79
|
+
export async function createCandidateTable(db) {
|
|
80
|
+
await db.schema
|
|
81
|
+
.createTable("candidate")
|
|
82
|
+
.addColumn("name_key", "text", (c) => c.notNull())
|
|
83
|
+
.addColumn("country_id", "integer", (c) => c.notNull())
|
|
84
|
+
.addColumn("region_id", "integer", (c) => c.notNull())
|
|
85
|
+
.addColumn("placetype_id", "integer", (c) => c.notNull())
|
|
86
|
+
.addColumn("neg_rank", "real", (c) => c.notNull())
|
|
87
|
+
.addColumn("spr_id", "integer", (c) => c.notNull())
|
|
88
|
+
.addColumn("name", "text")
|
|
89
|
+
.addColumn("latitude", "real")
|
|
90
|
+
.addColumn("longitude", "real")
|
|
91
|
+
.addColumn("min_lat", "real")
|
|
92
|
+
.addColumn("min_lon", "real")
|
|
93
|
+
.addColumn("max_lat", "real")
|
|
94
|
+
.addColumn("max_lon", "real")
|
|
95
|
+
.addColumn("population", "integer")
|
|
96
|
+
.addColumn("is_primary", "integer")
|
|
97
|
+
.addPrimaryKeyConstraint("candidate_pk", [
|
|
98
|
+
"name_key",
|
|
99
|
+
"country_id",
|
|
100
|
+
"region_id",
|
|
101
|
+
"placetype_id",
|
|
102
|
+
"neg_rank",
|
|
103
|
+
"spr_id",
|
|
104
|
+
])
|
|
105
|
+
// `WITHOUT ROWID` has no first-class builder; the raw modifier is the idiomatic fallback.
|
|
106
|
+
.modifyEnd(sql `without rowid`)
|
|
107
|
+
.execute();
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=candidate-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidate-schema.js","sourceRoot":"","sources":["../candidate-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `buildCoincidentRoles` — derives the **coincident-roles relation** (#403, epic #402) into the
|
|
7
|
+
* unified gazetteer.
|
|
8
|
+
*
|
|
9
|
+
* Many places occupy MULTIPLE admin tiers under one name: German city-states (Berlin/Hamburg/Bremen
|
|
10
|
+
* = city == state), Italian provinces named after their capital (Milano, Varese…), Spanish
|
|
11
|
+
* provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals
|
|
12
|
+
* (Utrecht/Groningen), Shanghai. When an address surfaces only the admin role (the parser drops
|
|
13
|
+
* the locality span), the resolver has no locality to place. The hierarchy-completion step (#405)
|
|
14
|
+
* repairs that by consulting THIS relation; the table replaces #387's hardcoded 15 km constant
|
|
15
|
+
* with the gazetteer's own structure, so the runtime is an O(1) membership lookup with no
|
|
16
|
+
* distance math.
|
|
17
|
+
*
|
|
18
|
+
* V1 is REGION-tier only (admin.placetype = `region`): the ~124 places matching the census across 9
|
|
19
|
+
* countries (IT/ES/GB/JP/KR/FR/DE/NL/CN). County-tier same-name coincidences are deliberately
|
|
20
|
+
* excluded — they're dominated by French cantons and JP counties (admin subdivisions named after
|
|
21
|
+
* a seat town, not dual-role cities) that don't hit the parser-drops-locality failure; genuine
|
|
22
|
+
* consolidated city-counties (US SF/Denver) are a separate follow-up needing a relative-size
|
|
23
|
+
* filter.
|
|
24
|
+
*
|
|
25
|
+
* A pair `(admin, locality)` is recorded when all hold: same `name` (case-insensitive), the
|
|
26
|
+
* locality is a `descendant` of the admin (via the `ancestors` table), and their centroids are
|
|
27
|
+
* within a RELATIVE tolerance — `toleranceFraction × admin-bbox-diagonal`, floored at
|
|
28
|
+
* `minToleranceKm`. The relative term lets a large Italian province admit a city ~tens of km from
|
|
29
|
+
* its centroid while a tiny city-state stays tight; the floor catches city-states whose bbox is
|
|
30
|
+
* small (Bremen's centroids sit 9.3 km apart). The tolerance lives ONLY here at build time — it
|
|
31
|
+
* never enters the resolver hot path.
|
|
32
|
+
*
|
|
33
|
+
* `relationship_type` is recorded for debuggability / deferred per-type behavior; v1 completion is
|
|
34
|
+
* uniform (see #405). It's a coarse classification, not critical.
|
|
35
|
+
*
|
|
36
|
+
* Mirrors the derived-table builder pattern in `fts.ts` (`buildPlaceSearchFts`). Run incrementally
|
|
37
|
+
* against an existing `admin-global-priority.db` via `build-coincident-roles-cli.ts`; should also
|
|
38
|
+
* be wired as a post-step of the main `scripts/build-unified-wof.ts`.
|
|
39
|
+
*/
|
|
40
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
41
|
+
export declare const COINCIDENT_ROLES_TABLE = "coincident_roles";
|
|
42
|
+
/** A place that plays multiple admin roles — one row of the relation, keyed by `admin_id`. */
|
|
43
|
+
export interface CoincidentRole {
|
|
44
|
+
localityId: number;
|
|
45
|
+
relationshipType: "city-state" | "capital-seat" | "consolidated-county";
|
|
46
|
+
adminPlacetype: string;
|
|
47
|
+
distanceKm: number;
|
|
48
|
+
population: number;
|
|
49
|
+
}
|
|
50
|
+
export interface BuildCoincidentRolesOpts {
|
|
51
|
+
/** Drop + rebuild the table if it already exists. Default true (the build is cheap + idempotent). */
|
|
52
|
+
drop?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Relative tolerance: a pair is kept when centroid distance ≤ `toleranceFraction ×
|
|
55
|
+
* bbox-diagonal`. Default 0.15.
|
|
56
|
+
*/
|
|
57
|
+
toleranceFraction?: number;
|
|
58
|
+
/** Floor (km) under the relative tolerance, so small-bbox city-states still qualify. Default 12. */
|
|
59
|
+
minToleranceKm?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Centroid distance (km) below which a region-tier pair is classed `city-state` (metadata only).
|
|
62
|
+
* Default 2.
|
|
63
|
+
*/
|
|
64
|
+
cityStateMaxKm?: number;
|
|
65
|
+
onProgress?: (phase: string, detail?: string) => void;
|
|
66
|
+
}
|
|
67
|
+
export interface BuildCoincidentRolesResult {
|
|
68
|
+
created: boolean;
|
|
69
|
+
rowCount: number;
|
|
70
|
+
byCountry: Record<string, number>;
|
|
71
|
+
durationMs: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Derive the coincident-roles relation into `db`. Additive — only creates/replaces the
|
|
75
|
+
* `coincident_roles` table; never touches `spr`/`names`/`ancestors`. Idempotent.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildCoincidentRoles(db: DatabaseSync, opts?: BuildCoincidentRolesOpts): BuildCoincidentRolesResult;
|
|
78
|
+
/** True iff the relation table exists. Used by the resolver to decide whether completion can run. */
|
|
79
|
+
export declare function coincidentRolesExists(db: DatabaseSync): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Load the relation into an in-memory map keyed by `admin_id` for O(1) runtime lookup (#405). Each
|
|
82
|
+
* admin may map to MULTIPLE same-name descendants; the consumer disambiguates (min distance →
|
|
83
|
+
* population → abstain). Returns an empty map when the table is absent.
|
|
84
|
+
*/
|
|
85
|
+
export declare function loadCoincidentRoles(db: DatabaseSync): Map<number, CoincidentRole[]>;
|
|
86
|
+
//# sourceMappingURL=coincident-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coincident-roles.d.ts","sourceRoot":"","sources":["../coincident-roles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,eAAO,MAAM,sBAAsB,qBAAqB,CAAA;AAExD,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,YAAY,GAAG,cAAc,GAAG,qBAAqB,CAAA;IACvE,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,wBAAwB;IACxC,qGAAqG;IACrG,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,oGAAoG;IACpG,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACrD;AAED,MAAM,WAAW,0BAA0B;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,CAAA;CAClB;AAsBD;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,EAAE,EAAE,YAAY,EAChB,IAAI,GAAE,wBAA6B,GACjC,0BAA0B,CAmF5B;AAED,qGAAqG;AACrG,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CA6BnF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `buildCoincidentRoles` — derives the **coincident-roles relation** (#403, epic #402) into the
|
|
7
|
+
* unified gazetteer.
|
|
8
|
+
*
|
|
9
|
+
* Many places occupy MULTIPLE admin tiers under one name: German city-states (Berlin/Hamburg/Bremen
|
|
10
|
+
* = city == state), Italian provinces named after their capital (Milano, Varese…), Spanish
|
|
11
|
+
* provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals
|
|
12
|
+
* (Utrecht/Groningen), Shanghai. When an address surfaces only the admin role (the parser drops
|
|
13
|
+
* the locality span), the resolver has no locality to place. The hierarchy-completion step (#405)
|
|
14
|
+
* repairs that by consulting THIS relation; the table replaces #387's hardcoded 15 km constant
|
|
15
|
+
* with the gazetteer's own structure, so the runtime is an O(1) membership lookup with no
|
|
16
|
+
* distance math.
|
|
17
|
+
*
|
|
18
|
+
* V1 is REGION-tier only (admin.placetype = `region`): the ~124 places matching the census across 9
|
|
19
|
+
* countries (IT/ES/GB/JP/KR/FR/DE/NL/CN). County-tier same-name coincidences are deliberately
|
|
20
|
+
* excluded — they're dominated by French cantons and JP counties (admin subdivisions named after
|
|
21
|
+
* a seat town, not dual-role cities) that don't hit the parser-drops-locality failure; genuine
|
|
22
|
+
* consolidated city-counties (US SF/Denver) are a separate follow-up needing a relative-size
|
|
23
|
+
* filter.
|
|
24
|
+
*
|
|
25
|
+
* A pair `(admin, locality)` is recorded when all hold: same `name` (case-insensitive), the
|
|
26
|
+
* locality is a `descendant` of the admin (via the `ancestors` table), and their centroids are
|
|
27
|
+
* within a RELATIVE tolerance — `toleranceFraction × admin-bbox-diagonal`, floored at
|
|
28
|
+
* `minToleranceKm`. The relative term lets a large Italian province admit a city ~tens of km from
|
|
29
|
+
* its centroid while a tiny city-state stays tight; the floor catches city-states whose bbox is
|
|
30
|
+
* small (Bremen's centroids sit 9.3 km apart). The tolerance lives ONLY here at build time — it
|
|
31
|
+
* never enters the resolver hot path.
|
|
32
|
+
*
|
|
33
|
+
* `relationship_type` is recorded for debuggability / deferred per-type behavior; v1 completion is
|
|
34
|
+
* uniform (see #405). It's a coarse classification, not critical.
|
|
35
|
+
*
|
|
36
|
+
* Mirrors the derived-table builder pattern in `fts.ts` (`buildPlaceSearchFts`). Run incrementally
|
|
37
|
+
* against an existing `admin-global-priority.db` via `build-coincident-roles-cli.ts`; should also
|
|
38
|
+
* be wired as a post-step of the main `scripts/build-unified-wof.ts`.
|
|
39
|
+
*/
|
|
40
|
+
import { haversineKm } from "@mailwoman/spatial";
|
|
41
|
+
export const COINCIDENT_ROLES_TABLE = "coincident_roles";
|
|
42
|
+
function tableExists(db, name) {
|
|
43
|
+
return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(name);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Derive the coincident-roles relation into `db`. Additive — only creates/replaces the
|
|
47
|
+
* `coincident_roles` table; never touches `spr`/`names`/`ancestors`. Idempotent.
|
|
48
|
+
*/
|
|
49
|
+
export function buildCoincidentRoles(db, opts = {}) {
|
|
50
|
+
const start = Date.now();
|
|
51
|
+
const drop = opts.drop ?? true;
|
|
52
|
+
const toleranceFraction = opts.toleranceFraction ?? 0.15;
|
|
53
|
+
const minToleranceKm = opts.minToleranceKm ?? 12;
|
|
54
|
+
const cityStateMaxKm = opts.cityStateMaxKm ?? 2;
|
|
55
|
+
const onProgress = opts.onProgress ?? (() => { });
|
|
56
|
+
if (tableExists(db, COINCIDENT_ROLES_TABLE) && drop) {
|
|
57
|
+
onProgress("dropping", COINCIDENT_ROLES_TABLE);
|
|
58
|
+
db.exec(`DROP TABLE ${COINCIDENT_ROLES_TABLE}`);
|
|
59
|
+
}
|
|
60
|
+
onProgress("creating", COINCIDENT_ROLES_TABLE);
|
|
61
|
+
// Raw DDL by design: this is a sync builder consumed by a sync CLI (build-coincident-roles-cli) and
|
|
62
|
+
// 6 sync unit tests, so routing one table through async Kysely would cascade async through all of
|
|
63
|
+
// them for no real gain. See AGENTS.md "Database / inline SQL". (The SELECT + INSERT loop below are
|
|
64
|
+
// likewise the raw hot path.)
|
|
65
|
+
db.exec(`
|
|
66
|
+
CREATE TABLE IF NOT EXISTS ${COINCIDENT_ROLES_TABLE} (
|
|
67
|
+
admin_id INTEGER NOT NULL,
|
|
68
|
+
locality_id INTEGER NOT NULL,
|
|
69
|
+
relationship_type TEXT NOT NULL,
|
|
70
|
+
admin_placetype TEXT NOT NULL,
|
|
71
|
+
distance_km REAL NOT NULL,
|
|
72
|
+
locality_population INTEGER NOT NULL DEFAULT 0,
|
|
73
|
+
PRIMARY KEY (admin_id, locality_id)
|
|
74
|
+
)
|
|
75
|
+
`);
|
|
76
|
+
onProgress("scanning");
|
|
77
|
+
// Admin (region/county tier) ⋈ same-name DESCENDANT locality. `place_population` is optional (LEFT
|
|
78
|
+
// JOIN → 0 when absent). The relative-tolerance filter + relationship classification happen in JS so
|
|
79
|
+
// the SQL stays a plain join. `spr` exposes the bbox columns we need for the diagonal.
|
|
80
|
+
const candidates = db
|
|
81
|
+
.prepare(`SELECT r.id AS admin_id, r.placetype AS admin_placetype, r.country AS country, l.id AS locality_id,
|
|
82
|
+
r.latitude AS rlat, r.longitude AS rlon, l.latitude AS llat, l.longitude AS llon,
|
|
83
|
+
r.min_latitude, r.min_longitude, r.max_latitude, r.max_longitude,
|
|
84
|
+
COALESCE(p.population, 0) AS pop
|
|
85
|
+
FROM spr r
|
|
86
|
+
JOIN spr l ON lower(l.name) = lower(r.name) AND l.placetype = 'locality'
|
|
87
|
+
AND l.is_current != 0 AND l.is_deprecated = 0
|
|
88
|
+
JOIN ${"ancestors"} a ON a.id = l.id AND a.ancestor_id = r.id
|
|
89
|
+
LEFT JOIN place_population p ON p.id = l.id
|
|
90
|
+
WHERE r.placetype = 'region'
|
|
91
|
+
AND r.is_current != 0 AND r.is_deprecated = 0`)
|
|
92
|
+
.all();
|
|
93
|
+
onProgress("filtering", `${candidates.length} candidates`);
|
|
94
|
+
const insert = db.prepare(`INSERT OR REPLACE INTO ${COINCIDENT_ROLES_TABLE}
|
|
95
|
+
(admin_id, locality_id, relationship_type, admin_placetype, distance_km, locality_population)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
97
|
+
const byCountry = {};
|
|
98
|
+
let rowCount = 0;
|
|
99
|
+
db.exec("BEGIN");
|
|
100
|
+
try {
|
|
101
|
+
for (const c of candidates) {
|
|
102
|
+
const dist = haversineKm(c.rlat, c.rlon, c.llat, c.llon);
|
|
103
|
+
const diag = haversineKm(c.min_latitude, c.min_longitude, c.max_latitude, c.max_longitude);
|
|
104
|
+
const tolerance = Math.max(toleranceFraction * diag, minToleranceKm);
|
|
105
|
+
if (dist > tolerance)
|
|
106
|
+
continue;
|
|
107
|
+
// v1 is region-tier only: a place is a `city-state` when its centroid coincides with the
|
|
108
|
+
// region's (Berlin/Hamburg), else `capital-seat` (a region named after its principal city, e.g.
|
|
109
|
+
// Milano province → Milano comune). `consolidated-county` is reserved for a future county-tier
|
|
110
|
+
// pass (US SF/Denver) — excluded from v1 because county-tier same-name coincidences are
|
|
111
|
+
// dominated by French cantons / JP counties that don't hit the parser-drops-locality failure.
|
|
112
|
+
const relationshipType = dist <= cityStateMaxKm ? "city-state" : "capital-seat";
|
|
113
|
+
insert.run(c.admin_id, c.locality_id, relationshipType, c.admin_placetype, dist, c.pop);
|
|
114
|
+
rowCount++;
|
|
115
|
+
byCountry[c.country] = (byCountry[c.country] ?? 0) + 1;
|
|
116
|
+
}
|
|
117
|
+
db.exec("COMMIT");
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
db.exec("ROLLBACK");
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
db.exec(`CREATE INDEX IF NOT EXISTS coincident_roles_by_admin ON ${COINCIDENT_ROLES_TABLE} (admin_id)`);
|
|
124
|
+
onProgress("done", `${rowCount} coincident-role rows`);
|
|
125
|
+
return { created: true, rowCount, byCountry, durationMs: Date.now() - start };
|
|
126
|
+
}
|
|
127
|
+
/** True iff the relation table exists. Used by the resolver to decide whether completion can run. */
|
|
128
|
+
export function coincidentRolesExists(db) {
|
|
129
|
+
return tableExists(db, COINCIDENT_ROLES_TABLE);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Load the relation into an in-memory map keyed by `admin_id` for O(1) runtime lookup (#405). Each
|
|
133
|
+
* admin may map to MULTIPLE same-name descendants; the consumer disambiguates (min distance →
|
|
134
|
+
* population → abstain). Returns an empty map when the table is absent.
|
|
135
|
+
*/
|
|
136
|
+
export function loadCoincidentRoles(db) {
|
|
137
|
+
const map = new Map();
|
|
138
|
+
if (!coincidentRolesExists(db))
|
|
139
|
+
return map;
|
|
140
|
+
const rows = db
|
|
141
|
+
.prepare(`SELECT admin_id, locality_id, relationship_type, admin_placetype, distance_km, locality_population
|
|
142
|
+
FROM ${COINCIDENT_ROLES_TABLE}`)
|
|
143
|
+
.all();
|
|
144
|
+
for (const r of rows) {
|
|
145
|
+
const entry = {
|
|
146
|
+
localityId: r.locality_id,
|
|
147
|
+
relationshipType: r.relationship_type,
|
|
148
|
+
adminPlacetype: r.admin_placetype,
|
|
149
|
+
distanceKm: r.distance_km,
|
|
150
|
+
population: r.locality_population,
|
|
151
|
+
};
|
|
152
|
+
const list = map.get(r.admin_id);
|
|
153
|
+
if (list)
|
|
154
|
+
list.push(entry);
|
|
155
|
+
else
|
|
156
|
+
map.set(r.admin_id, [entry]);
|
|
157
|
+
}
|
|
158
|
+
return map;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=coincident-roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coincident-roles.js","sourceRoot":"","sources":["../coincident-roles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The **Geographic Rule Engine** convention model (Direction E, #289 — see
|
|
7
|
+
* `docs/articles/plan/2026-06-05-geographic-rule-engine.md` and epic #288).
|
|
8
|
+
*
|
|
9
|
+
* A `Convention` is a declarative resolution profile attached to a Who's-On-First admin polygon.
|
|
10
|
+
* The engine deep-merges the conventions along a resolved place's ancestor chain — country →
|
|
11
|
+
* region → … → locality, most-specific winning — and the backend dispatches the named strategies
|
|
12
|
+
* in `candidateStrategies`, first to return candidates wins.
|
|
13
|
+
*
|
|
14
|
+
* This module is the backend-agnostic core: the convention TYPES, the deep-merge, and the seed
|
|
15
|
+
* source. The strategy IMPLEMENTATIONS are SQL-bound and live in `lookup.ts`, registered by
|
|
16
|
+
* name.
|
|
17
|
+
*
|
|
18
|
+
* For the existing EU locales (DE/FR/GB/NL) the seed source is empty, so every query resolves to
|
|
19
|
+
* `WORLD_DEFAULT` and the dispatch is byte-identical to the pre-engine coordinate-first path. JP
|
|
20
|
+
* / KR / TW add rows here (and #290 swaps the seed map for a build-from-source sqlite-backed
|
|
21
|
+
* source).
|
|
22
|
+
*/
|
|
23
|
+
import type { FindPlaceQuery, PlaceCandidate } from "./types.js";
|
|
24
|
+
/**
|
|
25
|
+
* Soft-scoring weights for the `postcode_area_resolution` strategy: `pc·S_pc + name·S_name +
|
|
26
|
+
* pop·S_pop`.
|
|
27
|
+
*/
|
|
28
|
+
export interface ScoringWeights {
|
|
29
|
+
pc: number;
|
|
30
|
+
name: number;
|
|
31
|
+
pop: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A geographically-scoped resolution profile. Namespaced sections grow per phase; #289 ships the
|
|
35
|
+
* dispatch + scoring slice (`candidateStrategies` + `scoringWeights`). Later phases add
|
|
36
|
+
* `fieldMapping` (locale semantics for `locator[]`), `tokenNormalization`, etc.
|
|
37
|
+
*/
|
|
38
|
+
export interface Convention {
|
|
39
|
+
/** Ordered strategy names the dispatcher runs; the first to return a non-null result wins. */
|
|
40
|
+
candidateStrategies?: string[];
|
|
41
|
+
/**
|
|
42
|
+
* Weights for `postcode_area_resolution`'s soft-score. Partial — a layer may nudge one weight and
|
|
43
|
+
* inherit the rest from the layers below it (`resolveConvention` fills any gaps from
|
|
44
|
+
* WORLD_DEFAULT).
|
|
45
|
+
*/
|
|
46
|
+
scoringWeights?: Partial<ScoringWeights>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A fully-resolved convention: every field present, weights complete. What `resolveConvention`
|
|
50
|
+
* returns and what strategies consume.
|
|
51
|
+
*/
|
|
52
|
+
export interface ResolvedConvention {
|
|
53
|
+
candidateStrategies: string[];
|
|
54
|
+
scoringWeights: ScoringWeights;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The base layer every ancestor chain starts from. Reproduces the pre-engine coordinate-first
|
|
58
|
+
* behavior exactly: try `postcode_area_resolution`, else fall back to fuzzy name match; soft-score
|
|
59
|
+
* weights 0.6 / 0.3 / 0.1. Changing these changes EU behavior — don't, without a byte-stability
|
|
60
|
+
* run.
|
|
61
|
+
*/
|
|
62
|
+
export declare const WORLD_DEFAULT: ResolvedConvention;
|
|
63
|
+
/**
|
|
64
|
+
* The strategy names the backend registers. The single source of truth shared by the dispatch
|
|
65
|
+
* registry and the build-time validator, so an authored convention that names a non-existent
|
|
66
|
+
* strategy is caught at build (loud) rather than silently skipped at runtime.
|
|
67
|
+
*/
|
|
68
|
+
export declare const BUILTIN_STRATEGY_NAMES: readonly ["postcode_area_resolution", "fallback_fuzzy_name_match"];
|
|
69
|
+
/**
|
|
70
|
+
* Table name for the convention asset (#290). Carried here so the build script, the runtime source,
|
|
71
|
+
* and the shard auto-detect all agree.
|
|
72
|
+
*/
|
|
73
|
+
export declare const ADDRESS_CONVENTION_TABLE = "address_convention";
|
|
74
|
+
/**
|
|
75
|
+
* A named resolution primitive. Returns `null` to abstain (gate unmet / no data) → the dispatcher
|
|
76
|
+
* tries the next strategy; returns an array (possibly empty) to claim the result.
|
|
77
|
+
*/
|
|
78
|
+
export type Strategy = (query: FindPlaceQuery, convention: ResolvedConvention) => Promise<PlaceCandidate[] | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Look up a convention record by WOF polygon id. Returns `undefined` when the polygon has no
|
|
81
|
+
* override.
|
|
82
|
+
*/
|
|
83
|
+
export interface ConventionSource {
|
|
84
|
+
get(wofId: number): Convention | undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* In-memory convention source seeded from a `{ wofId: Convention }` map. Empty for the EU locales
|
|
88
|
+
* (they ride `WORLD_DEFAULT`); JP / KR / TW add rows. #290 replaces this with a sqlite-backed
|
|
89
|
+
* source built from source, same distributable-asset discipline as `postcode-locality-intl.db`.
|
|
90
|
+
*/
|
|
91
|
+
export declare class SeedConventionSource implements ConventionSource {
|
|
92
|
+
#private;
|
|
93
|
+
constructor(rows?: Record<number, Convention>);
|
|
94
|
+
get(wofId: number): Convention | undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Deep-merge convention layers, later (more-specific) layers winning per field.
|
|
98
|
+
* `candidateStrategies` is replaced wholesale — a convention names its full ordered list, it does
|
|
99
|
+
* not append. `scoringWeights` is merged key-by-key so a locality can nudge one weight without
|
|
100
|
+
* restating the others.
|
|
101
|
+
*/
|
|
102
|
+
export declare function mergeConventions(base: Convention, ...overrides: Array<Convention | undefined>): Convention;
|
|
103
|
+
/**
|
|
104
|
+
* Resolve the effective convention for a place given its ancestor chain, ordered MOST-GENERAL →
|
|
105
|
+
* MOST-SPECIFIC (country, region, …, locality). Starts from `WORLD_DEFAULT` so every field is
|
|
106
|
+
* defined regardless of which (if any) ancestors carry an override.
|
|
107
|
+
*/
|
|
108
|
+
export declare function resolveConvention(source: ConventionSource, ancestorIds: readonly number[]): ResolvedConvention;
|
|
109
|
+
//# sourceMappingURL=convention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convention.d.ts","sourceRoot":"","sources":["../convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACX;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAC7B,cAAc,EAAE,cAAc,CAAA;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,kBAG3B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,oEAAqE,CAAA;AAExG;;;GAGG;AACH,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAE5D;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,kBAAkB,KAAK,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAAA;AAElH;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;CAC1C;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;;gBAGhD,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM;IAIjD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAG1C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,SAAS,EAAE,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,UAAU,CAa1G;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CAQ9G"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The **Geographic Rule Engine** convention model (Direction E, #289 — see
|
|
7
|
+
* `docs/articles/plan/2026-06-05-geographic-rule-engine.md` and epic #288).
|
|
8
|
+
*
|
|
9
|
+
* A `Convention` is a declarative resolution profile attached to a Who's-On-First admin polygon.
|
|
10
|
+
* The engine deep-merges the conventions along a resolved place's ancestor chain — country →
|
|
11
|
+
* region → … → locality, most-specific winning — and the backend dispatches the named strategies
|
|
12
|
+
* in `candidateStrategies`, first to return candidates wins.
|
|
13
|
+
*
|
|
14
|
+
* This module is the backend-agnostic core: the convention TYPES, the deep-merge, and the seed
|
|
15
|
+
* source. The strategy IMPLEMENTATIONS are SQL-bound and live in `lookup.ts`, registered by
|
|
16
|
+
* name.
|
|
17
|
+
*
|
|
18
|
+
* For the existing EU locales (DE/FR/GB/NL) the seed source is empty, so every query resolves to
|
|
19
|
+
* `WORLD_DEFAULT` and the dispatch is byte-identical to the pre-engine coordinate-first path. JP
|
|
20
|
+
* / KR / TW add rows here (and #290 swaps the seed map for a build-from-source sqlite-backed
|
|
21
|
+
* source).
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The base layer every ancestor chain starts from. Reproduces the pre-engine coordinate-first
|
|
25
|
+
* behavior exactly: try `postcode_area_resolution`, else fall back to fuzzy name match; soft-score
|
|
26
|
+
* weights 0.6 / 0.3 / 0.1. Changing these changes EU behavior — don't, without a byte-stability
|
|
27
|
+
* run.
|
|
28
|
+
*/
|
|
29
|
+
export const WORLD_DEFAULT = {
|
|
30
|
+
candidateStrategies: ["postcode_area_resolution", "fallback_fuzzy_name_match"],
|
|
31
|
+
scoringWeights: { pc: 0.6, name: 0.3, pop: 0.1 },
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The strategy names the backend registers. The single source of truth shared by the dispatch
|
|
35
|
+
* registry and the build-time validator, so an authored convention that names a non-existent
|
|
36
|
+
* strategy is caught at build (loud) rather than silently skipped at runtime.
|
|
37
|
+
*/
|
|
38
|
+
export const BUILTIN_STRATEGY_NAMES = ["postcode_area_resolution", "fallback_fuzzy_name_match"];
|
|
39
|
+
/**
|
|
40
|
+
* Table name for the convention asset (#290). Carried here so the build script, the runtime source,
|
|
41
|
+
* and the shard auto-detect all agree.
|
|
42
|
+
*/
|
|
43
|
+
export const ADDRESS_CONVENTION_TABLE = "address_convention";
|
|
44
|
+
/**
|
|
45
|
+
* In-memory convention source seeded from a `{ wofId: Convention }` map. Empty for the EU locales
|
|
46
|
+
* (they ride `WORLD_DEFAULT`); JP / KR / TW add rows. #290 replaces this with a sqlite-backed
|
|
47
|
+
* source built from source, same distributable-asset discipline as `postcode-locality-intl.db`.
|
|
48
|
+
*/
|
|
49
|
+
export class SeedConventionSource {
|
|
50
|
+
#rows;
|
|
51
|
+
constructor(rows = {}) {
|
|
52
|
+
this.#rows = new Map(Object.entries(rows).map(([k, v]) => [Number(k), v]));
|
|
53
|
+
}
|
|
54
|
+
get(wofId) {
|
|
55
|
+
return this.#rows.get(wofId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Deep-merge convention layers, later (more-specific) layers winning per field.
|
|
60
|
+
* `candidateStrategies` is replaced wholesale — a convention names its full ordered list, it does
|
|
61
|
+
* not append. `scoringWeights` is merged key-by-key so a locality can nudge one weight without
|
|
62
|
+
* restating the others.
|
|
63
|
+
*/
|
|
64
|
+
export function mergeConventions(base, ...overrides) {
|
|
65
|
+
const out = {
|
|
66
|
+
candidateStrategies: base.candidateStrategies ? [...base.candidateStrategies] : undefined,
|
|
67
|
+
scoringWeights: base.scoringWeights ? { ...base.scoringWeights } : undefined,
|
|
68
|
+
};
|
|
69
|
+
for (const o of overrides) {
|
|
70
|
+
if (!o)
|
|
71
|
+
continue;
|
|
72
|
+
if (o.candidateStrategies !== undefined)
|
|
73
|
+
out.candidateStrategies = [...o.candidateStrategies];
|
|
74
|
+
if (o.scoringWeights !== undefined) {
|
|
75
|
+
out.scoringWeights = { ...(out.scoringWeights ?? WORLD_DEFAULT.scoringWeights), ...o.scoringWeights };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve the effective convention for a place given its ancestor chain, ordered MOST-GENERAL →
|
|
82
|
+
* MOST-SPECIFIC (country, region, …, locality). Starts from `WORLD_DEFAULT` so every field is
|
|
83
|
+
* defined regardless of which (if any) ancestors carry an override.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveConvention(source, ancestorIds) {
|
|
86
|
+
const layers = ancestorIds.map((id) => source.get(id));
|
|
87
|
+
const merged = mergeConventions(WORLD_DEFAULT, ...layers);
|
|
88
|
+
return {
|
|
89
|
+
candidateStrategies: merged.candidateStrategies ?? WORLD_DEFAULT.candidateStrategies,
|
|
90
|
+
// Fill any weight gaps from the base so strategies always see a complete set.
|
|
91
|
+
scoringWeights: { ...WORLD_DEFAULT.scoringWeights, ...merged.scoringWeights },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=convention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convention.js","sourceRoot":"","sources":["../convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuCH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAuB;IAChD,mBAAmB,EAAE,CAAC,0BAA0B,EAAE,2BAA2B,CAAC;IAC9E,cAAc,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;CAChD,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,0BAA0B,EAAE,2BAA2B,CAAU,CAAA;AAExG;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAAoB,CAAA;AAgB5D;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IACvB,KAAK,CAAyB;IAEvC,YAAY,OAAmC,EAAE;QAChD,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3E,CAAC;IAED,GAAG,CAAC,KAAa;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;CACD;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAgB,EAAE,GAAG,SAAwC;IAC7F,MAAM,GAAG,GAAe;QACvB,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS;QACzF,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS;KAC5E,CAAA;IACD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC;YAAE,SAAQ;QAChB,IAAI,CAAC,CAAC,mBAAmB,KAAK,SAAS;YAAE,GAAG,CAAC,mBAAmB,GAAG,CAAC,GAAG,CAAC,CAAC,mBAAmB,CAAC,CAAA;QAC7F,IAAI,CAAC,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YACpC,GAAG,CAAC,cAAc,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,cAAc,IAAI,aAAa,CAAC,cAAc,CAAC,EAAE,GAAG,CAAC,CAAC,cAAc,EAAE,CAAA;QACtG,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAA;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAwB,EAAE,WAA8B;IACzF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IACtD,MAAM,MAAM,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,CAAA;IACzD,OAAO;QACN,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,aAAa,CAAC,mBAAmB;QACpF,8EAA8E;QAC9E,cAAc,EAAE,EAAE,GAAG,aAAa,CAAC,cAAc,EAAE,GAAG,MAAM,CAAC,cAAc,EAAE;KAC7E,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* FST-based autocomplete. Prefix walk + BFS expansion to collect ranked place suggestions. O(depth
|
|
7
|
+
* × branching) — the FST IS the autocomplete index.
|
|
8
|
+
*
|
|
9
|
+
* Two query shapes are handled (the FST is a trie over normalized WORD tokens):
|
|
10
|
+
*
|
|
11
|
+
* - COMPLETE tokens ("new york") — `walk` lands on a state; collect its accepting entries + BFS a
|
|
12
|
+
* couple tokens past it for nearby completions. This is the CLI's "complete a place word"
|
|
13
|
+
* path.
|
|
14
|
+
* - A PARTIAL last token ("new yor", "chic") — `walk` fails (there is no "yor" edge, only "york"). So
|
|
15
|
+
* walk the complete prefix, then complete the partial token by prefix-filtering the
|
|
16
|
+
* continuation edges (`token.startsWith(partial)`). This is what a char-level typeahead
|
|
17
|
+
* needs; without it "new yor" returns nothing useful. (#587)
|
|
18
|
+
*/
|
|
19
|
+
import { FstMatcher } from "./fst-matcher.js";
|
|
20
|
+
export interface AutocompleteResult {
|
|
21
|
+
query: string;
|
|
22
|
+
normalizedTokens: string[];
|
|
23
|
+
depth: number;
|
|
24
|
+
suggestions: AutocompleteSuggestion[];
|
|
25
|
+
}
|
|
26
|
+
export interface AutocompleteSuggestion {
|
|
27
|
+
name: string;
|
|
28
|
+
placetype: string;
|
|
29
|
+
importance: number;
|
|
30
|
+
wofID: number;
|
|
31
|
+
parentChain: number[];
|
|
32
|
+
matchDepth: number;
|
|
33
|
+
completionTokens: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface AutocompleteOpts {
|
|
36
|
+
maxSuggestions?: number;
|
|
37
|
+
maxExpansionDepth?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Collapse same-name suggestions to the single highest-importance one. Off by default (the CLI
|
|
40
|
+
* surfaces distinct same-name places — New York the city vs the county); a typeahead wants it ON
|
|
41
|
+
* so the dropdown isn't four "New London"s. (#587)
|
|
42
|
+
*/
|
|
43
|
+
dedupeByName?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Autocomplete from the current prefix. Returns suggestions ranked importance-descending.
|
|
47
|
+
*/
|
|
48
|
+
export declare function autocomplete(fst: FstMatcher, query: string, opts?: AutocompleteOpts): AutocompleteResult;
|
|
49
|
+
//# sourceMappingURL=fst-autocomplete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fst-autocomplete.d.ts","sourceRoot":"","sources":["../fst-autocomplete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAA;AAG9D,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,sBAAsB,EAAE,CAAA;CACrC;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAChC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACtB;AAoBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,kBAAkB,CA2D5G"}
|