@mailwoman/codex 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -28,7 +28,9 @@ import type { Tagged } from "type-fest"
28
28
  */
29
29
  export type Postleitzahl = Tagged<string, "Postleitzahl">
30
30
 
31
- /** The PLZ shape: exactly five digits. */
31
+ /**
32
+ * The PLZ shape: exactly five digits.
33
+ */
32
34
  export const PLZ_PATTERN = /^\d{5}$/
33
35
 
34
36
  /**
@@ -42,20 +44,30 @@ export function normalizePLZ(raw: unknown): Postleitzahl | null {
42
44
  return PLZ_PATTERN.test(s) ? (s as Postleitzahl) : null
43
45
  }
44
46
 
45
- /** Type-predicate for a (normalized) German postal code. */
47
+ /**
48
+ * Type-predicate for a (normalized) German postal code.
49
+ */
46
50
  export function isPostleitzahl(input: unknown): input is Postleitzahl {
47
51
  return typeof input === "string" && PLZ_PATTERN.test(input)
48
52
  }
49
53
 
50
- /** A PLZ Leitzone first digit. */
54
+ /**
55
+ * A PLZ Leitzone first digit.
56
+ */
51
57
  export type LeitzoneDigit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
52
58
 
53
- /** Per-Leitzone descriptor: the routing region and a few anchor cities (coarse, postal not admin). */
59
+ /**
60
+ * Per-Leitzone descriptor: the routing region and a few anchor cities (coarse, postal not admin).
61
+ */
54
62
  export interface LeitzoneInfo {
55
63
  digit: LeitzoneDigit
56
- /** Coarse routing-region label. */
64
+ /**
65
+ * Coarse routing-region label.
66
+ */
57
67
  region: string
58
- /** Well-known anchor cities in the zone (illustrative, not exhaustive). */
68
+ /**
69
+ * Well-known anchor cities in the zone (illustrative, not exhaustive).
70
+ */
59
71
  cities: readonly string[]
60
72
  }
61
73
 
@@ -81,7 +93,9 @@ export const PLZ_LEITZONEN = {
81
93
  9: { digit: 9, region: "nördliches Bayern / Oberpfalz", cities: ["Nürnberg", "Würzburg", "Regensburg"] },
82
94
  } as const satisfies Record<LeitzoneDigit, LeitzoneInfo>
83
95
 
84
- /** The Leitzone of a PLZ (its first digit's routing region), or null if the input is not a PLZ. */
96
+ /**
97
+ * The Leitzone of a PLZ (its first digit's routing region), or null if the input is not a PLZ.
98
+ */
85
99
  export function leitzoneOf(plz: unknown): LeitzoneInfo | null {
86
100
  const normalized = normalizePLZ(plz)
87
101
 
package/de/street-type.ts CHANGED
@@ -25,6 +25,15 @@
25
25
  * spelling split). The full reference table, used for synthesis/expansion. For the "is this token part of a street"
26
26
  * test, use {@link DE_STREET_SUFFIXES} / {@link isGermanStreetToken}, which exclude the place-name-colliding suffixes.
27
27
  */
28
+ /**
29
+ * Shortest token still plausible as a German street-type suffix (`weg`).
30
+ */
31
+ const MIN_STREET_TYPE_LENGTH = 3
32
+
33
+ /**
34
+ * German street-type suffixes mapped to their surface variants, including the compounded forms that attach directly to
35
+ * the name (`-strasse`, `-str.`).
36
+ */
28
37
  export const DE_STREET_TYPE_VARIANTS = {
29
38
  Straße: ["Str.", "Str", "Strasse"],
30
39
  Weg: [],
@@ -41,7 +50,9 @@ export const DE_STREET_TYPE_VARIANTS = {
41
50
  Twiete: [],
42
51
  } as const satisfies Record<string, readonly string[]>
43
52
 
44
- /** A canonical German street type (e.g. `Straße`, `Weg`, `Platz`). */
53
+ /**
54
+ * A canonical German street type (e.g. `Straße`, `Weg`, `Platz`).
55
+ */
45
56
  export type GermanStreetType = keyof typeof DE_STREET_TYPE_VARIANTS
46
57
 
47
58
  /**
@@ -75,9 +86,9 @@ export const DE_STREET_SUFFIXES = [
75
86
  */
76
87
  export function isGermanStreetToken(token: unknown): boolean {
77
88
  if (typeof token !== "string") return false
78
- const t = token.toLowerCase().replace(/[^a-zà-ÿß]/g, "")
89
+ const t = token.toLowerCase().replaceAll(/[^a-zà-ÿß]/g, "")
79
90
 
80
- if (t.length < 3) return false
91
+ if (t.length < MIN_STREET_TYPE_LENGTH) return false
81
92
 
82
93
  return DE_STREET_SUFFIXES.some((s) => t.endsWith(s))
83
94
  }
package/fr/cedex.ts CHANGED
@@ -14,16 +14,24 @@
14
14
  * from here (the provenance-first discipline: one provenanced source).
15
15
  */
16
16
 
17
- /** Matches a CEDEX phrase: the keyword plus an optional 1–2 digit office number. */
17
+ /**
18
+ * Matches a CEDEX phrase: the keyword plus an optional 1–2 digit office number.
19
+ */
18
20
  export const CEDEX_PATTERN = /\bCEDEX(?:\s+(\d{1,2}))?\b/i
19
21
 
20
- /** A matched CEDEX phrase with its char range and optional office number. */
22
+ /**
23
+ * A matched CEDEX phrase with its char range and optional office number.
24
+ */
21
25
  export interface CedexMatch {
22
- /** The full matched phrase as it appears ("CEDEX 08", "Cedex"). */
26
+ /**
27
+ * The full matched phrase as it appears ("CEDEX 08", "Cedex").
28
+ */
23
29
  matched: string
24
30
  start: number
25
31
  end: number
26
- /** The office number when present ("08"), undefined for bare CEDEX. */
32
+ /**
33
+ * The office number when present ("08"), undefined for bare CEDEX.
34
+ */
27
35
  office?: string
28
36
  }
29
37
 
@@ -47,7 +55,9 @@ export function matchCedex(text: string): CedexMatch | null {
47
55
  return match
48
56
  }
49
57
 
50
- /** True when the string is exactly a CEDEX phrase (the component-value validator). */
58
+ /**
59
+ * True when the string is exactly a CEDEX phrase (the component-value validator).
60
+ */
51
61
  export function isCedex(input: unknown): boolean {
52
62
  if (typeof input !== "string") return false
53
63
  const m = input.trim().match(CEDEX_PATTERN)
package/fr/code-postal.ts CHANGED
@@ -38,9 +38,17 @@ import { FR_REGIONS, type FrenchRegionInfo } from "./region.ts"
38
38
  * @title Code postal
39
39
  * @pattern ^\d{5}$
40
40
  */
41
+ /**
42
+ * Postcode at which Corsica splits between departments 2A (Corse-du-Sud) and 2B (Haute-Corse). Both share the 20xxx
43
+ * range, so the numeric boundary is the only way to tell them apart.
44
+ */
45
+ const CORSICA_2A_2B_BOUNDARY = 20_200
46
+
41
47
  export type CodePostal = Tagged<string, "CodePostal">
42
48
 
43
- /** The code-postal shape: exactly five digits. */
49
+ /**
50
+ * The code-postal shape: exactly five digits.
51
+ */
44
52
  export const CODE_POSTAL_PATTERN = /^\d{5}$/
45
53
 
46
54
  /**
@@ -54,7 +62,9 @@ export function normalizeCodePostal(raw: unknown): CodePostal | null {
54
62
  return CODE_POSTAL_PATTERN.test(s) ? (s as CodePostal) : null
55
63
  }
56
64
 
57
- /** Type-predicate for a (normalized) French postal code. */
65
+ /**
66
+ * Type-predicate for a (normalized) French postal code.
67
+ */
58
68
  export function isCodePostal(input: unknown): input is CodePostal {
59
69
  return typeof input === "string" && CODE_POSTAL_PATTERN.test(input)
60
70
  }
@@ -76,7 +86,7 @@ export function departementOfCodePostal(codePostal: unknown): DepartementCode |
76
86
 
77
87
  if (cp.startsWith("20")) {
78
88
  // Corsica: prefix 20 covers both départements; the numeric value splits them.
79
- return Number(cp) < 20200 ? "2A" : "2B"
89
+ return Number(cp) < CORSICA_2A_2B_BOUNDARY ? "2A" : "2B"
80
90
  }
81
91
 
82
92
  if (cp.startsWith("97") || cp.startsWith("98")) {
@@ -85,12 +95,15 @@ export function departementOfCodePostal(codePostal: unknown): DepartementCode |
85
95
 
86
96
  return departementInfo(dom) ? (dom as DepartementCode) : null
87
97
  }
98
+
88
99
  const dd = cp.slice(0, 2)
89
100
 
90
101
  return departementInfo(dd) ? (dd as DepartementCode) : null
91
102
  }
92
103
 
93
- /** The full département record a postal code resolves to (name + région), or null. */
104
+ /**
105
+ * The full département record a postal code resolves to (name + région), or null.
106
+ */
94
107
  export function departementForCodePostal(codePostal: unknown): DepartementInfo | null {
95
108
  return departementInfo(departementOfCodePostal(codePostal))
96
109
  }
package/fr/departement.ts CHANGED
@@ -13,17 +13,27 @@
13
13
 
14
14
  import type { FrenchRegionCode } from "./region.ts"
15
15
 
16
- /** Per-département record: code (2-digit, or `2A`/`2B`, or 3-digit DOM) + name + its région. */
16
+ /**
17
+ * Per-département record: code (2-digit, or `2A`/`2B`, or 3-digit DOM) + name + its région.
18
+ */
17
19
  export interface DepartementInfo {
18
- /** Département code: `01`–`95` (metropolitan), `2A`/`2B` (Corsica), or `971`–`976` (overseas). */
20
+ /**
21
+ * Département code: `01`–`95` (metropolitan), `2A`/`2B` (Corsica), or `971`–`976` (overseas).
22
+ */
19
23
  code: string
20
- /** French name (e.g. `Bouches-du-Rhône`). */
24
+ /**
25
+ * French name (e.g. `Bouches-du-Rhône`).
26
+ */
21
27
  name: string
22
- /** The ISO 3166-2:FR code of the région this département belongs to. */
28
+ /**
29
+ * The ISO 3166-2:FR code of the région this département belongs to.
30
+ */
23
31
  region: FrenchRegionCode
24
32
  }
25
33
 
26
- /** Département code → info. 96 metropolitan (incl. 2A/2B) + 5 overseas = 101. */
34
+ /**
35
+ * Département code → info. 96 metropolitan (incl. 2A/2B) + 5 overseas = 101.
36
+ */
27
37
  export const FR_DEPARTEMENTS = {
28
38
  "01": { code: "01", name: "Ain", region: "ARA" },
29
39
  "02": { code: "02", name: "Aisne", region: "HDF" },
@@ -128,7 +138,9 @@ export const FR_DEPARTEMENTS = {
128
138
  "976": { code: "976", name: "Mayotte", region: "MAY" },
129
139
  } as const satisfies Record<string, DepartementInfo>
130
140
 
131
- /** A French département code (`01`–`95`, `2A`/`2B`, or `971`–`976`). */
141
+ /**
142
+ * A French département code (`01`–`95`, `2A`/`2B`, or `971`–`976`).
143
+ */
132
144
  export type DepartementCode = keyof typeof FR_DEPARTEMENTS
133
145
 
134
146
  /**
package/fr/region.ts CHANGED
@@ -13,15 +13,23 @@
13
13
  * `code-postal.ts`).
14
14
  */
15
15
 
16
- /** Per-region record: ISO 3166-2:FR code (sans `FR-` prefix) + French name. */
16
+ /**
17
+ * Per-region record: ISO 3166-2:FR code (sans `FR-` prefix) + French name.
18
+ */
17
19
  export interface FrenchRegionInfo {
18
- /** ISO 3166-2:FR region code without the `FR-` prefix (e.g. `IDF` for `FR-IDF`). */
20
+ /**
21
+ * ISO 3166-2:FR region code without the `FR-` prefix (e.g. `IDF` for `FR-IDF`).
22
+ */
19
23
  code: string
20
- /** French name (e.g. `Île-de-France`). */
24
+ /**
25
+ * French name (e.g. `Île-de-France`).
26
+ */
21
27
  name: string
22
28
  }
23
29
 
24
- /** ISO 3166-2:FR region code → info, for all 18 régions (13 metropolitan + 5 overseas). */
30
+ /**
31
+ * ISO 3166-2:FR region code → info, for all 18 régions (13 metropolitan + 5 overseas).
32
+ */
25
33
  export const FR_REGIONS = {
26
34
  ARA: { code: "ARA", name: "Auvergne-Rhône-Alpes" },
27
35
  BFC: { code: "BFC", name: "Bourgogne-Franche-Comté" },
@@ -43,23 +51,29 @@ export const FR_REGIONS = {
43
51
  MAY: { code: "MAY", name: "Mayotte" },
44
52
  } as const satisfies Record<string, FrenchRegionInfo>
45
53
 
46
- /** An ISO 3166-2:FR region code (`ARA`, `IDF`, `PAC`, …). */
54
+ /**
55
+ * An ISO 3166-2:FR region code (`ARA`, `IDF`, `PAC`, …).
56
+ */
47
57
  export type FrenchRegionCode = keyof typeof FR_REGIONS
48
58
 
49
59
  const REGION_CODE_SET: ReadonlySet<string> = new Set(Object.keys(FR_REGIONS))
50
60
 
51
- /** Type-predicate for an ISO 3166-2:FR region code. Case-insensitive. */
61
+ /**
62
+ * Type-predicate for an ISO 3166-2:FR region code. Case-insensitive.
63
+ */
52
64
  export function isFrenchRegionCode(input: unknown): input is FrenchRegionCode {
53
65
  return typeof input === "string" && REGION_CODE_SET.has(input.toUpperCase())
54
66
  }
55
67
 
56
- /** Strip diacritics + lowercase so `Île-de-France`, `ile-de-france`, `Ile de France` all key alike. */
68
+ /**
69
+ * Strip diacritics + lowercase so `Île-de-France`, `ile-de-france`, `Ile de France` all key alike.
70
+ */
57
71
  function foldName(s: string): string {
58
72
  return s
59
73
  .toLowerCase()
60
74
  .normalize("NFD")
61
- .replace(/[\u0300-\u036f]/g, "")
62
- .replace(/[^a-z0-9]+/g, " ")
75
+ .replaceAll(/[\u0300-\u036F]/g, "")
76
+ .replaceAll(/[^a-z0-9]+/g, " ")
63
77
  .trim()
64
78
  }
65
79
 
package/fr/voie.ts CHANGED
@@ -55,7 +55,9 @@ export const FR_VOIE_TYPES = {
55
55
  lotissement: ["lot"],
56
56
  } as const satisfies Record<string, readonly string[]>
57
57
 
58
- /** A canonical French voie type (e.g. `rue`, `avenue`, `boulevard`). */
58
+ /**
59
+ * A canonical French voie type (e.g. `rue`, `avenue`, `boulevard`).
60
+ */
59
61
  export type FrenchVoieType = keyof typeof FR_VOIE_TYPES
60
62
 
61
63
  /**
@@ -67,7 +69,8 @@ const VOIE_TOKEN_SET: ReadonlySet<string> = (() => {
67
69
  s
68
70
  .toLowerCase()
69
71
  .normalize("NFD")
70
- .replace(/[\u0300-\u036f]/g, "")
72
+ .replaceAll(/[\u0300-\u036F]/g, "")
73
+
71
74
  const out = new Set<string>()
72
75
 
73
76
  for (const canonical of Object.keys(FR_VOIE_TYPES) as FrenchVoieType[]) {
@@ -88,11 +91,12 @@ const VOIE_TOKEN_SET: ReadonlySet<string> = (() => {
88
91
  */
89
92
  export function isFrenchStreetWord(token: unknown): boolean {
90
93
  if (typeof token !== "string") return false
94
+
91
95
  const t = token
92
96
  .toLowerCase()
93
97
  .normalize("NFD")
94
- .replace(/[\u0300-\u036f]/g, "")
95
- .replace(/[^a-z-]/g, "")
98
+ .replaceAll(/[\u0300-\u036F]/g, "")
99
+ .replaceAll(/[^a-z-]/g, "")
96
100
 
97
101
  return t.length > 0 && VOIE_TOKEN_SET.has(t)
98
102
  }
package/gb/country.ts CHANGED
@@ -14,15 +14,23 @@
14
14
  * postcode is the thing that actually carries the geography.
15
15
  */
16
16
 
17
- /** Per-country record: ISO 3166-2:GB code (sans `GB-` prefix) + English name. */
17
+ /**
18
+ * Per-country record: ISO 3166-2:GB code (sans `GB-` prefix) + English name.
19
+ */
18
20
  export interface UkCountryInfo {
19
- /** ISO 3166-2:GB country code without the `GB-` prefix (e.g. `ENG` for `GB-ENG`). */
21
+ /**
22
+ * ISO 3166-2:GB country code without the `GB-` prefix (e.g. `ENG` for `GB-ENG`).
23
+ */
20
24
  code: string
21
- /** English name (e.g. `Scotland`). */
25
+ /**
26
+ * English name (e.g. `Scotland`).
27
+ */
22
28
  name: string
23
29
  }
24
30
 
25
- /** ISO 3166-2:GB country code → info, for all four constituent countries. */
31
+ /**
32
+ * ISO 3166-2:GB country code → info, for all four constituent countries.
33
+ */
26
34
  export const GB_COUNTRIES = {
27
35
  ENG: { code: "ENG", name: "England" },
28
36
  SCT: { code: "SCT", name: "Scotland" },
@@ -30,25 +38,33 @@ export const GB_COUNTRIES = {
30
38
  NIR: { code: "NIR", name: "Northern Ireland" },
31
39
  } as const satisfies Record<string, UkCountryInfo>
32
40
 
33
- /** An ISO 3166-2:GB constituent-country code (`ENG`, `SCT`, `WLS`, `NIR`). */
41
+ /**
42
+ * An ISO 3166-2:GB constituent-country code (`ENG`, `SCT`, `WLS`, `NIR`).
43
+ */
34
44
  export type UkCountryCode = keyof typeof GB_COUNTRIES
35
45
 
36
46
  const COUNTRY_CODE_SET: ReadonlySet<string> = new Set(Object.keys(GB_COUNTRIES))
37
47
 
38
- /** Type-predicate for an ISO 3166-2:GB country code. Case-insensitive. */
48
+ /**
49
+ * Type-predicate for an ISO 3166-2:GB country code. Case-insensitive.
50
+ */
39
51
  export function isUkCountryCode(input: unknown): input is UkCountryCode {
40
52
  return typeof input === "string" && COUNTRY_CODE_SET.has(input.toUpperCase())
41
53
  }
42
54
 
43
- /** Lowercase + collapse non-alphanumerics so `Northern Ireland`, `northern-ireland` key alike. */
55
+ /**
56
+ * Lowercase + collapse non-alphanumerics so `Northern Ireland`, `northern-ireland` key alike.
57
+ */
44
58
  function foldName(s: string): string {
45
59
  return s
46
60
  .toLowerCase()
47
- .replace(/[^a-z0-9]+/g, " ")
61
+ .replaceAll(/[^a-z0-9]+/g, " ")
48
62
  .trim()
49
63
  }
50
64
 
51
- /** Folded country name / code → ISO 3166-2:GB code, so a surface form maps regardless of casing. */
65
+ /**
66
+ * Folded country name / code → ISO 3166-2:GB code, so a surface form maps regardless of casing.
67
+ */
52
68
  const COUNTRY_NAME_TO_CODE: ReadonlyMap<string, UkCountryCode> = (() => {
53
69
  const out = new Map<string, UkCountryCode>()
54
70
 
@@ -24,7 +24,9 @@
24
24
 
25
25
  import type { UkCountryCode } from "./country.ts"
26
26
 
27
- /** Northern Ireland is a single postcode area: BT (Belfast). */
27
+ /**
28
+ * Northern Ireland is a single postcode area: BT (Belfast).
29
+ */
28
30
  const NORTHERN_IRELAND_AREAS = ["BT"] as const
29
31
 
30
32
  /**
@@ -74,7 +76,9 @@ export const GB_POSTCODE_AREA_COUNTRY: Record<string, UkCountryCode> = {
74
76
  ...Object.fromEntries(WALES_AREAS.map((a) => [a, "WLS" as const])),
75
77
  }
76
78
 
77
- /** True when `area` looks like a valid postcode-area string: one or two ASCII letters. */
79
+ /**
80
+ * True when `area` looks like a valid postcode-area string: one or two ASCII letters.
81
+ */
78
82
  function isAreaShape(area: unknown): area is string {
79
83
  return typeof area === "string" && /^[A-Z]{1,2}$/i.test(area)
80
84
  }
package/gb/postcode.ts CHANGED
@@ -39,6 +39,11 @@ import type { Tagged } from "type-fest"
39
39
  * @title UK postcode
40
40
  * @pattern ^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
41
41
  */
42
+ /**
43
+ * Shortest valid UK postcode once spaces are stripped, e.g. `M11AE`.
44
+ */
45
+ const MIN_POSTCODE_LENGTH = 5
46
+
42
47
  export type Postcode = Tagged<string, "UkPostcode">
43
48
 
44
49
  /**
@@ -57,15 +62,17 @@ export const UK_POSTCODE_PATTERN = /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i
57
62
  export function normalizeUkPostcode(raw: unknown): Postcode | null {
58
63
  if (typeof raw !== "string") return null
59
64
  // Drop all whitespace, uppercase, then re-insert the single canonical space before the inward 3.
60
- const compact = raw.replace(/\s+/g, "").toUpperCase()
65
+ const compact = raw.replaceAll(/\s+/g, "").toUpperCase()
61
66
 
62
- if (compact.length < 5) return null
67
+ if (compact.length < MIN_POSTCODE_LENGTH) return null
63
68
  const spaced = `${compact.slice(0, -3)} ${compact.slice(-3)}`
64
69
 
65
70
  return UK_POSTCODE_PATTERN.test(spaced) ? (spaced as Postcode) : null
66
71
  }
67
72
 
68
- /** Type-predicate for a UK postcode surface form (space optional). */
73
+ /**
74
+ * Type-predicate for a UK postcode surface form (space optional).
75
+ */
69
76
  export function isUkPostcode(input: unknown): input is Postcode {
70
77
  return typeof input === "string" && UK_POSTCODE_PATTERN.test(input.trim())
71
78
  }
package/gb/street-type.ts CHANGED
@@ -72,7 +72,9 @@ export const GB_STREET_TYPES = [
72
72
  "gait",
73
73
  ] as const
74
74
 
75
- /** A canonical British thoroughfare word (e.g. `street`, `crescent`, `mews`). */
75
+ /**
76
+ * A canonical British thoroughfare word (e.g. `street`, `crescent`, `mews`).
77
+ */
76
78
  export type BritishStreetType = (typeof GB_STREET_TYPES)[number]
77
79
 
78
80
  const STREET_TYPE_SET: ReadonlySet<string> = new Set(GB_STREET_TYPES)
@@ -84,7 +86,7 @@ const STREET_TYPE_SET: ReadonlySet<string> = new Set(GB_STREET_TYPES)
84
86
  */
85
87
  export function isBritishStreetWord(token: unknown): boolean {
86
88
  if (typeof token !== "string") return false
87
- const t = token.toLowerCase().replace(/[^a-z]/g, "")
89
+ const t = token.toLowerCase().replaceAll(/[^a-z]/g, "")
88
90
 
89
91
  return t.length > 0 && STREET_TYPE_SET.has(t)
90
92
  }
package/index.ts CHANGED
@@ -26,6 +26,7 @@ export {
26
26
  conventionsForSystem,
27
27
  type AddressSystemConventions,
28
28
  } from "./address-system-conventions.ts"
29
+
29
30
  export * as au from "./au/index.ts"
30
31
  export * as ca from "./ca/index.ts"
31
32
  export * as de from "./de/index.ts"
@@ -43,7 +43,9 @@
43
43
  */
44
44
  export const JP_ADMIN_SUFFIXES = ["都", "道", "府", "県", "市", "区", "郡", "町", "村"] as const
45
45
 
46
- /** A single administrative-area suffix kanji (`都`, `市`, `区`, …). */
46
+ /**
47
+ * A single administrative-area suffix kanji (`都`, `市`, `区`, …).
48
+ */
47
49
  export type JapaneseAdminSuffix = (typeof JP_ADMIN_SUFFIXES)[number]
48
50
 
49
51
  /**
@@ -57,7 +59,9 @@ export type JapaneseAdminSuffix = (typeof JP_ADMIN_SUFFIXES)[number]
57
59
  */
58
60
  export const JP_BLOCK_MARKERS = ["丁目", "番地", "番", "号"] as const
59
61
 
60
- /** A numbered-tail marker (`丁目`, `番地`, `番`, `号`). */
62
+ /**
63
+ * A numbered-tail marker (`丁目`, `番地`, `番`, `号`).
64
+ */
61
65
  export type JapaneseBlockMarker = (typeof JP_BLOCK_MARKERS)[number]
62
66
 
63
67
  const ADMIN_SUFFIX_SET: ReadonlySet<string> = new Set(JP_ADMIN_SUFFIXES)
@@ -78,10 +82,10 @@ export function isJapaneseAdminSuffix(ch: unknown): ch is JapaneseAdminSuffix {
78
82
  * than clipped to `北海` — mirroring the same carve-out in `prefecture.ts`.
79
83
  */
80
84
  export function stripAdminSuffix(name: string): string {
81
- if (typeof name !== "string" || name.length === 0) return name
85
+ if (typeof name !== "string" || !name.length) return name
82
86
 
83
87
  if (name === "北海道") return name
84
- const last = name[name.length - 1]!
88
+ const last = name.at(-1)!
85
89
 
86
90
  return ADMIN_SUFFIX_SET.has(last) ? name.slice(0, -1) : name
87
91
  }
package/jp/postal-code.ts CHANGED
@@ -36,7 +36,9 @@ import type { Tagged } from "type-fest"
36
36
  */
37
37
  export type PostalCode = Tagged<string, "JpPostalCode">
38
38
 
39
- /** The postal-code shape: `NNN-NNNN`, the hyphen optional on input (`1000001` or `100-0001`). */
39
+ /**
40
+ * The postal-code shape: `NNN-NNNN`, the hyphen optional on input (`1000001` or `100-0001`).
41
+ */
40
42
  export const JP_POSTAL_CODE_PATTERN = /^\d{3}-?\d{4}$/
41
43
 
42
44
  /**
@@ -47,14 +49,16 @@ export const JP_POSTAL_CODE_PATTERN = /^\d{3}-?\d{4}$/
47
49
  export function normalizeJpPostalCode(raw: unknown): PostalCode | null {
48
50
  if (typeof raw !== "string") return null
49
51
  // Drop the 〒 mark and all whitespace, then keep only the digits.
50
- const digits = raw.replace(/〒/g, "").replace(/\s+/g, "").replace(/-/g, "")
52
+ const digits = raw.replaceAll("〒", "").replaceAll(/\s+/g, "").replaceAll("-", "")
51
53
 
52
54
  if (!/^\d{7}$/.test(digits)) return null
53
55
 
54
56
  return `${digits.slice(0, 3)}-${digits.slice(3)}` as PostalCode
55
57
  }
56
58
 
57
- /** Type-predicate for a Japanese postal code (hyphen optional, `100-0001` or `1000001`). */
59
+ /**
60
+ * Type-predicate for a Japanese postal code (hyphen optional, `100-0001` or `1000001`).
61
+ */
58
62
  export function isJpPostalCode(input: unknown): input is PostalCode {
59
63
  return typeof input === "string" && JP_POSTAL_CODE_PATTERN.test(input)
60
64
  }
package/jp/prefecture.ts CHANGED
@@ -23,18 +23,30 @@
23
23
  * `postal-code.ts` for why the postcode is nonetheless the single most reliable anchor.
24
24
  */
25
25
 
26
- /** A to/dō/fu/ken classification of the top-level admin unit. */
26
+ /**
27
+ * A to/dō/fu/ken classification of the top-level admin unit.
28
+ */
27
29
  export type JapanesePrefectureType = "to" | "do" | "fu" | "ken"
28
30
 
29
- /** Per-prefecture record: ISO 3166-2:JP numeric code + kanji + romaji + to/dō/fu/ken type. */
31
+ /**
32
+ * Per-prefecture record: ISO 3166-2:JP numeric code + kanji + romaji + to/dō/fu/ken type.
33
+ */
30
34
  export interface JapanesePrefectureInfo {
31
- /** ISO 3166-2:JP code without the `JP-` prefix: a two-digit numeric string (`"13"` for `JP-13`). */
35
+ /**
36
+ * ISO 3166-2:JP code without the `JP-` prefix: a two-digit numeric string (`"13"` for `JP-13`).
37
+ */
32
38
  code: string
33
- /** Kanji name, including its 都/道/府/県 suffix (e.g. `東京都`). */
39
+ /**
40
+ * Kanji name, including its 都/道/府/県 suffix (e.g. `東京都`).
41
+ */
34
42
  kanji: string
35
- /** Macron-free romaji name, suffix-less (e.g. `Tokyo`). */
43
+ /**
44
+ * Macron-free romaji name, suffix-less (e.g. `Tokyo`).
45
+ */
36
46
  romaji: string
37
- /** Which of the four flavours of top-level unit this is. */
47
+ /**
48
+ * Which of the four flavours of top-level unit this is.
49
+ */
38
50
  type: JapanesePrefectureType
39
51
  }
40
52
 
@@ -92,12 +104,16 @@ export const JP_PREFECTURES = {
92
104
  "47": { code: "47", kanji: "沖縄県", romaji: "Okinawa", type: "ken" },
93
105
  } as const satisfies Record<string, JapanesePrefectureInfo>
94
106
 
95
- /** An ISO 3166-2:JP prefecture code (`"01"`..`"47"`). */
107
+ /**
108
+ * An ISO 3166-2:JP prefecture code (`"01"`..`"47"`).
109
+ */
96
110
  export type JapanesePrefectureCode = keyof typeof JP_PREFECTURES
97
111
 
98
112
  const PREFECTURE_CODE_SET: ReadonlySet<string> = new Set(Object.keys(JP_PREFECTURES))
99
113
 
100
- /** Type-predicate for an ISO 3166-2:JP prefecture code (`"01"`..`"47"`). */
114
+ /**
115
+ * Type-predicate for an ISO 3166-2:JP prefecture code (`"01"`..`"47"`).
116
+ */
101
117
  export function isJapanesePrefectureCode(input: unknown): input is JapanesePrefectureCode {
102
118
  return typeof input === "string" && PREFECTURE_CODE_SET.has(input)
103
119
  }
@@ -117,11 +133,12 @@ function foldRomaji(s: string): string {
117
133
  const lowered = s
118
134
  .toLowerCase()
119
135
  .normalize("NFD")
120
- .replace(/[\u0300-\u036f]/g, "")
136
+ .replaceAll(/[\u0300-\u036F]/g, "")
137
+
121
138
  // Strip an appended type-suffix only when a separator (hyphen / space / middle dot) precedes it.
122
139
  const desuffixed = lowered.replace(/[-\s·][\s]*(to|do|fu|ken)$/, "")
123
140
 
124
- return desuffixed.replace(/[^a-z]/g, "")
141
+ return desuffixed.replaceAll(/[^a-z]/g, "")
125
142
  }
126
143
 
127
144
  /**