* chore(scripts): replace xlsx with exceljs
xlsx has known vulnerabilities with no patched version available.
exceljs is actively maintained (~6M weekly downloads) and covers
the same read-only parsing needs in seed-fuel-prices.mjs and
backfill-fuel-prices-prev.mjs.
* fix(scripts): handle ExcelJS Date cells and add Czechia mapping
ExcelJS returns Date objects for date cells (unlike xlsx which returned
raw strings). Convert to DD/MM/YYYY format to preserve downstream regex
parsing. Also add 'Czechia' alias to EU country map since the EU XLSX
now uses the short form instead of 'Czech Republic' (27/27 countries).
* fix(scripts): handle ExcelJS rich-text cell values
ExcelJS returns {richText: [{text: '...'}]} for formatted cells.
String() on these produces '[object Object]', breaking header
detection regex. Extract plain text from rich-text arrays.
* fix(fuel-prices): add xlsx to scripts/package-lock.json so npm ci succeeds
* chore(pre-push): detect scripts/package-lock.json out-of-sync with package.json
Sanctions seed has been failing since PR #2008 with:
ERR_MODULE_NOT_FOUND: Cannot find package 'sax'
PR #2008 replaced fast-xml-parser with SAX streaming but only updated
the root package.json, not scripts/package.json which is the Railway
container manifest. Railway runs npm ci from scripts/ so sax was never
installed. Add sax ^1.6.0 and remove the now-unused fast-xml-parser.
Also raise consumer-prices SEED_META maxStaleMin from 90-120 to 1500 min.
publish.ts runs once daily at 02:30 UTC; all five consumer-prices keys
were permanently STALE_SEED for 22+ hours/day after the daily run.
1500 min (25h) = 24h cadence + 1h grace before warning.
seed-sanctions-pressure.mjs imports fast-xml-parser to parse OFAC SDN
XML feeds, but the package was never added to scripts/package.json.
Railway deploys crash with ERR_MODULE_NOT_FOUND on startup.
* feat: add WTO trade policy service with 4 RPC endpoints and TradePolicyPanel
Adds a new `trade` RPC domain backed by the WTO API (apiportal.wto.org) for
trade policy intelligence: quantitative restrictions, tariff timeseries,
bilateral trade flows, and SPS/TBT barrier notifications.
New files: 6 protos, generated server/client, 4 server handlers + shared WTO
fetch utility, client service with circuit breakers, TradePolicyPanel (4 tabs),
and full API key infrastructure (Rust keychain, sidecar, runtime config).
Panel registered for FULL and FINANCE variants with data loader integration,
command palette entry, status panel tracking, data freshness monitoring, and
i18n across all 17 locale files.
https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye
* chore: update package-lock.json
https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye
* fix: move tab click listener to constructor to prevent leak
The delegated click handler was added inside render(), which runs
on every data update (4× per load cycle). Since the listener targets
this.content (a persistent container), each call stacked a duplicate
handler. Moving it to the constructor binds it exactly once.
https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye
---------
Co-authored-by: Claude <noreply@anthropic.com>