@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.
Files changed (210) hide show
  1. package/address-system-conventions.ts +7 -1
  2. package/au/delivery-service.ts +34 -12
  3. package/au/level-designator.ts +38 -14
  4. package/au/postcode.ts +9 -3
  5. package/au/state.ts +9 -3
  6. package/ca/postal-code.ts +10 -3
  7. package/ca/province.ts +23 -9
  8. package/ca/street-type.ts +9 -5
  9. package/country/country.ts +14 -8
  10. package/country/official-languages.ts +12 -4
  11. package/country/reference-data.ts +7 -3
  12. package/country/reference.ts +1 -1
  13. package/country/subdivision.ts +19 -8
  14. package/de/bundesland.ts +19 -6
  15. package/de/postleitzahl.ts +21 -7
  16. package/de/street-type.ts +14 -3
  17. package/fr/cedex.ts +15 -5
  18. package/fr/code-postal.ts +17 -4
  19. package/fr/departement.ts +18 -6
  20. package/fr/region.ts +23 -9
  21. package/fr/voie.ts +8 -4
  22. package/gb/country.ts +25 -9
  23. package/gb/postcode-area.ts +6 -2
  24. package/gb/postcode.ts +10 -3
  25. package/gb/street-type.ts +4 -2
  26. package/index.ts +1 -0
  27. package/jp/address-unit.ts +8 -4
  28. package/jp/postal-code.ts +7 -3
  29. package/jp/prefecture.ts +27 -10
  30. package/level-semantics.ts +55 -19
  31. package/nz/delivery-service.ts +43 -15
  32. package/nz/postcode.ts +9 -3
  33. package/out/address-system-conventions.d.ts +7 -1
  34. package/out/address-system-conventions.d.ts.map +1 -1
  35. package/out/address-system-conventions.js +7 -1
  36. package/out/address-system-conventions.js.map +1 -1
  37. package/out/au/delivery-service.d.ts +30 -10
  38. package/out/au/delivery-service.d.ts.map +1 -1
  39. package/out/au/delivery-service.js +7 -3
  40. package/out/au/delivery-service.js.map +1 -1
  41. package/out/au/level-designator.d.ts +27 -9
  42. package/out/au/level-designator.d.ts.map +1 -1
  43. package/out/au/level-designator.js +10 -6
  44. package/out/au/level-designator.js.map +1 -1
  45. package/out/au/postcode.d.ts +9 -3
  46. package/out/au/postcode.d.ts.map +1 -1
  47. package/out/au/postcode.js +9 -3
  48. package/out/au/postcode.js.map +1 -1
  49. package/out/au/state.d.ts +9 -3
  50. package/out/au/state.d.ts.map +1 -1
  51. package/out/au/state.js +6 -2
  52. package/out/au/state.js.map +1 -1
  53. package/out/ca/postal-code.d.ts +3 -1
  54. package/out/ca/postal-code.d.ts.map +1 -1
  55. package/out/ca/postal-code.js +9 -3
  56. package/out/ca/postal-code.js.map +1 -1
  57. package/out/ca/province.d.ts +18 -6
  58. package/out/ca/province.d.ts.map +1 -1
  59. package/out/ca/province.js +8 -4
  60. package/out/ca/province.js.map +1 -1
  61. package/out/ca/street-type.d.ts.map +1 -1
  62. package/out/ca/street-type.js +9 -5
  63. package/out/ca/street-type.js.map +1 -1
  64. package/out/country/country.d.ts +11 -7
  65. package/out/country/country.d.ts.map +1 -1
  66. package/out/country/country.js +7 -4
  67. package/out/country/country.js.map +1 -1
  68. package/out/country/official-languages.d.ts +12 -4
  69. package/out/country/official-languages.d.ts.map +1 -1
  70. package/out/country/official-languages.js +3 -1
  71. package/out/country/official-languages.js.map +1 -1
  72. package/out/country/reference-data.d.ts +6 -2
  73. package/out/country/reference-data.d.ts.map +1 -1
  74. package/out/country/reference-data.js +4 -2
  75. package/out/country/reference-data.js.map +1 -1
  76. package/out/country/reference.js +1 -1
  77. package/out/country/reference.js.map +1 -1
  78. package/out/country/subdivision.d.ts +12 -4
  79. package/out/country/subdivision.d.ts.map +1 -1
  80. package/out/country/subdivision.js +6 -4
  81. package/out/country/subdivision.js.map +1 -1
  82. package/out/de/bundesland.d.ts +18 -6
  83. package/out/de/bundesland.d.ts.map +1 -1
  84. package/out/de/bundesland.js +3 -1
  85. package/out/de/bundesland.js.map +1 -1
  86. package/out/de/postleitzahl.d.ts +21 -7
  87. package/out/de/postleitzahl.d.ts.map +1 -1
  88. package/out/de/postleitzahl.js +9 -3
  89. package/out/de/postleitzahl.js.map +1 -1
  90. package/out/de/street-type.d.ts +5 -4
  91. package/out/de/street-type.d.ts.map +1 -1
  92. package/out/de/street-type.js +10 -2
  93. package/out/de/street-type.js.map +1 -1
  94. package/out/fr/cedex.d.ts +15 -5
  95. package/out/fr/cedex.d.ts.map +1 -1
  96. package/out/fr/cedex.js +6 -2
  97. package/out/fr/cedex.js.map +1 -1
  98. package/out/fr/code-postal.d.ts +8 -11
  99. package/out/fr/code-postal.d.ts.map +1 -1
  100. package/out/fr/code-postal.js +24 -4
  101. package/out/fr/code-postal.js.map +1 -1
  102. package/out/fr/departement.d.ts +18 -6
  103. package/out/fr/departement.d.ts.map +1 -1
  104. package/out/fr/departement.js +3 -1
  105. package/out/fr/departement.js.map +1 -1
  106. package/out/fr/region.d.ts +18 -6
  107. package/out/fr/region.d.ts.map +1 -1
  108. package/out/fr/region.js +11 -5
  109. package/out/fr/region.js.map +1 -1
  110. package/out/fr/voie.d.ts +3 -1
  111. package/out/fr/voie.d.ts.map +1 -1
  112. package/out/fr/voie.js +3 -3
  113. package/out/fr/voie.js.map +1 -1
  114. package/out/gb/country.d.ts +18 -6
  115. package/out/gb/country.d.ts.map +1 -1
  116. package/out/gb/country.js +13 -5
  117. package/out/gb/country.js.map +1 -1
  118. package/out/gb/postcode-area.d.ts.map +1 -1
  119. package/out/gb/postcode-area.js +6 -2
  120. package/out/gb/postcode-area.js.map +1 -1
  121. package/out/gb/postcode.d.ts +3 -11
  122. package/out/gb/postcode.d.ts.map +1 -1
  123. package/out/gb/postcode.js +19 -3
  124. package/out/gb/postcode.js.map +1 -1
  125. package/out/gb/street-type.d.ts +3 -1
  126. package/out/gb/street-type.d.ts.map +1 -1
  127. package/out/gb/street-type.js +1 -1
  128. package/out/gb/street-type.js.map +1 -1
  129. package/out/index.d.ts.map +1 -1
  130. package/out/index.js.map +1 -1
  131. package/out/jp/address-unit.d.ts +6 -2
  132. package/out/jp/address-unit.d.ts.map +1 -1
  133. package/out/jp/address-unit.js +2 -2
  134. package/out/jp/address-unit.js.map +1 -1
  135. package/out/jp/postal-code.d.ts +6 -2
  136. package/out/jp/postal-code.d.ts.map +1 -1
  137. package/out/jp/postal-code.js +7 -3
  138. package/out/jp/postal-code.js.map +1 -1
  139. package/out/jp/prefecture.d.ts +24 -8
  140. package/out/jp/prefecture.d.ts.map +1 -1
  141. package/out/jp/prefecture.js +5 -3
  142. package/out/jp/prefecture.js.map +1 -1
  143. package/out/level-semantics.d.ts +48 -16
  144. package/out/level-semantics.d.ts.map +1 -1
  145. package/out/level-semantics.js +34 -12
  146. package/out/level-semantics.js.map +1 -1
  147. package/out/nz/delivery-service.d.ts +39 -13
  148. package/out/nz/delivery-service.d.ts.map +1 -1
  149. package/out/nz/delivery-service.js +16 -6
  150. package/out/nz/delivery-service.js.map +1 -1
  151. package/out/nz/postcode.d.ts +9 -3
  152. package/out/nz/postcode.d.ts.map +1 -1
  153. package/out/nz/postcode.js +9 -3
  154. package/out/nz/postcode.js.map +1 -1
  155. package/out/postcode-systems.d.ts +3 -1
  156. package/out/postcode-systems.d.ts.map +1 -1
  157. package/out/postcode-systems.js +1 -1
  158. package/out/postcode-systems.js.map +1 -1
  159. package/out/tools/build-country-surface-lexicon.js +26 -12
  160. package/out/tools/build-country-surface-lexicon.js.map +1 -1
  161. package/out/tools/export-country-surfaces.js.map +1 -1
  162. package/out/tools/generate-country-reference.d.ts +12 -4
  163. package/out/tools/generate-country-reference.d.ts.map +1 -1
  164. package/out/tools/generate-country-reference.js +10 -8
  165. package/out/tools/generate-country-reference.js.map +1 -1
  166. package/out/tools/generate-official-languages.d.ts +18 -6
  167. package/out/tools/generate-official-languages.d.ts.map +1 -1
  168. package/out/tools/generate-official-languages.js +9 -7
  169. package/out/tools/generate-official-languages.js.map +1 -1
  170. package/out/us/floor-designator.d.ts +15 -5
  171. package/out/us/floor-designator.d.ts.map +1 -1
  172. package/out/us/floor-designator.js +3 -1
  173. package/out/us/floor-designator.js.map +1 -1
  174. package/out/us/military-address.d.ts +36 -12
  175. package/out/us/military-address.d.ts.map +1 -1
  176. package/out/us/military-address.js +26 -15
  177. package/out/us/military-address.js.map +1 -1
  178. package/out/us/po-box.d.ts +9 -3
  179. package/out/us/po-box.d.ts.map +1 -1
  180. package/out/us/po-box.js +4 -2
  181. package/out/us/po-box.js.map +1 -1
  182. package/out/us/state.d.ts +6 -2
  183. package/out/us/state.d.ts.map +1 -1
  184. package/out/us/state.js +3 -1
  185. package/out/us/state.js.map +1 -1
  186. package/out/us/street-directional.d.ts +12 -3
  187. package/out/us/street-directional.d.ts.map +1 -1
  188. package/out/us/street-directional.js +8 -3
  189. package/out/us/street-directional.js.map +1 -1
  190. package/out/us/street-suffix.d.ts +24 -8
  191. package/out/us/street-suffix.d.ts.map +1 -1
  192. package/out/us/street-suffix.js +7 -3
  193. package/out/us/street-suffix.js.map +1 -1
  194. package/out/us/unit-designator.d.ts +24 -8
  195. package/out/us/unit-designator.d.ts.map +1 -1
  196. package/out/us/unit-designator.js +3 -1
  197. package/out/us/unit-designator.js.map +1 -1
  198. package/package.json +2 -2
  199. package/postcode-systems.ts +4 -2
  200. package/tools/build-country-surface-lexicon.ts +30 -12
  201. package/tools/export-country-surfaces.ts +2 -0
  202. package/tools/generate-country-reference.ts +27 -13
  203. package/tools/generate-official-languages.ts +27 -12
  204. package/us/floor-designator.ts +15 -5
  205. package/us/military-address.ts +53 -24
  206. package/us/po-box.ts +13 -5
  207. package/us/state.ts +6 -2
  208. package/us/street-directional.ts +14 -5
  209. package/us/street-suffix.ts +27 -9
  210. 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
- /** Look up conventions for a system. Absent row = no constraints KNOWN (parse unconstrained). */
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
 
@@ -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
- /** One Postal Delivery Type row from the Australia Post AMAS abbreviation table. */
43
+ /**
44
+ * One Postal Delivery Type row from the Australia Post AMAS abbreviation table.
45
+ */
44
46
  export interface AuDeliveryServiceDesignator {
45
- /** The full Postal Delivery Type name, verbatim from the table (uppercase as published). */
47
+ /**
48
+ * The full Postal Delivery Type name, verbatim from the table (uppercase as published).
49
+ */
46
50
  name: string
47
- /** The standard abbreviation — the surface form written on mail ("GPO BOX", "LOCKED BAG"). */
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
- /** A canonical Australia Post Postal Delivery Type abbreviation. */
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
- // One anchored regex per designator: phrase + (required|optional) identifier. The id shape matches
116
- // the US slice ([\dA-Za-z][\dA-Za-z-]*); MS additionally requires a digit-leading id (see above).
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
- /** Result of an AU delivery-service parse. */
137
+ /**
138
+ * Result of an AU delivery-service parse.
139
+ */
128
140
  export interface AuDeliveryServiceMatch {
129
- /** The designator phrase as it appeared ("G.P.O. Box", "Locked Bag"). */
141
+ /**
142
+ * The designator phrase as it appeared ("G.P.O. Box", "Locked Bag").
143
+ */
130
144
  matched: string
131
- /** The canonical Postal Delivery Type abbreviation ("GPO BOX", "LOCKED BAG"). */
145
+ /**
146
+ * The canonical Postal Delivery Type abbreviation ("GPO BOX", "LOCKED BAG").
147
+ */
132
148
  designator: AuDeliveryServiceAbbreviation
133
- /** The delivery-service number when present ("9999", "4600"). */
149
+ /**
150
+ * The delivery-service number when present ("9999", "4600").
151
+ */
134
152
  id?: string
135
- /** True when the designator is an AMAS-only legacy form (see the table). */
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
- /** Type-predicate: does the input look like a standalone AU delivery-service address line? */
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
  }
@@ -45,11 +45,17 @@
45
45
  * identifier from standalone ones.
46
46
  */
47
47
  export interface AuLevelDesignator {
48
- /** AS 4590.1 LEVEL_TYPE_CODE (the GNAF / AMAS internal code). */
48
+ /**
49
+ * AS 4590.1 LEVEL_TYPE_CODE (the GNAF / AMAS internal code).
50
+ */
49
51
  code: string
50
- /** Full descriptive name (AMAS table label). */
52
+ /**
53
+ * Full descriptive name (AMAS table label).
54
+ */
51
55
  name: string
52
- /** The approved AMAS surface abbreviation written on mail ("L", "B", "M"). */
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
- /** A canonical AS 4590.1 LEVEL_TYPE_CODE. */
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 === 0) {
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
- /** Result of an AU level designator parse. */
146
+ /**
147
+ * Result of an AU level designator parse.
148
+ */
138
149
  export interface AuLevelDesignatorMatch {
139
- /** The code as it appeared in the input ("Level", "L", "lvl"). */
150
+ /**
151
+ * The code as it appeared in the input ("Level", "L", "lvl").
152
+ */
140
153
  matched: string
141
- /** The canonical AS 4590.1 LEVEL_TYPE_CODE ("L", "B", "M"). */
154
+ /**
155
+ * The canonical AS 4590.1 LEVEL_TYPE_CODE ("L", "B", "M").
156
+ */
142
157
  code: AuLevelCode
143
- /** The floor identifier when present ("3", "G", "B2"). */
158
+ /**
159
+ * The floor identifier when present ("3", "G", "B2").
160
+ */
144
161
  identifier?: string
145
162
  }
146
163
 
147
- // One regex per level code. Multi-word variants ("LOWER GROUND", "GROUND FLOOR") are matched
148
- // before their shorter constituents by ordering the variant list longest-first within each code.
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
- .sort((a, b) => b.length - a.length)
155
- .map((v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, String.raw`\s+`))
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
- /** Type-predicate: does the input look like a standalone AU level designator phrase? */
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
- /** The AU postcode shape: exactly four digits. */
37
+ /**
38
+ * The AU postcode shape: exactly four digits.
39
+ */
38
40
  export const AU_POSTCODE_PATTERN = /^\d{4}$/
39
41
 
40
- /** Normalize a postcode surface form (trim only — AU has no country-prefix courtesy form). */
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
- /** Type-predicate for a (normalized) Australian postcode. */
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
- /** State/territory abbreviation → full name (ISO 3166-2:AU subdivision set). */
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
- /** An Australian state/territory abbreviation as written on the last address line. */
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
- /** Type-predicate for an AU state/territory abbreviation (case-insensitive). */
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().replace(/\s+/g, "")
63
+ const compact = raw.trim().toUpperCase().replaceAll(/\s+/g, "")
59
64
 
60
- if (compact.length !== 6) return null
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
- /** Type-predicate for a Canadian postal code (accepts the spaced or unspaced surface form). */
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
- /** Per-province record: ISO 3166-2:CA code, English name, and the co-official French name. */
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
- /** ISO 3166-2:CA subdivision code without the `CA-` prefix (e.g. `ON` for `CA-ON`). */
23
+ /**
24
+ * ISO 3166-2:CA subdivision code without the `CA-` prefix (e.g. `ON` for `CA-ON`).
25
+ */
22
26
  code: string
23
- /** English name (e.g. `Quebec`). */
27
+ /**
28
+ * English name (e.g. `Quebec`).
29
+ */
24
30
  name: string
25
- /** Co-official French name (e.g. `Québec`). */
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
- /** An ISO 3166-2:CA province/territory code (`AB`, `ON`, `QC`, …). */
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
- /** Type-predicate for an ISO 3166-2:CA province/territory code. Case-insensitive. */
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
- /** Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike. */
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
- .replace(/[\u0300-\u036f]/g, "")
65
- .replace(/[^a-z0-9]+/g, " ")
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
- /** Strip diacritics + lowercase so `Côte`/`cote`, `Allée`/`allee`, `Crescent`/`crescent` key alike. */
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
- .replace(/[\u0300-\u036f]/g, "")
94
- .replace(/[^a-z]/g, "")
95
+ .replaceAll(/[\u0300-\u036F]/g, "")
96
+ .replaceAll(/[^a-z]/g, "")
95
97
  }
96
98
 
97
- /** The English + French street-type vocabularies, both folded, for one position-agnostic lookup. */
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 === 0) return false
159
+ if (!t.length) return false
156
160
 
157
161
  if (t in CA_DIRECTIONALS) return true
158
162
 
@@ -11,8 +11,10 @@
11
11
  * the other codex matchers (street-suffix, directional, po-box).
12
12
  */
13
13
 
14
- import { Alpha3ToCountryRecord, CountryISO2, type CountryISO3 } from "./codes.ts"
15
- import { type CountryName } from "./names.ts"
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
- /** Alpha-2 → canonical English name (inverted from the salvaged CountryISO2 enum). */
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
- /** Result of a country match: the alpha-2 code, the canonical English name, and the matched surface. */
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
- /** Case-insensitive check: is the token any recognized country form? */
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
- /** Official-language spellings for one territory. */
15
+ /**
16
+ * Official-language spellings for one territory.
17
+ */
16
18
  export interface OfficialLanguageEntry {
17
- /** CLDR `official` + `de_facto_official`, in every ISO-639 spelling. */
19
+ /**
20
+ * CLDR `official` + `de_facto_official`, in every ISO-639 spelling.
21
+ */
18
22
  official: readonly string[]
19
- /** CLDR `official_regional` (e.g. Catalan in ES) — opt-in for consumers. */
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
- /** ISO 3166-1 alpha-2 → official languages. */
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
- /** Static per-country reference: calling code + currency. */
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
- /** ISO 3166-1 alpha-2 → reference. */
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: 35818, currency: { isoCode: "EUR", name: "Euro", symbol: "€" } },
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: "$" } },
@@ -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 = 0x1f1e6
16
+ const REGIONAL_INDICATOR_BASE = 0x1_f1_e6
17
17
  const A_UPPER = "A".charCodeAt(0)
18
18
 
19
19
  /**
@@ -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
- /** A resolved subdivision: its ISO 3166-2 code (sans country prefix), canonical English name, and ISO 3166-1 country. */
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
- /** ISO 3166-2 subdivision code without the country prefix (e.g. `QC` for `CA-QC`, `IL` for `US-IL`). */
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
- /** Canonical English name (e.g. `Quebec`, `Illinois`). */
40
+ /**
41
+ * Canonical English name (e.g. `Quebec`, `Illinois`).
42
+ */
37
43
  name: string
38
- /** ISO 3166-1 alpha-2 country the subdivision belongs to (`CA`, `US`). */
44
+ /**
45
+ * ISO 3166-1 alpha-2 country the subdivision belongs to (`CA`, `US`).
46
+ */
39
47
  country: string
40
48
  }
41
49
 
42
- /** Strip diacritics + lowercase so `Québec`, `Quebec`, and `quebec` all key alike (mirrors `ca/province.ts`). */
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
- .replace(/[\u0300-\u036f]/g, "")
48
- .replace(/[^a-z0-9]+/g, " ")
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 > 0 && !out.has(folded)) {
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
- /** Per-state record: ISO 3166-2:DE code, native German name, and the common English exonym. */
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
- /** ISO 3166-2:DE subdivision code without the `DE-` prefix (e.g. `BY` for `DE-BY`). */
18
+ /**
19
+ * ISO 3166-2:DE subdivision code without the `DE-` prefix (e.g. `BY` for `DE-BY`).
20
+ */
17
21
  code: string
18
- /** Native German name (e.g. `Bayern`). */
22
+ /**
23
+ * Native German name (e.g. `Bayern`).
24
+ */
19
25
  name: string
20
- /** Common English name (e.g. `Bavaria`). */
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
- /** An ISO 3166-2:DE state code (`BW`, `BY`, `BE`, …). */
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
- /** Type-predicate for an ISO 3166-2:DE state code. Case-insensitive. */
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",