@mailwoman/codex 9.1.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +48 -5
  2. package/ca/index.ts +1 -1
  3. package/ca/postal-code.ts +11 -11
  4. package/country/display-names.ts +150 -0
  5. package/country/index.ts +2 -0
  6. package/country/population.ts +265 -0
  7. package/country/subdivision.ts +59 -0
  8. package/de/index.ts +1 -1
  9. package/de/postleitzahl.ts +3 -3
  10. package/es/codigo-postal.ts +2 -2
  11. package/fr/code-postal.ts +6 -6
  12. package/fr/index.ts +1 -1
  13. package/index.ts +10 -1
  14. package/it/cap.ts +2 -2
  15. package/jp/index.ts +1 -1
  16. package/jp/postal-code.ts +11 -11
  17. package/out/ca/index.d.ts +1 -1
  18. package/out/ca/index.js +1 -1
  19. package/out/ca/postal-code.d.ts +10 -10
  20. package/out/ca/postal-code.js +9 -9
  21. package/out/country/display-names.d.ts +67 -0
  22. package/out/country/display-names.d.ts.map +1 -0
  23. package/out/country/display-names.js +118 -0
  24. package/out/country/display-names.js.map +1 -0
  25. package/out/country/index.d.ts +2 -0
  26. package/out/country/index.d.ts.map +1 -1
  27. package/out/country/index.js +2 -0
  28. package/out/country/index.js.map +1 -1
  29. package/out/country/population.d.ts +16 -0
  30. package/out/country/population.d.ts.map +1 -0
  31. package/out/country/population.js +265 -0
  32. package/out/country/population.js.map +1 -0
  33. package/out/country/subdivision.d.ts +7 -0
  34. package/out/country/subdivision.d.ts.map +1 -1
  35. package/out/country/subdivision.js +49 -0
  36. package/out/country/subdivision.js.map +1 -1
  37. package/out/de/index.d.ts +1 -1
  38. package/out/de/index.js +1 -1
  39. package/out/de/postleitzahl.d.ts +3 -3
  40. package/out/de/postleitzahl.js +2 -2
  41. package/out/es/codigo-postal.d.ts +2 -2
  42. package/out/es/codigo-postal.js +1 -1
  43. package/out/fr/code-postal.d.ts +5 -5
  44. package/out/fr/code-postal.js +6 -6
  45. package/out/fr/index.d.ts +1 -1
  46. package/out/fr/index.js +1 -1
  47. package/out/index.d.ts +1 -1
  48. package/out/index.d.ts.map +1 -1
  49. package/out/index.js +1 -1
  50. package/out/index.js.map +1 -1
  51. package/out/it/cap.d.ts +2 -2
  52. package/out/it/cap.js +1 -1
  53. package/out/jp/index.d.ts +1 -1
  54. package/out/jp/index.js +1 -1
  55. package/out/jp/postal-code.d.ts +11 -11
  56. package/out/jp/postal-code.js +8 -8
  57. package/out/postcode-systems.d.ts +84 -0
  58. package/out/postcode-systems.d.ts.map +1 -1
  59. package/out/postcode-systems.js +106 -0
  60. package/out/postcode-systems.js.map +1 -1
  61. package/out/tools/build-country-surface-lexicon.js +1 -1
  62. package/out/tools/build-country-surface-lexicon.js.map +1 -1
  63. package/out/tools/export-country-surfaces.js +1 -1
  64. package/out/tools/export-country-surfaces.js.map +1 -1
  65. package/out/tools/generate-country-population.d.ts +42 -0
  66. package/out/tools/generate-country-population.d.ts.map +1 -0
  67. package/out/tools/generate-country-population.js +106 -0
  68. package/out/tools/generate-country-population.js.map +1 -0
  69. package/out/tools/generate-country-reference.d.ts.map +1 -1
  70. package/out/tools/generate-country-reference.js +4 -4
  71. package/out/tools/generate-country-reference.js.map +1 -1
  72. package/out/tools/generate-official-languages.d.ts.map +1 -1
  73. package/out/tools/generate-official-languages.js +7 -4
  74. package/out/tools/generate-official-languages.js.map +1 -1
  75. package/out/tools/index.d.ts +2 -2
  76. package/out/tools/index.d.ts.map +1 -1
  77. package/out/tools/index.js +2 -2
  78. package/out/tools/index.js.map +1 -1
  79. package/out/us/zipcode.d.ts +1 -1
  80. package/out/us/zipcode.js +1 -1
  81. package/package.json +326 -4
  82. package/postcode-systems.ts +112 -0
  83. package/tools/build-country-surface-lexicon.ts +1 -1
  84. package/tools/export-country-surfaces.ts +1 -1
  85. package/tools/generate-country-population.ts +143 -0
  86. package/tools/generate-country-reference.ts +5 -3
  87. package/tools/generate-official-languages.ts +8 -4
  88. package/tools/index.ts +2 -2
  89. package/us/zipcode.ts +1 -1
@@ -3,7 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Japanese postal codes (郵便番号, yūbin-bangō): the branded type, the shape, normalization, and the
6
+ * Japanese postcodes (郵便番号, yūbin-bangō): the branded type, the shape, normalization, and the
7
7
  * first-digit → coarse-region prior.
8
8
  *
9
9
  * This file is the far end of a spectrum whose other end is `us/zipcode.ts`. A US address leans on
@@ -16,16 +16,16 @@
16
16
  * navigate by named streets; you navigate by nested administrative areas and numbered
17
17
  * blocks/lots (丁目 / 番地 / 号 — see `address-unit.ts`).
18
18
  *
19
- * With no street name to anchor on and a reverse field order, the **postal code is the single most
19
+ * With no street name to anchor on and a reverse field order, the **postcode is the single most
20
20
  * reliable geographic anchor** for a Japanese address — it pins the chōme-level area directly,
21
21
  * far tighter than a US ZIP pins a US address. So where the US parser treats the postcode as a
22
22
  * tie-breaker behind the street, the Japan parser should treat it as the primary key.
23
23
  */
24
24
  import type { Tagged } from "type-fest";
25
25
  /**
26
- * A Japanese postal code: three digits, a hyphen, then four digits (`100-0001`), conventionally written after the 〒
27
- * mark (`〒100-0001`). Branded so a normalized code is distinct from an arbitrary string — the 7-digit shape alone does
28
- * not prove a code is real, only well-formed.
26
+ * A Japanese postcode: three digits, a hyphen, then four digits (`100-0001`), conventionally written after the 〒 mark
27
+ * (`〒100-0001`). Branded so a normalized code is distinct from an arbitrary string — the 7-digit shape alone does not
28
+ * prove a code is real, only well-formed.
29
29
  *
30
30
  * @category Postal
31
31
  * @type string
@@ -44,20 +44,20 @@ export declare const JP_POSTAL_CODE_PATTERN: RegExp;
44
44
  */
45
45
  export declare function normalizeJpPostalCode(raw: unknown): PostalCode | null;
46
46
  /**
47
- * Type-predicate for a Japanese postal code (hyphen optional, `100-0001` or `1000001`).
47
+ * Type-predicate for a Japanese postcode (hyphen optional, `100-0001` or `1000001`).
48
48
  */
49
49
  export declare function isJpPostalCode(input: unknown): input is PostalCode;
50
50
  /**
51
- * First digit of the postal code → a coarse region label. Japan Post's numbering grows roughly outward from Tokyo
52
- * (`1xx`) and is **approximate** at this granularity — a single leading digit spans large, irregular areas and the
53
- * boundaries are postal-routing, not administrative. Use it as a weak prior, never as a hard region assignment; the
54
- * full code is what actually anchors the address.
51
+ * First digit of the postcode → a coarse region label. Japan Post's numbering grows roughly outward from Tokyo (`1xx`)
52
+ * and is **approximate** at this granularity — a single leading digit spans large, irregular areas and the boundaries
53
+ * are postal-routing, not administrative. Use it as a weak prior, never as a hard region assignment; the full code is
54
+ * what actually anchors the address.
55
55
  *
56
56
  * Approximate — the labels below are illustrative routing regions, not precise prefecture sets.
57
57
  */
58
58
  export declare const JP_FIRST_DIGIT_REGION: Record<string, string>;
59
59
  /**
60
- * The coarse region label for a postal code's first digit, or null if the input is not a Japanese postal code. A weak,
60
+ * The coarse region label for a postcode's first digit, or null if the input is not a Japanese postcode. A weak,
61
61
  * approximate prior (see {@link JP_FIRST_DIGIT_REGION}); the full code anchors the address.
62
62
  */
63
63
  export declare function firstDigitRegion(postalCode: unknown): string | null;
@@ -3,7 +3,7 @@
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
5
  *
6
- * Japanese postal codes (郵便番号, yūbin-bangō): the branded type, the shape, normalization, and the
6
+ * Japanese postcodes (郵便番号, yūbin-bangō): the branded type, the shape, normalization, and the
7
7
  * first-digit → coarse-region prior.
8
8
  *
9
9
  * This file is the far end of a spectrum whose other end is `us/zipcode.ts`. A US address leans on
@@ -16,7 +16,7 @@
16
16
  * navigate by named streets; you navigate by nested administrative areas and numbered
17
17
  * blocks/lots (丁目 / 番地 / 号 — see `address-unit.ts`).
18
18
  *
19
- * With no street name to anchor on and a reverse field order, the **postal code is the single most
19
+ * With no street name to anchor on and a reverse field order, the **postcode is the single most
20
20
  * reliable geographic anchor** for a Japanese address — it pins the chōme-level area directly,
21
21
  * far tighter than a US ZIP pins a US address. So where the US parser treats the postcode as a
22
22
  * tie-breaker behind the street, the Japan parser should treat it as the primary key.
@@ -40,16 +40,16 @@ export function normalizeJpPostalCode(raw) {
40
40
  return `${digits.slice(0, 3)}-${digits.slice(3)}`;
41
41
  }
42
42
  /**
43
- * Type-predicate for a Japanese postal code (hyphen optional, `100-0001` or `1000001`).
43
+ * Type-predicate for a Japanese postcode (hyphen optional, `100-0001` or `1000001`).
44
44
  */
45
45
  export function isJpPostalCode(input) {
46
46
  return typeof input === "string" && JP_POSTAL_CODE_PATTERN.test(input);
47
47
  }
48
48
  /**
49
- * First digit of the postal code → a coarse region label. Japan Post's numbering grows roughly outward from Tokyo
50
- * (`1xx`) and is **approximate** at this granularity — a single leading digit spans large, irregular areas and the
51
- * boundaries are postal-routing, not administrative. Use it as a weak prior, never as a hard region assignment; the
52
- * full code is what actually anchors the address.
49
+ * First digit of the postcode → a coarse region label. Japan Post's numbering grows roughly outward from Tokyo (`1xx`)
50
+ * and is **approximate** at this granularity — a single leading digit spans large, irregular areas and the boundaries
51
+ * are postal-routing, not administrative. Use it as a weak prior, never as a hard region assignment; the full code is
52
+ * what actually anchors the address.
53
53
  *
54
54
  * Approximate — the labels below are illustrative routing regions, not precise prefecture sets.
55
55
  */
@@ -66,7 +66,7 @@ export const JP_FIRST_DIGIT_REGION = {
66
66
  "9": "Tōhoku north / other",
67
67
  };
68
68
  /**
69
- * The coarse region label for a postal code's first digit, or null if the input is not a Japanese postal code. A weak,
69
+ * The coarse region label for a postcode's first digit, or null if the input is not a Japanese postcode. A weak,
70
70
  * approximate prior (see {@link JP_FIRST_DIGIT_REGION}); the full code anchors the address.
71
71
  */
72
72
  export function firstDigitRegion(postalCode) {
@@ -30,4 +30,88 @@ export type SystemCode = "us" | "de" | "fr" | "ca" | "gb" | "jp" | "au" | "nz";
30
30
  * postcode-shaped spans an address contains.
31
31
  */
32
32
  export declare function candidateSystemsForPostcode(postcode: string): SystemCode[];
33
+ /**
34
+ * Postcode shapes whose code is UNIT-GRADE — a delivery-walk or street-block unit, categorically tighter than any
35
+ * locality centroid, so an EXACT hit on one may lead the admin ladder instead of following the locality-first epoch
36
+ * convention.
37
+ *
38
+ * The convention exists because most postal systems are AREA-class: an FR 5-digit zone is coarser than the commune it
39
+ * contains, so promoting it would trade a good answer for a worse one. Two systems are the other way round, and
40
+ * membership here is earned by MEASUREMENT of the code's granularity, never by "the code has letters in it":
41
+ *
42
+ * - **NL PC6** (`1012 LG`) — ~8 addresses per code; the CBS polygon centroid (#977, the original carve-out).
43
+ * - **GB unit** (`N7 0BT`) — ~15 addresses per code, 1,751,733 shipped from OS Code-Point Open. Measured 2026-08-10
44
+ * against the panel-v2 GB rooftop truth: unit centroid within 1 km on 15/15 rows, median 38 m, max 100 m, while the
45
+ * locality centroid the ladder returned instead was 5.1–14.6 km out.
46
+ * - **CA URBAN LDU** (`M1J 1A8`) — 843,739 six-character codes. Measured on 879 graded rows of the CA OSM-rooftop panel,
47
+ * through the production candidate backend, ladder arm against ladder arm:
48
+ *
49
+ * | 732 URBAN rows | p50 | p75 | p90 | ≤1 km | | -------------- | ---: | ---: | ---: | ---: | | locality-first | 2.51
50
+ * km | 5.42 km | 9.53 km | 26.4% | | postcode-first | **78 m** | **162 m** | **373 m** | **94.7%** |
51
+ *
52
+ * Closer on 90.4% of them. That is GB's tier on a sample fifty times larger than GB's.
53
+ *
54
+ * **CA RURAL is excluded, and the code says which.** Canada Post puts a `0` in the SECOND position of a rural forward
55
+ * sortation area, so `T0H 1M0` is rural and `M1J 1A8` is not — no lookup required. A rural LDU serves a delivery route
56
+ * rather than a block face, and it measures like one. On the same panel, the 114 rural rows:
57
+ *
58
+ * | 114 RURAL rows | p50 | p75 | p90 | ≤1 km |
59
+ * | -------------- | --------: | ----------: | ----------: | --------: |
60
+ * | locality-first | **929 m** | **2.02 km** | **5.73 km** | **53.5%** |
61
+ * | postcode-first | 2.08 km | 4.79 km | 8.11 km | 25.4% |
62
+ *
63
+ * Postcode-first is closer on only 26.3% of them, so the pattern below admits `[1-9]` in that position and nothing
64
+ * else. `509 Main Street South-West Falher AB T0H 1M0` is the worked case: 0.29 km from the locality centroid and 43.18
65
+ * km from its own postal code.
66
+ *
67
+ * The pooled CA number hides that entirely — 0.10 km p50 across both populations reads as a uniform win and is not one.
68
+ * A tier claim that averages two granularities is the thing this table exists to prevent.
69
+ *
70
+ * Lives in codex (per-address-system postal reference) so the Node result assembly (`mailwoman/geocode-core`) and the
71
+ * demo's pin ranking consume ONE tier definition — the 2026-08-11 staged-repoint e2e measured the two disagreeing.
72
+ */
73
+ export declare const UNIT_GRADE_POSTCODE: ReadonlyArray<RegExp>;
74
+ /**
75
+ * True when a resolved postcode is an EXACT hit on a unit-grade code — the #977 three-way guard, shared by the Node
76
+ * ladder and the demo pin ranking:
77
+ *
78
+ * 1. The PARSED span is a full unit shape ({@link UNIT_GRADE_POSTCODE}), not a stem the user typed;
79
+ * 2. The node resolved (a coordinate is present — checked by the caller); and
80
+ * 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
81
+ * AREA-class, and promoting it is the exact trade the epoch convention forbids).
82
+ */
83
+ export declare function isUnitGradePostcodeHit(parsed: string, resolverName: string | undefined): boolean;
84
+ /**
85
+ * Address systems whose AREA-grade postal code is still FINER than the locality containing it — the third granularity
86
+ * tier, between {@link UNIT_GRADE_POSTCODE} and the locality-first default.
87
+ *
88
+ * Whether a postal zone is coarser than its locality is a fact about a country's ADMINISTRATIVE geography, not about
89
+ * its postal system, and code length does not predict it: FR and DE are both 5-digit and land on opposite sides. France
90
+ * has ~35,000 communes and one code postal often spans several, so the commune is finer. A German Gemeinde can be
91
+ * enormous — Berlin is one WOF locality — so the PLZ is finer by a wide margin.
92
+ *
93
+ * Membership is earned by a full-panel measurement, the same bar {@link UNIT_GRADE_POSTCODE} sets for CA. Coordinate
94
+ * p50 on the OpenAddresses panels, locality-first (the default) against the postcode point:
95
+ *
96
+ * | country | rows | locality-first | postcode point | verdict |
97
+ * | ------- | ----: | -------------: | -------------: | ------------------------------------------------------------------ |
98
+ * | **DE** | 2,997 | 5.84 km | **1.24 km** | postcode, on EVERY percentile incl. p99 (21.50 → 10.57) |
99
+ * | FR | 3,000 | **0.97 km** | 2.64 km | locality, closer on 77.5% of rows |
100
+ * | IT | 2,833 | **1.34 km** | 3.05 km | locality, closer on 66.4% |
101
+ * | ES | 2,929 | **0.68 km** | 0.97 km | locality, but near a coin flip — 46.1% of rows prefer the postcode |
102
+ * | US | 577 | **2.28 km** | 4.15 km | locality (see below) |
103
+ *
104
+ * **The US row is measured on the population production actually sends to the ladder.** Its rooftop cascade is US-only
105
+ * by construction (`selectAddressPointsDB` composes `address-points-us-<slug>.db`), and it serves 94.2% of US queries,
106
+ * so only 577 of 10,000 panel rows reach an admin decision at all. Those are the rows no rooftop or interpolation shard
107
+ * could place, which skews rural — exactly where a locality centroid sits close and a ZIP zone is wide. Measured over
108
+ * all 10,000 rows instead, the US looks like a postcode-first country (2.41 km vs 3.63); that is a selection effect,
109
+ * and it is why this table reports 577 rows for the US and full panels for the others, which have no such cascade.
110
+ */
111
+ export declare const AREA_POSTCODE_FINER_THAN_LOCALITY: ReadonlySet<string>;
112
+ /**
113
+ * True when this country's area-grade postal code outranks its locality. Absent or unknown country → false, so the
114
+ * locality-first convention is what an unscoped query gets.
115
+ */
116
+ export declare function areaPostcodeLeadsLocality(country: string | undefined): boolean;
33
117
  //# 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":["../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;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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iCAAiC,EAAE,WAAW,CAAC,MAAM,CAAmB,CAAA;AAErF;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE9E"}
@@ -59,4 +59,110 @@ export function candidateSystemsForPostcode(postcode) {
59
59
  }
60
60
  return out;
61
61
  }
62
+ /**
63
+ * Postcode shapes whose code is UNIT-GRADE — a delivery-walk or street-block unit, categorically tighter than any
64
+ * locality centroid, so an EXACT hit on one may lead the admin ladder instead of following the locality-first epoch
65
+ * convention.
66
+ *
67
+ * The convention exists because most postal systems are AREA-class: an FR 5-digit zone is coarser than the commune it
68
+ * contains, so promoting it would trade a good answer for a worse one. Two systems are the other way round, and
69
+ * membership here is earned by MEASUREMENT of the code's granularity, never by "the code has letters in it":
70
+ *
71
+ * - **NL PC6** (`1012 LG`) — ~8 addresses per code; the CBS polygon centroid (#977, the original carve-out).
72
+ * - **GB unit** (`N7 0BT`) — ~15 addresses per code, 1,751,733 shipped from OS Code-Point Open. Measured 2026-08-10
73
+ * against the panel-v2 GB rooftop truth: unit centroid within 1 km on 15/15 rows, median 38 m, max 100 m, while the
74
+ * locality centroid the ladder returned instead was 5.1–14.6 km out.
75
+ * - **CA URBAN LDU** (`M1J 1A8`) — 843,739 six-character codes. Measured on 879 graded rows of the CA OSM-rooftop panel,
76
+ * through the production candidate backend, ladder arm against ladder arm:
77
+ *
78
+ * | 732 URBAN rows | p50 | p75 | p90 | ≤1 km | | -------------- | ---: | ---: | ---: | ---: | | locality-first | 2.51
79
+ * km | 5.42 km | 9.53 km | 26.4% | | postcode-first | **78 m** | **162 m** | **373 m** | **94.7%** |
80
+ *
81
+ * Closer on 90.4% of them. That is GB's tier on a sample fifty times larger than GB's.
82
+ *
83
+ * **CA RURAL is excluded, and the code says which.** Canada Post puts a `0` in the SECOND position of a rural forward
84
+ * sortation area, so `T0H 1M0` is rural and `M1J 1A8` is not — no lookup required. A rural LDU serves a delivery route
85
+ * rather than a block face, and it measures like one. On the same panel, the 114 rural rows:
86
+ *
87
+ * | 114 RURAL rows | p50 | p75 | p90 | ≤1 km |
88
+ * | -------------- | --------: | ----------: | ----------: | --------: |
89
+ * | locality-first | **929 m** | **2.02 km** | **5.73 km** | **53.5%** |
90
+ * | postcode-first | 2.08 km | 4.79 km | 8.11 km | 25.4% |
91
+ *
92
+ * Postcode-first is closer on only 26.3% of them, so the pattern below admits `[1-9]` in that position and nothing
93
+ * else. `509 Main Street South-West Falher AB T0H 1M0` is the worked case: 0.29 km from the locality centroid and 43.18
94
+ * km from its own postal code.
95
+ *
96
+ * The pooled CA number hides that entirely — 0.10 km p50 across both populations reads as a uniform win and is not one.
97
+ * A tier claim that averages two granularities is the thing this table exists to prevent.
98
+ *
99
+ * Lives in codex (per-address-system postal reference) so the Node result assembly (`mailwoman/geocode-core`) and the
100
+ * demo's pin ranking consume ONE tier definition — the 2026-08-11 staged-repoint e2e measured the two disagreeing.
101
+ */
102
+ export const UNIT_GRADE_POSTCODE = [
103
+ // NL PC6 — `1012 LG` / `1012LG`.
104
+ /^\d{4}\s?[A-Z]{2}$/i,
105
+ // GB unit — outward (1-2 letters + digit + optional alnum) + inward `\d[A-Z]{2}`, the same shape
106
+ // `@mailwoman/codex/gb`'s UK_POSTCODE_PATTERN anchors, restated here so this module stays
107
+ // dependency-free within the package (the slices import THIS, never the reverse).
108
+ /^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$/i,
109
+ // CA urban LDU — `M1J 1A8`. The `[1-9]` in the second position is the whole tier claim: a `0` there marks a RURAL
110
+ // forward sortation area, which measures 2.08 km p50 against the locality's 929 m and does not belong here.
111
+ /^[A-Z][1-9][A-Z]\s?\d[A-Z]\d$/i,
112
+ ];
113
+ /**
114
+ * Strip everything but letters and digits, upper-cased — the comparison surface for "did the resolver hit the FULL code
115
+ * or a coarser stem?". `N7 0BT` and `N70BT` are the same code; `N7` is not.
116
+ */
117
+ const alnum = (s) => s.replaceAll(/[^\p{L}\p{N}]/gu, "").toUpperCase();
118
+ /**
119
+ * True when a resolved postcode is an EXACT hit on a unit-grade code — the #977 three-way guard, shared by the Node
120
+ * ladder and the demo pin ranking:
121
+ *
122
+ * 1. The PARSED span is a full unit shape ({@link UNIT_GRADE_POSTCODE}), not a stem the user typed;
123
+ * 2. The node resolved (a coordinate is present — checked by the caller); and
124
+ * 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
125
+ * AREA-class, and promoting it is the exact trade the epoch convention forbids).
126
+ */
127
+ export function isUnitGradePostcodeHit(parsed, resolverName) {
128
+ const value = parsed.trim();
129
+ if (!value || !UNIT_GRADE_POSTCODE.some((re) => re.test(value)))
130
+ return false;
131
+ return alnum(resolverName ?? "") === alnum(value);
132
+ }
133
+ /**
134
+ * Address systems whose AREA-grade postal code is still FINER than the locality containing it — the third granularity
135
+ * tier, between {@link UNIT_GRADE_POSTCODE} and the locality-first default.
136
+ *
137
+ * Whether a postal zone is coarser than its locality is a fact about a country's ADMINISTRATIVE geography, not about
138
+ * its postal system, and code length does not predict it: FR and DE are both 5-digit and land on opposite sides. France
139
+ * has ~35,000 communes and one code postal often spans several, so the commune is finer. A German Gemeinde can be
140
+ * enormous — Berlin is one WOF locality — so the PLZ is finer by a wide margin.
141
+ *
142
+ * Membership is earned by a full-panel measurement, the same bar {@link UNIT_GRADE_POSTCODE} sets for CA. Coordinate
143
+ * p50 on the OpenAddresses panels, locality-first (the default) against the postcode point:
144
+ *
145
+ * | country | rows | locality-first | postcode point | verdict |
146
+ * | ------- | ----: | -------------: | -------------: | ------------------------------------------------------------------ |
147
+ * | **DE** | 2,997 | 5.84 km | **1.24 km** | postcode, on EVERY percentile incl. p99 (21.50 → 10.57) |
148
+ * | FR | 3,000 | **0.97 km** | 2.64 km | locality, closer on 77.5% of rows |
149
+ * | IT | 2,833 | **1.34 km** | 3.05 km | locality, closer on 66.4% |
150
+ * | ES | 2,929 | **0.68 km** | 0.97 km | locality, but near a coin flip — 46.1% of rows prefer the postcode |
151
+ * | US | 577 | **2.28 km** | 4.15 km | locality (see below) |
152
+ *
153
+ * **The US row is measured on the population production actually sends to the ladder.** Its rooftop cascade is US-only
154
+ * by construction (`selectAddressPointsDB` composes `address-points-us-<slug>.db`), and it serves 94.2% of US queries,
155
+ * so only 577 of 10,000 panel rows reach an admin decision at all. Those are the rows no rooftop or interpolation shard
156
+ * could place, which skews rural — exactly where a locality centroid sits close and a ZIP zone is wide. Measured over
157
+ * all 10,000 rows instead, the US looks like a postcode-first country (2.41 km vs 3.63); that is a selection effect,
158
+ * and it is why this table reports 577 rows for the US and full panels for the others, which have no such cascade.
159
+ */
160
+ export const AREA_POSTCODE_FINER_THAN_LOCALITY = new Set(["DE"]);
161
+ /**
162
+ * True when this country's area-grade postal code outranks its locality. Absent or unknown country → false, so the
163
+ * locality-first convention is what an unscoped query gets.
164
+ */
165
+ export function areaPostcodeLeadsLocality(country) {
166
+ return country !== undefined && AREA_POSTCODE_FINER_THAN_LOCALITY.has(country.trim().toUpperCase());
167
+ }
62
168
  //# 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":["../postcode-systems.ts"],"names":[],"mappings":"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"}
@@ -58,7 +58,7 @@ const SLOTS = ["country_surface", "country_ambiguous"];
58
58
  * Committed output path (a codex-derived artifact, like export-country-surfaces.ts — no argv, so the no-process-globals
59
59
  * lint policy holds; codex stays zero-runtime-dep).
60
60
  */
61
- const OUTPUT = resolve(import.meta.dirname, "../../data/gazetteer/country-surface-lexicon-v1.json");
61
+ const OUTPUT = resolve(import.meta.dirname, "../../../data/gazetteer/country-surface-lexicon-v1.json");
62
62
  /**
63
63
  * THE shared word-normalization rule (identical to build-gazetteer-anchor-lexicon.mjs and mirrored in
64
64
  * gazetteer_char_paint on both sides): per whitespace-word, strip LEADING/TRAILING characters that are not Unicode
@@ -1 +1 @@
1
- {"version":3,"file":"build-country-surface-lexicon.js","sourceRoot":"","sources":["../../tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE5C,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEvE;;GAEG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAAC,CAAA;AAElC,MAAM,GAAG,GAAG,EAAE,eAAe,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAA;AACxD,MAAM,KAAK,GAAG,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAA;AAEtD;;;GAGG;AACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,sDAAsD,CAAC,CAAA;AAEnG;;;;;GAKG;AACH;;GAEG;AACH,MAAM,WAAW,GAAG,CAAC,CAAS,EAAW,EAAE;IAC1C,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAE9C,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,wBAAwB,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACpG,CAAC,CAAA;AAED,qGAAqG;AACrG,mGAAmG;AACnG,qDAAqD;AACrD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;AACxE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,sBAA2C,CAAC,CAAA;AAEnF;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAA;AAExH,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAW,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAExH,wFAAwF;AACxF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA,CAAC,gBAAgB;AAC1D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAA,CAAC,sBAAsB;AACpE,IAAI,QAAQ,GAAG,CAAC,CAAA;AAEhB,SAAS,GAAG,CAAC,OAAe;IAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAExB,IAAI,CAAC,CAAC;QAAE,OAAM;IAEd,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;QAErC,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,gGAAgG;QAChG,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACxF,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;QAExD,OAAM;IACP,CAAC;IAED,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IAE5B,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5B,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC3C,4FAA4F;IAC5F,oBAAoB;IACpB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AACjD,CAAC;AAED,qGAAqG;AACrG,kGAAkG;AAClG,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACvB,GAAG,CAAC,CAAC,CAAC,CAAA;IACP,CAAC;AACF,CAAC;AAED,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,CAAA;AACV,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;AAElH,MAAM,OAAO,GAAG;IACf,OAAO,EAAE,CAAC;IACV,YAAY,EACX,8GAA8G;IAC/G,WAAW,EAAE,KAAK,CAAC,MAAM;IACzB,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,GAAG;IACT,SAAS,EAAE,QAAQ;IACnB,KAAK,EAAE;QACN,SAAS,EACR,wFAAwF;YACxF,+GAA+G;QAChH,OAAO,EACN,uKAAuK;QACxK,YAAY,EACX,mLAAmL;QACpL,IAAI,EAAE,gHAAgH;QACtH,OAAO,EACN,4HAA4H;KAC7H;IACD,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7F,CAAA;AAED,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAC/C,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAE9D,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,cAAc,WAAW,CAAC,IAAI,iBAAiB;IAC9E,aAAa,QAAQ,KAAK,gBAAgB,CAAC,MAAM,eAAe,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAC7L,CAAA"}
1
+ {"version":3,"file":"build-country-surface-lexicon.js","sourceRoot":"","sources":["../../tools/build-country-surface-lexicon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE5C,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEvE;;GAEG;AACH,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAAC,CAAA;AAElC,MAAM,GAAG,GAAG,EAAE,eAAe,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAA;AACxD,MAAM,KAAK,GAAG,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAA;AAEtD;;;GAGG;AACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,yDAAyD,CAAC,CAAA;AAEtG;;;;;GAKG;AACH;;GAEG;AACH,MAAM,WAAW,GAAG,CAAC,CAAS,EAAW,EAAE;IAC1C,MAAM,OAAO,GAAG,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAE9C,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,wBAAwB,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACpG,CAAC,CAAA;AAED,qGAAqG;AACrG,mGAAmG;AACnG,qDAAqD;AACrD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;AACxE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,sBAA2C,CAAC,CAAA;AAEnF;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAA;AAExH,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAW,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAExH,wFAAwF;AACxF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAA,CAAC,gBAAgB;AAC1D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAA,CAAC,sBAAsB;AACpE,IAAI,QAAQ,GAAG,CAAC,CAAA;AAEhB,SAAS,GAAG,CAAC,OAAe;IAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAExB,IAAI,CAAC,CAAC;QAAE,OAAM;IAEd,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;QAErC,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,gGAAgG;QAChG,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACxF,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;QAExD,OAAM;IACP,CAAC;IAED,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IAE5B,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5B,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC3C,4FAA4F;IAC5F,oBAAoB;IACpB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AACjD,CAAC;AAED,qGAAqG;AACrG,kGAAkG;AAClG,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACvB,GAAG,CAAC,CAAC,CAAC,CAAA;IACP,CAAC;AACF,CAAC;AAED,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,CAAA;AACV,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;AAElH,MAAM,OAAO,GAAG;IACf,OAAO,EAAE,CAAC;IACV,YAAY,EACX,8GAA8G;IAC/G,WAAW,EAAE,KAAK,CAAC,MAAM;IACzB,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,GAAG;IACT,SAAS,EAAE,QAAQ;IACnB,KAAK,EAAE;QACN,SAAS,EACR,wFAAwF;YACxF,+GAA+G;QAChH,OAAO,EACN,uKAAuK;QACxK,YAAY,EACX,mLAAmL;QACpL,IAAI,EAAE,gHAAgH;QACtH,OAAO,EACN,4HAA4H;KAC7H;IACD,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7F,CAAA;AAED,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AAC/C,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAE9D,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,cAAc,WAAW,CAAC,IAAI,iBAAiB;IAC9E,aAAa,QAAQ,KAAK,gBAAgB,CAAC,MAAM,eAAe,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAC7L,CAAA"}
@@ -25,7 +25,7 @@ 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");
28
+ const out = resolve(import.meta.dirname, "../../../corpus-python/src/mailwoman_train/data/country-surfaces.json");
29
29
  writeFileSync(out, JSON.stringify({
30
30
  _generated: "codex/tools/export-country-surfaces.ts from @mailwoman/codex COUNTRY_SURFACE_FORMS + ISO2_TO_NAME",
31
31
  surfaces,
@@ -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":["../../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,uEAAuE,CAAC,CAAA;AAEjH,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"}
@@ -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":["../../tools/generate-country-population.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAoCH;;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 { writeFileSync } from "node:fs";
23
+ import { fileURLToPath } from "node:url";
24
+ import { APIClient, pluckResponseData } from "@mailwoman/core/api";
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 = fileURLToPath(new URL("../country/population.ts", import.meta.url));
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
+ writeFileSync(outPath, `${header}\n${body}\n}\n`);
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":["../../tools/generate-country-population.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"generate-country-reference.d.ts","sourceRoot":"","sources":["../../tools/generate-country-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgCH;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CACf;AA6BD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,EAC7C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,+BAA+B,CAAC,CAqE1C"}
1
+ {"version":3,"file":"generate-country-reference.d.ts","sourceRoot":"","sources":["../../tools/generate-country-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkCH;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CACf;AA6BD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,+BAAoC,EAC7C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,+BAA+B,CAAC,CAqE1C"}
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { writeFileSync } from "node:fs";
17
17
  import { fileURLToPath } from "node:url";
18
+ import { APIClient, pluckResponseData } from "@mailwoman/core/api";
18
19
  const SOURCE = "https://raw.githubusercontent.com/mledoze/countries/master/countries.json";
19
20
  /**
20
21
  * The committed output path, resolved relative to this module (codex/tools/ → codex/country/). The codegen is
@@ -48,10 +49,9 @@ const serialize = (o) => JSON.stringify(o, null, 0)
48
49
  */
49
50
  export async function generateCountryReference(options = {}, report) {
50
51
  const outPath = options.out ?? DEFAULT_OUT;
51
- const response = await fetch(SOURCE);
52
- if (!response.ok)
53
- throw new Error(`fetch ${SOURCE} failed: ${response.status}`);
54
- const countries = (await response.json());
52
+ const countries = await new APIClient({ displayName: "mledoze-countries", retry: true })
53
+ .fetch({ url: SOURCE })
54
+ .then(pluckResponseData);
55
55
  const rows = {};
56
56
  for (const country of countries) {
57
57
  const alpha2 = country.cca2;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-country-reference.js","sourceRoot":"","sources":["../../tools/generate-country-reference.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generate-country-reference.js","sourceRoot":"","sources":["../../tools/generate-country-reference.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"generate-official-languages.d.ts","sourceRoot":"","sources":["../../tools/generate-official-languages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAeH;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CACf;AAmBD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,GAAE,gCAAqC,EAC9C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,gCAAgC,CAAC,CA2H3C"}
1
+ {"version":3,"file":"generate-official-languages.d.ts","sourceRoot":"","sources":["../../tools/generate-official-languages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAChD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CACf;AAsBD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,GAAE,gCAAqC,EAC9C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,gCAAgC,CAAC,CA2H3C"}
@@ -21,6 +21,7 @@
21
21
  import { readFileSync, writeFileSync } from "node:fs";
22
22
  import { join } from "node:path";
23
23
  import { fileURLToPath } from "node:url";
24
+ import { APIClient, pluckResponseData } from "@mailwoman/core/api";
24
25
  import { parseJSONStrict } from "@mailwoman/core/objects";
25
26
  /**
26
27
  * The committed output path, resolved relative to this module (codex/tools/ → codex/country/). The codegen is
@@ -28,15 +29,17 @@ import { parseJSONStrict } from "@mailwoman/core/objects";
28
29
  * `import.meta.url` points at the source tree.
29
30
  */
30
31
  const DEFAULT_OUT = fileURLToPath(new URL("../country/official-languages.ts", import.meta.url));
32
+ /**
33
+ * One call per CLDR supplemental file, against a CDN that throttles. Retry only — the caller makes a handful of
34
+ * requests, so a rate budget would be ceremony over a burst that never happens.
35
+ */
36
+ const cldrClient = new APIClient({ displayName: "cldr", retry: true });
31
37
  async function loadCLDR(file, cldrDir, cldrVersion) {
32
38
  if (cldrDir) {
33
39
  return parseJSONStrict(readFileSync(join(cldrDir, `cldr-${file}.json`), "utf8"));
34
40
  }
35
41
  const url = `https://cdn.jsdelivr.net/npm/cldr-core@${cldrVersion}/supplemental/${file}.json`;
36
- const res = await fetch(url);
37
- if (!res.ok)
38
- throw new Error(`${url}: HTTP ${res.status}`);
39
- return res.json();
42
+ return cldrClient.fetch({ url }).then(pluckResponseData);
40
43
  }
41
44
  /**
42
45
  * Regenerate the committed `OFFICIAL_LANGUAGES` table from CLDR supplemental data.