@mailwoman/codex 9.3.0 → 10.0.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 +221 -75
- package/lib/address/format.ts +144 -0
- package/lib/address/key.ts +134 -0
- package/lib/address/layout.ts +189 -0
- package/lib/address/layouts/generated.ts +1576 -0
- package/lib/address/layouts/index.ts +213 -0
- package/lib/address/render.ts +176 -0
- package/lib/{address-system-conventions.ts → address/system-conventions.ts} +6 -5
- package/lib/component.ts +96 -0
- package/lib/country/index.ts +3 -2
- package/lib/country/{reference.ts → reference/index.ts} +1 -1
- package/lib/country/region-languages.ts +46 -0
- package/lib/es/co-official-languages.ts +62 -0
- package/lib/es/index.ts +1 -0
- package/lib/gb/index.ts +2 -2
- package/lib/index.ts +4 -2
- package/lib/jp/index.ts +1 -0
- package/lib/jp/municipality-register.ts +62 -0
- package/lib/kr/index.ts +10 -0
- package/lib/kr/sigungu-register.ts +294 -0
- package/lib/placetype-map.ts +157 -0
- package/lib/postcode/shapes.json +80 -0
- package/lib/postcode/shapes.ts +56 -0
- package/lib/{postcode-systems.ts → postcode/systems.ts} +8 -1
- package/lib/register-completion.ts +24 -0
- package/lib/us/index.ts +2 -2
- package/lib/us/po-box.ts +37 -10
- package/lib/us/{street-directional.ts → street/directional.ts} +1 -1
- package/lib/us/{street-suffix.ts → street/suffix.ts} +1 -1
- package/out/address/format.d.ts +87 -0
- package/out/address/format.d.ts.map +1 -0
- package/out/address/format.js +85 -0
- package/out/address/format.js.map +1 -0
- package/out/address/key.d.ts +67 -0
- package/out/address/key.d.ts.map +1 -0
- package/out/address/key.js +91 -0
- package/out/address/key.js.map +1 -0
- package/out/address/layout.d.ts +104 -0
- package/out/address/layout.d.ts.map +1 -0
- package/out/address/layout.js +123 -0
- package/out/address/layout.js.map +1 -0
- package/out/address/layouts/generated.d.ts +21 -0
- package/out/address/layouts/generated.d.ts.map +1 -0
- package/out/address/layouts/generated.js +1380 -0
- package/out/address/layouts/generated.js.map +1 -0
- package/out/address/layouts/index.d.ts +81 -0
- package/out/address/layouts/index.d.ts.map +1 -0
- package/out/address/layouts/index.js +184 -0
- package/out/address/layouts/index.js.map +1 -0
- package/out/address/render.d.ts +64 -0
- package/out/address/render.d.ts.map +1 -0
- package/out/address/render.js +115 -0
- package/out/address/render.js.map +1 -0
- package/out/{address-system-conventions.d.ts → address/system-conventions.d.ts} +6 -5
- package/out/address/system-conventions.d.ts.map +1 -0
- package/out/{address-system-conventions.js → address/system-conventions.js} +2 -2
- package/out/address/system-conventions.js.map +1 -0
- package/out/au/delivery-service.d.ts.map +1 -1
- package/out/au/level-designator.d.ts.map +1 -1
- package/out/au/state.d.ts.map +1 -1
- package/out/ca/province.d.ts.map +1 -1
- package/out/component.d.ts +50 -0
- package/out/component.d.ts.map +1 -0
- package/out/component.js +84 -0
- package/out/component.js.map +1 -0
- package/out/country/codes.d.ts +2 -2
- package/out/country/codes.d.ts.map +1 -1
- package/out/country/display-names.d.ts.map +1 -1
- package/out/country/display-names.js.map +1 -1
- package/out/country/index.d.ts +3 -2
- package/out/country/index.d.ts.map +1 -1
- package/out/country/index.js +3 -2
- package/out/country/index.js.map +1 -1
- package/out/country/{reference-data.d.ts → reference/data.d.ts} +1 -1
- package/out/country/reference/data.d.ts.map +1 -0
- package/out/country/{reference-data.js → reference/data.js} +1 -1
- package/out/country/reference/data.js.map +1 -0
- package/out/country/{reference.d.ts → reference/index.d.ts} +1 -1
- package/out/country/reference/index.d.ts.map +1 -0
- package/out/country/{reference.js → reference/index.js} +2 -2
- package/out/country/reference/index.js.map +1 -0
- package/out/country/region-languages.d.ts +25 -0
- package/out/country/region-languages.d.ts.map +1 -0
- package/out/country/region-languages.js +41 -0
- package/out/country/region-languages.js.map +1 -0
- package/out/de/bundesland.d.ts.map +1 -1
- package/out/de/postleitzahl.d.ts.map +1 -1
- package/out/de/street-type.d.ts.map +1 -1
- package/out/es/co-official-languages.d.ts +37 -0
- package/out/es/co-official-languages.d.ts.map +1 -0
- package/out/es/co-official-languages.js +61 -0
- package/out/es/co-official-languages.js.map +1 -0
- package/out/es/index.d.ts +1 -0
- package/out/es/index.d.ts.map +1 -1
- package/out/es/index.js +1 -0
- package/out/es/index.js.map +1 -1
- package/out/fr/departement.d.ts.map +1 -1
- package/out/fr/region.d.ts.map +1 -1
- package/out/gb/country.d.ts.map +1 -1
- package/out/gb/index.d.ts +2 -2
- package/out/gb/index.d.ts.map +1 -1
- package/out/gb/index.js +2 -2
- package/out/gb/index.js.map +1 -1
- package/out/gb/{postcode-area.d.ts → postcode/area.d.ts} +1 -1
- package/out/gb/postcode/area.d.ts.map +1 -0
- package/out/gb/{postcode-area.js → postcode/area.js} +1 -1
- package/out/gb/postcode/area.js.map +1 -0
- package/out/gb/{postcode.d.ts → postcode/index.d.ts} +1 -1
- package/out/gb/postcode/index.d.ts.map +1 -0
- package/out/gb/{postcode.js → postcode/index.js} +1 -1
- package/out/gb/postcode/index.js.map +1 -0
- package/out/gb/street-type.d.ts.map +1 -1
- package/out/index.d.ts +4 -2
- package/out/index.d.ts.map +1 -1
- package/out/index.js +4 -2
- package/out/index.js.map +1 -1
- package/out/jp/address-unit.d.ts.map +1 -1
- package/out/jp/index.d.ts +1 -0
- package/out/jp/index.d.ts.map +1 -1
- package/out/jp/index.js +1 -0
- package/out/jp/index.js.map +1 -1
- package/out/jp/municipality-register.d.ts +48 -0
- package/out/jp/municipality-register.d.ts.map +1 -0
- package/out/jp/municipality-register.js +41 -0
- package/out/jp/municipality-register.js.map +1 -0
- package/out/jp/prefecture.d.ts.map +1 -1
- package/out/kr/index.d.ts +10 -0
- package/out/kr/index.d.ts.map +1 -0
- package/out/kr/index.js +10 -0
- package/out/kr/index.js.map +1 -0
- package/out/kr/sigungu-register.d.ts +37 -0
- package/out/kr/sigungu-register.d.ts.map +1 -0
- package/out/kr/sigungu-register.js +283 -0
- package/out/kr/sigungu-register.js.map +1 -0
- package/out/level-semantics.d.ts.map +1 -1
- package/out/level-semantics.js.map +1 -1
- package/out/nz/delivery-service.d.ts.map +1 -1
- package/out/placetype-map.d.ts +83 -0
- package/out/placetype-map.d.ts.map +1 -0
- package/out/placetype-map.js +130 -0
- package/out/placetype-map.js.map +1 -0
- package/out/postcode/shapes.d.ts +47 -0
- package/out/postcode/shapes.d.ts.map +1 -0
- package/out/postcode/shapes.js +38 -0
- package/out/postcode/shapes.js.map +1 -0
- package/out/postcode/shapes.json +80 -0
- package/out/{postcode-systems.d.ts → postcode/systems.d.ts} +8 -1
- package/out/postcode/systems.d.ts.map +1 -0
- package/out/{postcode-systems.js → postcode/systems.js} +9 -2
- package/out/postcode/systems.js.map +1 -0
- package/out/register-completion.d.ts +14 -0
- package/out/register-completion.d.ts.map +1 -0
- package/out/register-completion.js +23 -0
- package/out/register-completion.js.map +1 -0
- package/out/us/floor-designator.d.ts.map +1 -1
- package/out/us/index.d.ts +2 -2
- package/out/us/index.js +2 -2
- package/out/us/military-address.d.ts.map +1 -1
- package/out/us/po-box.d.ts +6 -0
- package/out/us/po-box.d.ts.map +1 -1
- package/out/us/po-box.js +35 -10
- package/out/us/po-box.js.map +1 -1
- package/out/us/state.d.ts.map +1 -1
- package/out/us/{street-directional.d.ts → street/directional.d.ts} +1 -1
- package/out/us/street/directional.d.ts.map +1 -0
- package/out/us/{street-directional.js → street/directional.js} +2 -2
- package/out/us/street/directional.js.map +1 -0
- package/out/us/{street-suffix.d.ts → street/suffix.d.ts} +1 -1
- package/out/us/street/suffix.d.ts.map +1 -0
- package/out/us/{street-suffix.js → street/suffix.js} +2 -2
- package/out/us/street/suffix.js.map +1 -0
- package/out/us/zipcode.d.ts +1 -1
- package/out/us/zipcode.d.ts.map +1 -1
- package/package.json +120 -30
- package/lib/tools/build-country-surface-lexicon.ts +0 -177
- package/lib/tools/export-country-surfaces.ts +0 -48
- package/lib/tools/generate-country-population.ts +0 -142
- package/lib/tools/generate-country-reference.ts +0 -168
- package/lib/tools/generate-official-languages.ts +0 -210
- package/lib/tools/index.ts +0 -12
- package/out/address-system-conventions.d.ts.map +0 -1
- package/out/address-system-conventions.js.map +0 -1
- package/out/country/reference-data.d.ts.map +0 -1
- package/out/country/reference-data.js.map +0 -1
- package/out/country/reference.d.ts.map +0 -1
- package/out/country/reference.js.map +0 -1
- package/out/gb/postcode-area.d.ts.map +0 -1
- package/out/gb/postcode-area.js.map +0 -1
- package/out/gb/postcode.d.ts.map +0 -1
- package/out/gb/postcode.js.map +0 -1
- package/out/postcode-systems.d.ts.map +0 -1
- package/out/postcode-systems.js.map +0 -1
- package/out/tools/build-country-surface-lexicon.d.ts +0 -43
- package/out/tools/build-country-surface-lexicon.d.ts.map +0 -1
- package/out/tools/build-country-surface-lexicon.js +0 -149
- package/out/tools/build-country-surface-lexicon.js.map +0 -1
- package/out/tools/export-country-surfaces.d.ts +0 -15
- package/out/tools/export-country-surfaces.d.ts.map +0 -1
- package/out/tools/export-country-surfaces.js +0 -34
- package/out/tools/export-country-surfaces.js.map +0 -1
- package/out/tools/generate-country-population.d.ts +0 -42
- package/out/tools/generate-country-population.d.ts.map +0 -1
- package/out/tools/generate-country-population.js +0 -106
- package/out/tools/generate-country-population.js.map +0 -1
- package/out/tools/generate-country-reference.d.ts +0 -36
- package/out/tools/generate-country-reference.d.ts.map +0 -1
- package/out/tools/generate-country-reference.js +0 -107
- package/out/tools/generate-country-reference.js.map +0 -1
- package/out/tools/generate-official-languages.d.ts +0 -50
- package/out/tools/generate-official-languages.d.ts.map +0 -1
- package/out/tools/generate-official-languages.js +0 -149
- package/out/tools/generate-official-languages.js.map +0 -1
- package/out/tools/index.d.ts +0 -12
- package/out/tools/index.d.ts.map +0 -1
- package/out/tools/index.js +0 -12
- package/out/tools/index.js.map +0 -1
- package/out/us/street-directional.d.ts.map +0 -1
- package/out/us/street-directional.js.map +0 -1
- package/out/us/street-suffix.d.ts.map +0 -1
- package/out/us/street-suffix.js.map +0 -1
- /package/lib/country/{reference-data.ts → reference/data.ts} +0 -0
- /package/lib/gb/{postcode-area.ts → postcode/area.ts} +0 -0
- /package/lib/gb/{postcode.ts → postcode/index.ts} +0 -0
package/lib/jp/index.ts
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The towns whose own name carries the city marker 市 (shi) before the town suffix 町 / 村 (chō / son).
|
|
7
|
+
*
|
|
8
|
+
* A Japanese municipality span closes at 市 in the ordinary case (`富山市`, `神戸市西区`), and a character model
|
|
9
|
+
* learns that boundary from 1,700 cities. A town whose NAME contains 市 defeats it: `中新川郡上市町` reads as the
|
|
10
|
+
* city `上市` plus a district beginning with 町. Over the 1,892 municipalities in Japan Post's KEN_ALL list the
|
|
11
|
+
* shape has six members, so the boundary is stated here rather than learned — a positive attestation from the
|
|
12
|
+
* postal register, consumed after decode by `@mailwoman/neural`'s JP municipality repair. Two of the six (`上市町`,
|
|
13
|
+
* `下市町`) put 市 immediately before the suffix, the boundary the model closes at; the other four are listed for the same
|
|
14
|
+
* repair so an early close inside them is also repaired.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { completeFromRegister } from "#register-completion"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One town from Japan Post's KEN_ALL municipality list whose own name contains 市.
|
|
21
|
+
*/
|
|
22
|
+
export interface JapaneseInnerShiTown {
|
|
23
|
+
/**
|
|
24
|
+
* The prefecture, as written (`富山県`).
|
|
25
|
+
*/
|
|
26
|
+
prefecture: string
|
|
27
|
+
/**
|
|
28
|
+
* The county (郡) the town belongs to, as the postal form writes it before the town.
|
|
29
|
+
*/
|
|
30
|
+
county: string
|
|
31
|
+
/**
|
|
32
|
+
* The town's own name with its suffix (`上市町`).
|
|
33
|
+
*/
|
|
34
|
+
town: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The six towns, from KEN_ALL (1,892 municipalities). Ordered by prefecture code.
|
|
39
|
+
*/
|
|
40
|
+
export const JP_INNER_SHI_TOWNS: readonly JapaneseInnerShiTown[] = [
|
|
41
|
+
{ prefecture: "北海道", county: "余市郡", town: "余市町" },
|
|
42
|
+
{ prefecture: "栃木県", county: "芳賀郡", town: "市貝町" },
|
|
43
|
+
{ prefecture: "富山県", county: "中新川郡", town: "上市町" },
|
|
44
|
+
{ prefecture: "山梨県", county: "西八代郡", town: "市川三郷町" },
|
|
45
|
+
{ prefecture: "奈良県", county: "吉野郡", town: "下市町" },
|
|
46
|
+
{ prefecture: "兵庫県", county: "神崎郡", town: "市川町" },
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Every surface a municipality span may legitimately close on for these towns: the postal form with the county
|
|
51
|
+
* (`中新川郡上市町`) and the bare town (`上市町`).
|
|
52
|
+
*/
|
|
53
|
+
export const JP_INNER_SHI_TOWN_NAMES: readonly string[] = JP_INNER_SHI_TOWNS.flatMap((t) => [t.county + t.town, t.town])
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The characters a municipality surface must absorb from what follows it to become one of the register's names, or null
|
|
57
|
+
* when no name extends it. `following` is the text after the surface; the answer is a prefix of it. A surface that
|
|
58
|
+
* already IS a register name answers null: nothing to absorb.
|
|
59
|
+
*/
|
|
60
|
+
export function jpMunicipalityCompletion(surface: string, following: string): string | null {
|
|
61
|
+
return completeFromRegister(JP_INNER_SHI_TOWN_NAMES, surface, following)
|
|
62
|
+
}
|
package/lib/kr/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The Korean address system as the road-name address register (도로명주소) writes it: the 시군구 register the
|
|
7
|
+
* decode-time boundary repair reads.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from "#kr/sigungu-register"
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The Korean 시군구 (si / gun / gu: the second administrative tier) as the road-name address register writes them,
|
|
7
|
+
* each under its 시/도 (the first tier). 249 pairs over 226 distinct names; a compound city is
|
|
8
|
+
* one name (`성남시분당구`), the way the register keys it. 세종특별자치시 has no second tier and is absent.
|
|
9
|
+
*
|
|
10
|
+
* Why a register: a character model closes a `subregion` span where the training set taught it names end, and a
|
|
11
|
+
* name it never saw (`해운대구`, held out of the training corpus by the board's design and unseen by the shipped
|
|
12
|
+
* model for the same reason) closes early, at `해` or `해운대`. The register states every boundary outright, so the
|
|
13
|
+
* decode-time repair in `@mailwoman/neural` extends a run to the one name the following characters spell.
|
|
14
|
+
*
|
|
15
|
+
* Source: 행정안전부 (Ministry of the Interior and Safety), 도로명주소 (juso.go.kr), 공공누리 제1유형 (KOGL Type 1):
|
|
16
|
+
* attribution required; commercial use, derivatives and redistribution permitted.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { completeFromRegister } from "#register-completion"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* One second-tier unit under its first-tier region, both as the register writes them.
|
|
23
|
+
*/
|
|
24
|
+
export interface KoreanSigungu {
|
|
25
|
+
region: string
|
|
26
|
+
city: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The register: every (시/도, 시군구) pair the road-name address register carries, in register order.
|
|
31
|
+
*/
|
|
32
|
+
export const KR_SIGUNGU: readonly KoreanSigungu[] = [
|
|
33
|
+
{ region: "강원도", city: "강릉시" },
|
|
34
|
+
{ region: "강원도", city: "고성군" },
|
|
35
|
+
{ region: "강원도", city: "동해시" },
|
|
36
|
+
{ region: "강원도", city: "삼척시" },
|
|
37
|
+
{ region: "강원도", city: "속초시" },
|
|
38
|
+
{ region: "강원도", city: "양구군" },
|
|
39
|
+
{ region: "강원도", city: "양양군" },
|
|
40
|
+
{ region: "강원도", city: "영월군" },
|
|
41
|
+
{ region: "강원도", city: "원주시" },
|
|
42
|
+
{ region: "강원도", city: "인제군" },
|
|
43
|
+
{ region: "강원도", city: "정선군" },
|
|
44
|
+
{ region: "강원도", city: "철원군" },
|
|
45
|
+
{ region: "강원도", city: "춘천시" },
|
|
46
|
+
{ region: "강원도", city: "태백시" },
|
|
47
|
+
{ region: "강원도", city: "평창군" },
|
|
48
|
+
{ region: "강원도", city: "홍천군" },
|
|
49
|
+
{ region: "강원도", city: "화천군" },
|
|
50
|
+
{ region: "강원도", city: "횡성군" },
|
|
51
|
+
{ region: "경기도", city: "가평군" },
|
|
52
|
+
{ region: "경기도", city: "고양시덕양구" },
|
|
53
|
+
{ region: "경기도", city: "고양시일산동구" },
|
|
54
|
+
{ region: "경기도", city: "고양시일산서구" },
|
|
55
|
+
{ region: "경기도", city: "과천시" },
|
|
56
|
+
{ region: "경기도", city: "광명시" },
|
|
57
|
+
{ region: "경기도", city: "광주시" },
|
|
58
|
+
{ region: "경기도", city: "구리시" },
|
|
59
|
+
{ region: "경기도", city: "군포시" },
|
|
60
|
+
{ region: "경기도", city: "김포시" },
|
|
61
|
+
{ region: "경기도", city: "남양주시" },
|
|
62
|
+
{ region: "경기도", city: "동두천시" },
|
|
63
|
+
{ region: "경기도", city: "부천시" },
|
|
64
|
+
{ region: "경기도", city: "성남시분당구" },
|
|
65
|
+
{ region: "경기도", city: "성남시수정구" },
|
|
66
|
+
{ region: "경기도", city: "성남시중원구" },
|
|
67
|
+
{ region: "경기도", city: "수원시권선구" },
|
|
68
|
+
{ region: "경기도", city: "수원시영통구" },
|
|
69
|
+
{ region: "경기도", city: "수원시장안구" },
|
|
70
|
+
{ region: "경기도", city: "수원시팔달구" },
|
|
71
|
+
{ region: "경기도", city: "시흥시" },
|
|
72
|
+
{ region: "경기도", city: "안산시단원구" },
|
|
73
|
+
{ region: "경기도", city: "안산시상록구" },
|
|
74
|
+
{ region: "경기도", city: "안성시" },
|
|
75
|
+
{ region: "경기도", city: "안양시동안구" },
|
|
76
|
+
{ region: "경기도", city: "안양시만안구" },
|
|
77
|
+
{ region: "경기도", city: "양주시" },
|
|
78
|
+
{ region: "경기도", city: "양평군" },
|
|
79
|
+
{ region: "경기도", city: "여주시" },
|
|
80
|
+
{ region: "경기도", city: "연천군" },
|
|
81
|
+
{ region: "경기도", city: "오산시" },
|
|
82
|
+
{ region: "경기도", city: "용인시기흥구" },
|
|
83
|
+
{ region: "경기도", city: "용인시수지구" },
|
|
84
|
+
{ region: "경기도", city: "용인시처인구" },
|
|
85
|
+
{ region: "경기도", city: "의왕시" },
|
|
86
|
+
{ region: "경기도", city: "의정부시" },
|
|
87
|
+
{ region: "경기도", city: "이천시" },
|
|
88
|
+
{ region: "경기도", city: "파주시" },
|
|
89
|
+
{ region: "경기도", city: "평택시" },
|
|
90
|
+
{ region: "경기도", city: "포천시" },
|
|
91
|
+
{ region: "경기도", city: "하남시" },
|
|
92
|
+
{ region: "경기도", city: "화성시" },
|
|
93
|
+
{ region: "경상남도", city: "거제시" },
|
|
94
|
+
{ region: "경상남도", city: "거창군" },
|
|
95
|
+
{ region: "경상남도", city: "고성군" },
|
|
96
|
+
{ region: "경상남도", city: "김해시" },
|
|
97
|
+
{ region: "경상남도", city: "남해군" },
|
|
98
|
+
{ region: "경상남도", city: "밀양시" },
|
|
99
|
+
{ region: "경상남도", city: "사천시" },
|
|
100
|
+
{ region: "경상남도", city: "산청군" },
|
|
101
|
+
{ region: "경상남도", city: "양산시" },
|
|
102
|
+
{ region: "경상남도", city: "의령군" },
|
|
103
|
+
{ region: "경상남도", city: "진주시" },
|
|
104
|
+
{ region: "경상남도", city: "창녕군" },
|
|
105
|
+
{ region: "경상남도", city: "창원시마산합포구" },
|
|
106
|
+
{ region: "경상남도", city: "창원시마산회원구" },
|
|
107
|
+
{ region: "경상남도", city: "창원시성산구" },
|
|
108
|
+
{ region: "경상남도", city: "창원시의창구" },
|
|
109
|
+
{ region: "경상남도", city: "창원시진해구" },
|
|
110
|
+
{ region: "경상남도", city: "통영시" },
|
|
111
|
+
{ region: "경상남도", city: "하동군" },
|
|
112
|
+
{ region: "경상남도", city: "함안군" },
|
|
113
|
+
{ region: "경상남도", city: "함양군" },
|
|
114
|
+
{ region: "경상남도", city: "합천군" },
|
|
115
|
+
{ region: "경상북도", city: "경산시" },
|
|
116
|
+
{ region: "경상북도", city: "경주시" },
|
|
117
|
+
{ region: "경상북도", city: "고령군" },
|
|
118
|
+
{ region: "경상북도", city: "구미시" },
|
|
119
|
+
{ region: "경상북도", city: "군위군" },
|
|
120
|
+
{ region: "경상북도", city: "김천시" },
|
|
121
|
+
{ region: "경상북도", city: "문경시" },
|
|
122
|
+
{ region: "경상북도", city: "봉화군" },
|
|
123
|
+
{ region: "경상북도", city: "상주시" },
|
|
124
|
+
{ region: "경상북도", city: "성주군" },
|
|
125
|
+
{ region: "경상북도", city: "안동시" },
|
|
126
|
+
{ region: "경상북도", city: "영덕군" },
|
|
127
|
+
{ region: "경상북도", city: "영양군" },
|
|
128
|
+
{ region: "경상북도", city: "영주시" },
|
|
129
|
+
{ region: "경상북도", city: "영천시" },
|
|
130
|
+
{ region: "경상북도", city: "예천군" },
|
|
131
|
+
{ region: "경상북도", city: "울릉군" },
|
|
132
|
+
{ region: "경상북도", city: "울진군" },
|
|
133
|
+
{ region: "경상북도", city: "의성군" },
|
|
134
|
+
{ region: "경상북도", city: "청도군" },
|
|
135
|
+
{ region: "경상북도", city: "청송군" },
|
|
136
|
+
{ region: "경상북도", city: "칠곡군" },
|
|
137
|
+
{ region: "경상북도", city: "포항시남구" },
|
|
138
|
+
{ region: "경상북도", city: "포항시북구" },
|
|
139
|
+
{ region: "광주광역시", city: "광산구" },
|
|
140
|
+
{ region: "광주광역시", city: "남구" },
|
|
141
|
+
{ region: "광주광역시", city: "동구" },
|
|
142
|
+
{ region: "광주광역시", city: "북구" },
|
|
143
|
+
{ region: "광주광역시", city: "서구" },
|
|
144
|
+
{ region: "대구광역시", city: "남구" },
|
|
145
|
+
{ region: "대구광역시", city: "달서구" },
|
|
146
|
+
{ region: "대구광역시", city: "달성군" },
|
|
147
|
+
{ region: "대구광역시", city: "동구" },
|
|
148
|
+
{ region: "대구광역시", city: "북구" },
|
|
149
|
+
{ region: "대구광역시", city: "서구" },
|
|
150
|
+
{ region: "대구광역시", city: "수성구" },
|
|
151
|
+
{ region: "대구광역시", city: "중구" },
|
|
152
|
+
{ region: "대전광역시", city: "대덕구" },
|
|
153
|
+
{ region: "대전광역시", city: "동구" },
|
|
154
|
+
{ region: "대전광역시", city: "서구" },
|
|
155
|
+
{ region: "대전광역시", city: "유성구" },
|
|
156
|
+
{ region: "대전광역시", city: "중구" },
|
|
157
|
+
{ region: "부산광역시", city: "강서구" },
|
|
158
|
+
{ region: "부산광역시", city: "금정구" },
|
|
159
|
+
{ region: "부산광역시", city: "기장군" },
|
|
160
|
+
{ region: "부산광역시", city: "남구" },
|
|
161
|
+
{ region: "부산광역시", city: "동구" },
|
|
162
|
+
{ region: "부산광역시", city: "동래구" },
|
|
163
|
+
{ region: "부산광역시", city: "부산진구" },
|
|
164
|
+
{ region: "부산광역시", city: "북구" },
|
|
165
|
+
{ region: "부산광역시", city: "사상구" },
|
|
166
|
+
{ region: "부산광역시", city: "사하구" },
|
|
167
|
+
{ region: "부산광역시", city: "서구" },
|
|
168
|
+
{ region: "부산광역시", city: "수영구" },
|
|
169
|
+
{ region: "부산광역시", city: "연제구" },
|
|
170
|
+
{ region: "부산광역시", city: "영도구" },
|
|
171
|
+
{ region: "부산광역시", city: "중구" },
|
|
172
|
+
{ region: "부산광역시", city: "해운대구" },
|
|
173
|
+
{ region: "서울특별시", city: "강남구" },
|
|
174
|
+
{ region: "서울특별시", city: "강동구" },
|
|
175
|
+
{ region: "서울특별시", city: "강북구" },
|
|
176
|
+
{ region: "서울특별시", city: "강서구" },
|
|
177
|
+
{ region: "서울특별시", city: "관악구" },
|
|
178
|
+
{ region: "서울특별시", city: "광진구" },
|
|
179
|
+
{ region: "서울특별시", city: "구로구" },
|
|
180
|
+
{ region: "서울특별시", city: "금천구" },
|
|
181
|
+
{ region: "서울특별시", city: "노원구" },
|
|
182
|
+
{ region: "서울특별시", city: "도봉구" },
|
|
183
|
+
{ region: "서울특별시", city: "동대문구" },
|
|
184
|
+
{ region: "서울특별시", city: "동작구" },
|
|
185
|
+
{ region: "서울특별시", city: "마포구" },
|
|
186
|
+
{ region: "서울특별시", city: "서대문구" },
|
|
187
|
+
{ region: "서울특별시", city: "서초구" },
|
|
188
|
+
{ region: "서울특별시", city: "성동구" },
|
|
189
|
+
{ region: "서울특별시", city: "성북구" },
|
|
190
|
+
{ region: "서울특별시", city: "송파구" },
|
|
191
|
+
{ region: "서울특별시", city: "양천구" },
|
|
192
|
+
{ region: "서울특별시", city: "영등포구" },
|
|
193
|
+
{ region: "서울특별시", city: "용산구" },
|
|
194
|
+
{ region: "서울특별시", city: "은평구" },
|
|
195
|
+
{ region: "서울특별시", city: "종로구" },
|
|
196
|
+
{ region: "서울특별시", city: "중구" },
|
|
197
|
+
{ region: "서울특별시", city: "중랑구" },
|
|
198
|
+
{ region: "울산광역시", city: "남구" },
|
|
199
|
+
{ region: "울산광역시", city: "동구" },
|
|
200
|
+
{ region: "울산광역시", city: "북구" },
|
|
201
|
+
{ region: "울산광역시", city: "울주군" },
|
|
202
|
+
{ region: "울산광역시", city: "중구" },
|
|
203
|
+
{ region: "인천광역시", city: "강화군" },
|
|
204
|
+
{ region: "인천광역시", city: "계양구" },
|
|
205
|
+
{ region: "인천광역시", city: "남구" },
|
|
206
|
+
{ region: "인천광역시", city: "남동구" },
|
|
207
|
+
{ region: "인천광역시", city: "동구" },
|
|
208
|
+
{ region: "인천광역시", city: "부평구" },
|
|
209
|
+
{ region: "인천광역시", city: "서구" },
|
|
210
|
+
{ region: "인천광역시", city: "연수구" },
|
|
211
|
+
{ region: "인천광역시", city: "옹진군" },
|
|
212
|
+
{ region: "인천광역시", city: "중구" },
|
|
213
|
+
{ region: "전라남도", city: "강진군" },
|
|
214
|
+
{ region: "전라남도", city: "고흥군" },
|
|
215
|
+
{ region: "전라남도", city: "곡성군" },
|
|
216
|
+
{ region: "전라남도", city: "광양시" },
|
|
217
|
+
{ region: "전라남도", city: "구례군" },
|
|
218
|
+
{ region: "전라남도", city: "나주시" },
|
|
219
|
+
{ region: "전라남도", city: "담양군" },
|
|
220
|
+
{ region: "전라남도", city: "목포시" },
|
|
221
|
+
{ region: "전라남도", city: "무안군" },
|
|
222
|
+
{ region: "전라남도", city: "보성군" },
|
|
223
|
+
{ region: "전라남도", city: "순천시" },
|
|
224
|
+
{ region: "전라남도", city: "신안군" },
|
|
225
|
+
{ region: "전라남도", city: "여수시" },
|
|
226
|
+
{ region: "전라남도", city: "영광군" },
|
|
227
|
+
{ region: "전라남도", city: "영암군" },
|
|
228
|
+
{ region: "전라남도", city: "완도군" },
|
|
229
|
+
{ region: "전라남도", city: "장성군" },
|
|
230
|
+
{ region: "전라남도", city: "장흥군" },
|
|
231
|
+
{ region: "전라남도", city: "진도군" },
|
|
232
|
+
{ region: "전라남도", city: "함평군" },
|
|
233
|
+
{ region: "전라남도", city: "해남군" },
|
|
234
|
+
{ region: "전라남도", city: "화순군" },
|
|
235
|
+
{ region: "전라북도", city: "고창군" },
|
|
236
|
+
{ region: "전라북도", city: "군산시" },
|
|
237
|
+
{ region: "전라북도", city: "김제시" },
|
|
238
|
+
{ region: "전라북도", city: "남원시" },
|
|
239
|
+
{ region: "전라북도", city: "무주군" },
|
|
240
|
+
{ region: "전라북도", city: "부안군" },
|
|
241
|
+
{ region: "전라북도", city: "순창군" },
|
|
242
|
+
{ region: "전라북도", city: "완주군" },
|
|
243
|
+
{ region: "전라북도", city: "익산시" },
|
|
244
|
+
{ region: "전라북도", city: "임실군" },
|
|
245
|
+
{ region: "전라북도", city: "장수군" },
|
|
246
|
+
{ region: "전라북도", city: "전주시덕진구" },
|
|
247
|
+
{ region: "전라북도", city: "전주시완산구" },
|
|
248
|
+
{ region: "전라북도", city: "정읍시" },
|
|
249
|
+
{ region: "전라북도", city: "진안군" },
|
|
250
|
+
{ region: "제주특별자치도", city: "서귀포시" },
|
|
251
|
+
{ region: "제주특별자치도", city: "제주시" },
|
|
252
|
+
{ region: "충청남도", city: "계룡시" },
|
|
253
|
+
{ region: "충청남도", city: "공주시" },
|
|
254
|
+
{ region: "충청남도", city: "금산군" },
|
|
255
|
+
{ region: "충청남도", city: "논산시" },
|
|
256
|
+
{ region: "충청남도", city: "당진시" },
|
|
257
|
+
{ region: "충청남도", city: "보령시" },
|
|
258
|
+
{ region: "충청남도", city: "부여군" },
|
|
259
|
+
{ region: "충청남도", city: "서산시" },
|
|
260
|
+
{ region: "충청남도", city: "서천군" },
|
|
261
|
+
{ region: "충청남도", city: "아산시" },
|
|
262
|
+
{ region: "충청남도", city: "예산군" },
|
|
263
|
+
{ region: "충청남도", city: "천안시동남구" },
|
|
264
|
+
{ region: "충청남도", city: "천안시서북구" },
|
|
265
|
+
{ region: "충청남도", city: "청양군" },
|
|
266
|
+
{ region: "충청남도", city: "태안군" },
|
|
267
|
+
{ region: "충청남도", city: "홍성군" },
|
|
268
|
+
{ region: "충청북도", city: "괴산군" },
|
|
269
|
+
{ region: "충청북도", city: "단양군" },
|
|
270
|
+
{ region: "충청북도", city: "보은군" },
|
|
271
|
+
{ region: "충청북도", city: "영동군" },
|
|
272
|
+
{ region: "충청북도", city: "옥천군" },
|
|
273
|
+
{ region: "충청북도", city: "음성군" },
|
|
274
|
+
{ region: "충청북도", city: "제천시" },
|
|
275
|
+
{ region: "충청북도", city: "증평군" },
|
|
276
|
+
{ region: "충청북도", city: "진천군" },
|
|
277
|
+
{ region: "충청북도", city: "청주시상당구" },
|
|
278
|
+
{ region: "충청북도", city: "청주시서원구" },
|
|
279
|
+
{ region: "충청북도", city: "청주시청원구" },
|
|
280
|
+
{ region: "충청북도", city: "청주시흥덕구" },
|
|
281
|
+
{ region: "충청북도", city: "충주시" },
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Every 시군구 name a `subregion` span may close on.
|
|
286
|
+
*/
|
|
287
|
+
export const KR_SIGUNGU_NAMES: readonly string[] = [...new Set(KR_SIGUNGU.map((s) => s.city))]
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The characters a `subregion` surface must absorb from what follows it to become a register name, or null.
|
|
291
|
+
*/
|
|
292
|
+
export function krSubregionCompletion(surface: string, following: string): string | null {
|
|
293
|
+
return completeFromRegister(KR_SIGUNGU_NAMES, surface, following)
|
|
294
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* Component-tag → resolver-placetype mapping, the equivalence groups a query expands into, and the test
|
|
7
|
+
* that tells a widened match from an exact one.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ComponentTag } from "#component"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mapping from mailwoman's address-component tags to the resolver's placetype taxonomy.
|
|
14
|
+
*
|
|
15
|
+
* PARTIAL on purpose: a tag absent from the map is NOT queried, and the resolver pass leaves its classifier attribution
|
|
16
|
+
* untouched. Omission is therefore a routing decision, not an oversight.
|
|
17
|
+
*/
|
|
18
|
+
export type PlacetypeMap = Partial<Record<ComponentTag, string>>
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The map used when a backend does not supply its own.
|
|
22
|
+
*
|
|
23
|
+
* `street` and `house_number` are absent because WOF admin has no rows for them — they resolve through the situs
|
|
24
|
+
* extracts instead, which are keyed by street, not by placetype.
|
|
25
|
+
*
|
|
26
|
+
* The JP tiers are present because the candidate gazetteer keys them: 91.3% of Japanese records carry a kanji or kana
|
|
27
|
+
* key (49,255 of 53,920), a prefecture is a WOF `region`, a municipality a `locality` (its filter group admits the
|
|
28
|
+
* `borough` wards and `localadmin`), and a district (大字 / 町名) sits in the `locality` band beside the neighbourhoods.
|
|
29
|
+
* Measured on 300 JP board rows: with these entries and the JP rungs on the admin ladder, 271 resolve within 15 km;
|
|
30
|
+
* without them, 0. Only the character-path CJK model emits the tags, so no Latin parse reaches these rows.
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_PLACETYPE_MAP: PlacetypeMap = {
|
|
33
|
+
country: "country",
|
|
34
|
+
region: "region",
|
|
35
|
+
locality: "locality",
|
|
36
|
+
dependent_locality: "locality",
|
|
37
|
+
subregion: "county",
|
|
38
|
+
prefecture: "region",
|
|
39
|
+
municipality: "locality",
|
|
40
|
+
district: "locality",
|
|
41
|
+
// `postcode` (mailwoman tag) maps to WOF's `postalcode` placetype. Resolves only when the
|
|
42
|
+
// backend has the postcode extract available — `WOFSQLitePlaceLookup` auto-routes `postalcode`
|
|
43
|
+
// queries to a `postalcode_us` (or similarly-named) extract, falling back to main if absent.
|
|
44
|
+
postcode: "postalcode",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Where a country's WOF typing of a tier departs from {@link DEFAULT_PLACETYPE_MAP}, the entries that differ. The map
|
|
49
|
+
* names the tier by tag; WOF names it by placetype, and the two agree for most countries and not for all.
|
|
50
|
+
*
|
|
51
|
+
* - **TW** — 鄉鎮市區, the tier below the 縣市 that the parse tags `subregion`, is `locality` or `localadmin` in WOF for 164 of
|
|
52
|
+
* the 178 held-out districts the candidate table carries and `county` for 14 (the census in
|
|
53
|
+
* `docs/records/evals/2026-09-08-v8-cjk-regs.md` §5). Under the default `county` band `臺北市中正區` reached nothing and
|
|
54
|
+
* the admin tier fell to the 縣市. The `locality` group admits `localadmin` too, the rule the JP `municipality` tag
|
|
55
|
+
* already relies on, and the exact-type preference in ranking keeps a `locality` row ahead of a same-name
|
|
56
|
+
* `localadmin` row.
|
|
57
|
+
*/
|
|
58
|
+
const COUNTRY_PLACETYPE_OVERRIDES: Readonly<Record<string, PlacetypeMap>> = {
|
|
59
|
+
tw: { subregion: "locality" },
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The placetype map for a resolve scoped to `countryCode` (ISO alpha-2, any case): the default map with the country's
|
|
64
|
+
* overrides applied, or the default map itself when the country has none or is unknown. The identity of the default
|
|
65
|
+
* object is preserved in that case, so a caller comparing maps to decide whether a second resolve is needed reads
|
|
66
|
+
* "unchanged" correctly.
|
|
67
|
+
*/
|
|
68
|
+
export function placetypeMapForCountry(countryCode: string | null | undefined): PlacetypeMap {
|
|
69
|
+
const overrides = countryCode ? COUNTRY_PLACETYPE_OVERRIDES[countryCode.toLowerCase()] : undefined
|
|
70
|
+
|
|
71
|
+
return overrides ? { ...DEFAULT_PLACETYPE_MAP, ...overrides } : DEFAULT_PLACETYPE_MAP
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Placetype-equivalence groups for lookup FILTERING. WOF splits a single addressing tier across several placetypes, but
|
|
76
|
+
* an address's span can name ANY of them. A backend that filters to the one "obvious" placetype makes the equivalents
|
|
77
|
+
* unreachable, so a fuzzy same-name place in the wrong tier wins instead.
|
|
78
|
+
*
|
|
79
|
+
* Three tiers are affected (the value of each entry is the set the SQL filter should accept; the FIRST entry is the
|
|
80
|
+
* canonical/requested type, which extract routing keys off):
|
|
81
|
+
*
|
|
82
|
+
* - **`locality`** — `locality` (most cities), `borough` (Brooklyn, the Paris arrondissements, the London boroughs), and
|
|
83
|
+
* `localadmin` (FR communes, US towns/townships in New England). Without the group, Brooklyn-the-borough (pop 2.5M)
|
|
84
|
+
* was unreachable and the fuzzy "Brooklyn Park, MN" won.
|
|
85
|
+
* - **`region`** — `region` + `macroregion` (#718). WOF does NOT model every country's top-level civil division as
|
|
86
|
+
* `region`: Italian regions (Lombardia, Veneto, Toscana…) are `macroregion` (their PROVINCES are `region`), and the
|
|
87
|
+
* post-2016 French régions (Île-de-France) are `macroregion` too. An address's `region` span names exactly those, so
|
|
88
|
+
* a `region`-only filter resolved them to NOTHING (confirmed against the IT/FR eval rows). US states / DE
|
|
89
|
+
* Bundesländer / ES provincias are genuine `region`, so the EXACT-type match is preferred in ranking (see the
|
|
90
|
+
* resolve.ts fallback-quality annotation) — the macro is the recall safety net, not a demotion.
|
|
91
|
+
* - **`county`** — `county` + `macrocounty` (#718). The `subregion` ComponentTag maps to `county` via
|
|
92
|
+
* {@link DEFAULT_PLACETYPE_MAP}; WOF carries `macrocounty` for FR départements-grouping / DE / GB tiers above the
|
|
93
|
+
* county. Proactive (no eval row exercises `subregion` today) but symmetric with `region` — biasing to inclusion,
|
|
94
|
+
* since a missed resolution costs more than a too-broad candidate (which is QA-visible). Same exact-type preference
|
|
95
|
+
* applies.
|
|
96
|
+
*
|
|
97
|
+
* This table is the single source of truth for that expansion, shared by every lookup backend
|
|
98
|
+
* (`@mailwoman/core/resolver-wof-sqlite`, `@mailwoman/core/resolver-wof-wasm`, and the demo's httpvfs lookup) so the
|
|
99
|
+
* Node and browser resolvers can't drift. Keyed by the REQUESTED placetype. Placetypes without an entry pass through
|
|
100
|
+
* unchanged — an explicit `placetype: "borough"` query stays narrow.
|
|
101
|
+
*/
|
|
102
|
+
export const PLACETYPE_FILTER_GROUPS: Readonly<Record<string, readonly string[]>> = {
|
|
103
|
+
locality: ["locality", "borough", "localadmin"],
|
|
104
|
+
region: ["region", "macroregion"],
|
|
105
|
+
county: ["county", "macrocounty"],
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Expand a placetype filter through {@link PLACETYPE_FILTER_GROUPS}, deduplicated and order-preserving (the first entry
|
|
110
|
+
* stays first — extract routing keys off it). `null`/`undefined` (no filter) passes through untouched.
|
|
111
|
+
*/
|
|
112
|
+
export function expandPlacetypeFilter(placetypes: null): null
|
|
113
|
+
export function expandPlacetypeFilter(placetypes: readonly string[]): string[]
|
|
114
|
+
export function expandPlacetypeFilter(placetypes: readonly string[] | null): string[] | null
|
|
115
|
+
|
|
116
|
+
export function expandPlacetypeFilter(placetypes: readonly string[] | null): string[] | null {
|
|
117
|
+
if (!placetypes) return null
|
|
118
|
+
const out: string[] = []
|
|
119
|
+
|
|
120
|
+
for (const placetype of placetypes) {
|
|
121
|
+
for (const expanded of PLACETYPE_FILTER_GROUPS[placetype] ?? [placetype]) {
|
|
122
|
+
if (!out.includes(expanded)) {
|
|
123
|
+
out.push(expanded)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return out
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Macro/broader-tier members of {@link PLACETYPE_FILTER_GROUPS} — the recall safety net a query may fall through to
|
|
133
|
+
* when no candidate of the EXACT requested placetype exists (#718). DELIBERATELY scoped to the `macro*` tiers only: the
|
|
134
|
+
* `locality` group's `borough`/`localadmin` are genuine peers (Brooklyn-the-borough is a first-class locality answer,
|
|
135
|
+
* #404-class), NOT fallbacks — so they must NOT be deprioritized or annotated. Only `macroregion`/`macrocounty` are a
|
|
136
|
+
* broader admin tier standing in for a true `region`/`county`.
|
|
137
|
+
*/
|
|
138
|
+
const MACRO_FALLBACK_PLACETYPES: ReadonlySet<string> = new Set(["macroregion", "macrocounty"])
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Did `candidatePlacetype` resolve `requestedPlacetype` only via a BROADER admin tier (a macro-type fallback within the
|
|
142
|
+
* {@link PLACETYPE_FILTER_GROUPS} expansion), rather than the exact type (#718)?
|
|
143
|
+
*
|
|
144
|
+
* `region` → `region` is exact (false); `region` → `macroregion` is a fallback (true). Scoped to the `macro*` tiers
|
|
145
|
+
* (see {@link MACRO_FALLBACK_PLACETYPES}) so the `locality` group's borough/localadmin peers stay exact. The resolver
|
|
146
|
+
* uses this to (a) prefer an exact-type candidate in ranking and (b) annotate `resolutionQuality: "fallback"` when only
|
|
147
|
+
* a macro-type matched. A placetype outside the requested group, or any non-macro member, is treated as exact (false).
|
|
148
|
+
*/
|
|
149
|
+
export function isPlacetypeFallback(requestedPlacetype: string, candidatePlacetype: string): boolean {
|
|
150
|
+
const group = PLACETYPE_FILTER_GROUPS[requestedPlacetype]
|
|
151
|
+
|
|
152
|
+
if (!group) return false
|
|
153
|
+
|
|
154
|
+
if (candidatePlacetype === requestedPlacetype) return false
|
|
155
|
+
|
|
156
|
+
return MACRO_FALLBACK_PLACETYPES.has(candidatePlacetype) && group.includes(candidatePlacetype)
|
|
157
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "postcode-shapes-v1",
|
|
3
|
+
"note": "Postcode SHAPE patterns for detecting postcode-like spans in free text, ordered most-specific to least. Priority IS the array index: a lower index wins an overlap. This is a SHAPE test, not gazetteer membership — see postcode-systems.ts for the per-system membership question. Alphanumeric patterns require UPPERCASE letters, which keeps them off lowercase prose. The regex bodies are written in the subset both JavaScript RegExp and Python re accept, because two runtimes read this file; a row that needs different source text in the two dialects needs a second field and a stated reason, not a loosened comparison.",
|
|
4
|
+
"shapes": [
|
|
5
|
+
{
|
|
6
|
+
"label": "JP-marked",
|
|
7
|
+
"kind": "designated",
|
|
8
|
+
"pattern": "(?<=〒\\s?)\\d{3}-\\d{4}\\b",
|
|
9
|
+
"unicode": true,
|
|
10
|
+
"javascriptOnly": "Python's `re` refuses a variable-width lookbehind, and `\\s?` makes this one variable-width. Nothing on the Python side compiles this row.",
|
|
11
|
+
"note": "The digits behind 〒, optionally spaced (〒506-0025, 〒 100-0001). The character path keeps the mark in the text, and on a venue-led line the model has read the digits as a house number. A designated match may overwrite any existing label."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"label": "GB",
|
|
15
|
+
"kind": "alnum",
|
|
16
|
+
"pattern": "\\b[A-Z]{1,2}\\d[A-Z\\d]?\\s+\\d[A-Z]{2}\\b",
|
|
17
|
+
"note": "Outward + space + inward: SW1A 1AA, EH8 9YL, W1J 9PN, IP13 6SU, B12 8QX."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"label": "CA",
|
|
21
|
+
"kind": "alnum",
|
|
22
|
+
"pattern": "\\b[A-Z]\\d[A-Z]\\s?\\d[A-Z]\\d\\b",
|
|
23
|
+
"note": "A1A 1A1, space optional: M5V 2T6, H2X 2T6, H3B 1A3."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"label": "IE",
|
|
27
|
+
"kind": "alnum",
|
|
28
|
+
"pattern": "\\b(?:[A-Z]\\d{2}|D6W)\\s+[A-Z\\d]{4}\\b",
|
|
29
|
+
"note": "Eircode: routing key (letter + 2 digits, or the D6W special) + a 4-alnum unique part — D02 AF30, T12 X70A, F91 Y5CY. Space REQUIRED: a glued 7-alnum blob is too forgeable for the ADD path. No GB collision, because a letter+2-digit GB outward always has a 3-character inward (B12 8QX), never 4."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"label": "DE",
|
|
33
|
+
"kind": "alnum",
|
|
34
|
+
"pattern": "\\bD-\\d{5}\\b",
|
|
35
|
+
"note": "The D-prefixed form: D-68161."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"label": "NL",
|
|
39
|
+
"kind": "alnum",
|
|
40
|
+
"pattern": "\\b\\d{4}\\s?[A-Z]{2}\\b",
|
|
41
|
+
"note": "1234 AB / 1234AB — space optional, glued is common. The US \"2737 CA\" collision (a ZIP+4 tail followed by a state) is settled by longest-match-wins, which lets the ZIP+4 claim it."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"label": "ZIP4",
|
|
45
|
+
"kind": "numeric",
|
|
46
|
+
"pattern": "\\b\\d{5}-\\d{4}\\b",
|
|
47
|
+
"note": "US ZIP+4. A numeric match is eligible only for snapping, never for adding a postcode where the model emitted none."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"label": "BR",
|
|
51
|
+
"kind": "numeric",
|
|
52
|
+
"pattern": "\\b\\d{5}-\\d{3}\\b",
|
|
53
|
+
"note": "CEP: 70390-100, 95090-020. Without it the generic NUM5 below matched the five-digit head and the trailing-smear clip discarded the sector suffix, so the repair pass truncated a parse the model had got right (repair OFF \"95090-020\", repair ON \"95090\"). The trailing word boundary keeps it off a ZIP+4's first nine characters, since \"94610-2737\" has a digit after \"273\"."
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"label": "JP",
|
|
57
|
+
"kind": "numeric",
|
|
58
|
+
"pattern": "\\b\\d{3}-\\d{4}\\b",
|
|
59
|
+
"note": "100-0001."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"label": "PT",
|
|
63
|
+
"kind": "numeric",
|
|
64
|
+
"pattern": "\\b\\d{4}-\\d{3}\\b",
|
|
65
|
+
"note": "3060-187."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"label": "PL",
|
|
69
|
+
"kind": "numeric",
|
|
70
|
+
"pattern": "\\b\\d{2}-\\d{3}\\b",
|
|
71
|
+
"note": "47-400."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"label": "NUM5",
|
|
75
|
+
"kind": "numeric",
|
|
76
|
+
"pattern": "\\b\\d{5}\\b",
|
|
77
|
+
"note": "The bare 5-digit shape US, FR, DE and ES all share. Last, so every more-specific row above claims its overlap first."
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|