fifa-wc26 0.2.0 → 0.2.2

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 (3) hide show
  1. package/README.md +2 -23
  2. package/dist/cli.js +127 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@ The package name is `fifa-wc26`; the binary it installs is `wc26`.
14
14
 
15
15
  ## Quick start
16
16
 
17
- Default providers work keyless (`espn`, `thesportsdb`). The bundled `football-data` provider talks to a project-hosted proxy (no user-side key required); see [Providers](#providers) for proxy details.
17
+ Default providers work keyless. No user-side key required.
18
18
 
19
19
  ```bash
20
20
  wc26 fixtures --team ARG --from today
@@ -45,33 +45,12 @@ export WC26_THESPORTSDB_KEY=...
45
45
 
46
46
  `wc26` chains providers with automatic failover:
47
47
 
48
- 1. `football-data` (api.football-data.org via the project proxy — full 104-match WC schedule + knockout stages; activates when a proxy URL is baked into the build or `WC26_PROXY_URL` is set)
48
+ 1. `football-data` (api.football-data.org — full 104-match WC schedule + knockout stages; no key needed)
49
49
  2. `espn` (site.api.espn.com — keyless)
50
50
  3. `thesportsdb` (thesportsdb.com — free tier uses key `3`; set a paid key for higher limits)
51
51
 
52
52
  Set a paid `thesportsdb` key via env var (`WC26_THESPORTSDB_KEY`) or `wc26 config set apiKey thesportsdb <key>`.
53
53
 
54
- ### football-data proxy
55
-
56
- The football-data.org API token is **not** shipped to end users. Requests are
57
- forwarded through a small Vercel Edge Function (`proxy/vercel/`) that attaches
58
- the token server-side and CDN-caches responses to amortize the 10 req/min
59
- upstream limit.
60
-
61
- Maintainers: deploy the proxy, then set the resulting URL once before
62
- publishing:
63
-
64
- ```bash
65
- cd proxy/vercel
66
- npm install && npx vercel login && npx vercel link
67
- npx vercel env add FOOTBALL_DATA_TOKEN production # paste token when prompted
68
- npm run deploy # prints https://<project>.vercel.app
69
- ```
70
-
71
- Then edit `DEFAULT_PROXY_BASE` in `src/providers/football-data.ts` to that URL
72
- and `npm run build`. End users get the proxied provider automatically; no key,
73
- no signup. Override at runtime with `WC26_PROXY_URL=...`.
74
-
75
54
  ## Cache
76
55
 
77
56
  JSON files under `~/.wc26/cache/`. TTLs: fixtures 6 h, standings 1 h, bracket 1 h, finished match 24 h, live 10 s. On provider failure the CLI serves stale cache and tags output `[STALE]` (or `"stale": true` in JSON).
package/dist/cli.js CHANGED
@@ -673,6 +673,120 @@ var TheSportsDbProvider = class {
673
673
 
674
674
  // src/providers/football-data.ts
675
675
  init_esm_shims();
676
+
677
+ // src/data/wc26-venues.ts
678
+ init_esm_shims();
679
+ var WC26_VENUES = {
680
+ "537327": "Estadio Azteca, Mexico City",
681
+ "537328": "Estadio Akron, Guadalajara",
682
+ "537329": "Mercedes-Benz Stadium, Atlanta",
683
+ "537330": "Estadio Akron, Guadalajara",
684
+ "537331": "Estadio Azteca, Mexico City",
685
+ "537332": "Estadio BBVA, Monterrey",
686
+ "537333": "BMO Field, Toronto",
687
+ "537334": "Levi's Stadium, Bay Area",
688
+ "537335": "SoFi Stadium, Los Angeles",
689
+ "537336": "BC Place, Vancouver",
690
+ "537337": "BC Place, Vancouver",
691
+ "537338": "Lumen Field, Seattle",
692
+ "537339": "MetLife Stadium, New York/New Jersey",
693
+ "537340": "Gillette Stadium, Boston",
694
+ "537341": "Lincoln Financial Field, Philadelphia",
695
+ "537342": "Gillette Stadium, Boston",
696
+ "537343": "Hard Rock Stadium, Miami",
697
+ "537344": "Mercedes-Benz Stadium, Atlanta",
698
+ "537345": "SoFi Stadium, Los Angeles",
699
+ "537346": "BC Place, Vancouver",
700
+ "537347": "Levi's Stadium, Bay Area",
701
+ "537348": "Lumen Field, Seattle",
702
+ "537349": "SoFi Stadium, Los Angeles",
703
+ "537350": "Levi's Stadium, Bay Area",
704
+ "537351": "NRG Stadium, Houston",
705
+ "537352": "Lincoln Financial Field, Philadelphia",
706
+ "537353": "BMO Field, Toronto",
707
+ "537354": "Arrowhead Stadium, Kansas City",
708
+ "537355": "MetLife Stadium, New York/New Jersey",
709
+ "537356": "Lincoln Financial Field, Philadelphia",
710
+ "537357": "AT&T Stadium, Dallas",
711
+ "537358": "Estadio BBVA, Monterrey",
712
+ "537359": "NRG Stadium, Houston",
713
+ "537360": "Estadio BBVA, Monterrey",
714
+ "537361": "Arrowhead Stadium, Kansas City",
715
+ "537362": "AT&T Stadium, Dallas",
716
+ "537363": "Lumen Field, Seattle",
717
+ "537364": "SoFi Stadium, Los Angeles",
718
+ "537365": "SoFi Stadium, Los Angeles",
719
+ "537366": "BC Place, Vancouver",
720
+ "537367": "BC Place, Vancouver",
721
+ "537368": "Lumen Field, Seattle",
722
+ "537369": "Mercedes-Benz Stadium, Atlanta",
723
+ "537370": "Hard Rock Stadium, Miami",
724
+ "537371": "Mercedes-Benz Stadium, Atlanta",
725
+ "537372": "Hard Rock Stadium, Miami",
726
+ "537373": "Estadio Akron, Guadalajara",
727
+ "537374": "NRG Stadium, Houston",
728
+ "537375": "Lincoln Financial Field, Philadelphia",
729
+ "537376": "NRG Stadium, Houston",
730
+ "537377": "MetLife Stadium, New York/New Jersey",
731
+ "537378": "Estadio Azteca, Mexico City",
732
+ "537379": "AT&T Stadium, Dallas",
733
+ "537380": "Lumen Field, Seattle",
734
+ "537381": "Mercedes-Benz Stadium, Atlanta",
735
+ "537382": "BC Place, Vancouver",
736
+ "537383": "Gillette Stadium, Boston",
737
+ "537384": "SoFi Stadium, Los Angeles",
738
+ "537385": "Hard Rock Stadium, Miami",
739
+ "537386": "Arrowhead Stadium, Kansas City",
740
+ "537387": "AT&T Stadium, Dallas",
741
+ "537388": "Mercedes-Benz Stadium, Atlanta",
742
+ "537389": "Hard Rock Stadium, Miami",
743
+ "537390": "MetLife Stadium, New York/New Jersey",
744
+ "537391": "MetLife Stadium, New York/New Jersey",
745
+ "537392": "Gillette Stadium, Boston",
746
+ "537393": "Lincoln Financial Field, Philadelphia",
747
+ "537394": "MetLife Stadium, New York/New Jersey",
748
+ "537395": "Gillette Stadium, Boston",
749
+ "537396": "BMO Field, Toronto",
750
+ "537397": "Arrowhead Stadium, Kansas City",
751
+ "537398": "Levi's Stadium, Bay Area",
752
+ "537399": "AT&T Stadium, Dallas",
753
+ "537400": "Levi's Stadium, Bay Area",
754
+ "537401": "AT&T Stadium, Dallas",
755
+ "537402": "Arrowhead Stadium, Kansas City",
756
+ "537403": "NRG Stadium, Houston",
757
+ "537404": "Estadio Azteca, Mexico City",
758
+ "537405": "NRG Stadium, Houston",
759
+ "537406": "Estadio Akron, Guadalajara",
760
+ "537407": "Hard Rock Stadium, Miami",
761
+ "537408": "Mercedes-Benz Stadium, Atlanta",
762
+ "537409": "AT&T Stadium, Dallas",
763
+ "537410": "BMO Field, Toronto",
764
+ "537411": "Gillette Stadium, Boston",
765
+ "537412": "BMO Field, Toronto",
766
+ "537413": "MetLife Stadium, New York/New Jersey",
767
+ "537414": "Lincoln Financial Field, Philadelphia",
768
+ "537415": "Gillette Stadium, Boston",
769
+ "537416": "MetLife Stadium, New York/New Jersey",
770
+ "537417": "SoFi Stadium, Los Angeles",
771
+ "537418": "Estadio BBVA, Monterrey",
772
+ "537419": "BMO Field, Toronto",
773
+ "537420": "SoFi Stadium, Los Angeles",
774
+ "537421": "Levi's Stadium, Bay Area",
775
+ "537422": "Lumen Field, Seattle",
776
+ "537423": "NRG Stadium, Houston",
777
+ "537424": "AT&T Stadium, Dallas",
778
+ "537425": "Estadio Azteca, Mexico City",
779
+ "537426": "Mercedes-Benz Stadium, Atlanta",
780
+ "537427": "Hard Rock Stadium, Miami",
781
+ "537428": "AT&T Stadium, Dallas",
782
+ "537429": "BC Place, Vancouver",
783
+ "537430": "Arrowhead Stadium, Kansas City"
784
+ };
785
+ function lookupWC26Venue(matchId) {
786
+ return WC26_VENUES[String(matchId)];
787
+ }
788
+
789
+ // src/providers/football-data.ts
676
790
  var DEFAULT_PROXY_BASE = "https://wc26-proxy.vercel.app";
677
791
  function resolveBase(override) {
678
792
  return (override ?? process.env.WC26_PROXY_URL ?? DEFAULT_PROXY_BASE).replace(/\/+$/, "");
@@ -722,15 +836,16 @@ function fixtureFromMatch(m) {
722
836
  homePens: m.score?.penalties?.home ?? void 0,
723
837
  awayPens: m.score?.penalties?.away ?? void 0
724
838
  } : void 0;
725
- const group = m.group ? m.group.replace(/^Group\s+/i, "").trim().toUpperCase().slice(0, 1) : void 0;
839
+ const group = m.group ? m.group.replace(/^GROUP[_\s]+/i, "").trim().toUpperCase().slice(0, 1) : void 0;
840
+ const id = String(m.id);
726
841
  return {
727
- id: String(m.id),
842
+ id,
728
843
  utcKickoff: new Date(m.utcDate).toISOString(),
729
844
  stage: mapStage3(m.stage),
730
845
  group: group || void 0,
731
846
  home: teamRef2(m.homeTeam),
732
847
  away: teamRef2(m.awayTeam),
733
- venue: m.venue ?? "",
848
+ venue: m.venue ?? lookupWC26Venue(id) ?? "",
734
849
  status,
735
850
  score
736
851
  };
@@ -885,18 +1000,19 @@ async function buildRegistry(opts) {
885
1000
  }
886
1001
  return new ProviderRegistry(providers);
887
1002
  }
888
- async function runCached(key, ttlSec, fetcher, opts) {
1003
+ async function runCached(key, ttlSec, fetcher, opts, cacheOpts = {}) {
1004
+ const validate = cacheOpts.validate ?? (() => true);
889
1005
  if (!opts.noCache) {
890
1006
  const hit = await env.cache.read(key, { allowStale: false });
891
- if (hit) return { data: hit.data, stale: false };
1007
+ if (hit && validate(hit.data)) return { data: hit.data, stale: false };
892
1008
  }
893
1009
  try {
894
1010
  const { data, provider } = await fetcher();
895
- await env.cache.write(key, data, ttlSec, provider);
1011
+ if (validate(data)) await env.cache.write(key, data, ttlSec, provider);
896
1012
  return { data, stale: false };
897
1013
  } catch (e) {
898
1014
  const stale = await env.cache.read(key, { allowStale: true });
899
- if (stale) {
1015
+ if (stale && validate(stale.data)) {
900
1016
  const reason = e instanceof WC26Error ? e.code.toLowerCase() : "unreachable";
901
1017
  return { data: stale.data, stale: true, reason };
902
1018
  }
@@ -1061,7 +1177,8 @@ function fixturesCmd(p) {
1061
1177
  });
1062
1178
  return { data: data2, provider };
1063
1179
  },
1064
- g
1180
+ g,
1181
+ { validate: (d) => g.team || g.from || g.to || g.stage ? d.length > 0 : d.length >= 50 }
1065
1182
  );
1066
1183
  const filtered = data.filter(
1067
1184
  (f) => (!q.stage || f.stage === q.stage) && (!q.teamCode || f.home.code === q.teamCode || f.away.code === q.teamCode)
@@ -1099,7 +1216,8 @@ function nextCmd(p) {
1099
1216
  });
1100
1217
  return { data: data2, provider };
1101
1218
  },
1102
- g
1219
+ g,
1220
+ { validate: (d) => g.team ? d.length > 0 : d.length >= 50 }
1103
1221
  );
1104
1222
  const now = Date.now();
1105
1223
  const out = data.filter((f) => f.status === "scheduled" && new Date(f.utcKickoff).getTime() > now).sort((a, b) => a.utcKickoff.localeCompare(b.utcKickoff)).slice(0, n);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fifa-wc26",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "FIFA World Cup 2026 CLI: fixtures, live scores, group standings, ASCII bracket, team lookup. Keyless providers, smart cache, terminal-friendly output.",
5
5
  "type": "module",
6
6
  "bin": {