@mailwoman/codex 9.1.0 → 9.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 (287) hide show
  1. package/README.md +51 -8
  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 +4 -4
  5. package/{ca → lib/ca}/postal-code.ts +12 -12
  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/display-names.ts +150 -0
  11. package/lib/country/index.ts +17 -0
  12. package/lib/country/population.ts +265 -0
  13. package/{country → lib/country}/reference.ts +2 -2
  14. package/{country → lib/country}/subdivision.ts +62 -3
  15. package/{de → lib/de}/index.ts +4 -4
  16. package/{de → lib/de}/postleitzahl.ts +3 -3
  17. package/{es → lib/es}/codigo-postal.ts +24 -6
  18. package/{es → lib/es}/index.ts +1 -1
  19. package/{fr → lib/fr}/cedex.ts +1 -1
  20. package/{fr → lib/fr}/code-postal.ts +8 -8
  21. package/{fr → lib/fr}/departement.ts +5 -2
  22. package/{fr → lib/fr}/index.ts +6 -6
  23. package/{fr → lib/fr}/region.ts +1 -1
  24. package/{fr → lib/fr}/voie.ts +5 -13
  25. package/{gb → lib/gb}/country.ts +2 -10
  26. package/{gb → lib/gb}/index.ts +4 -4
  27. package/{gb → lib/gb}/postcode-area.ts +1 -1
  28. package/{gb → lib/gb}/street-type.ts +1 -1
  29. package/{index.ts → lib/index.ts} +22 -12
  30. package/{it → lib/it}/cap.ts +23 -5
  31. package/{it → lib/it}/index.ts +1 -1
  32. package/{jp → lib/jp}/index.ts +4 -4
  33. package/{jp → lib/jp}/postal-code.ts +11 -11
  34. package/{jp → lib/jp}/prefecture.ts +7 -5
  35. package/{normalize.ts → lib/normalize.ts} +15 -1
  36. package/{nz → lib/nz}/delivery-service.ts +1 -1
  37. package/{nz → lib/nz}/index.ts +2 -2
  38. package/lib/postcode-systems.ts +198 -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/lib/tools/generate-country-population.ts +142 -0
  42. package/{tools → lib/tools}/generate-country-reference.ts +8 -7
  43. package/{tools → lib/tools}/generate-official-languages.ts +15 -13
  44. package/{tools → lib/tools}/index.ts +4 -4
  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 +2 -2
  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 +4 -4
  68. package/out/ca/index.d.ts.map +1 -1
  69. package/out/ca/index.js +4 -4
  70. package/out/ca/index.js.map +1 -1
  71. package/out/ca/postal-code.d.ts +11 -11
  72. package/out/ca/postal-code.d.ts.map +1 -1
  73. package/out/ca/postal-code.js +9 -9
  74. package/out/ca/postal-code.js.map +1 -1
  75. package/out/ca/province.d.ts.map +1 -1
  76. package/out/ca/province.js +1 -1
  77. package/out/ca/province.js.map +1 -1
  78. package/out/ca/street-type.d.ts.map +1 -1
  79. package/out/ca/street-type.js +9 -12
  80. package/out/ca/street-type.js.map +1 -1
  81. package/out/country/codes.d.ts.map +1 -1
  82. package/out/country/codes.js.map +1 -1
  83. package/out/country/country.d.ts +13 -11
  84. package/out/country/country.d.ts.map +1 -1
  85. package/out/country/country.js +35 -13
  86. package/out/country/country.js.map +1 -1
  87. package/out/country/display-names.d.ts +67 -0
  88. package/out/country/display-names.d.ts.map +1 -0
  89. package/out/country/display-names.js +118 -0
  90. package/out/country/display-names.js.map +1 -0
  91. package/out/country/index.d.ts +10 -8
  92. package/out/country/index.d.ts.map +1 -1
  93. package/out/country/index.js +10 -8
  94. package/out/country/index.js.map +1 -1
  95. package/out/country/names.d.ts.map +1 -1
  96. package/out/country/names.js.map +1 -1
  97. package/out/country/official-languages.d.ts.map +1 -1
  98. package/out/country/official-languages.js.map +1 -1
  99. package/out/country/population.d.ts +16 -0
  100. package/out/country/population.d.ts.map +1 -0
  101. package/out/country/population.js +265 -0
  102. package/out/country/population.js.map +1 -0
  103. package/out/country/reference-data.d.ts.map +1 -1
  104. package/out/country/reference-data.js.map +1 -1
  105. package/out/country/reference.d.ts +1 -1
  106. package/out/country/reference.d.ts.map +1 -1
  107. package/out/country/reference.js +2 -2
  108. package/out/country/reference.js.map +1 -1
  109. package/out/country/subdivision.d.ts +7 -0
  110. package/out/country/subdivision.d.ts.map +1 -1
  111. package/out/country/subdivision.js +52 -3
  112. package/out/country/subdivision.js.map +1 -1
  113. package/out/de/bundesland.d.ts.map +1 -1
  114. package/out/de/bundesland.js.map +1 -1
  115. package/out/de/index.d.ts +4 -4
  116. package/out/de/index.d.ts.map +1 -1
  117. package/out/de/index.js +4 -4
  118. package/out/de/index.js.map +1 -1
  119. package/out/de/postleitzahl.d.ts +3 -3
  120. package/out/de/postleitzahl.d.ts.map +1 -1
  121. package/out/de/postleitzahl.js +2 -2
  122. package/out/de/postleitzahl.js.map +1 -1
  123. package/out/de/street-type.d.ts.map +1 -1
  124. package/out/de/street-type.js.map +1 -1
  125. package/out/es/codigo-postal.d.ts +13 -2
  126. package/out/es/codigo-postal.d.ts.map +1 -1
  127. package/out/es/codigo-postal.js +21 -4
  128. package/out/es/codigo-postal.js.map +1 -1
  129. package/out/es/index.d.ts +1 -1
  130. package/out/es/index.d.ts.map +1 -1
  131. package/out/es/index.js +1 -1
  132. package/out/es/index.js.map +1 -1
  133. package/out/fr/cedex.d.ts +1 -1
  134. package/out/fr/cedex.d.ts.map +1 -1
  135. package/out/fr/cedex.js +1 -1
  136. package/out/fr/cedex.js.map +1 -1
  137. package/out/fr/code-postal.d.ts +7 -7
  138. package/out/fr/code-postal.d.ts.map +1 -1
  139. package/out/fr/code-postal.js +8 -8
  140. package/out/fr/code-postal.js.map +1 -1
  141. package/out/fr/departement.d.ts +2 -2
  142. package/out/fr/departement.d.ts.map +1 -1
  143. package/out/fr/departement.js +3 -0
  144. package/out/fr/departement.js.map +1 -1
  145. package/out/fr/index.d.ts +6 -6
  146. package/out/fr/index.d.ts.map +1 -1
  147. package/out/fr/index.js +6 -6
  148. package/out/fr/index.js.map +1 -1
  149. package/out/fr/region.d.ts.map +1 -1
  150. package/out/fr/region.js +1 -1
  151. package/out/fr/region.js.map +1 -1
  152. package/out/fr/voie.d.ts.map +1 -1
  153. package/out/fr/voie.js +4 -11
  154. package/out/fr/voie.js.map +1 -1
  155. package/out/gb/country.d.ts.map +1 -1
  156. package/out/gb/country.js +1 -9
  157. package/out/gb/country.js.map +1 -1
  158. package/out/gb/index.d.ts +4 -4
  159. package/out/gb/index.d.ts.map +1 -1
  160. package/out/gb/index.js +4 -4
  161. package/out/gb/index.js.map +1 -1
  162. package/out/gb/postcode-area.d.ts +1 -1
  163. package/out/gb/postcode-area.d.ts.map +1 -1
  164. package/out/gb/postcode-area.js.map +1 -1
  165. package/out/gb/postcode.d.ts.map +1 -1
  166. package/out/gb/postcode.js.map +1 -1
  167. package/out/gb/street-type.d.ts +1 -1
  168. package/out/gb/street-type.d.ts.map +1 -1
  169. package/out/gb/street-type.js +1 -1
  170. package/out/gb/street-type.js.map +1 -1
  171. package/out/index.d.ts +12 -12
  172. package/out/index.d.ts.map +1 -1
  173. package/out/index.js +12 -12
  174. package/out/index.js.map +1 -1
  175. package/out/it/cap.d.ts +13 -2
  176. package/out/it/cap.d.ts.map +1 -1
  177. package/out/it/cap.js +20 -3
  178. package/out/it/cap.js.map +1 -1
  179. package/out/it/index.d.ts +1 -1
  180. package/out/it/index.d.ts.map +1 -1
  181. package/out/it/index.js +1 -1
  182. package/out/it/index.js.map +1 -1
  183. package/out/jp/address-unit.d.ts.map +1 -1
  184. package/out/jp/address-unit.js.map +1 -1
  185. package/out/jp/index.d.ts +4 -4
  186. package/out/jp/index.d.ts.map +1 -1
  187. package/out/jp/index.js +4 -4
  188. package/out/jp/index.js.map +1 -1
  189. package/out/jp/postal-code.d.ts +11 -11
  190. package/out/jp/postal-code.d.ts.map +1 -1
  191. package/out/jp/postal-code.js +8 -8
  192. package/out/jp/postal-code.js.map +1 -1
  193. package/out/jp/prefecture.d.ts +4 -1
  194. package/out/jp/prefecture.d.ts.map +1 -1
  195. package/out/jp/prefecture.js +6 -5
  196. package/out/jp/prefecture.js.map +1 -1
  197. package/out/level-semantics.d.ts.map +1 -1
  198. package/out/level-semantics.js.map +1 -1
  199. package/out/normalize.d.ts +8 -0
  200. package/out/normalize.d.ts.map +1 -1
  201. package/out/normalize.js +14 -1
  202. package/out/normalize.js.map +1 -1
  203. package/out/nz/delivery-service.d.ts +1 -1
  204. package/out/nz/delivery-service.d.ts.map +1 -1
  205. package/out/nz/delivery-service.js +1 -1
  206. package/out/nz/delivery-service.js.map +1 -1
  207. package/out/nz/index.d.ts +2 -2
  208. package/out/nz/index.d.ts.map +1 -1
  209. package/out/nz/index.js +2 -2
  210. package/out/nz/index.js.map +1 -1
  211. package/out/nz/postcode.d.ts.map +1 -1
  212. package/out/nz/postcode.js.map +1 -1
  213. package/out/postcode-systems.d.ts +99 -4
  214. package/out/postcode-systems.d.ts.map +1 -1
  215. package/out/postcode-systems.js +132 -11
  216. package/out/postcode-systems.js.map +1 -1
  217. package/out/tools/build-country-surface-lexicon.d.ts +1 -1
  218. package/out/tools/build-country-surface-lexicon.d.ts.map +1 -1
  219. package/out/tools/build-country-surface-lexicon.js +10 -9
  220. package/out/tools/build-country-surface-lexicon.js.map +1 -1
  221. package/out/tools/export-country-surfaces.d.ts +1 -1
  222. package/out/tools/export-country-surfaces.d.ts.map +1 -1
  223. package/out/tools/export-country-surfaces.js +7 -7
  224. package/out/tools/export-country-surfaces.js.map +1 -1
  225. package/out/tools/generate-country-population.d.ts +42 -0
  226. package/out/tools/generate-country-population.d.ts.map +1 -0
  227. package/out/tools/generate-country-population.js +106 -0
  228. package/out/tools/generate-country-population.js.map +1 -0
  229. package/out/tools/generate-country-reference.d.ts.map +1 -1
  230. package/out/tools/generate-country-reference.js +8 -8
  231. package/out/tools/generate-country-reference.js.map +1 -1
  232. package/out/tools/generate-official-languages.d.ts.map +1 -1
  233. package/out/tools/generate-official-languages.js +14 -11
  234. package/out/tools/generate-official-languages.js.map +1 -1
  235. package/out/tools/index.d.ts +4 -4
  236. package/out/tools/index.d.ts.map +1 -1
  237. package/out/tools/index.js +4 -4
  238. package/out/tools/index.js.map +1 -1
  239. package/out/us/floor-designator.d.ts.map +1 -1
  240. package/out/us/floor-designator.js.map +1 -1
  241. package/out/us/index.d.ts +8 -8
  242. package/out/us/index.d.ts.map +1 -1
  243. package/out/us/index.js +8 -8
  244. package/out/us/index.js.map +1 -1
  245. package/out/us/military-address.d.ts.map +1 -1
  246. package/out/us/military-address.js.map +1 -1
  247. package/out/us/po-box.d.ts +1 -1
  248. package/out/us/po-box.d.ts.map +1 -1
  249. package/out/us/po-box.js +1 -1
  250. package/out/us/po-box.js.map +1 -1
  251. package/out/us/state.d.ts.map +1 -1
  252. package/out/us/state.js.map +1 -1
  253. package/out/us/street-directional.d.ts.map +1 -1
  254. package/out/us/street-directional.js +2 -2
  255. package/out/us/street-directional.js.map +1 -1
  256. package/out/us/street-suffix.d.ts +1 -1
  257. package/out/us/street-suffix.d.ts.map +1 -1
  258. package/out/us/street-suffix.js +1 -1
  259. package/out/us/street-suffix.js.map +1 -1
  260. package/out/us/unit-designator.d.ts +1 -1
  261. package/out/us/unit-designator.d.ts.map +1 -1
  262. package/out/us/unit-designator.js +1 -1
  263. package/out/us/unit-designator.js.map +1 -1
  264. package/out/us/zipcode.d.ts +2 -2
  265. package/out/us/zipcode.d.ts.map +1 -1
  266. package/out/us/zipcode.js +2 -2
  267. package/out/us/zipcode.js.map +1 -1
  268. package/package.json +129 -6
  269. package/country/index.ts +0 -15
  270. package/postcode-systems.ts +0 -70
  271. /package/{au → lib/au}/delivery-service.ts +0 -0
  272. /package/{au → lib/au}/level-designator.ts +0 -0
  273. /package/{au → lib/au}/postcode.ts +0 -0
  274. /package/{au → lib/au}/state.ts +0 -0
  275. /package/{country → lib/country}/names.ts +0 -0
  276. /package/{country → lib/country}/official-languages.ts +0 -0
  277. /package/{country → lib/country}/reference-data.ts +0 -0
  278. /package/{de → lib/de}/bundesland.ts +0 -0
  279. /package/{de → lib/de}/street-type.ts +0 -0
  280. /package/{gb → lib/gb}/postcode.ts +0 -0
  281. /package/{jp → lib/jp}/address-unit.ts +0 -0
  282. /package/{level-semantics.ts → lib/level-semantics.ts} +0 -0
  283. /package/{nz → lib/nz}/postcode.ts +0 -0
  284. /package/{us → lib/us}/floor-designator.ts +0 -0
  285. /package/{us → lib/us}/military-address.ts +0 -0
  286. /package/{us → lib/us}/state.ts +0 -0
  287. /package/{us → lib/us}/street-suffix.json +0 -0
@@ -10,24 +10,119 @@
10
10
  *
11
11
  * It is the shared source of truth for "which systems can this shape be" — consumed by the postcode
12
12
  * anchor (to narrow which systems' street vocabularies it checks) and, in time, by the runtime
13
- * pipeline's locale gate (so its format→locale scoring derives from the same patterns rather than
13
+ * pipeline's `@mailwoman/locale-hint` stage (so its format→locale scoring derives from the same patterns rather than
14
14
  * a second, divergent copy). The point is to unify the DATA, not to couple the modules: callers
15
15
  * depend on this pure function, never on each other.
16
16
  *
17
17
  * Note this is a SHAPE test, not a gazetteer-membership test. A bare `68161` matches the US,
18
- * German, AND French 5-digit shapes, so it returns `["us", "de", "fr"]` — the shape alone cannot
19
- * split the numeric-postcode systems. The anchor uses real gazetteer membership for the finer
18
+ * German, French, Spanish and Italian 5-digit shapes, so it returns `["us", "de", "fr", "es", "it"]` —
19
+ * the shape alone cannot split the numeric-postcode systems. The anchor uses real gazetteer membership for the finer
20
20
  * call; this function answers the coarser, model-free "which systems is this shape even eligible
21
21
  * for".
22
22
  */
23
23
  /**
24
24
  * A codex address-system code — the subpath under `@mailwoman/codex/<system>`.
25
25
  */
26
- export type SystemCode = "us" | "de" | "fr" | "ca" | "gb" | "jp" | "au" | "nz";
26
+ export type SystemCode = "us" | "de" | "fr" | "es" | "it" | "ca" | "gb" | "jp" | "au" | "nz";
27
+ /**
28
+ * Every address system with a postcode shape, in {@link SYSTEM_ACCEPTS} order. The one list a consumer that needs the
29
+ * universe of systems (rather than the systems a given code fits) should read, so it cannot drift from the table.
30
+ */
31
+ export declare const SYSTEM_CODES: readonly SystemCode[];
27
32
  /**
28
33
  * Every address system whose own postcode shape accepts `postcode`. Empty when no system recognizes the shape (e.g. a
29
34
  * bare `27`, or a 7-digit run). O(number of systems) — a handful of cheap regex tests, run only on the few
30
35
  * postcode-shaped spans an address contains.
31
36
  */
32
37
  export declare function candidateSystemsForPostcode(postcode: string): SystemCode[];
38
+ /**
39
+ * Postcode shapes whose code is UNIT-GRADE — a delivery-walk or street-block unit, categorically tighter than any
40
+ * locality centroid, so an EXACT hit on one may lead the admin ladder instead of following the locality-first epoch
41
+ * convention.
42
+ *
43
+ * The convention exists because most postal systems are AREA-class: an FR 5-digit zone is coarser than the commune it
44
+ * contains, so promoting it would trade a good answer for a worse one. Two systems are the other way round, and
45
+ * membership here is earned by MEASUREMENT of the code's granularity, never by "the code has letters in it":
46
+ *
47
+ * - **NL PC6** (`1012 LG`) — ~8 addresses per code; the CBS polygon centroid (#977, the original carve-out).
48
+ * - **GB unit** (`N7 0BT`) — ~15 addresses per code, 1,751,733 shipped from OS Code-Point Open. Measured 2026-08-10
49
+ * against the panel-v2 GB rooftop truth: unit centroid within 1 km on 15/15 rows, median 38 m, max 100 m, while the
50
+ * locality centroid the ladder returned instead was 5.1–14.6 km out.
51
+ * - **CA URBAN LDU** (`M1J 1A8`) — 843,739 six-character codes. Measured on 879 graded rows of the CA OSM-rooftop panel,
52
+ * through the production candidate backend, ladder arm against ladder arm:
53
+ *
54
+ * | 732 URBAN rows | p50 | p75 | p90 | ≤1 km | | -------------- | ---: | ---: | ---: | ---: | | locality-first | 2.51
55
+ * km | 5.42 km | 9.53 km | 26.4% | | postcode-first | **78 m** | **162 m** | **373 m** | **94.7%** |
56
+ *
57
+ * Closer on 90.4% of them. That is GB's tier on a sample fifty times larger than GB's.
58
+ *
59
+ * **CA RURAL is excluded, and the code says which.** Canada Post puts a `0` in the SECOND position of a rural forward
60
+ * sortation area, so `T0H 1M0` is rural and `M1J 1A8` is not — no lookup required. A rural LDU serves a delivery route
61
+ * rather than a block face, and it measures like one. On the same panel, the 114 rural rows:
62
+ *
63
+ * | 114 RURAL rows | p50 | p75 | p90 | ≤1 km |
64
+ * | -------------- | --------: | ----------: | ----------: | --------: |
65
+ * | locality-first | **929 m** | **2.02 km** | **5.73 km** | **53.5%** |
66
+ * | postcode-first | 2.08 km | 4.79 km | 8.11 km | 25.4% |
67
+ *
68
+ * Postcode-first is closer on only 26.3% of them, so the pattern below admits `[1-9]` in that position and nothing
69
+ * else. `509 Main Street South-West Falher AB T0H 1M0` is the worked case: 0.29 km from the locality centroid and 43.18
70
+ * km from its own postal code.
71
+ *
72
+ * The pooled CA number hides that entirely — 0.10 km p50 across both populations reads as a uniform win and is not one.
73
+ * A tier claim that averages two granularities is the thing this table exists to prevent.
74
+ *
75
+ * Lives in codex (per-address-system postal reference) so the Node result assembly (`mailwoman/geocode-core`) and the
76
+ * demo's pin ranking consume ONE tier definition — the 2026-08-11 staged-repoint e2e measured the two disagreeing.
77
+ */
78
+ export declare const UNIT_GRADE_POSTCODE: ReadonlyArray<RegExp>;
79
+ /**
80
+ * True when a resolved postcode is an EXACT hit on a unit-grade code — the #977 three-way guard, shared by the Node
81
+ * ladder and the demo pin ranking:
82
+ *
83
+ * 1. The PARSED span is a full unit shape ({@link UNIT_GRADE_POSTCODE}), not a stem the user typed;
84
+ * 2. The node resolved (a coordinate is present — checked by the caller); and
85
+ * 3. The resolver's own hit is the FULL code, not a coarsened prefix (a 4-digit NL stem or a GB outward district is
86
+ * AREA-class, and promoting it is the exact trade the epoch convention forbids).
87
+ */
88
+ export declare function isUnitGradePostcodeHit(parsed: string, resolverName: string | undefined): boolean;
89
+ /**
90
+ * Address systems whose AREA-grade postal code is still FINER than the locality containing it — the third granularity
91
+ * tier, between {@link UNIT_GRADE_POSTCODE} and the locality-first default.
92
+ *
93
+ * Whether a postal zone is coarser than its locality is a fact about a country's ADMINISTRATIVE geography, not about
94
+ * its postal system, and code length does not predict it: FR and DE are both 5-digit and land on opposite sides. France
95
+ * has ~35,000 communes and one code postal often spans several, so the commune is finer. A German Gemeinde can be
96
+ * enormous — Berlin is one WOF locality — so the PLZ is finer by a wide margin.
97
+ *
98
+ * Membership is earned by a full-panel measurement, the same bar {@link UNIT_GRADE_POSTCODE} sets for CA. Coordinate
99
+ * p50 on the OpenAddresses panels, locality-first (the default) against the postcode point:
100
+ *
101
+ * | country | rows | locality-first | postcode point | verdict |
102
+ * | ------- | ----: | -------------: | -------------: | ---------------------------------------------------------------------- |
103
+ * | **DE** | 2,997 | 5.84 km | **1.24 km** | postcode, on EVERY percentile incl. p99 (21.50 → 10.57) |
104
+ * | FR | 3,000 | **0.97 km** | 2.64 km | locality, closer on 77.5% of rows |
105
+ * | IT | 2,833 | **1.34 km** | 3.05 km | locality, closer on 66.4% |
106
+ * | ES | 2,929 | **0.68 km** | 0.97 km | locality, but near a coin flip — 46.1% of rows prefer the postcode |
107
+ * | US | 577 | **2.28 km** | 4.15 km | locality (see below) |
108
+ * | **JP** | 586 | 4.93 km | **0.48 km** | postcode, closer on 97.3% of rows (p90 13.55 → 1.21, p99 19.63 → 3.54) |
109
+ *
110
+ * **The US row is measured on the population production actually sends to the ladder.** Its rooftop cascade is US-only
111
+ * by construction (`selectAddressPointsDB` composes `address-points-us-<slug>.db`), and it serves 94.2% of US queries,
112
+ * so only 577 of 10,000 panel rows reach an admin decision at all. Those are the rows no rooftop or interpolation
113
+ * extract could place, which skews rural — exactly where a locality centroid sits close and a ZIP zone is wide.
114
+ * Measured over all 10,000 rows instead, the US looks like a postcode-first country (2.41 km vs 3.63); that is a
115
+ * selection effect, and it is why this table reports 577 rows for the US and full panels for the others, which have no
116
+ * such cascade.
117
+ *
118
+ * **The JP row is the JP board through the served path** (`jp-served-resolve.run.ts`, 2,000 rows, seed 42): the 586
119
+ * rows carrying a postcode the candidate table keys, graded on the row's own entrance point — the locality-first answer
120
+ * is the municipality centroid, the postcode answer the code's 町域 centroid from the WOF extract.
121
+ */
122
+ export declare const AREA_POSTCODE_FINER_THAN_LOCALITY: ReadonlySet<string>;
123
+ /**
124
+ * True when this country's area-grade postal code outranks its locality. Absent or unknown country → false, so the
125
+ * locality-first convention is what an unscoped query gets.
126
+ */
127
+ export declare function areaPostcodeLeadsLocality(country: string | undefined): boolean;
33
128
  //# sourceMappingURL=postcode-systems.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postcode-systems.d.ts","sourceRoot":"","sources":["../postcode-systems.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAkB9E;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAW1E"}
1
+ {"version":3,"file":"postcode-systems.d.ts","sourceRoot":"","sources":["../lib/postcode-systems.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAaH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAoB5F;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAA6C,CAAA;AAE3F;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAW1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAUrD,CAAA;AAQD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,iCAAiC,EAAE,WAAW,CAAC,MAAM,CAAyB,CAAA;AAE3F;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE9E"}
@@ -10,24 +10,26 @@
10
10
  *
11
11
  * It is the shared source of truth for "which systems can this shape be" — consumed by the postcode
12
12
  * anchor (to narrow which systems' street vocabularies it checks) and, in time, by the runtime
13
- * pipeline's locale gate (so its format→locale scoring derives from the same patterns rather than
13
+ * pipeline's `@mailwoman/locale-hint` stage (so its format→locale scoring derives from the same patterns rather than
14
14
  * a second, divergent copy). The point is to unify the DATA, not to couple the modules: callers
15
15
  * depend on this pure function, never on each other.
16
16
  *
17
17
  * Note this is a SHAPE test, not a gazetteer-membership test. A bare `68161` matches the US,
18
- * German, AND French 5-digit shapes, so it returns `["us", "de", "fr"]` — the shape alone cannot
19
- * split the numeric-postcode systems. The anchor uses real gazetteer membership for the finer
18
+ * German, French, Spanish and Italian 5-digit shapes, so it returns `["us", "de", "fr", "es", "it"]` —
19
+ * the shape alone cannot split the numeric-postcode systems. The anchor uses real gazetteer membership for the finer
20
20
  * call; this function answers the coarser, model-free "which systems is this shape even eligible
21
21
  * for".
22
22
  */
23
- import { normalizeAuPostcode } from "./au/index.js";
24
- import { normalizeCaPostalCode } from "./ca/index.js";
25
- import { normalizePLZ } from "./de/index.js";
26
- import { normalizeCodePostal } from "./fr/index.js";
27
- import { normalizeUkPostcode } from "./gb/index.js";
28
- import { normalizeJpPostalCode } from "./jp/index.js";
29
- import { normalizeNZPostcode } from "./nz/index.js";
30
- import { isZipCode } from "./us/index.js";
23
+ import { normalizeAuPostcode } from "#au/index";
24
+ import { normalizeCaPostalCode } from "#ca/index";
25
+ import { normalizePLZ } from "#de/index";
26
+ import { normalizeCodigoPostal } from "#es/index";
27
+ import { normalizeCodePostal } from "#fr/index";
28
+ import { normalizeUkPostcode } from "#gb/index";
29
+ import { normalizeCAP } from "#it/index";
30
+ import { normalizeJpPostalCode } from "#jp/index";
31
+ import { normalizeNZPostcode } from "#nz/index";
32
+ import { isZipCode } from "#us/index";
31
33
  /**
32
34
  * Per-system membership test: each entry returns true when the string is accepted by that system's own postcode shape
33
35
  * (after that system's normalization — so `D-68161` reaches `de`, `1012 LM` reaches nothing here since NL has no slice
@@ -37,12 +39,19 @@ const SYSTEM_ACCEPTS = [
37
39
  ["us", (s) => isZipCode(s)],
38
40
  ["de", (s) => normalizePLZ(s) !== null],
39
41
  ["fr", (s) => normalizeCodePostal(s) !== null],
42
+ ["es", (s) => normalizeCodigoPostal(s) !== null],
43
+ ["it", (s) => normalizeCAP(s) !== null],
40
44
  ["ca", (s) => normalizeCaPostalCode(s) !== null],
41
45
  ["gb", (s) => normalizeUkPostcode(s) !== null],
42
46
  ["jp", (s) => normalizeJpPostalCode(s) !== null],
43
47
  ["au", (s) => normalizeAuPostcode(s) !== null],
44
48
  ["nz", (s) => normalizeNZPostcode(s) !== null],
45
49
  ];
50
+ /**
51
+ * Every address system with a postcode shape, in {@link SYSTEM_ACCEPTS} order. The one list a consumer that needs the
52
+ * universe of systems (rather than the systems a given code fits) should read, so it cannot drift from the table.
53
+ */
54
+ export const SYSTEM_CODES = SYSTEM_ACCEPTS.map(([system]) => system);
46
55
  /**
47
56
  * Every address system whose own postcode shape accepts `postcode`. Empty when no system recognizes the shape (e.g. a
48
57
  * bare `27`, or a 7-digit run). O(number of systems) — a handful of cheap regex tests, run only on the few
@@ -59,4 +68,116 @@ export function candidateSystemsForPostcode(postcode) {
59
68
  }
60
69
  return out;
61
70
  }
71
+ /**
72
+ * Postcode shapes whose code is UNIT-GRADE — a delivery-walk or street-block unit, categorically tighter than any
73
+ * locality centroid, so an EXACT hit on one may lead the admin ladder instead of following the locality-first epoch
74
+ * convention.
75
+ *
76
+ * The convention exists because most postal systems are AREA-class: an FR 5-digit zone is coarser than the commune it
77
+ * contains, so promoting it would trade a good answer for a worse one. Two systems are the other way round, and
78
+ * membership here is earned by MEASUREMENT of the code's granularity, never by "the code has letters in it":
79
+ *
80
+ * - **NL PC6** (`1012 LG`) — ~8 addresses per code; the CBS polygon centroid (#977, the original carve-out).
81
+ * - **GB unit** (`N7 0BT`) — ~15 addresses per code, 1,751,733 shipped from OS Code-Point Open. Measured 2026-08-10
82
+ * against the panel-v2 GB rooftop truth: unit centroid within 1 km on 15/15 rows, median 38 m, max 100 m, while the
83
+ * locality centroid the ladder returned instead was 5.1–14.6 km out.
84
+ * - **CA URBAN LDU** (`M1J 1A8`) — 843,739 six-character codes. Measured on 879 graded rows of the CA OSM-rooftop panel,
85
+ * through the production candidate backend, ladder arm against ladder arm:
86
+ *
87
+ * | 732 URBAN rows | p50 | p75 | p90 | ≤1 km | | -------------- | ---: | ---: | ---: | ---: | | locality-first | 2.51
88
+ * km | 5.42 km | 9.53 km | 26.4% | | postcode-first | **78 m** | **162 m** | **373 m** | **94.7%** |
89
+ *
90
+ * Closer on 90.4% of them. That is GB's tier on a sample fifty times larger than GB's.
91
+ *
92
+ * **CA RURAL is excluded, and the code says which.** Canada Post puts a `0` in the SECOND position of a rural forward
93
+ * sortation area, so `T0H 1M0` is rural and `M1J 1A8` is not — no lookup required. A rural LDU serves a delivery route
94
+ * rather than a block face, and it measures like one. On the same panel, the 114 rural rows:
95
+ *
96
+ * | 114 RURAL rows | p50 | p75 | p90 | ≤1 km |
97
+ * | -------------- | --------: | ----------: | ----------: | --------: |
98
+ * | locality-first | **929 m** | **2.02 km** | **5.73 km** | **53.5%** |
99
+ * | postcode-first | 2.08 km | 4.79 km | 8.11 km | 25.4% |
100
+ *
101
+ * Postcode-first is closer on only 26.3% of them, so the pattern below admits `[1-9]` in that position and nothing
102
+ * else. `509 Main Street South-West Falher AB T0H 1M0` is the worked case: 0.29 km from the locality centroid and 43.18
103
+ * km from its own postal code.
104
+ *
105
+ * The pooled CA number hides that entirely — 0.10 km p50 across both populations reads as a uniform win and is not one.
106
+ * A tier claim that averages two granularities is the thing this table exists to prevent.
107
+ *
108
+ * Lives in codex (per-address-system postal reference) so the Node result assembly (`mailwoman/geocode-core`) and the
109
+ * demo's pin ranking consume ONE tier definition — the 2026-08-11 staged-repoint e2e measured the two disagreeing.
110
+ */
111
+ export const UNIT_GRADE_POSTCODE = [
112
+ // NL PC6 — `1012 LG` / `1012LG`.
113
+ /^\d{4}\s?[A-Z]{2}$/i,
114
+ // GB unit — outward (1-2 letters + digit + optional alnum) + inward `\d[A-Z]{2}`, the same shape
115
+ // `@mailwoman/codex/gb`'s UK_POSTCODE_PATTERN anchors, restated here so this module stays
116
+ // dependency-free within the package (the slices import THIS, never the reverse).
117
+ /^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$/i,
118
+ // CA urban LDU — `M1J 1A8`. The `[1-9]` in the second position is the whole tier claim: a `0` there marks a RURAL
119
+ // forward sortation area, which measures 2.08 km p50 against the locality's 929 m and does not belong here.
120
+ /^[A-Z][1-9][A-Z]\s?\d[A-Z]\d$/i,
121
+ ];
122
+ /**
123
+ * Strip everything but letters and digits, upper-cased — the comparison surface for "did the resolver hit the FULL code
124
+ * or a coarser stem?". `N7 0BT` and `N70BT` are the same code; `N7` is not.
125
+ */
126
+ const alnum = (s) => s.replaceAll(/[^\p{L}\p{N}]/gu, "").toUpperCase();
127
+ /**
128
+ * True when a resolved postcode is an EXACT hit on a unit-grade code — the #977 three-way guard, shared by the Node
129
+ * ladder and the demo pin ranking:
130
+ *
131
+ * 1. The PARSED span is a full unit shape ({@link UNIT_GRADE_POSTCODE}), not a stem the user typed;
132
+ * 2. The node resolved (a coordinate is present — checked by the caller); and
133
+ * 3. The resolver's own hit is the FULL code, not a coarsened prefix (a 4-digit NL stem or a GB outward district is
134
+ * AREA-class, and promoting it is the exact trade the epoch convention forbids).
135
+ */
136
+ export function isUnitGradePostcodeHit(parsed, resolverName) {
137
+ const value = parsed.trim();
138
+ if (!value || !UNIT_GRADE_POSTCODE.some((re) => re.test(value)))
139
+ return false;
140
+ return alnum(resolverName ?? "") === alnum(value);
141
+ }
142
+ /**
143
+ * Address systems whose AREA-grade postal code is still FINER than the locality containing it — the third granularity
144
+ * tier, between {@link UNIT_GRADE_POSTCODE} and the locality-first default.
145
+ *
146
+ * Whether a postal zone is coarser than its locality is a fact about a country's ADMINISTRATIVE geography, not about
147
+ * its postal system, and code length does not predict it: FR and DE are both 5-digit and land on opposite sides. France
148
+ * has ~35,000 communes and one code postal often spans several, so the commune is finer. A German Gemeinde can be
149
+ * enormous — Berlin is one WOF locality — so the PLZ is finer by a wide margin.
150
+ *
151
+ * Membership is earned by a full-panel measurement, the same bar {@link UNIT_GRADE_POSTCODE} sets for CA. Coordinate
152
+ * p50 on the OpenAddresses panels, locality-first (the default) against the postcode point:
153
+ *
154
+ * | country | rows | locality-first | postcode point | verdict |
155
+ * | ------- | ----: | -------------: | -------------: | ---------------------------------------------------------------------- |
156
+ * | **DE** | 2,997 | 5.84 km | **1.24 km** | postcode, on EVERY percentile incl. p99 (21.50 → 10.57) |
157
+ * | FR | 3,000 | **0.97 km** | 2.64 km | locality, closer on 77.5% of rows |
158
+ * | IT | 2,833 | **1.34 km** | 3.05 km | locality, closer on 66.4% |
159
+ * | ES | 2,929 | **0.68 km** | 0.97 km | locality, but near a coin flip — 46.1% of rows prefer the postcode |
160
+ * | US | 577 | **2.28 km** | 4.15 km | locality (see below) |
161
+ * | **JP** | 586 | 4.93 km | **0.48 km** | postcode, closer on 97.3% of rows (p90 13.55 → 1.21, p99 19.63 → 3.54) |
162
+ *
163
+ * **The US row is measured on the population production actually sends to the ladder.** Its rooftop cascade is US-only
164
+ * by construction (`selectAddressPointsDB` composes `address-points-us-<slug>.db`), and it serves 94.2% of US queries,
165
+ * so only 577 of 10,000 panel rows reach an admin decision at all. Those are the rows no rooftop or interpolation
166
+ * extract could place, which skews rural — exactly where a locality centroid sits close and a ZIP zone is wide.
167
+ * Measured over all 10,000 rows instead, the US looks like a postcode-first country (2.41 km vs 3.63); that is a
168
+ * selection effect, and it is why this table reports 577 rows for the US and full panels for the others, which have no
169
+ * such cascade.
170
+ *
171
+ * **The JP row is the JP board through the served path** (`jp-served-resolve.run.ts`, 2,000 rows, seed 42): the 586
172
+ * rows carrying a postcode the candidate table keys, graded on the row's own entrance point — the locality-first answer
173
+ * is the municipality centroid, the postcode answer the code's 町域 centroid from the WOF extract.
174
+ */
175
+ export const AREA_POSTCODE_FINER_THAN_LOCALITY = new Set(["DE", "JP"]);
176
+ /**
177
+ * True when this country's area-grade postal code outranks its locality. Absent or unknown country → false, so the
178
+ * locality-first convention is what an unscoped query gets.
179
+ */
180
+ export function areaPostcodeLeadsLocality(country) {
181
+ return country !== undefined && AREA_POSTCODE_FINER_THAN_LOCALITY.has(country.trim().toUpperCase());
182
+ }
62
183
  //# sourceMappingURL=postcode-systems.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"postcode-systems.js","sourceRoot":"","sources":["../postcode-systems.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAOzC;;;;GAIG;AACH,MAAM,cAAc,GAAiE;IACpF,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACvC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC9C,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAChD,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC9C,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAChD,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC9C,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC9C,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAgB;IAC3D,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAC/D,MAAM,GAAG,GAAiB,EAAE,CAAA;IAE5B,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,cAAc,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC;IACF,CAAC;IAED,OAAO,GAAG,CAAA;AACX,CAAC"}
1
+ {"version":3,"file":"postcode-systems.js","sourceRoot":"","sources":["../lib/postcode-systems.ts"],"names":[],"mappings":"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"}
@@ -34,7 +34,7 @@
34
34
  *
35
35
  * Source of truth: `@mailwoman/codex` (COUNTRY_SURFACE_FORMS + ISO2_TO_NAME) — the SAME data the
36
36
  * corpus-python bridge `country-surfaces.json` is generated from (export-country-surfaces.ts), so
37
- * the channel and the corpus shard synthesizer cannot diverge on what a country surface IS.
37
+ * the channel and the corpus extract synthesizer cannot diverge on what a country surface IS.
38
38
  *
39
39
  * Output: data/gazetteer/country-surface-lexicon-v1.json (small, committed, provenance-tracked).
40
40
  * Regenerate: `node codex/tools/build-country-surface-lexicon.ts`
@@ -1 +1 @@
1
- {"version":3,"file":"build-country-surface-lexicon.d.ts","sourceRoot":"","sources":["../../tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG"}
1
+ {"version":3,"file":"build-country-surface-lexicon.d.ts","sourceRoot":"","sources":["../../lib/tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG"}
@@ -34,16 +34,17 @@
34
34
  *
35
35
  * Source of truth: `@mailwoman/codex` (COUNTRY_SURFACE_FORMS + ISO2_TO_NAME) — the SAME data the
36
36
  * corpus-python bridge `country-surfaces.json` is generated from (export-country-surfaces.ts), so
37
- * the channel and the corpus shard synthesizer cannot diverge on what a country surface IS.
37
+ * the channel and the corpus extract synthesizer cannot diverge on what a country surface IS.
38
38
  *
39
39
  * Output: data/gazetteer/country-surface-lexicon-v1.json (small, committed, provenance-tracked).
40
40
  * Regenerate: `node codex/tools/build-country-surface-lexicon.ts`
41
41
  */
42
- import { mkdirSync, writeFileSync } from "node:fs";
43
- import { dirname, resolve } from "node:path";
44
- import { COUNTRY_SURFACE_FORMS, ISO2_TO_NAME } from "../country/country.js";
45
- import { wordNorm, wordNormLower } from "../normalize.js";
46
- import { US_STATE_ABBREVIATIONS, US_STATE_NAMES } from "../us/state.js";
42
+ import { makeDirectories, writeLocalTextFile } from "@mailwoman/core/fs/writers";
43
+ import { repoRootPath } from "@mailwoman/core/paths";
44
+ import { dirname } from "path-ts";
45
+ import { COUNTRY_SURFACE_FORMS, ISO2_TO_NAME } from "#country/country";
46
+ import { wordNorm, wordNormLower } from "#normalize";
47
+ import { US_STATE_ABBREVIATIONS, US_STATE_NAMES } from "#us/state";
47
48
  /**
48
49
  * Ambiguous entries printed before the list is truncated.
49
50
  */
@@ -58,7 +59,7 @@ const SLOTS = ["country_surface", "country_ambiguous"];
58
59
  * Committed output path (a codex-derived artifact, like export-country-surfaces.ts — no argv, so the no-process-globals
59
60
  * lint policy holds; codex stays zero-runtime-dep).
60
61
  */
61
- const OUTPUT = resolve(import.meta.dirname, "../../data/gazetteer/country-surface-lexicon-v1.json");
62
+ const OUTPUT = repoRootPath("data", "gazetteer", "country-surface-lexicon-v1.json");
62
63
  /**
63
64
  * THE shared word-normalization rule (identical to build-gazetteer-anchor-lexicon.mjs and mirrored in
64
65
  * gazetteer_char_paint on both sides): per whitespace-word, strip LEADING/TRAILING characters that are not Unicode
@@ -141,8 +142,8 @@ const lexicon = {
141
142
  entries: Object.fromEntries([...entries].toSorted(([a], [b]) => a.localeCompare(b))),
142
143
  code_entries: Object.fromEntries([...codeEntries].toSorted(([a], [b]) => a.localeCompare(b))),
143
144
  };
144
- mkdirSync(dirname(OUTPUT), { recursive: true });
145
- writeFileSync(OUTPUT, JSON.stringify(lexicon, null, 1) + "\n");
145
+ await makeDirectories(dirname(OUTPUT));
146
+ await writeLocalTextFile(JSON.stringify(lexicon, null, 1) + "\n", OUTPUT);
146
147
  process.stderr.write(`wrote ${OUTPUT}: ${entries.size} entries + ${codeEntries.size} code_entries, ` +
147
148
  `max_ngram=${maxNgram}, ${ambiguousEntries.length} ambiguous: ${ambiguousEntries.slice(0, MAX_LISTED_AMBIGUOUS).join(", ")}${ambiguousEntries.length > MAX_LISTED_AMBIGUOUS ? ", …" : ""}\n`);
148
149
  //# sourceMappingURL=build-country-surface-lexicon.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-country-surface-lexicon.js","sourceRoot":"","sources":["../../tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-country-surface-lexicon.js","sourceRoot":"","sources":["../../lib/tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * codex → corpus-python bridge: emit the authoritative country surface forms as JSON so the Python
7
- * shard generators can synthesize address tails ("…, USA" / "…, United States of America") without
7
+ * extract generators can synthesize address tails ("…, USA" / "…, United States of America") without
8
8
  * re-deriving the country name/alias data. `@mailwoman/codex` stays the single source of truth
9
9
  * (COUNTRY_SURFACE_FORMS + ISO2_TO_NAME, salvaged from isp-nexus spatial/countries); this writes a
10
10
  * snapshot the language boundary can't import directly.
@@ -1 +1 @@
1
- {"version":3,"file":"export-country-surfaces.d.ts","sourceRoot":"","sources":["../../tools/export-country-surfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
1
+ {"version":3,"file":"export-country-surfaces.d.ts","sourceRoot":"","sources":["../../lib/tools/export-country-surfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
@@ -4,16 +4,16 @@
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
6
  * codex → corpus-python bridge: emit the authoritative country surface forms as JSON so the Python
7
- * shard generators can synthesize address tails ("…, USA" / "…, United States of America") without
7
+ * extract generators can synthesize address tails ("…, USA" / "…, United States of America") without
8
8
  * re-deriving the country name/alias data. `@mailwoman/codex` stays the single source of truth
9
9
  * (COUNTRY_SURFACE_FORMS + ISO2_TO_NAME, salvaged from isp-nexus spatial/countries); this writes a
10
10
  * snapshot the language boundary can't import directly.
11
11
  *
12
12
  * Regenerate: `node codex/tools/export-country-surfaces.ts` (writes the corpus-python data file).
13
13
  */
14
- import { writeFileSync } from "node:fs";
15
- import { resolve } from "node:path";
16
- import { COUNTRY_SURFACE_FORMS, ISO2_TO_NAME } from "../country/country.js";
14
+ import { writeLocalTextFile } from "@mailwoman/core/fs/writers";
15
+ import { repoRootPath } from "@mailwoman/core/paths";
16
+ import { COUNTRY_SURFACE_FORMS, ISO2_TO_NAME } from "#country/country";
17
17
  // Merge: rich surface forms where the codex curates them, else the canonical English name for every
18
18
  // ISO 3166-1 alpha-2. Canonical-name-first (the codex's own ordering) so the common form leads.
19
19
  const surfaces = {};
@@ -25,10 +25,10 @@ for (const [iso2, name] of ISO2_TO_NAME) {
25
25
  surfaces[iso2] = [name];
26
26
  }
27
27
  }
28
- const out = resolve(import.meta.dirname, "../../corpus-python/src/mailwoman_train/data/country-surfaces.json");
29
- writeFileSync(out, JSON.stringify({
28
+ const out = repoRootPath("corpus-python", "src", "mailwoman_train", "data", "country-surfaces.json");
29
+ await writeLocalTextFile(JSON.stringify({
30
30
  _generated: "codex/tools/export-country-surfaces.ts from @mailwoman/codex COUNTRY_SURFACE_FORMS + ISO2_TO_NAME",
31
31
  surfaces,
32
- }, null, 2) + "\n");
32
+ }, null, 2) + "\n", out);
33
33
  process.stderr.write(`wrote ${Object.keys(surfaces).length} countries → ${out}\n`);
34
34
  //# sourceMappingURL=export-country-surfaces.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"export-country-surfaces.js","sourceRoot":"","sources":["../../tools/export-country-surfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAE3E,oGAAoG;AACpG,gGAAgG;AAChG,MAAM,QAAQ,GAA6B,EAAE,CAAA;AAE7C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,EAAE,CAAC;IACnE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;AAC5B,CAAC;AAED,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;AACF,CAAC;AAED,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,oEAAoE,CAAC,CAAA;AAE9G,aAAa,CACZ,GAAG,EACH,IAAI,CAAC,SAAS,CACb;IACC,UAAU,EAAE,mGAAmG;IAC/G,QAAQ;CACR,EACD,IAAI,EACJ,CAAC,CACD,GAAG,IAAI,CACR,CAAA;AAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"export-country-surfaces.js","sourceRoot":"","sources":["../../lib/tools/export-country-surfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEtE,oGAAoG;AACpG,gGAAgG;AAChG,MAAM,QAAQ,GAA6B,EAAE,CAAA;AAE7C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,EAAE,CAAC;IACnE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;AAC5B,CAAC;AAED,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;AACF,CAAC;AAED,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,uBAAuB,CAAC,CAAA;AAEpG,MAAM,kBAAkB,CACvB,IAAI,CAAC,SAAS,CACb;IACC,UAAU,EAAE,mGAAmG;IAC/G,QAAQ;CACR,EACD,IAAI,EACJ,CAAC,CACD,GAAG,IAAI,EACR,GAAG,CACH,CAAA;AAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,CAAA"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Regenerate `codex/country/population.ts` — the per-country population table — from GeoNames
7
+ * `countryInfo.txt` (https://download.geonames.org/export/dump/countryInfo.txt, CC-BY-4.0). The
8
+ * output is committed; this tool makes it reproducible (provenance), not a hand-typed dictionary.
9
+ *
10
+ * Why it exists (#1650): WOF carries no readable population for 147 of 237 country records
11
+ * (measured against the 2026-08-18 candidate build), so those countries entered every prominence
12
+ * race at an asserted ZERO — ranked below any namesake hamlet. The magnitude is what a fame race
13
+ * reads, so a census-vintage figure is entirely sufficient; currency of the estimate is not the
14
+ * point.
15
+ *
16
+ * Rows whose GeoNames population is 0 are DROPPED rather than emitted: an entry in this table is a
17
+ * positive claim, and the consumer's absence branch (`?? undefined`) must stay reachable for
18
+ * territories GeoNames itself declines to estimate (the meaning-of-zero rule).
19
+ *
20
+ * Usage: mailwoman dev generate country-population
21
+ */
22
+ /**
23
+ * Options for {@linkcode generateCountryPopulation}.
24
+ */
25
+ export interface GenerateCountryPopulationOptions {
26
+ /**
27
+ * Output path override. Default: `codex/country/population.ts` (the committed table).
28
+ */
29
+ out?: string;
30
+ }
31
+ /**
32
+ * Summary returned by {@linkcode generateCountryPopulation}.
33
+ */
34
+ export interface GenerateCountryPopulationSummary {
35
+ countries: number;
36
+ outPath: string;
37
+ }
38
+ /**
39
+ * Fetch GeoNames `countryInfo.txt` and regenerate the committed `COUNTRY_POPULATION` table.
40
+ */
41
+ export declare function generateCountryPopulation(options?: GenerateCountryPopulationOptions, report?: (line: string) => void): Promise<GenerateCountryPopulationSummary>;
42
+ //# sourceMappingURL=generate-country-population.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-country-population.d.ts","sourceRoot":"","sources":["../../lib/tools/generate-country-population.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAmCH;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,GAAE,gCAAqC,EAC9C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,gCAAgC,CAAC,CA8D3C"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Regenerate `codex/country/population.ts` — the per-country population table — from GeoNames
7
+ * `countryInfo.txt` (https://download.geonames.org/export/dump/countryInfo.txt, CC-BY-4.0). The
8
+ * output is committed; this tool makes it reproducible (provenance), not a hand-typed dictionary.
9
+ *
10
+ * Why it exists (#1650): WOF carries no readable population for 147 of 237 country records
11
+ * (measured against the 2026-08-18 candidate build), so those countries entered every prominence
12
+ * race at an asserted ZERO — ranked below any namesake hamlet. The magnitude is what a fame race
13
+ * reads, so a census-vintage figure is entirely sufficient; currency of the estimate is not the
14
+ * point.
15
+ *
16
+ * Rows whose GeoNames population is 0 are DROPPED rather than emitted: an entry in this table is a
17
+ * positive claim, and the consumer's absence branch (`?? undefined`) must stay reachable for
18
+ * territories GeoNames itself declines to estimate (the meaning-of-zero rule).
19
+ *
20
+ * Usage: mailwoman dev generate country-population
21
+ */
22
+ import { APIClient, pluckResponseData } from "@mailwoman/core/api";
23
+ import { writeLocalTextFile } from "@mailwoman/core/fs/writers";
24
+ import { resolvePackagePath } from "@mailwoman/core/module/resolvers";
25
+ const SOURCE = "https://download.geonames.org/export/dump/countryInfo.txt";
26
+ /**
27
+ * The committed output path, resolved relative to this module (codex/tools/ → codex/country/). See
28
+ * `generate-country-reference.ts` for why this is `import.meta.url` rather than a `core` path helper (codex is
29
+ * zero-runtime-dep and `core` already references `codex` — importing core here would cycle the project graph).
30
+ */
31
+ const DEFAULT_OUT = resolvePackagePath("@mailwoman/codex", "lib", "country", "population.ts");
32
+ /**
33
+ * Tab positions this tool reads from `countryInfo.txt`'s 19-column rows. Named so the parse states which columns it
34
+ * believes in; a GeoNames format change fails the count guard below rather than silently reading the wrong column.
35
+ */
36
+ const COLUMN_ISO2 = 0;
37
+ const COLUMN_POPULATION = 7;
38
+ const MINIMUM_COLUMNS = 8;
39
+ /**
40
+ * GeoNames publishes ~250 countries/territories; a parse recovering fewer than this read the wrong column or a
41
+ * truncated body, and the guard fails loudly instead of committing a hollow table.
42
+ */
43
+ const MINIMUM_PLAUSIBLE_COUNTRIES = 200;
44
+ /**
45
+ * Numbers below five digits are emitted bare — the house numeric-separator style groups by three and only from five
46
+ * digits up (`8450`, not `8_450`).
47
+ */
48
+ const SEPARATOR_MINIMUM = 10_000;
49
+ /**
50
+ * Fetch GeoNames `countryInfo.txt` and regenerate the committed `COUNTRY_POPULATION` table.
51
+ */
52
+ export async function generateCountryPopulation(options = {}, report) {
53
+ const outPath = options.out ?? DEFAULT_OUT;
54
+ // `responseType: "text"` because the source is a tab-separated dump, not JSON.
55
+ const text = await new APIClient({ displayName: "geonames-country-info", retry: true })
56
+ .fetch({ url: SOURCE, responseType: "text" })
57
+ .then(pluckResponseData);
58
+ const rows = {};
59
+ // oxlint-disable-next-line mailwoman/prefer-spliterator -- countryInfo.txt is ~35 kB and bounded (~300 rows)
60
+ for (const line of text.split("\n")) {
61
+ if (!line || line.startsWith("#"))
62
+ continue;
63
+ // oxlint-disable-next-line mailwoman/prefer-spliterator -- one 19-column row
64
+ const columns = line.split("\t");
65
+ if (columns.length < MINIMUM_COLUMNS)
66
+ continue;
67
+ const alpha2 = columns[COLUMN_ISO2].trim();
68
+ const population = Number(columns[COLUMN_POPULATION]);
69
+ if (!/^[A-Z]{2}$/.test(alpha2))
70
+ continue;
71
+ if (!Number.isFinite(population) || population <= 0)
72
+ continue;
73
+ rows[alpha2] = population;
74
+ }
75
+ if (Object.keys(rows).length < MINIMUM_PLAUSIBLE_COUNTRIES) {
76
+ throw new Error(`generateCountryPopulation: only ${Object.keys(rows).length} rows parsed — format drift?`);
77
+ }
78
+ const body = Object.keys(rows)
79
+ .toSorted()
80
+ .map((k) => {
81
+ const n = rows[k];
82
+ const literal = n >= SEPARATOR_MINIMUM ? n.toLocaleString("en-US").replaceAll(",", "_") : String(n);
83
+ return `\t${k}: ${literal},`;
84
+ })
85
+ .join("\n");
86
+ const header = `/**
87
+ * @copyright Sister Software
88
+ * @license AGPL-3.0
89
+ * @author Teffen Ellis, et al.
90
+ *
91
+ * GENERATED — do not edit by hand. Per-country population, derived from GeoNames countryInfo.txt
92
+ * (https://download.geonames.org/export/dump/countryInfo.txt, CC-BY-4.0). Estimates are
93
+ * census-vintage; a prominence race reads the magnitude, not the currency. Countries GeoNames
94
+ * declines to estimate are ABSENT, never zero.
95
+ * Regenerate with: mailwoman dev generate country-population
96
+ */
97
+
98
+ /**
99
+ * ISO 3166-1 alpha-2 → population estimate.
100
+ */
101
+ export const COUNTRY_POPULATION: Readonly<Record<string, number>> = {`;
102
+ await writeLocalTextFile(`${header}\n${body}\n}\n`, outPath);
103
+ report?.(`wrote ${outPath} (${Object.keys(rows).length} countries)`);
104
+ return { countries: Object.keys(rows).length, outPath };
105
+ }
106
+ //# sourceMappingURL=generate-country-population.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-country-population.js","sourceRoot":"","sources":["../../lib/tools/generate-country-population.ts"],"names":[],"mappings":"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"}