Files
worldmonitor/server/_shared/country-token.ts
Lucas Passos 4a25d8c133 feat(resilience): add country dimension scorers (#2659)
* feat(resilience): add dimension scorers

Root cause: the resilience domain had proto/static-data prerequisites but no scoring layer tied to the actual seed keys in this repo.

Add 13 country dimension scorers backed by the existing resilience static snapshot and live Redis domains, plus focused contract tests that exercise plausible NO/US/YE ordering and memoized seed reads.

Validation:
- npx tsx --test tests/resilience-stats.test.mts tests/resilience-dimension-scorers.test.mts
- npm run typecheck:api (fails on pre-existing server/__tests__/entitlement-check.test.ts vitest import)

* fix(review): align WGI keys, fix no-data score, extract shared util, harden matching

- Align WGI indicator keys in test fixtures to match seed format (VA.EST, PV.EST, etc.)
- Return score 0 (not 50) at coverage 0 — avoids misleading neutral score when no data exists
- Extract normalizeCountryToken to server/_shared/country-token.ts for reuse across resilience modules
- Add console.warn when AQUASTAT indicator falls through to value-range heuristic
- Skip single-word aliases shorter than 6 chars in matchesCountryText to prevent false positives (e.g., "Guinea" matching "Equatorial Guinea")

* fix(review): use year-suffixed displacement key, replace blanket alias cutoff

P1: Displacement data is stored as displacement:summary:v1:{year} but
the scorers were reading displacement:summary:v1 (no year suffix),
causing socialCohesion and borderSecurity to systematically miss
displacement data in production. Fixed by appending current year.

P2: The < 6 char alias cutoff silently dropped legitimate countries
(Yemen, Chile, China, Japan, Italy). Replaced with an explicit
AMBIGUOUS_ALIASES set listing only the names that are genuine substrings
of other country names (guinea, congo, niger, samoa, sudan, korea,
virgin, georgia, dominica).

* fix(review): build full ISO2→ISO3 from geojson, sort BIS credit, evict failed cache, fix zero-event scoring

- P1: Replace 32-country conflict ISO2_TO_ISO3 with full mapping built
  from countries.geojson (~250 countries). Debt lookup now works for
  JP, KR, CA, AU, IT, MX and all other countries.
- P2: Sort BIS credit entries by date before picking latest, matching
  the pattern already used for exchange rates.
- P2: Evict rejected promises from memoized seed reader cache so
  transient Redis timeouts don't become permanent misses.
- P2: Zero events from a queried data source now scores 100 (clean)
  instead of null (missing). Check source availability (raw != null)
  instead of event count > 0 to distinguish "no data" from "no events".

---------

Co-authored-by: Elie Habib <elie.habib@gmail.com>
2026-04-04 11:47:45 +04:00

11 lines
273 B
TypeScript

export function normalizeCountryToken(value: unknown): string {
return String(value || '')
.normalize('NFKD')
.replace(/\p{Diacritic}/gu, '')
.toLowerCase()
.replace(/&/g, ' and ')
.replace(/[''.(),/-]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}