@mailwoman/codex 8.1.0 → 8.3.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/address-system-conventions.ts +7 -1
- package/au/delivery-service.ts +34 -12
- package/au/level-designator.ts +38 -14
- package/au/postcode.ts +9 -3
- package/au/state.ts +9 -3
- package/ca/postal-code.ts +10 -3
- package/ca/province.ts +23 -9
- package/ca/street-type.ts +9 -5
- package/country/country.ts +14 -8
- package/country/official-languages.ts +12 -4
- package/country/reference-data.ts +7 -3
- package/country/reference.ts +1 -1
- package/country/subdivision.ts +19 -8
- package/de/bundesland.ts +19 -6
- package/de/postleitzahl.ts +21 -7
- package/de/street-type.ts +14 -3
- package/fr/cedex.ts +15 -5
- package/fr/code-postal.ts +17 -4
- package/fr/departement.ts +18 -6
- package/fr/region.ts +23 -9
- package/fr/voie.ts +8 -4
- package/gb/country.ts +25 -9
- package/gb/postcode-area.ts +6 -2
- package/gb/postcode.ts +10 -3
- package/gb/street-type.ts +4 -2
- package/index.ts +1 -0
- package/jp/address-unit.ts +8 -4
- package/jp/postal-code.ts +7 -3
- package/jp/prefecture.ts +27 -10
- package/level-semantics.ts +55 -19
- package/nz/delivery-service.ts +43 -15
- package/nz/postcode.ts +9 -3
- package/out/address-system-conventions.d.ts +7 -1
- package/out/address-system-conventions.d.ts.map +1 -1
- package/out/address-system-conventions.js +7 -1
- package/out/address-system-conventions.js.map +1 -1
- package/out/au/delivery-service.d.ts +30 -10
- package/out/au/delivery-service.d.ts.map +1 -1
- package/out/au/delivery-service.js +7 -3
- package/out/au/delivery-service.js.map +1 -1
- package/out/au/level-designator.d.ts +27 -9
- package/out/au/level-designator.d.ts.map +1 -1
- package/out/au/level-designator.js +10 -6
- package/out/au/level-designator.js.map +1 -1
- package/out/au/postcode.d.ts +9 -3
- package/out/au/postcode.d.ts.map +1 -1
- package/out/au/postcode.js +9 -3
- package/out/au/postcode.js.map +1 -1
- package/out/au/state.d.ts +9 -3
- package/out/au/state.d.ts.map +1 -1
- package/out/au/state.js +6 -2
- package/out/au/state.js.map +1 -1
- package/out/ca/postal-code.d.ts +3 -1
- package/out/ca/postal-code.d.ts.map +1 -1
- package/out/ca/postal-code.js +9 -3
- package/out/ca/postal-code.js.map +1 -1
- package/out/ca/province.d.ts +18 -6
- package/out/ca/province.d.ts.map +1 -1
- package/out/ca/province.js +8 -4
- package/out/ca/province.js.map +1 -1
- package/out/ca/street-type.d.ts.map +1 -1
- package/out/ca/street-type.js +9 -5
- package/out/ca/street-type.js.map +1 -1
- package/out/country/country.d.ts +11 -7
- package/out/country/country.d.ts.map +1 -1
- package/out/country/country.js +7 -4
- package/out/country/country.js.map +1 -1
- package/out/country/official-languages.d.ts +12 -4
- package/out/country/official-languages.d.ts.map +1 -1
- package/out/country/official-languages.js +3 -1
- package/out/country/official-languages.js.map +1 -1
- package/out/country/reference-data.d.ts +6 -2
- package/out/country/reference-data.d.ts.map +1 -1
- package/out/country/reference-data.js +4 -2
- package/out/country/reference-data.js.map +1 -1
- package/out/country/reference.js +1 -1
- package/out/country/reference.js.map +1 -1
- package/out/country/subdivision.d.ts +12 -4
- package/out/country/subdivision.d.ts.map +1 -1
- package/out/country/subdivision.js +6 -4
- package/out/country/subdivision.js.map +1 -1
- package/out/de/bundesland.d.ts +18 -6
- package/out/de/bundesland.d.ts.map +1 -1
- package/out/de/bundesland.js +3 -1
- package/out/de/bundesland.js.map +1 -1
- package/out/de/postleitzahl.d.ts +21 -7
- package/out/de/postleitzahl.d.ts.map +1 -1
- package/out/de/postleitzahl.js +9 -3
- package/out/de/postleitzahl.js.map +1 -1
- package/out/de/street-type.d.ts +5 -4
- package/out/de/street-type.d.ts.map +1 -1
- package/out/de/street-type.js +10 -2
- package/out/de/street-type.js.map +1 -1
- package/out/fr/cedex.d.ts +15 -5
- package/out/fr/cedex.d.ts.map +1 -1
- package/out/fr/cedex.js +6 -2
- package/out/fr/cedex.js.map +1 -1
- package/out/fr/code-postal.d.ts +8 -11
- package/out/fr/code-postal.d.ts.map +1 -1
- package/out/fr/code-postal.js +24 -4
- package/out/fr/code-postal.js.map +1 -1
- package/out/fr/departement.d.ts +18 -6
- package/out/fr/departement.d.ts.map +1 -1
- package/out/fr/departement.js +3 -1
- package/out/fr/departement.js.map +1 -1
- package/out/fr/region.d.ts +18 -6
- package/out/fr/region.d.ts.map +1 -1
- package/out/fr/region.js +11 -5
- package/out/fr/region.js.map +1 -1
- package/out/fr/voie.d.ts +3 -1
- package/out/fr/voie.d.ts.map +1 -1
- package/out/fr/voie.js +3 -3
- package/out/fr/voie.js.map +1 -1
- package/out/gb/country.d.ts +18 -6
- package/out/gb/country.d.ts.map +1 -1
- package/out/gb/country.js +13 -5
- package/out/gb/country.js.map +1 -1
- package/out/gb/postcode-area.d.ts.map +1 -1
- package/out/gb/postcode-area.js +6 -2
- package/out/gb/postcode-area.js.map +1 -1
- package/out/gb/postcode.d.ts +3 -11
- package/out/gb/postcode.d.ts.map +1 -1
- package/out/gb/postcode.js +19 -3
- package/out/gb/postcode.js.map +1 -1
- package/out/gb/street-type.d.ts +3 -1
- package/out/gb/street-type.d.ts.map +1 -1
- package/out/gb/street-type.js +1 -1
- package/out/gb/street-type.js.map +1 -1
- package/out/index.d.ts.map +1 -1
- package/out/index.js.map +1 -1
- package/out/jp/address-unit.d.ts +6 -2
- package/out/jp/address-unit.d.ts.map +1 -1
- package/out/jp/address-unit.js +2 -2
- package/out/jp/address-unit.js.map +1 -1
- package/out/jp/postal-code.d.ts +6 -2
- package/out/jp/postal-code.d.ts.map +1 -1
- package/out/jp/postal-code.js +7 -3
- package/out/jp/postal-code.js.map +1 -1
- package/out/jp/prefecture.d.ts +24 -8
- package/out/jp/prefecture.d.ts.map +1 -1
- package/out/jp/prefecture.js +5 -3
- package/out/jp/prefecture.js.map +1 -1
- package/out/level-semantics.d.ts +48 -16
- package/out/level-semantics.d.ts.map +1 -1
- package/out/level-semantics.js +34 -12
- package/out/level-semantics.js.map +1 -1
- package/out/nz/delivery-service.d.ts +39 -13
- package/out/nz/delivery-service.d.ts.map +1 -1
- package/out/nz/delivery-service.js +16 -6
- package/out/nz/delivery-service.js.map +1 -1
- package/out/nz/postcode.d.ts +9 -3
- package/out/nz/postcode.d.ts.map +1 -1
- package/out/nz/postcode.js +9 -3
- package/out/nz/postcode.js.map +1 -1
- package/out/postcode-systems.d.ts +3 -1
- package/out/postcode-systems.d.ts.map +1 -1
- package/out/postcode-systems.js +1 -1
- package/out/postcode-systems.js.map +1 -1
- package/out/tools/build-country-surface-lexicon.js +26 -12
- package/out/tools/build-country-surface-lexicon.js.map +1 -1
- package/out/tools/export-country-surfaces.js.map +1 -1
- package/out/tools/generate-country-reference.d.ts +12 -4
- package/out/tools/generate-country-reference.d.ts.map +1 -1
- package/out/tools/generate-country-reference.js +10 -8
- package/out/tools/generate-country-reference.js.map +1 -1
- package/out/tools/generate-official-languages.d.ts +18 -6
- package/out/tools/generate-official-languages.d.ts.map +1 -1
- package/out/tools/generate-official-languages.js +9 -7
- package/out/tools/generate-official-languages.js.map +1 -1
- package/out/us/floor-designator.d.ts +15 -5
- package/out/us/floor-designator.d.ts.map +1 -1
- package/out/us/floor-designator.js +3 -1
- package/out/us/floor-designator.js.map +1 -1
- package/out/us/military-address.d.ts +36 -12
- package/out/us/military-address.d.ts.map +1 -1
- package/out/us/military-address.js +26 -15
- package/out/us/military-address.js.map +1 -1
- package/out/us/po-box.d.ts +9 -3
- package/out/us/po-box.d.ts.map +1 -1
- package/out/us/po-box.js +4 -2
- package/out/us/po-box.js.map +1 -1
- package/out/us/state.d.ts +6 -2
- package/out/us/state.d.ts.map +1 -1
- package/out/us/state.js +3 -1
- package/out/us/state.js.map +1 -1
- package/out/us/street-directional.d.ts +12 -3
- package/out/us/street-directional.d.ts.map +1 -1
- package/out/us/street-directional.js +8 -3
- package/out/us/street-directional.js.map +1 -1
- package/out/us/street-suffix.d.ts +24 -8
- package/out/us/street-suffix.d.ts.map +1 -1
- package/out/us/street-suffix.js +7 -3
- package/out/us/street-suffix.js.map +1 -1
- package/out/us/unit-designator.d.ts +24 -8
- package/out/us/unit-designator.d.ts.map +1 -1
- package/out/us/unit-designator.js +3 -1
- package/out/us/unit-designator.js.map +1 -1
- package/package.json +2 -2
- package/postcode-systems.ts +4 -2
- package/tools/build-country-surface-lexicon.ts +30 -12
- package/tools/export-country-surfaces.ts +2 -0
- package/tools/generate-country-reference.ts +27 -13
- package/tools/generate-official-languages.ts +27 -12
- package/us/floor-designator.ts +15 -5
- package/us/military-address.ts +53 -24
- package/us/po-box.ts +13 -5
- package/us/state.ts +6 -2
- package/us/street-directional.ts +14 -5
- package/us/street-suffix.ts +27 -9
- package/us/unit-designator.ts +25 -8
|
@@ -36,6 +36,10 @@ export interface AddressSystemConventions {
|
|
|
36
36
|
readonly postcodePattern?: RegExp
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Per-address-system ordering and formatting conventions — which component leads a line, whether the postcode precedes
|
|
41
|
+
* the locality, and so on. Keyed by address system rather than country, since several share one.
|
|
42
|
+
*/
|
|
39
43
|
export const ADDRESS_SYSTEM_CONVENTIONS: Partial<Record<SystemCode, AddressSystemConventions>> = {
|
|
40
44
|
/**
|
|
41
45
|
* France (La Poste / AFNOR NF Z 10-011): the street TYPE is a LEADING particle of the street name ("Rue de Rivoli",
|
|
@@ -79,7 +83,9 @@ export const ADDRESS_SYSTEM_CONVENTIONS: Partial<Record<SystemCode, AddressSyste
|
|
|
79
83
|
},
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Look up conventions for a system. Absent row = no constraints KNOWN (parse unconstrained).
|
|
88
|
+
*/
|
|
83
89
|
export function conventionsForSystem(system: SystemCode | null | undefined): AddressSystemConventions | null {
|
|
84
90
|
if (!system) return null
|
|
85
91
|
|
package/au/delivery-service.ts
CHANGED
|
@@ -40,11 +40,17 @@
|
|
|
40
40
|
* @see {@link https://auspost.com.au/business/business-admin/po-boxes-and-locked-bags Australia Post — business PO Boxes, GPO Boxes and Locked Bags}
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* One Postal Delivery Type row from the Australia Post AMAS abbreviation table.
|
|
45
|
+
*/
|
|
44
46
|
export interface AuDeliveryServiceDesignator {
|
|
45
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The full Postal Delivery Type name, verbatim from the table (uppercase as published).
|
|
49
|
+
*/
|
|
46
50
|
name: string
|
|
47
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* The standard abbreviation — the surface form written on mail ("GPO BOX", "LOCKED BAG").
|
|
53
|
+
*/
|
|
48
54
|
abbreviation: string
|
|
49
55
|
/**
|
|
50
56
|
* Whether the designator "must have an associated number for a match to occur" (AMAS rule; exceptions are Care of
|
|
@@ -81,7 +87,9 @@ export const AU_DELIVERY_SERVICE_DESIGNATORS = [
|
|
|
81
87
|
{ name: "ROADSIDE MAIL SERVICE", abbreviation: "RMS", requiresNumber: true, legacy: true },
|
|
82
88
|
] as const satisfies readonly AuDeliveryServiceDesignator[]
|
|
83
89
|
|
|
84
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* A canonical Australia Post Postal Delivery Type abbreviation.
|
|
92
|
+
*/
|
|
85
93
|
export type AuDeliveryServiceAbbreviation = (typeof AU_DELIVERY_SERVICE_DESIGNATORS)[number]["abbreviation"]
|
|
86
94
|
|
|
87
95
|
/**
|
|
@@ -112,8 +120,10 @@ const DESIGNATOR_INFO = new Map<AuDeliveryServiceAbbreviation, { requiresNumber:
|
|
|
112
120
|
AU_DELIVERY_SERVICE_DESIGNATORS.map((d) => [d.abbreviation, { requiresNumber: d.requiresNumber, legacy: d.legacy }])
|
|
113
121
|
)
|
|
114
122
|
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
/**
|
|
124
|
+
* One anchored regex per designator: phrase + (required|optional) identifier. The id shape matches the US slice
|
|
125
|
+
* ([\dA-Za-z][\dA-Za-z-]*); MS additionally requires a digit-leading id (see above).
|
|
126
|
+
*/
|
|
117
127
|
const MATCHERS: ReadonlyArray<{ abbreviation: AuDeliveryServiceAbbreviation; re: RegExp }> = DESIGNATOR_PATTERNS.map(
|
|
118
128
|
([abbreviation, src]) => {
|
|
119
129
|
const { requiresNumber } = DESIGNATOR_INFO.get(abbreviation)!
|
|
@@ -124,15 +134,25 @@ const MATCHERS: ReadonlyArray<{ abbreviation: AuDeliveryServiceAbbreviation; re:
|
|
|
124
134
|
}
|
|
125
135
|
)
|
|
126
136
|
|
|
127
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* Result of an AU delivery-service parse.
|
|
139
|
+
*/
|
|
128
140
|
export interface AuDeliveryServiceMatch {
|
|
129
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* The designator phrase as it appeared ("G.P.O. Box", "Locked Bag").
|
|
143
|
+
*/
|
|
130
144
|
matched: string
|
|
131
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* The canonical Postal Delivery Type abbreviation ("GPO BOX", "LOCKED BAG").
|
|
147
|
+
*/
|
|
132
148
|
designator: AuDeliveryServiceAbbreviation
|
|
133
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* The delivery-service number when present ("9999", "4600").
|
|
151
|
+
*/
|
|
134
152
|
id?: string
|
|
135
|
-
/**
|
|
153
|
+
/**
|
|
154
|
+
* True when the designator is an AMAS-only legacy form (see the table).
|
|
155
|
+
*/
|
|
136
156
|
legacy: boolean
|
|
137
157
|
}
|
|
138
158
|
|
|
@@ -161,7 +181,9 @@ export function matchAuDeliveryService(input: unknown): AuDeliveryServiceMatch |
|
|
|
161
181
|
return null
|
|
162
182
|
}
|
|
163
183
|
|
|
164
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Type-predicate: does the input look like a standalone AU delivery-service address line?
|
|
186
|
+
*/
|
|
165
187
|
export function isAuDeliveryService(input: unknown): boolean {
|
|
166
188
|
return matchAuDeliveryService(input) !== null
|
|
167
189
|
}
|
package/au/level-designator.ts
CHANGED
|
@@ -45,11 +45,17 @@
|
|
|
45
45
|
* identifier from standalone ones.
|
|
46
46
|
*/
|
|
47
47
|
export interface AuLevelDesignator {
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* AS 4590.1 LEVEL_TYPE_CODE (the GNAF / AMAS internal code).
|
|
50
|
+
*/
|
|
49
51
|
code: string
|
|
50
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Full descriptive name (AMAS table label).
|
|
54
|
+
*/
|
|
51
55
|
name: string
|
|
52
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* The approved AMAS surface abbreviation written on mail ("L", "B", "M").
|
|
58
|
+
*/
|
|
53
59
|
abbreviation: string
|
|
54
60
|
/**
|
|
55
61
|
* True when the designator takes a numeric or alphanumeric floor identifier after it (`LEVEL 3`, `BASEMENT 2`). False
|
|
@@ -74,7 +80,9 @@ export const AU_LEVEL_DESIGNATORS = [
|
|
|
74
80
|
{ code: "RT", name: "ROOFTOP", abbreviation: "RT", requiresNumber: false },
|
|
75
81
|
] as const satisfies readonly AuLevelDesignator[]
|
|
76
82
|
|
|
77
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* A canonical AS 4590.1 LEVEL_TYPE_CODE.
|
|
85
|
+
*/
|
|
78
86
|
export type AuLevelCode = (typeof AU_LEVEL_DESIGNATORS)[number]["code"]
|
|
79
87
|
|
|
80
88
|
/**
|
|
@@ -107,7 +115,7 @@ export const AU_LEVEL_DESIGNATOR_LOOKUP: ReadonlyMap<string, AuLevelCode> = (()
|
|
|
107
115
|
for (const { code } of AU_LEVEL_DESIGNATORS) {
|
|
108
116
|
const variants = AU_LEVEL_DESIGNATOR_VARIANTS[code]
|
|
109
117
|
|
|
110
|
-
if (!variants || variants.length
|
|
118
|
+
if (!variants || !variants.length) {
|
|
111
119
|
throw new Error(`[codex/au/level-designator] code "${code}" has no variants in AU_LEVEL_DESIGNATOR_VARIANTS`)
|
|
112
120
|
}
|
|
113
121
|
|
|
@@ -119,6 +127,7 @@ export const AU_LEVEL_DESIGNATOR_LOOKUP: ReadonlyMap<string, AuLevelCode> = (()
|
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
}
|
|
130
|
+
|
|
122
131
|
const out = new Map<string, AuLevelCode>()
|
|
123
132
|
|
|
124
133
|
for (const { code } of AU_LEVEL_DESIGNATORS) {
|
|
@@ -134,30 +143,43 @@ export const AU_LEVEL_DESIGNATOR_LOOKUP: ReadonlyMap<string, AuLevelCode> = (()
|
|
|
134
143
|
return out
|
|
135
144
|
})()
|
|
136
145
|
|
|
137
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* Result of an AU level designator parse.
|
|
148
|
+
*/
|
|
138
149
|
export interface AuLevelDesignatorMatch {
|
|
139
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* The code as it appeared in the input ("Level", "L", "lvl").
|
|
152
|
+
*/
|
|
140
153
|
matched: string
|
|
141
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* The canonical AS 4590.1 LEVEL_TYPE_CODE ("L", "B", "M").
|
|
156
|
+
*/
|
|
142
157
|
code: AuLevelCode
|
|
143
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* The floor identifier when present ("3", "G", "B2").
|
|
160
|
+
*/
|
|
144
161
|
identifier?: string
|
|
145
162
|
}
|
|
146
163
|
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
/**
|
|
165
|
+
* One regex per level code. Multi-word variants ("LOWER GROUND", "GROUND FLOOR") are matched before their shorter
|
|
166
|
+
* constituents by ordering the variant list longest-first within each code.
|
|
167
|
+
*/
|
|
149
168
|
const LEVEL_MATCHERS: ReadonlyArray<{ code: AuLevelCode; requiresNumber: boolean; re: RegExp }> = (() => {
|
|
150
169
|
const rows: Array<{ code: AuLevelCode; requiresNumber: boolean; re: RegExp }> = []
|
|
151
170
|
|
|
152
171
|
for (const { code, requiresNumber } of AU_LEVEL_DESIGNATORS) {
|
|
153
172
|
const variants = [...AU_LEVEL_DESIGNATOR_VARIANTS[code]]
|
|
154
|
-
.
|
|
155
|
-
.map((v) => v.
|
|
173
|
+
.toSorted((a, b) => b.length - a.length)
|
|
174
|
+
.map((v) => v.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&").replaceAll(/\s+/g, String.raw`\s+`))
|
|
175
|
+
|
|
156
176
|
const alts = variants.join("|")
|
|
177
|
+
|
|
157
178
|
// Identifier: optional alphanumeric (B2, 12, G). requiresNumber=true → identifier required.
|
|
158
179
|
const tail = requiresNumber
|
|
159
180
|
? String.raw`\s+([A-Za-z]?\d[\dA-Za-z-]*|\d[\dA-Za-z-]*)`
|
|
160
181
|
: String.raw`(?:\s+([A-Za-z]?\d[\dA-Za-z-]*|\d[\dA-Za-z-]*))?`
|
|
182
|
+
|
|
161
183
|
rows.push({ code, requiresNumber, re: new RegExp(String.raw`^\s*(${alts})${tail}\s*$`, "i") })
|
|
162
184
|
}
|
|
163
185
|
|
|
@@ -188,7 +210,9 @@ export function matchAuLevelDesignator(input: unknown): AuLevelDesignatorMatch |
|
|
|
188
210
|
return null
|
|
189
211
|
}
|
|
190
212
|
|
|
191
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* Type-predicate: does the input look like a standalone AU level designator phrase?
|
|
215
|
+
*/
|
|
192
216
|
export function isAuLevelDesignator(input: unknown): boolean {
|
|
193
217
|
return matchAuLevelDesignator(input) !== null
|
|
194
218
|
}
|
package/au/postcode.ts
CHANGED
|
@@ -34,10 +34,14 @@ import type { Tagged } from "type-fest"
|
|
|
34
34
|
*/
|
|
35
35
|
export type AuPostcode = Tagged<string, "AuPostcode">
|
|
36
36
|
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The AU postcode shape: exactly four digits.
|
|
39
|
+
*/
|
|
38
40
|
export const AU_POSTCODE_PATTERN = /^\d{4}$/
|
|
39
41
|
|
|
40
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Normalize a postcode surface form (trim only — AU has no country-prefix courtesy form).
|
|
44
|
+
*/
|
|
41
45
|
export function normalizeAuPostcode(raw: unknown): AuPostcode | null {
|
|
42
46
|
if (typeof raw !== "string") return null
|
|
43
47
|
const s = raw.trim()
|
|
@@ -45,7 +49,9 @@ export function normalizeAuPostcode(raw: unknown): AuPostcode | null {
|
|
|
45
49
|
return AU_POSTCODE_PATTERN.test(s) ? (s as AuPostcode) : null
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Type-predicate for a (normalized) Australian postcode.
|
|
54
|
+
*/
|
|
49
55
|
export function isAuPostcode(input: unknown): input is AuPostcode {
|
|
50
56
|
return typeof input === "string" && AU_POSTCODE_PATTERN.test(input)
|
|
51
57
|
}
|
package/au/state.ts
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* @see {@link https://www.iso.org/obp/ui/#iso:code:3166:AU ISO 3166-2:AU}
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* State/territory abbreviation → full name (ISO 3166-2:AU subdivision set).
|
|
19
|
+
*/
|
|
18
20
|
export const AU_STATE_ABBREVIATIONS = {
|
|
19
21
|
ACT: "Australian Capital Territory",
|
|
20
22
|
NSW: "New South Wales",
|
|
@@ -26,10 +28,14 @@ export const AU_STATE_ABBREVIATIONS = {
|
|
|
26
28
|
WA: "Western Australia",
|
|
27
29
|
} as const satisfies Record<string, string>
|
|
28
30
|
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* An Australian state/territory abbreviation as written on the last address line.
|
|
33
|
+
*/
|
|
30
34
|
export type AuStateAbbreviation = keyof typeof AU_STATE_ABBREVIATIONS
|
|
31
35
|
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Type-predicate for an AU state/territory abbreviation (case-insensitive).
|
|
38
|
+
*/
|
|
33
39
|
export function isAuStateAbbreviation(input: unknown): input is AuStateAbbreviation {
|
|
34
40
|
return typeof input === "string" && Object.hasOwn(AU_STATE_ABBREVIATIONS, input.toUpperCase())
|
|
35
41
|
}
|
package/ca/postal-code.ts
CHANGED
|
@@ -29,6 +29,11 @@ import type { Tagged } from "type-fest"
|
|
|
29
29
|
|
|
30
30
|
import type { CanadianProvinceCode } from "./province.ts"
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Characters in a Canadian postal code once spaces are stripped: `A1A1A1`.
|
|
34
|
+
*/
|
|
35
|
+
const POSTAL_CODE_LENGTH = 6
|
|
36
|
+
|
|
32
37
|
/**
|
|
33
38
|
* A Canadian postal code: `A1A 1A1`. Six alphanumeric characters in a strict Letter-Digit-Letter-Digit-Letter-Digit
|
|
34
39
|
* pattern, conventionally written with a single space after the third. Unlike the other systems' bare five digits, the
|
|
@@ -55,15 +60,17 @@ export const CA_POSTAL_CODE_PATTERN = /^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z] ?
|
|
|
55
60
|
*/
|
|
56
61
|
export function normalizeCaPostalCode(raw: unknown): PostalCode | null {
|
|
57
62
|
if (typeof raw !== "string") return null
|
|
58
|
-
const compact = raw.trim().toUpperCase().
|
|
63
|
+
const compact = raw.trim().toUpperCase().replaceAll(/\s+/g, "")
|
|
59
64
|
|
|
60
|
-
if (compact.length !==
|
|
65
|
+
if (compact.length !== POSTAL_CODE_LENGTH) return null
|
|
61
66
|
const spaced = `${compact.slice(0, 3)} ${compact.slice(3)}`
|
|
62
67
|
|
|
63
68
|
return CA_POSTAL_CODE_PATTERN.test(spaced) ? (spaced as PostalCode) : null
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Type-predicate for a Canadian postal code (accepts the spaced or unspaced surface form).
|
|
73
|
+
*/
|
|
67
74
|
export function isCaPostalCode(input: unknown): input is PostalCode {
|
|
68
75
|
return typeof input === "string" && CA_POSTAL_CODE_PATTERN.test(input)
|
|
69
76
|
}
|
package/ca/province.ts
CHANGED
|
@@ -16,13 +16,21 @@
|
|
|
16
16
|
* regions, the Canadian code IS a surface form, not just a resolver key.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Per-province record: ISO 3166-2:CA code, English name, and the co-official French name.
|
|
21
|
+
*/
|
|
20
22
|
export interface CanadianProvinceInfo {
|
|
21
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* ISO 3166-2:CA subdivision code without the `CA-` prefix (e.g. `ON` for `CA-ON`).
|
|
25
|
+
*/
|
|
22
26
|
code: string
|
|
23
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* English name (e.g. `Quebec`).
|
|
29
|
+
*/
|
|
24
30
|
name: string
|
|
25
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Co-official French name (e.g. `Québec`).
|
|
33
|
+
*/
|
|
26
34
|
french: string
|
|
27
35
|
}
|
|
28
36
|
|
|
@@ -46,23 +54,29 @@ export const CA_PROVINCES = {
|
|
|
46
54
|
YT: { code: "YT", name: "Yukon", french: "Yukon" },
|
|
47
55
|
} as const satisfies Record<string, CanadianProvinceInfo>
|
|
48
56
|
|
|
49
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* An ISO 3166-2:CA province/territory code (`AB`, `ON`, `QC`, …).
|
|
59
|
+
*/
|
|
50
60
|
export type CanadianProvinceCode = keyof typeof CA_PROVINCES
|
|
51
61
|
|
|
52
62
|
const PROVINCE_CODE_SET: ReadonlySet<string> = new Set(Object.keys(CA_PROVINCES))
|
|
53
63
|
|
|
54
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Type-predicate for an ISO 3166-2:CA province/territory code. Case-insensitive.
|
|
66
|
+
*/
|
|
55
67
|
export function isCanadianProvinceCode(input: unknown): input is CanadianProvinceCode {
|
|
56
68
|
return typeof input === "string" && PROVINCE_CODE_SET.has(input.toUpperCase())
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike.
|
|
73
|
+
*/
|
|
60
74
|
function foldName(s: string): string {
|
|
61
75
|
return s
|
|
62
76
|
.toLowerCase()
|
|
63
77
|
.normalize("NFD")
|
|
64
|
-
.
|
|
65
|
-
.
|
|
78
|
+
.replaceAll(/[\u0300-\u036F]/g, "")
|
|
79
|
+
.replaceAll(/[^a-z0-9]+/g, " ")
|
|
66
80
|
.trim()
|
|
67
81
|
}
|
|
68
82
|
|
package/ca/street-type.ts
CHANGED
|
@@ -85,16 +85,20 @@ export const CA_STREET_TYPES_FR: ReadonlySet<string> = new Set([
|
|
|
85
85
|
"sentier",
|
|
86
86
|
])
|
|
87
87
|
|
|
88
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Strip diacritics + lowercase so `Côte`/`cote`, `Allée`/`allee`, `Crescent`/`crescent` key alike.
|
|
90
|
+
*/
|
|
89
91
|
function foldToken(s: string): string {
|
|
90
92
|
return s
|
|
91
93
|
.toLowerCase()
|
|
92
94
|
.normalize("NFD")
|
|
93
|
-
.
|
|
94
|
-
.
|
|
95
|
+
.replaceAll(/[\u0300-\u036F]/g, "")
|
|
96
|
+
.replaceAll(/[^a-z]/g, "")
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* The English + French street-type vocabularies, both folded, for one position-agnostic lookup.
|
|
101
|
+
*/
|
|
98
102
|
const STREET_WORD_SET: ReadonlySet<string> = (() => {
|
|
99
103
|
const out = new Set<string>()
|
|
100
104
|
|
|
@@ -152,7 +156,7 @@ export function isCanadianDirectional(token: unknown): boolean {
|
|
|
152
156
|
if (typeof token !== "string") return false
|
|
153
157
|
const t = foldToken(token)
|
|
154
158
|
|
|
155
|
-
if (t.length
|
|
159
|
+
if (!t.length) return false
|
|
156
160
|
|
|
157
161
|
if (t in CA_DIRECTIONALS) return true
|
|
158
162
|
|
package/country/country.ts
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
* the other codex matchers (street-suffix, directional, po-box).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { Alpha3ToCountryRecord, CountryISO2
|
|
15
|
-
import {
|
|
14
|
+
import { Alpha3ToCountryRecord, CountryISO2 } from "./codes.ts"
|
|
15
|
+
import type { CountryName } from "./names.ts"
|
|
16
|
+
|
|
17
|
+
export { Alpha3ToCountryRecord, CountryISO2, type CountryISO3 } from "./codes.ts"
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Common real-address surface forms per ISO 3166-1 alpha-2, **canonical English name first** then endonym +
|
|
@@ -41,7 +43,9 @@ export const COUNTRY_SURFACE_FORMS = {
|
|
|
41
43
|
|
|
42
44
|
export type CountrySurfaceISO2 = keyof typeof COUNTRY_SURFACE_FORMS
|
|
43
45
|
|
|
44
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Alpha-2 → canonical English name (inverted from the salvaged CountryISO2 enum).
|
|
48
|
+
*/
|
|
45
49
|
export const ISO2_TO_NAME: ReadonlyMap<string, CountryName> = new Map(
|
|
46
50
|
Object.entries(CountryISO2).map(([name, code]) => [code as string, name as CountryName])
|
|
47
51
|
)
|
|
@@ -53,6 +57,7 @@ export const ISO2_TO_NAME: ReadonlyMap<string, CountryName> = new Map(
|
|
|
53
57
|
*/
|
|
54
58
|
export const COUNTRY_LOOKUP: ReadonlyMap<string, string> = (() => {
|
|
55
59
|
const out = new Map<string, string>()
|
|
60
|
+
|
|
56
61
|
const put = (k: string, iso2: string) => {
|
|
57
62
|
const key = k.trim().toLowerCase()
|
|
58
63
|
|
|
@@ -89,7 +94,9 @@ export const COUNTRY_LOOKUP: ReadonlyMap<string, string> = (() => {
|
|
|
89
94
|
return out
|
|
90
95
|
})()
|
|
91
96
|
|
|
92
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Result of a country match: the alpha-2 code, the canonical English name, and the matched surface.
|
|
99
|
+
*/
|
|
93
100
|
export interface CountryMatch {
|
|
94
101
|
iso2: string
|
|
95
102
|
canonical: CountryName | undefined
|
|
@@ -110,7 +117,9 @@ export function matchCountry(token: string | null | undefined): CountryMatch | n
|
|
|
110
117
|
return { iso2, canonical: ISO2_TO_NAME.get(iso2), matched: token.trim() }
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Case-insensitive check: is the token any recognized country form?
|
|
122
|
+
*/
|
|
114
123
|
export function isCountryToken(token: unknown): boolean {
|
|
115
124
|
return typeof token === "string" && COUNTRY_LOOKUP.has(token.trim().toLowerCase())
|
|
116
125
|
}
|
|
@@ -121,6 +130,3 @@ export function isCountryToken(token: unknown): boolean {
|
|
|
121
130
|
export function countrySurfaceForms(iso2: string): readonly string[] {
|
|
122
131
|
return (COUNTRY_SURFACE_FORMS as Record<string, readonly string[]>)[iso2.toUpperCase()] ?? []
|
|
123
132
|
}
|
|
124
|
-
|
|
125
|
-
export { Alpha3ToCountryRecord, CountryISO2 }
|
|
126
|
-
export type { CountryISO3, CountryName }
|
|
@@ -12,15 +12,23 @@
|
|
|
12
12
|
* Regenerate with: mailwoman dev generate official-languages
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Official-language spellings for one territory.
|
|
17
|
+
*/
|
|
16
18
|
export interface OfficialLanguageEntry {
|
|
17
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* CLDR `official` + `de_facto_official`, in every ISO-639 spelling.
|
|
21
|
+
*/
|
|
18
22
|
official: readonly string[]
|
|
19
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* CLDR `official_regional` (e.g. Catalan in ES) — opt-in for consumers.
|
|
25
|
+
*/
|
|
20
26
|
regional?: readonly string[]
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* ISO 3166-1 alpha-2 → official languages.
|
|
31
|
+
*/
|
|
24
32
|
export const OFFICIAL_LANGUAGES: Record<string, OfficialLanguageEntry> = {
|
|
25
33
|
AD: { official: ["ca", "cat"] },
|
|
26
34
|
AE: { official: ["ar", "ara", "arb"] },
|
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
* Regenerate with: mailwoman dev generate country-reference
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Static per-country reference: calling code + currency.
|
|
13
|
+
*/
|
|
12
14
|
export interface CountryReference {
|
|
13
15
|
callingCode?: number
|
|
14
16
|
currency?: { isoCode: string; name?: string; symbol?: string }
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* ISO 3166-1 alpha-2 → reference.
|
|
21
|
+
*/
|
|
18
22
|
export const COUNTRY_REFERENCE: Record<string, CountryReference> = {
|
|
19
23
|
AD: { callingCode: 376, currency: { isoCode: "EUR", name: "Euro", symbol: "€" } },
|
|
20
24
|
AE: { callingCode: 971, currency: { isoCode: "AED", name: "United Arab Emirates dirham", symbol: "د.إ" } },
|
|
@@ -29,7 +33,7 @@ export const COUNTRY_REFERENCE: Record<string, CountryReference> = {
|
|
|
29
33
|
AT: { callingCode: 43, currency: { isoCode: "EUR", name: "Euro", symbol: "€" } },
|
|
30
34
|
AU: { callingCode: 61, currency: { isoCode: "AUD", name: "Australian dollar", symbol: "$" } },
|
|
31
35
|
AW: { callingCode: 297, currency: { isoCode: "AWG", name: "Aruban florin", symbol: "ƒ" } },
|
|
32
|
-
AX: { callingCode:
|
|
36
|
+
AX: { callingCode: 35_818, currency: { isoCode: "EUR", name: "Euro", symbol: "€" } },
|
|
33
37
|
AZ: { callingCode: 994, currency: { isoCode: "AZN", name: "Azerbaijani manat", symbol: "₼" } },
|
|
34
38
|
BA: { callingCode: 387, currency: { isoCode: "BAM", name: "Bosnia and Herzegovina convertible mark", symbol: "KM" } },
|
|
35
39
|
BB: { callingCode: 1, currency: { isoCode: "BBD", name: "Barbadian dollar", symbol: "$" } },
|
package/country/reference.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { AnnotationSet, Annotator } from "@mailwoman/annotations"
|
|
|
13
13
|
|
|
14
14
|
import { COUNTRY_REFERENCE } from "./reference-data.ts"
|
|
15
15
|
|
|
16
|
-
const REGIONAL_INDICATOR_BASE =
|
|
16
|
+
const REGIONAL_INDICATOR_BASE = 0x1_f1_e6
|
|
17
17
|
const A_UPPER = "A".charCodeAt(0)
|
|
18
18
|
|
|
19
19
|
/**
|
package/country/subdivision.ts
CHANGED
|
@@ -29,23 +29,33 @@
|
|
|
29
29
|
import { CA_PROVINCES } from "../ca/province.ts"
|
|
30
30
|
import { US_STATE_BY_ABBREVIATION } from "../us/state.ts"
|
|
31
31
|
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* A resolved subdivision: its ISO 3166-2 code (sans country prefix), canonical English name, and ISO 3166-1 country.
|
|
34
|
+
*/
|
|
33
35
|
export interface SubdivisionMatch {
|
|
34
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* ISO 3166-2 subdivision code without the country prefix (e.g. `QC` for `CA-QC`, `IL` for `US-IL`).
|
|
38
|
+
*/
|
|
35
39
|
code: string
|
|
36
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Canonical English name (e.g. `Quebec`, `Illinois`).
|
|
42
|
+
*/
|
|
37
43
|
name: string
|
|
38
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* ISO 3166-1 alpha-2 country the subdivision belongs to (`CA`, `US`).
|
|
46
|
+
*/
|
|
39
47
|
country: string
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike (mirrors `ca/province.ts`).
|
|
52
|
+
*/
|
|
43
53
|
function foldName(s: string): string {
|
|
44
54
|
return s
|
|
45
55
|
.toLowerCase()
|
|
46
56
|
.normalize("NFD")
|
|
47
|
-
.
|
|
48
|
-
.
|
|
57
|
+
.replaceAll(/[\u0300-\u036F]/g, "")
|
|
58
|
+
.replaceAll(/[^a-z0-9]+/g, " ")
|
|
49
59
|
.trim()
|
|
50
60
|
}
|
|
51
61
|
|
|
@@ -56,10 +66,11 @@ function foldName(s: string): string {
|
|
|
56
66
|
*/
|
|
57
67
|
const SUBDIVISION_LOOKUP: ReadonlyMap<string, SubdivisionMatch> = (() => {
|
|
58
68
|
const out = new Map<string, SubdivisionMatch>()
|
|
69
|
+
|
|
59
70
|
const put = (key: string, match: SubdivisionMatch): void => {
|
|
60
71
|
const folded = foldName(key)
|
|
61
72
|
|
|
62
|
-
if (folded.length
|
|
73
|
+
if (folded.length && !out.has(folded)) {
|
|
63
74
|
out.set(folded, match)
|
|
64
75
|
}
|
|
65
76
|
}
|
package/de/bundesland.ts
CHANGED
|
@@ -11,13 +11,21 @@
|
|
|
11
11
|
* codes matter for resolver region-matching and display, not for parsing the surface string.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Per-state record: ISO 3166-2:DE code, native German name, and the common English exonym.
|
|
16
|
+
*/
|
|
15
17
|
export interface GermanStateInfo {
|
|
16
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* ISO 3166-2:DE subdivision code without the `DE-` prefix (e.g. `BY` for `DE-BY`).
|
|
20
|
+
*/
|
|
17
21
|
code: string
|
|
18
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Native German name (e.g. `Bayern`).
|
|
24
|
+
*/
|
|
19
25
|
name: string
|
|
20
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Common English name (e.g. `Bavaria`).
|
|
28
|
+
*/
|
|
21
29
|
english: string
|
|
22
30
|
}
|
|
23
31
|
|
|
@@ -44,12 +52,16 @@ export const DE_BUNDESLAENDER = {
|
|
|
44
52
|
TH: { code: "TH", name: "Thüringen", english: "Thuringia" },
|
|
45
53
|
} as const satisfies Record<string, GermanStateInfo>
|
|
46
54
|
|
|
47
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* An ISO 3166-2:DE state code (`BW`, `BY`, `BE`, …).
|
|
57
|
+
*/
|
|
48
58
|
export type GermanStateCode = keyof typeof DE_BUNDESLAENDER
|
|
49
59
|
|
|
50
60
|
const STATE_CODE_SET: ReadonlySet<string> = new Set(Object.keys(DE_BUNDESLAENDER))
|
|
51
61
|
|
|
52
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Type-predicate for an ISO 3166-2:DE state code. Case-insensitive.
|
|
64
|
+
*/
|
|
53
65
|
export function isGermanStateCode(input: unknown): input is GermanStateCode {
|
|
54
66
|
return typeof input === "string" && STATE_CODE_SET.has(input.toUpperCase())
|
|
55
67
|
}
|
|
@@ -69,6 +81,7 @@ export const DE_STATE_NAME_TO_CODE: ReadonlyMap<string, GermanStateCode> = (() =
|
|
|
69
81
|
out.set(info.english.toLowerCase(), code)
|
|
70
82
|
out.set(code.toLowerCase(), code)
|
|
71
83
|
}
|
|
84
|
+
|
|
72
85
|
// Everyday aliases that are neither the ISO code nor the canonical name.
|
|
73
86
|
const aliases: Record<string, GermanStateCode> = {
|
|
74
87
|
nrw: "NW",
|