@mailwoman/codex 9.2.0 → 9.4.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 (295) hide show
  1. package/README.md +4 -4
  2. package/{address-system-conventions.ts → lib/address-system-conventions.ts} +9 -9
  3. package/{au → lib/au}/index.ts +4 -4
  4. package/{ca → lib/ca}/index.ts +3 -3
  5. package/{ca → lib/ca}/postal-code.ts +1 -1
  6. package/{ca → lib/ca}/province.ts +1 -1
  7. package/{ca → lib/ca}/street-type.ts +10 -12
  8. package/{country → lib/country}/codes.ts +1 -1
  9. package/{country → lib/country}/country.ts +43 -14
  10. package/lib/country/index.ts +17 -0
  11. package/{country → lib/country}/reference.ts +2 -2
  12. package/{country → lib/country}/subdivision.ts +4 -4
  13. package/{de → lib/de}/index.ts +3 -3
  14. package/{es → lib/es}/codigo-postal.ts +22 -4
  15. package/{es → lib/es}/index.ts +1 -1
  16. package/{fr → lib/fr}/cedex.ts +1 -1
  17. package/{fr → lib/fr}/code-postal.ts +2 -2
  18. package/{fr → lib/fr}/departement.ts +5 -2
  19. package/{fr → lib/fr}/index.ts +5 -5
  20. package/{fr → lib/fr}/region.ts +1 -1
  21. package/{fr → lib/fr}/voie.ts +5 -13
  22. package/{gb → lib/gb}/country.ts +2 -10
  23. package/{gb → lib/gb}/index.ts +4 -4
  24. package/{gb → lib/gb}/postcode-area.ts +1 -1
  25. package/{gb → lib/gb}/street-type.ts +1 -1
  26. package/{index.ts → lib/index.ts} +13 -12
  27. package/{it → lib/it}/cap.ts +21 -3
  28. package/{it → lib/it}/index.ts +1 -1
  29. package/{jp → lib/jp}/index.ts +4 -3
  30. package/lib/jp/municipality-register.ts +62 -0
  31. package/{jp → lib/jp}/prefecture.ts +7 -5
  32. package/lib/kr/index.ts +10 -0
  33. package/lib/kr/sigungu-register.ts +294 -0
  34. package/{normalize.ts → lib/normalize.ts} +15 -1
  35. package/{nz → lib/nz}/delivery-service.ts +1 -1
  36. package/{nz → lib/nz}/index.ts +2 -2
  37. package/{postcode-systems.ts → lib/postcode-systems.ts} +40 -24
  38. package/lib/register-completion.ts +24 -0
  39. package/{tools → lib/tools}/build-country-surface-lexicon.ts +10 -9
  40. package/{tools → lib/tools}/export-country-surfaces.ts +8 -8
  41. package/{tools → lib/tools}/generate-country-population.ts +4 -5
  42. package/{tools → lib/tools}/generate-country-reference.ts +4 -5
  43. package/{tools → lib/tools}/generate-official-languages.ts +7 -9
  44. package/{tools → lib/tools}/index.ts +3 -3
  45. package/{us → lib/us}/index.ts +8 -8
  46. package/{us → lib/us}/po-box.ts +1 -1
  47. package/{us → lib/us}/street-directional.ts +2 -2
  48. package/{us → lib/us}/street-suffix.ts +1 -1
  49. package/{us → lib/us}/unit-designator.ts +1 -1
  50. package/{us → lib/us}/zipcode.ts +1 -1
  51. package/out/address-system-conventions.d.ts +1 -1
  52. package/out/address-system-conventions.d.ts.map +1 -1
  53. package/out/address-system-conventions.js +8 -8
  54. package/out/address-system-conventions.js.map +1 -1
  55. package/out/au/delivery-service.d.ts.map +1 -1
  56. package/out/au/delivery-service.js.map +1 -1
  57. package/out/au/index.d.ts +4 -4
  58. package/out/au/index.d.ts.map +1 -1
  59. package/out/au/index.js +4 -4
  60. package/out/au/index.js.map +1 -1
  61. package/out/au/level-designator.d.ts.map +1 -1
  62. package/out/au/level-designator.js.map +1 -1
  63. package/out/au/postcode.d.ts.map +1 -1
  64. package/out/au/postcode.js.map +1 -1
  65. package/out/au/state.d.ts.map +1 -1
  66. package/out/au/state.js.map +1 -1
  67. package/out/ca/index.d.ts +3 -3
  68. package/out/ca/index.d.ts.map +1 -1
  69. package/out/ca/index.js +3 -3
  70. package/out/ca/index.js.map +1 -1
  71. package/out/ca/postal-code.d.ts +1 -1
  72. package/out/ca/postal-code.d.ts.map +1 -1
  73. package/out/ca/postal-code.js.map +1 -1
  74. package/out/ca/province.d.ts.map +1 -1
  75. package/out/ca/province.js +1 -1
  76. package/out/ca/province.js.map +1 -1
  77. package/out/ca/street-type.d.ts.map +1 -1
  78. package/out/ca/street-type.js +9 -12
  79. package/out/ca/street-type.js.map +1 -1
  80. package/out/country/codes.d.ts.map +1 -1
  81. package/out/country/codes.js.map +1 -1
  82. package/out/country/country.d.ts +13 -11
  83. package/out/country/country.d.ts.map +1 -1
  84. package/out/country/country.js +35 -13
  85. package/out/country/country.js.map +1 -1
  86. package/out/country/display-names.d.ts.map +1 -1
  87. package/out/country/display-names.js.map +1 -1
  88. package/out/country/index.d.ts +10 -10
  89. package/out/country/index.d.ts.map +1 -1
  90. package/out/country/index.js +10 -10
  91. package/out/country/index.js.map +1 -1
  92. package/out/country/names.d.ts.map +1 -1
  93. package/out/country/names.js.map +1 -1
  94. package/out/country/official-languages.d.ts.map +1 -1
  95. package/out/country/official-languages.js.map +1 -1
  96. package/out/country/population.d.ts.map +1 -1
  97. package/out/country/population.js.map +1 -1
  98. package/out/country/reference-data.d.ts.map +1 -1
  99. package/out/country/reference-data.js.map +1 -1
  100. package/out/country/reference.d.ts +1 -1
  101. package/out/country/reference.d.ts.map +1 -1
  102. package/out/country/reference.js +2 -2
  103. package/out/country/reference.js.map +1 -1
  104. package/out/country/subdivision.d.ts.map +1 -1
  105. package/out/country/subdivision.js +4 -4
  106. package/out/country/subdivision.js.map +1 -1
  107. package/out/de/bundesland.d.ts.map +1 -1
  108. package/out/de/bundesland.js.map +1 -1
  109. package/out/de/index.d.ts +3 -3
  110. package/out/de/index.d.ts.map +1 -1
  111. package/out/de/index.js +3 -3
  112. package/out/de/index.js.map +1 -1
  113. package/out/de/postleitzahl.d.ts.map +1 -1
  114. package/out/de/postleitzahl.js.map +1 -1
  115. package/out/de/street-type.d.ts.map +1 -1
  116. package/out/de/street-type.js.map +1 -1
  117. package/out/es/codigo-postal.d.ts +11 -0
  118. package/out/es/codigo-postal.d.ts.map +1 -1
  119. package/out/es/codigo-postal.js +20 -3
  120. package/out/es/codigo-postal.js.map +1 -1
  121. package/out/es/index.d.ts +1 -1
  122. package/out/es/index.d.ts.map +1 -1
  123. package/out/es/index.js +1 -1
  124. package/out/es/index.js.map +1 -1
  125. package/out/fr/cedex.d.ts +1 -1
  126. package/out/fr/cedex.d.ts.map +1 -1
  127. package/out/fr/cedex.js +1 -1
  128. package/out/fr/cedex.js.map +1 -1
  129. package/out/fr/code-postal.d.ts +2 -2
  130. package/out/fr/code-postal.d.ts.map +1 -1
  131. package/out/fr/code-postal.js +2 -2
  132. package/out/fr/code-postal.js.map +1 -1
  133. package/out/fr/departement.d.ts +2 -2
  134. package/out/fr/departement.d.ts.map +1 -1
  135. package/out/fr/departement.js +3 -0
  136. package/out/fr/departement.js.map +1 -1
  137. package/out/fr/index.d.ts +5 -5
  138. package/out/fr/index.d.ts.map +1 -1
  139. package/out/fr/index.js +5 -5
  140. package/out/fr/index.js.map +1 -1
  141. package/out/fr/region.d.ts.map +1 -1
  142. package/out/fr/region.js +1 -1
  143. package/out/fr/region.js.map +1 -1
  144. package/out/fr/voie.d.ts.map +1 -1
  145. package/out/fr/voie.js +4 -11
  146. package/out/fr/voie.js.map +1 -1
  147. package/out/gb/country.d.ts.map +1 -1
  148. package/out/gb/country.js +1 -9
  149. package/out/gb/country.js.map +1 -1
  150. package/out/gb/index.d.ts +4 -4
  151. package/out/gb/index.d.ts.map +1 -1
  152. package/out/gb/index.js +4 -4
  153. package/out/gb/index.js.map +1 -1
  154. package/out/gb/postcode-area.d.ts +1 -1
  155. package/out/gb/postcode-area.d.ts.map +1 -1
  156. package/out/gb/postcode-area.js.map +1 -1
  157. package/out/gb/postcode.d.ts.map +1 -1
  158. package/out/gb/postcode.js.map +1 -1
  159. package/out/gb/street-type.d.ts +1 -1
  160. package/out/gb/street-type.d.ts.map +1 -1
  161. package/out/gb/street-type.js +1 -1
  162. package/out/gb/street-type.js.map +1 -1
  163. package/out/index.d.ts +12 -12
  164. package/out/index.d.ts.map +1 -1
  165. package/out/index.js +12 -12
  166. package/out/index.js.map +1 -1
  167. package/out/it/cap.d.ts +11 -0
  168. package/out/it/cap.d.ts.map +1 -1
  169. package/out/it/cap.js +19 -2
  170. package/out/it/cap.js.map +1 -1
  171. package/out/it/index.d.ts +1 -1
  172. package/out/it/index.d.ts.map +1 -1
  173. package/out/it/index.js +1 -1
  174. package/out/it/index.js.map +1 -1
  175. package/out/jp/address-unit.d.ts.map +1 -1
  176. package/out/jp/address-unit.js.map +1 -1
  177. package/out/jp/index.d.ts +4 -3
  178. package/out/jp/index.d.ts.map +1 -1
  179. package/out/jp/index.js +4 -3
  180. package/out/jp/index.js.map +1 -1
  181. package/out/jp/municipality-register.d.ts +48 -0
  182. package/out/jp/municipality-register.d.ts.map +1 -0
  183. package/out/jp/municipality-register.js +41 -0
  184. package/out/jp/municipality-register.js.map +1 -0
  185. package/out/jp/postal-code.d.ts.map +1 -1
  186. package/out/jp/postal-code.js.map +1 -1
  187. package/out/jp/prefecture.d.ts +4 -1
  188. package/out/jp/prefecture.d.ts.map +1 -1
  189. package/out/jp/prefecture.js +6 -5
  190. package/out/jp/prefecture.js.map +1 -1
  191. package/out/kr/index.d.ts +10 -0
  192. package/out/kr/index.d.ts.map +1 -0
  193. package/out/kr/index.js +10 -0
  194. package/out/kr/index.js.map +1 -0
  195. package/out/kr/sigungu-register.d.ts +37 -0
  196. package/out/kr/sigungu-register.d.ts.map +1 -0
  197. package/out/kr/sigungu-register.js +283 -0
  198. package/out/kr/sigungu-register.js.map +1 -0
  199. package/out/level-semantics.d.ts.map +1 -1
  200. package/out/level-semantics.js.map +1 -1
  201. package/out/normalize.d.ts +8 -0
  202. package/out/normalize.d.ts.map +1 -1
  203. package/out/normalize.js +14 -1
  204. package/out/normalize.js.map +1 -1
  205. package/out/nz/delivery-service.d.ts +1 -1
  206. package/out/nz/delivery-service.d.ts.map +1 -1
  207. package/out/nz/delivery-service.js +1 -1
  208. package/out/nz/delivery-service.js.map +1 -1
  209. package/out/nz/index.d.ts +2 -2
  210. package/out/nz/index.d.ts.map +1 -1
  211. package/out/nz/index.js +2 -2
  212. package/out/nz/index.js.map +1 -1
  213. package/out/nz/postcode.d.ts.map +1 -1
  214. package/out/nz/postcode.js.map +1 -1
  215. package/out/postcode-systems.d.ts +26 -15
  216. package/out/postcode-systems.d.ts.map +1 -1
  217. package/out/postcode-systems.js +38 -23
  218. package/out/postcode-systems.js.map +1 -1
  219. package/out/register-completion.d.ts +14 -0
  220. package/out/register-completion.d.ts.map +1 -0
  221. package/out/register-completion.js +23 -0
  222. package/out/register-completion.js.map +1 -0
  223. package/out/tools/build-country-surface-lexicon.d.ts +1 -1
  224. package/out/tools/build-country-surface-lexicon.d.ts.map +1 -1
  225. package/out/tools/build-country-surface-lexicon.js +10 -9
  226. package/out/tools/build-country-surface-lexicon.js.map +1 -1
  227. package/out/tools/export-country-surfaces.d.ts +1 -1
  228. package/out/tools/export-country-surfaces.d.ts.map +1 -1
  229. package/out/tools/export-country-surfaces.js +7 -7
  230. package/out/tools/export-country-surfaces.js.map +1 -1
  231. package/out/tools/generate-country-population.d.ts.map +1 -1
  232. package/out/tools/generate-country-population.js +4 -4
  233. package/out/tools/generate-country-population.js.map +1 -1
  234. package/out/tools/generate-country-reference.d.ts.map +1 -1
  235. package/out/tools/generate-country-reference.js +4 -4
  236. package/out/tools/generate-country-reference.js.map +1 -1
  237. package/out/tools/generate-official-languages.d.ts.map +1 -1
  238. package/out/tools/generate-official-languages.js +7 -7
  239. package/out/tools/generate-official-languages.js.map +1 -1
  240. package/out/tools/index.d.ts +3 -3
  241. package/out/tools/index.d.ts.map +1 -1
  242. package/out/tools/index.js +3 -3
  243. package/out/tools/index.js.map +1 -1
  244. package/out/us/floor-designator.d.ts.map +1 -1
  245. package/out/us/floor-designator.js.map +1 -1
  246. package/out/us/index.d.ts +8 -8
  247. package/out/us/index.d.ts.map +1 -1
  248. package/out/us/index.js +8 -8
  249. package/out/us/index.js.map +1 -1
  250. package/out/us/military-address.d.ts.map +1 -1
  251. package/out/us/military-address.js.map +1 -1
  252. package/out/us/po-box.d.ts +1 -1
  253. package/out/us/po-box.d.ts.map +1 -1
  254. package/out/us/po-box.js +1 -1
  255. package/out/us/po-box.js.map +1 -1
  256. package/out/us/state.d.ts.map +1 -1
  257. package/out/us/state.js.map +1 -1
  258. package/out/us/street-directional.d.ts.map +1 -1
  259. package/out/us/street-directional.js +2 -2
  260. package/out/us/street-directional.js.map +1 -1
  261. package/out/us/street-suffix.d.ts +1 -1
  262. package/out/us/street-suffix.d.ts.map +1 -1
  263. package/out/us/street-suffix.js +1 -1
  264. package/out/us/street-suffix.js.map +1 -1
  265. package/out/us/unit-designator.d.ts +1 -1
  266. package/out/us/unit-designator.d.ts.map +1 -1
  267. package/out/us/unit-designator.js +1 -1
  268. package/out/us/unit-designator.js.map +1 -1
  269. package/out/us/zipcode.d.ts +1 -1
  270. package/out/us/zipcode.d.ts.map +1 -1
  271. package/out/us/zipcode.js +1 -1
  272. package/out/us/zipcode.js.map +1 -1
  273. package/package.json +78 -267
  274. package/country/index.ts +0 -17
  275. /package/{au → lib/au}/delivery-service.ts +0 -0
  276. /package/{au → lib/au}/level-designator.ts +0 -0
  277. /package/{au → lib/au}/postcode.ts +0 -0
  278. /package/{au → lib/au}/state.ts +0 -0
  279. /package/{country → lib/country}/display-names.ts +0 -0
  280. /package/{country → lib/country}/names.ts +0 -0
  281. /package/{country → lib/country}/official-languages.ts +0 -0
  282. /package/{country → lib/country}/population.ts +0 -0
  283. /package/{country → lib/country}/reference-data.ts +0 -0
  284. /package/{de → lib/de}/bundesland.ts +0 -0
  285. /package/{de → lib/de}/postleitzahl.ts +0 -0
  286. /package/{de → lib/de}/street-type.ts +0 -0
  287. /package/{gb → lib/gb}/postcode.ts +0 -0
  288. /package/{jp → lib/jp}/address-unit.ts +0 -0
  289. /package/{jp → lib/jp}/postal-code.ts +0 -0
  290. /package/{level-semantics.ts → lib/level-semantics.ts} +0 -0
  291. /package/{nz → lib/nz}/postcode.ts +0 -0
  292. /package/{us → lib/us}/floor-designator.ts +0 -0
  293. /package/{us → lib/us}/military-address.ts +0 -0
  294. /package/{us → lib/us}/state.ts +0 -0
  295. /package/{us → lib/us}/street-suffix.json +0 -0
package/README.md CHANGED
@@ -23,7 +23,7 @@ fr.postcodePattern // → /^\d{5}$/
23
23
 
24
24
  // US ZIP code branded type
25
25
  import { us } from "@mailwoman/codex"
26
- const zip: us.ZipCode = "94043" // branded, not just string
26
+ const zip: us.ZipCode = "94043" // branded, not string alone
27
27
  ```
28
28
 
29
29
  ## Supported address systems
@@ -66,7 +66,7 @@ clothes.
66
66
 
67
67
  One `postalcode` placetype covers systems that are not comparable. An Irish Eircode
68
68
  names a single address; an Australian postcode names a locality. Between them sit
69
- most of the world, and the distinction that actually changes an answer is narrower:
69
+ most of the world, and the distinction that changes an answer is narrower:
70
70
  **is this code finer than the locality that contains it?**
71
71
 
72
72
  That is a fact about a country's _administrative_ geography, not its postal system,
@@ -106,7 +106,7 @@ nothing noticed.
106
106
  no network. Suitable for bundling into browser and edge environments.
107
107
  - **Branded types.** ZIP codes, postcodes, and abbreviations carry nominal types
108
108
  so the type system catches locale mismatches at compile time.
109
- - **Single source of truth.** The resolver, the decoder's convention masks, the
109
+ - **One definition.** The resolver, the decoder's convention masks, the
110
110
  corpus synthesis layer, and the matcher all import from `@mailwoman/codex`.
111
111
 
112
112
  ## The normative tier (codex vs the libpostal dictionaries)
@@ -120,7 +120,7 @@ be merged:
120
120
  invariance transforms, and formatting (rendering `N` vs `North` requires knowing
121
121
  which form the authority prints).
122
122
  - **The libpostal dictionaries** (`core/data/libpostal/dictionaries/`, Pelias
123
- lineage) **are descriptive**: everything people actually write, including forms no
123
+ lineage) **are descriptive**: everything people write, including forms no
124
124
  authority recognizes (`en/directionals.txt` lists `lower`/`upper`/`central`).
125
125
  Their consumers are recall-shaped — evidence-lexicon curation laws, street
126
126
  decomposition for training gold, the street-morphology FST. See the README in
@@ -18,9 +18,9 @@
18
18
  * by the caller) — an undetected system parses exactly as before.
19
19
  */
20
20
 
21
- import { CODE_POSTAL_PATTERN } from "./fr/code-postal.ts"
22
- import { UK_POSTCODE_PATTERN } from "./gb/postcode.ts"
23
- import type { SystemCode } from "./postcode-systems.ts"
21
+ import { CODE_POSTAL_PATTERN } from "#fr/code-postal"
22
+ import { UK_POSTCODE_PATTERN } from "#gb/postcode"
23
+ import type { SystemCode } from "#postcode-systems"
24
24
 
25
25
  export interface AddressSystemConventions {
26
26
  /**
@@ -48,11 +48,11 @@ export const ADDRESS_SYSTEM_CONVENTIONS: Partial<Record<SystemCode, AddressSyste
48
48
  * street-suffix class; Pub-28's suffix decomposition has no French counterpart).
49
49
  *
50
50
  * Provenance / why this is NOT a blanket prefix+suffix forbid (#719, 2026-06-18): an earlier model mis-tagged the
51
- * leading "Rue" as a US-style `street_suffix` (RUE is a Pub-28 suffix variant) — the 2026-06-10 v1.1.0 gate — so #511
52
- * forbade BOTH affix tags to stop that leakage. That forbid was correct for THAT model but became a live production
53
- * bug for the current one: the shipped model (v1.5.0) emits the FR `street_prefix` correctly, but the conventions
54
- * mask was a hard −1e9 on every B-/I-street_prefix emission, so the detected-FR parse could never KEEP a prefix — it
55
- * destroyed `street_prefix` wholesale (measured on data/eval/external/ fr-street-prefix-real.jsonl at
51
+ * leading "Rue" as a US-style `street_suffix` (RUE is a Pub-28 suffix variant) — the 2026-06-10 v1.1.0 promotion eval
52
+ * — so #511 forbade BOTH affix tags to stop that leakage. That forbid was correct for THAT model but became a live
53
+ * production bug for the current one: the shipped model (v1.5.0) emits the FR `street_prefix` correctly, but the
54
+ * conventions mask was a hard −1e9 on every B-/I-street_prefix emission, so the detected-FR parse could never KEEP a
55
+ * prefix — it destroyed `street_prefix` wholesale (measured on data/eval/external/ fr-street-prefix-real.jsonl at
56
56
  * conventions=auto: F1 0.0 with the forbid on → 80.0 with it off; the larger real-FR eval reported the same collapse,
57
57
  * ~96 → ~0.6). We keep ONLY `street_suffix` forbidden: the current model with the forbid OFF shows zero FR
58
58
  * street_suffix leakage (fp=0 on that same slice) and FR has no trailing street suffix, so the constraint costs
@@ -72,7 +72,7 @@ export const ADDRESS_SYSTEM_CONVENTIONS: Partial<Record<SystemCode, AddressSyste
72
72
  * the raw text — exactly the shape-INVALID class `postcodePattern` exists to flag.
73
73
  *
74
74
  * Provenance (#1275, 2026-07-24): on the GB golden board's 106 postcode rows under the en-gb bundle, the clip class
75
- * (parsed postcode = proper suffix of the truth) was 44/106 with this row absent — the repair gate never opened
75
+ * (parsed postcode = proper suffix of the truth) was 44/106 with this row absent — the repair check never opened
76
76
  * because `conventionsForSystem("gb")` returned null. With the repair reachable, exact 26 → 83 and the clip class
77
77
  * goes to zero. No `forbiddenTags`: no measured GB-ungrammatical tag class exists (the FR street_suffix forbid's
78
78
  * lesson — a forbid needs measured zero-cost receipts, and GB street grammar shares the trailing-suffix family with
@@ -9,7 +9,7 @@
9
9
  * territories, and the 4-digit postcode.
10
10
  */
11
11
 
12
- export * from "./delivery-service.ts"
13
- export * from "./level-designator.ts"
14
- export * from "./postcode.ts"
15
- export * from "./state.ts"
12
+ export * from "#au/delivery-service"
13
+ export * from "#au/level-designator"
14
+ export * from "#au/postcode"
15
+ export * from "#au/state"
@@ -7,6 +7,6 @@
7
7
  * postcodes, and the provinces and territories.
8
8
  */
9
9
 
10
- export * from "./postal-code.ts"
11
- export * from "./province.ts"
12
- export * from "./street-type.ts"
10
+ export * from "#ca/postal-code"
11
+ export * from "#ca/province"
12
+ export * from "#ca/street-type"
@@ -27,7 +27,7 @@
27
27
 
28
28
  import type { Tagged } from "type-fest"
29
29
 
30
- import type { CanadianProvinceCode } from "./province.ts"
30
+ import type { CanadianProvinceCode } from "#ca/province"
31
31
 
32
32
  /**
33
33
  * Characters in a Canadian postcode once spaces are stripped: `A1A1A1`.
@@ -16,7 +16,7 @@
16
16
  * regions, the Canadian code IS a surface form, not just a resolver key.
17
17
  */
18
18
 
19
- import { foldName } from "../normalize.ts"
19
+ import { foldName } from "#normalize"
20
20
 
21
21
  /**
22
22
  * Per-province record: ISO 3166-2:CA code, English name, and the co-official French name.
@@ -23,6 +23,8 @@
23
23
  * Ouest = West is recognized.
24
24
  */
25
25
 
26
+ import { foldToken } from "#normalize"
27
+
26
28
  /**
27
29
  * English Canadian street-type words (Canada Post's recognized set, lowercase). Appear as the TRAILING token of an
28
30
  * English street name (`Maple Avenue`, `Sunset Crescent`).
@@ -53,7 +55,7 @@ export const CA_STREET_TYPES_EN: ReadonlySet<string> = new Set([
53
55
  "green",
54
56
  "bay",
55
57
  "cove",
56
- "gate",
58
+ "check",
57
59
  "point",
58
60
  "ridge",
59
61
  "view",
@@ -86,14 +88,10 @@ export const CA_STREET_TYPES_FR: ReadonlySet<string> = new Set([
86
88
  ])
87
89
 
88
90
  /**
89
- * Strip diacritics + lowercase so `Côte`/`cote`, `Allée`/`allee`, `Crescent`/`crescent` key alike.
91
+ * {@link foldToken}, letters only, so `Côte`/`cote`, `Allée`/`allee`, `Crescent`/`crescent` key alike.
90
92
  */
91
- function foldToken(s: string): string {
92
- return s
93
- .toLowerCase()
94
- .normalize("NFD")
95
- .replaceAll(/[\u0300-\u036F]/g, "")
96
- .replaceAll(/[^a-z]/g, "")
93
+ function foldLetters(s: string): string {
94
+ return foldToken(s).replaceAll(/[^a-z]/g, "")
97
95
  }
98
96
 
99
97
  /**
@@ -103,11 +101,11 @@ const STREET_WORD_SET: ReadonlySet<string> = (() => {
103
101
  const out = new Set<string>()
104
102
 
105
103
  for (const w of CA_STREET_TYPES_EN) {
106
- out.add(foldToken(w))
104
+ out.add(foldLetters(w))
107
105
  }
108
106
 
109
107
  for (const w of CA_STREET_TYPES_FR) {
110
- out.add(foldToken(w))
108
+ out.add(foldLetters(w))
111
109
  }
112
110
 
113
111
  return out
@@ -120,7 +118,7 @@ const STREET_WORD_SET: ReadonlySet<string> = (() => {
120
118
  */
121
119
  export function isCanadianStreetWord(token: unknown): boolean {
122
120
  if (typeof token !== "string") return false
123
- const t = foldToken(token)
121
+ const t = foldLetters(token)
124
122
 
125
123
  return t.length > 0 && STREET_WORD_SET.has(t)
126
124
  }
@@ -154,7 +152,7 @@ export const CA_DIRECTIONALS: Record<string, "N" | "S" | "E" | "W"> = {
154
152
  */
155
153
  export function isCanadianDirectional(token: unknown): boolean {
156
154
  if (typeof token !== "string") return false
157
- const t = foldToken(token)
155
+ const t = foldLetters(token)
158
156
 
159
157
  if (!t.length) return false
160
158
 
@@ -4,7 +4,7 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  */
6
6
 
7
- import type { CountryName } from "./names.ts"
7
+ import type { CountryName } from "#country/names"
8
8
 
9
9
  /**
10
10
  * @category Country
@@ -3,18 +3,19 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Country recognition for the `country` parity lever. The ISO 3166-1 base (names + alpha-2/alpha-3)
6
+ * Country recognition for the `country` parity change. The ISO 3166-1 base (names + alpha-2/alpha-3)
7
7
  * is salvaged from isp-nexus `spatial/countries` ({@link ./names.ts}, {@link ./codes.ts}); this
8
8
  * adds the layer ISO doesn't carry — the **surface forms** addresses actually use (endonyms +
9
9
  * common abbreviations: "USA"/"United States"/"U.S."; "Deutschland"/"Germany"; "España"/"Spain")
10
- * — plus a {@link matchCountry} resolver the corpus country-shard + parsing reuse. Same shape as
10
+ * — plus a {@link matchCountry} resolver the corpus country-extract + parsing reuse. Same shape as
11
11
  * the other codex matchers (street-suffix, directional, po-box).
12
12
  */
13
13
 
14
- import { Alpha3ToCountryRecord, CountryISO2 } from "./codes.ts"
15
- import type { CountryName } from "./names.ts"
14
+ import { Alpha3ToCountryRecord, CountryISO2 } from "#country/codes"
15
+ import type { CountryName } from "#country/names"
16
+ import { foldName } from "#normalize"
16
17
 
17
- export { Alpha3ToCountryRecord, CountryISO2, type CountryISO3 } from "./codes.ts"
18
+ export { Alpha3ToCountryRecord, CountryISO2, type CountryISO3 } from "#country/codes"
18
19
 
19
20
  /**
20
21
  * Common real-address surface forms per ISO 3166-1 alpha-2, **canonical English name first** then endonym +
@@ -51,9 +52,11 @@ export const ISO2_TO_NAME: ReadonlyMap<string, CountryName> = new Map(
51
52
  )
52
53
 
53
54
  /**
54
- * Any recognized country surface form / canonical name / alpha-2 / alpha-3 → alpha-2 code. Built once at module load,
55
- * lowercase-keyed. Canonical names + codes from the ISO base, plus the curated surface forms (surface forms win on
56
- * collisionthey're the address-facing spellings).
55
+ * Any recognized country surface form / canonical name / alpha-2 / alpha-3 → alpha-2 code. Built once at module load.
56
+ * Each surface contributes its lowercased key AND its {@link foldName}-folded key when the fold leaves anything a
57
+ * non-Latin surface like `日本` survives only as its lowercased self so accented and punctuated variants resolve.
58
+ * Canonical names + codes from the ISO base, plus the curated surface forms (surface forms win on collision — they're
59
+ * the address-facing spellings).
57
60
  */
58
61
  export const COUNTRY_LOOKUP: ReadonlyMap<string, string> = (() => {
59
62
  const out = new Map<string, string>()
@@ -64,6 +67,12 @@ export const COUNTRY_LOOKUP: ReadonlyMap<string, string> = (() => {
64
67
  if (key && !out.has(key)) {
65
68
  out.set(key, iso2)
66
69
  }
70
+
71
+ const folded = foldName(k)
72
+
73
+ if (folded && !out.has(folded)) {
74
+ out.set(folded, iso2)
75
+ }
67
76
  }
68
77
 
69
78
  // ISO base: canonical name + alpha-2 + alpha-3.
@@ -88,12 +97,32 @@ export const COUNTRY_LOOKUP: ReadonlyMap<string, string> = (() => {
88
97
  for (const [iso2, forms] of Object.entries(COUNTRY_SURFACE_FORMS)) {
89
98
  for (const f of forms) {
90
99
  out.set(f.trim().toLowerCase(), iso2)
100
+
101
+ const folded = foldName(f)
102
+
103
+ if (folded) {
104
+ out.set(folded, iso2)
105
+ }
91
106
  }
92
107
  }
93
108
 
94
109
  return out
95
110
  })()
96
111
 
112
+ /**
113
+ * Probe the lookup the way it is keyed: the lowercased surface first, then the {@link foldName} fold when it is
114
+ * non-empty.
115
+ */
116
+ function probeCountry(token: string): string | undefined {
117
+ const direct = COUNTRY_LOOKUP.get(token.trim().toLowerCase())
118
+
119
+ if (direct) return direct
120
+
121
+ const folded = foldName(token)
122
+
123
+ return folded ? COUNTRY_LOOKUP.get(folded) : undefined
124
+ }
125
+
97
126
  /**
98
127
  * Result of a country match: the alpha-2 code, the canonical English name, and the matched surface.
99
128
  */
@@ -104,13 +133,13 @@ export interface CountryMatch {
104
133
  }
105
134
 
106
135
  /**
107
- * Resolve a token (surface form, canonical name, alpha-2, or alpha-3) to a country. Case-insensitive. Returns null if
108
- * unrecognized. Multi-word names ("United States", "Great Britain") must be passed as the whole phrase — the caller
109
- * decides the span; this matches it.
136
+ * Resolve a token (surface form, canonical name, alpha-2, or alpha-3) to a country. Case-, accent-, and
137
+ * punctuation-insensitive. Returns null if unrecognized. Multi-word names ("United States", "Great Britain") must be
138
+ * passed as the whole phrase — the caller decides the span; this matches it.
110
139
  */
111
140
  export function matchCountry(token: string | null | undefined): CountryMatch | null {
112
141
  if (!token || typeof token !== "string") return null
113
- const iso2 = COUNTRY_LOOKUP.get(token.trim().toLowerCase())
142
+ const iso2 = probeCountry(token)
114
143
 
115
144
  if (!iso2) return null
116
145
 
@@ -121,11 +150,11 @@ export function matchCountry(token: string | null | undefined): CountryMatch | n
121
150
  * Case-insensitive check: is the token any recognized country form?
122
151
  */
123
152
  export function isCountryToken(token: unknown): boolean {
124
- return typeof token === "string" && COUNTRY_LOOKUP.has(token.trim().toLowerCase())
153
+ return typeof token === "string" && probeCountry(token) !== undefined
125
154
  }
126
155
 
127
156
  /**
128
- * The preferred render forms for an alpha-2 (canonical first), for synth shards. Empty if none curated.
157
+ * The preferred render forms for an alpha-2 (canonical first), for synth extracts. Empty if none curated.
129
158
  */
130
159
  export function countrySurfaceForms(iso2: string): readonly string[] {
131
160
  return (COUNTRY_SURFACE_FORMS as Record<string, readonly string[]>)[iso2.toUpperCase()] ?? []
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * ISO 3166-1 country reference + surface-form recognition (the `country` parity change).
7
+ */
8
+
9
+ export * from "#country/codes"
10
+ export * from "#country/country"
11
+ export * from "#country/display-names"
12
+ export * from "#country/names"
13
+ export * from "#country/official-languages"
14
+ export * from "#country/population"
15
+ export * from "#country/reference-data"
16
+ export * from "#country/reference"
17
+ export * from "#country/subdivision"
@@ -6,12 +6,12 @@
6
6
  * Country-reference annotators. The flag emoji is a pure function of the ISO 3166-1 alpha-2 code
7
7
  * (two Regional Indicator Symbols); calling code + currency come from {@link COUNTRY_REFERENCE}, a
8
8
  * table generated from mledoze/countries (provenance-tracked, not hand-typed — the
9
- * no-load-bearing-trivia rule).
9
+ * no-irrelevant-trivia rule).
10
10
  */
11
11
 
12
12
  import type { AnnotationSet, Annotator } from "@mailwoman/annotations"
13
13
 
14
- import { COUNTRY_REFERENCE } from "./reference-data.ts"
14
+ import { COUNTRY_REFERENCE } from "#country/reference-data"
15
15
 
16
16
  const REGIONAL_INDICATOR_BASE = 0x1_f1_e6
17
17
  const A_UPPER = "A".charCodeAt(0)
@@ -26,10 +26,10 @@
26
26
  * re-keys those two existing tables into one subdivision→country view.
27
27
  */
28
28
 
29
- import { AU_STATE_ABBREVIATIONS } from "../au/state.ts"
30
- import { CA_PROVINCES } from "../ca/province.ts"
31
- import { foldName } from "../normalize.ts"
32
- import { US_STATE_BY_ABBREVIATION } from "../us/state.ts"
29
+ import { AU_STATE_ABBREVIATIONS } from "#au/state"
30
+ import { CA_PROVINCES } from "#ca/province"
31
+ import { foldName } from "#normalize"
32
+ import { US_STATE_BY_ABBREVIATION } from "#us/state"
33
33
 
34
34
  /**
35
35
  * A resolved subdivision: its ISO 3166-2 code (sans country prefix), canonical English name, and ISO 3166-1 country.
@@ -7,6 +7,6 @@
7
7
  * (Postleitzahl), and the federal states (Bundesländer).
8
8
  */
9
9
 
10
- export * from "./bundesland.ts"
11
- export * from "./postleitzahl.ts"
12
- export * from "./street-type.ts"
10
+ export * from "#de/bundesland"
11
+ export * from "#de/postleitzahl"
12
+ export * from "#de/street-type"
@@ -32,20 +32,38 @@ export type CodigoPostal = Tagged<string, "CodigoPostal">
32
32
  export const CODIGO_POSTAL_PATTERN = /^\d{5}$/
33
33
 
34
34
  /**
35
- * Narrow a string to a {@link CodigoPostal}, or `null` when it is not one.
35
+ * Normalize a código-postal surface form to the bare five digits: trim surrounding whitespace (`" 28001 "` →
36
+ * `"28001"`). Returns null when the result is not a five-digit code.
36
37
  */
37
- export function parseCodigoPostal(input: string): CodigoPostal | null {
38
- const s = input.trim()
38
+ export function normalizeCodigoPostal(raw: unknown): CodigoPostal | null {
39
+ if (typeof raw !== "string") return null
40
+ const s = raw.trim()
39
41
 
40
42
  return CODIGO_POSTAL_PATTERN.test(s) ? (s as CodigoPostal) : null
41
43
  }
42
44
 
45
+ /**
46
+ * Type-predicate for a (normalized) Spanish código postal.
47
+ */
48
+ export function isCodigoPostal(input: unknown): input is CodigoPostal {
49
+ return typeof input === "string" && CODIGO_POSTAL_PATTERN.test(input)
50
+ }
51
+
52
+ /**
53
+ * Narrow a string to a {@link CodigoPostal}, or `null` when it is not one.
54
+ *
55
+ * @deprecated Use {@link normalizeCodigoPostal}.
56
+ */
57
+ export function parseCodigoPostal(input: string): CodigoPostal | null {
58
+ return normalizeCodigoPostal(input)
59
+ }
60
+
43
61
  /**
44
62
  * The two-digit province prefix (`"28001"` → `"28"`), or `null` for a non-postcode. Callers map it through their own
45
63
  * province table; this module deliberately does not ship one, since the campaign that needed it only needs the shape.
46
64
  */
47
65
  export function codigoPostalProvincePrefix(input: string): string | null {
48
- const code = parseCodigoPostal(input)
66
+ const code = normalizeCodigoPostal(input)
49
67
 
50
68
  return code ? code.slice(0, 2) : null
51
69
  }
@@ -6,4 +6,4 @@
6
6
  * Spanish (ES) address-system reference data.
7
7
  */
8
8
 
9
- export * from "./codigo-postal.ts"
9
+ export * from "#es/codigo-postal"
@@ -9,7 +9,7 @@
9
9
  * optionally followed by a 1–2 digit office number. The component is the `CEDEX [NN]` phrase
10
10
  * itself (the schema's `cedex` tag); the preceding postcode/locality keep their own tags.
11
11
  *
12
- * This slice closes the gap PR #516 documented: the shard builder sourced the shape from SCHEMA.mdx
12
+ * This slice closes the gap PR #516 documented: the extract builder sourced the shape from SCHEMA.mdx
13
13
  * prose because codex had no cedex home. Now it does — the builder and any future consumer import
14
14
  * from here (the provenance-first discipline: one provenanced source).
15
15
  */
@@ -26,8 +26,8 @@
26
26
 
27
27
  import type { Tagged } from "type-fest"
28
28
 
29
- import { departementInfo, type DepartementCode, type DepartementInfo } from "./departement.ts"
30
- import { FR_REGIONS, type FrenchRegionInfo } from "./region.ts"
29
+ import { departementInfo, type DepartementCode, type DepartementInfo } from "#fr/departement"
30
+ import { FR_REGIONS, type FrenchRegionInfo } from "#fr/region"
31
31
 
32
32
  /**
33
33
  * A French postcode: five digits (`75008`). Same shape as a US ZIP or a German PLZ — the shape alone does not
@@ -11,7 +11,7 @@
11
11
  * département. This table is therefore the hinge between `code-postal.ts` and `region.ts`.
12
12
  */
13
13
 
14
- import type { FrenchRegionCode } from "./region.ts"
14
+ import type { FrenchRegionCode } from "#fr/region"
15
15
 
16
16
  /**
17
17
  * Per-département record: code (2-digit, or `2A`/`2B`, or 3-digit DOM) + name + its région.
@@ -146,7 +146,10 @@ export type DepartementCode = keyof typeof FR_DEPARTEMENTS
146
146
  /**
147
147
  * Look up a département by code (case-insensitive for the Corsica `2A`/`2B` letters); null if unknown.
148
148
  */
149
- export function departementInfo(code: string | null | undefined): DepartementInfo | null {
149
+ // `unknown`, not `string | null | undefined`: this function's whole job is to answer null for anything that is not a
150
+ // departement code, and its callers hand it values off a CSV row or a JSON body. A narrower parameter did not make
151
+ // those callers safer — it made the test that proves the guard work assert its way past the signature.
152
+ export function departementInfo(code: unknown): DepartementInfo | null {
150
153
  if (!code || typeof code !== "string") return null
151
154
  const key = code.trim().toUpperCase()
152
155
 
@@ -7,8 +7,8 @@
7
7
  * postal), and the admin hierarchy of départements and régions.
8
8
  */
9
9
 
10
- export * from "./cedex.ts"
11
- export * from "./code-postal.ts"
12
- export * from "./departement.ts"
13
- export * from "./region.ts"
14
- export * from "./voie.ts"
10
+ export * from "#fr/cedex"
11
+ export * from "#fr/code-postal"
12
+ export * from "#fr/departement"
13
+ export * from "#fr/region"
14
+ export * from "#fr/voie"
@@ -13,7 +13,7 @@
13
13
  * `code-postal.ts`).
14
14
  */
15
15
 
16
- import { foldName } from "../normalize.ts"
16
+ import { foldName } from "#normalize"
17
17
 
18
18
  /**
19
19
  * Per-region record: ISO 3166-2:FR code (sans `FR-` prefix) + French name.
@@ -17,6 +17,8 @@
17
17
  * {@link isFrenchStreetWord} matches a whole token rather than a suffix.
18
18
  */
19
19
 
20
+ import { foldToken } from "#normalize"
21
+
20
22
  /**
21
23
  * Canonical French voie type → common written abbreviations. The leading word of a French street name. The first entry
22
24
  * of each list is the most common abbreviation where one exists.
@@ -65,19 +67,13 @@ export type FrenchVoieType = keyof typeof FR_VOIE_TYPES
65
67
  * matching. `Allée`/`allee`/`all` all resolve here.
66
68
  */
67
69
  const VOIE_TOKEN_SET: ReadonlySet<string> = (() => {
68
- const fold = (s: string): string =>
69
- s
70
- .toLowerCase()
71
- .normalize("NFD")
72
- .replaceAll(/[\u0300-\u036F]/g, "")
73
-
74
70
  const out = new Set<string>()
75
71
 
76
72
  for (const canonical of Object.keys(FR_VOIE_TYPES) as FrenchVoieType[]) {
77
- out.add(fold(canonical))
73
+ out.add(foldToken(canonical))
78
74
 
79
75
  for (const abbr of FR_VOIE_TYPES[canonical]) {
80
- out.add(fold(abbr))
76
+ out.add(foldToken(abbr))
81
77
  }
82
78
  }
83
79
 
@@ -92,11 +88,7 @@ const VOIE_TOKEN_SET: ReadonlySet<string> = (() => {
92
88
  export function isFrenchStreetWord(token: unknown): boolean {
93
89
  if (typeof token !== "string") return false
94
90
 
95
- const t = token
96
- .toLowerCase()
97
- .normalize("NFD")
98
- .replaceAll(/[\u0300-\u036F]/g, "")
99
- .replaceAll(/[^a-z-]/g, "")
91
+ const t = foldToken(token).replaceAll(/[^a-z-]/g, "")
100
92
 
101
93
  return t.length > 0 && VOIE_TOKEN_SET.has(t)
102
94
  }
@@ -14,6 +14,8 @@
14
14
  * postcode is the thing that actually carries the geography.
15
15
  */
16
16
 
17
+ import { foldName } from "#normalize"
18
+
17
19
  /**
18
20
  * Per-country record: ISO 3166-2:GB code (sans `GB-` prefix) + English name.
19
21
  */
@@ -52,16 +54,6 @@ export function isUkCountryCode(input: unknown): input is UkCountryCode {
52
54
  return typeof input === "string" && COUNTRY_CODE_SET.has(input.toUpperCase())
53
55
  }
54
56
 
55
- /**
56
- * Lowercase + collapse non-alphanumerics so `Northern Ireland`, `northern-ireland` key alike.
57
- */
58
- function foldName(s: string): string {
59
- return s
60
- .toLowerCase()
61
- .replaceAll(/[^a-z0-9]+/g, " ")
62
- .trim()
63
- }
64
-
65
57
  /**
66
58
  * Folded country name / code → ISO 3166-2:GB code, so a surface form maps regardless of casing.
67
59
  */
@@ -8,7 +8,7 @@
8
8
  * vocabulary.
9
9
  */
10
10
 
11
- export * from "./country.ts"
12
- export * from "./postcode-area.ts"
13
- export * from "./postcode.ts"
14
- export * from "./street-type.ts"
11
+ export * from "#gb/country"
12
+ export * from "#gb/postcode-area"
13
+ export * from "#gb/postcode"
14
+ export * from "#gb/street-type"
@@ -22,7 +22,7 @@
22
22
  * a UK postcode is not a county.
23
23
  */
24
24
 
25
- import type { UkCountryCode } from "./country.ts"
25
+ import type { UkCountryCode } from "#gb/country"
26
26
 
27
27
  /**
28
28
  * Northern Ireland is a single postcode area: BT (Belfast).
@@ -51,7 +51,7 @@ export const GB_STREET_TYPES = [
51
51
  "vale",
52
52
  "wharf",
53
53
  "yard",
54
- "gate",
54
+ "check",
55
55
  "croft",
56
56
  "dene",
57
57
  "end",
@@ -25,25 +25,26 @@ export {
25
25
  ADDRESS_SYSTEM_CONVENTIONS,
26
26
  conventionsForSystem,
27
27
  type AddressSystemConventions,
28
- } from "./address-system-conventions.ts"
28
+ } from "#address-system-conventions"
29
29
 
30
- export * from "./normalize.ts"
31
- export * as au from "./au/index.ts"
32
- export * as ca from "./ca/index.ts"
33
- export * as de from "./de/index.ts"
34
- export * as fr from "./fr/index.ts"
35
- export * as gb from "./gb/index.ts"
36
- export * as jp from "./jp/index.ts"
37
- export * as levels from "./level-semantics.ts"
38
- export * as nz from "./nz/index.ts"
30
+ export * from "#normalize"
31
+ export * as au from "#au/index"
32
+ export * as ca from "#ca/index"
33
+ export * as de from "#de/index"
34
+ export * as fr from "#fr/index"
35
+ export * as gb from "#gb/index"
36
+ export * as jp from "#jp/index"
37
+ export * as levels from "#level-semantics"
38
+ export * as nz from "#nz/index"
39
39
 
40
40
  export {
41
41
  AREA_POSTCODE_FINER_THAN_LOCALITY,
42
42
  areaPostcodeLeadsLocality,
43
43
  candidateSystemsForPostcode,
44
44
  isUnitGradePostcodeHit,
45
+ SYSTEM_CODES,
45
46
  type SystemCode,
46
47
  UNIT_GRADE_POSTCODE,
47
- } from "./postcode-systems.ts"
48
+ } from "#postcode-systems"
48
49
 
49
- export * as us from "./us/index.ts"
50
+ export * as us from "#us/index"