campus-stats 0.5.0 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.6.0
6
+
7
+ - **Remove injuries** from CLI, library, cache, and SQLite (not in StatsBomb API).
8
+ - **BYOK:** free Open Data by default; optional StatsBomb customer login
9
+ (`SB_USERNAME` / `SB_PASSWORD`) → paid API at `data.statsbombservices.com`
10
+ - **Paid extras:** `campus endpoints` + flags (`--with-paid-player-season-stats`, …)
11
+ - **README:** Campus organizes data — free path already connected; bring-your-own
12
+ StatsBomb licence with clear examples
13
+
5
14
  ## 0.5.0
6
15
 
7
16
  - **CI / data host:** GitLab → **GitHub Actions**
package/README.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Campus (`campus-stats`)
2
2
 
3
- **Open, normalized, queryable data for women's football — installable in your app.**
3
+ **A data layer for women's football — not a data vendor.**
4
4
 
5
- `campus-stats` is the npm package for **Campus**: a library + CLI that pulls
6
- women's football stats from public sources, normalizes them into one schema,
7
- and gives your web/API/app access to competitions, seasons, clubs, national
8
- teams, matches, lineups, player stats, and basic fantasy points.
5
+ Campus does **not** sell football data. It **organizes** data from existing
6
+ sources into one schema you can query from a CLI or drop into a web app.
7
+
8
+ You choose how to feed it:
9
+
10
+ | Path | What you do | What Campus does |
11
+ |------|-------------|------------------|
12
+ | **Free** | Nothing — already connected | Syncs [StatsBomb Open Data](https://github.com/statsbomb/open-data) (and an FBref schedule pilot) |
13
+ | **Your StatsBomb licence** | Set `SB_USERNAME` / `SB_PASSWORD` | Talks to the StatsBomb **paid API** with *your* credentials and normalizes the response |
14
+
15
+ Same CLI, same `CampusClient`, same local cache either way.
9
16
 
10
17
  Requires **Node.js ≥ 22**.
11
18
 
@@ -13,129 +20,240 @@ Requires **Node.js ≥ 22**.
13
20
 
14
21
  ## Install
15
22
 
16
- Published on **npmjs.com**:
17
-
18
23
  ```bash
19
24
  npm install campus-stats
25
+ ```
20
26
 
27
+ Binaries: `campus` and `campus-stats` (same entrypoint).
28
+
29
+ ```bash
21
30
  npx campus sync --competition "Liga F"
22
31
  npx campus teams --competition "Liga F"
23
32
  npx campus matches --competition "Liga F" --team "Barcelona"
24
33
  ```
25
34
 
26
- The CLI binaries are `campus` and `campus-stats` (same entrypoint).
35
+ ---
27
36
 
28
- Documentation lives in this README.
29
- Source: https://github.com/Minacava/campus-stats
37
+ ## Try the free path (no account)
30
38
 
31
- ---
39
+ Open Data is wired in. No signup, no API key:
32
40
 
33
- ## Use as a library (recommended for apps)
41
+ ```bash
42
+ # Sync one competition from StatsBomb Open Data
43
+ npx campus sync --competition "Liga F"
34
44
 
35
- ```ts
36
- import { CampusClient } from "campus-stats";
45
+ # Or every women's competition in the open catalogue
46
+ npx campus sync --fantasy
37
47
 
38
- const client = await CampusClient.open();
39
- await client.syncFantasy({ includePlayerStats: true });
40
- // or: const client = await CampusClient.fromBundle();
48
+ # Optional: lineups + basic player stats (capped)
49
+ npx campus sync --fantasy --with-players
41
50
 
42
- const clubs = client.teams({ competition: "Liga F", kind: "club" });
43
- const nations = client.teams({ competition: "Women's World Cup", kind: "national" });
44
- const matches = client.matches({ competition: "Liga F", team: "Barcelona" });
45
- const squad = client.squad({ competition: "Liga F", team: "Barcelona" });
46
- const points = client.fantasyPoints({ competition: "Liga F", player: "Walsh" });
51
+ # Query the local cache
52
+ npx campus competitions
53
+ npx campus matches --competition "Liga F" --team "Barcelona"
54
+ npx campus squad --competition "Liga F" --team "Barcelona"
55
+ npx campus fantasy-points --competition "Liga F"
47
56
 
48
- return Response.json({ clubs, nations, matches, squad, points });
57
+ # Confirm you are on free Open Data
58
+ npx campus credentials
59
+ # → statsbomb.mode: "open-data"
49
60
  ```
50
61
 
51
- ### What you get from the package
62
+ Data is stored in `.campus/cache.json` (or use `--sqlite` / `--db <path>`).
52
63
 
53
- | API | Purpose |
54
- |-----|---------|
55
- | `CampusClient` | Main entry for apps: sync / pull / query |
56
- | `syncFantasyBundle` / `updateCachedCompetitions` | Low-level sync helpers |
57
- | `scoreFantasyPoints` | Default fantasy scoring rules |
58
- | `listInjuries` | Stable stub (empty until a source exists) |
59
- | Types | `Competition`, `Team`, `Match`, `Player`, `LineupEntry`, … |
60
- | CLI bins `campus` / `campus-stats` | Same data from the terminal |
64
+ **Free sources today**
61
65
 
62
- ---
66
+ | Source | Coverage |
67
+ |--------|----------|
68
+ | StatsBomb Open Data | Women's competitions published on GitHub (e.g. Liga F, WSL, NWSL, major tournaments) |
69
+ | FBref | Schedule pilot — WSL + Liga F (`--source fbref`) |
63
70
 
64
- ## Keep data fresh (cron)
71
+ Coverage and freshness follow what those platforms publish for free.
65
72
 
66
- 1. GitHub Actions schedule on `main` (e.g. `0 6 * * *`) runs
67
- **Refresh fantasy data** and publishes the `data-latest` release
68
- (`cache.json`).
69
- 2. Apps call `CampusClient.fromBundle()` or `npx campus pull`.
73
+ ---
70
74
 
71
- Details: [`docs/cron.md`](./docs/cron.md).
75
+ ## Use your StatsBomb licence (optional)
72
76
 
73
- ---
77
+ If you already pay Hudl StatsBomb, Campus is a thin pipe: **your login → their
78
+ paid API → one Campus schema → your app**.
79
+
80
+ Campus never hosts or resells a StatsBomb subscription.
74
81
 
75
- ## CLI quick start
82
+ ### 1. Connect credentials
76
83
 
77
- After `npm install campus-stats`:
84
+ Same env vars as [statsbombpy](https://github.com/statsbomb/statsbombpy):
78
85
 
79
86
  ```bash
80
- npx campus sync --fantasy # all women's comps (clubs + selecciones)
81
- npx campus sync --fantasy --with-players # + lineups + player stats (capped)
82
- npx campus pull # download cron bundle
83
- npx campus competitions
84
- npx campus matches --competition "Liga F" --team "Barcelona"
85
- npx campus squad --competition "Liga F" --team "Barcelona"
86
- npx campus fantasy-points --competition "Liga F"
87
- npx campus injuries # empty + documented deferral
87
+ export SB_USERNAME="you@company.com"
88
+ export SB_PASSWORD="your-statsbomb-password"
89
+
90
+ npx campus credentials
91
+ # statsbomb.mode: "paid"
88
92
  ```
89
93
 
90
- Store: `.campus/cache.json` (or `--sqlite` / `--db <path>`).
94
+ Or write a local config file (the `.campus/` directory is gitignored):
95
+
96
+ ```json
97
+ {
98
+ "statsbomb": {
99
+ "username": "you@company.com",
100
+ "password": "your-statsbomb-password"
101
+ }
102
+ }
103
+ ```
104
+
105
+ Or one-off flags:
106
+
107
+ ```bash
108
+ npx campus sync --competition "Liga F" \
109
+ --sb-user "you@company.com" \
110
+ --sb-password "your-statsbomb-password"
111
+ ```
91
112
 
92
- Single-competition sync and identities still work:
113
+ ### 2. Sync (same commands as free)
93
114
 
94
115
  ```bash
95
116
  npx campus sync --competition "Liga F"
96
- npx campus sync --source fbref --competition "WSL"
97
- npx campus identities propose --competition "Liga F"
117
+ npx campus sync --fantasy
98
118
  ```
99
119
 
120
+ With a paid login, Campus calls `https://data.statsbombservices.com` instead of
121
+ Open Data. Which leagues and seasons you get depends on **your StatsBomb
122
+ contract**, not on Campus.
123
+
124
+ ### 3. Optional paid-only endpoints
125
+
126
+ These hit the StatsBomb paid API **directly** when your licence includes them:
127
+
128
+ | Flag | StatsBomb paid API |
129
+ |------|--------------------|
130
+ | `--with-paid-player-match-stats` | Player match aggregates |
131
+ | `--with-paid-team-match-stats` | Team match aggregates |
132
+ | `--with-paid-player-season-stats` | Player season aggregates |
133
+ | `--with-paid-team-season-stats` | Team season aggregates |
134
+ | `--with-paid-360` | 360 freeze frames |
135
+
136
+ ```bash
137
+ npx campus sync --competition "Liga F" \
138
+ --with-paid-player-season-stats \
139
+ --with-paid-player-match-stats
140
+ ```
141
+
142
+ See the full endpoint map:
143
+
144
+ ```bash
145
+ npx campus endpoints
146
+ ```
147
+
148
+ > **FBref:** there is no official API key. `--source fbref` remains free HTML only.
149
+
100
150
  ---
101
151
 
102
- ## Supported data (open sources)
152
+ ## Use in your app
103
153
 
104
- | Source | Coverage | Notes |
105
- |--------|----------|-------|
106
- | **StatsBomb Open Data** | Liga F, WSL, NWSL, Serie A Women, Frauen Bundesliga, Women's World Cup, UEFA Women's Euro | Clubs + national teams; optional player stats / lineups |
107
- | **FBref** | WSL + Liga F schedules (pilot) | Live HTML may hit Cloudflare |
154
+ ```ts
155
+ import { CampusClient, resolveCredentials, syncFantasyBundle } from "campus-stats";
156
+
157
+ // Free: omit credentials Open Data
158
+ // Paid: set SB_USERNAME / SB_PASSWORD in the environment
159
+ const creds = await resolveCredentials();
160
+
161
+ await syncFantasyBundle({
162
+ includePlayerStats: true,
163
+ credentials: creds.statsbombPaidReady
164
+ ? {
165
+ username: creds.statsbomb.username,
166
+ password: creds.statsbomb.password,
167
+ }
168
+ : undefined,
169
+ });
170
+
171
+ const client = await CampusClient.open();
108
172
 
109
- Injuries are **not** in these open feeds yet — the API is ready, the list is empty.
173
+ const clubs = client.teams({ competition: "Liga F", kind: "club" });
174
+ const matches = client.matches({ competition: "Liga F", team: "Barcelona" });
175
+ const squad = client.squad({ competition: "Liga F", team: "Barcelona" });
176
+ const points = client.fantasyPoints({ competition: "Liga F", player: "Walsh" });
177
+
178
+ return Response.json({ clubs, matches, squad, points });
179
+ ```
180
+
181
+ Or load a published bundle without syncing yourself:
182
+
183
+ ```ts
184
+ const client = await CampusClient.fromBundle();
185
+ ```
186
+
187
+ | Export | Purpose |
188
+ |--------|---------|
189
+ | `CampusClient` | Open cache / bundle, query teams, matches, squads, fantasy points |
190
+ | `syncFantasyBundle` / `updateCachedCompetitions` | Bulk sync helpers |
191
+ | `resolveCredentials` | Detect free vs paid StatsBomb login |
192
+ | `scoreFantasyPoints` | Default fantasy scoring rules |
193
+ | Types | `Competition`, `Season`, `Team`, `Match`, `Player`, `LineupEntry`, … |
194
+
195
+ ---
196
+
197
+ ## CLI cheat sheet
198
+
199
+ ```bash
200
+ npx campus sync --fantasy|--all [--with-players] [--player-stats-limit <n>]
201
+ npx campus sync --competition <name> [--source statsbomb|fbref] [--with-players]
202
+ npx campus update
203
+ npx campus pull
204
+ npx campus available
205
+ npx campus credentials
206
+ npx campus endpoints
207
+ npx campus competitions
208
+ npx campus seasons --competition <name>
209
+ npx campus teams --competition <name>
210
+ npx campus matches --competition <name> [--season <name>] [--team <name>]
211
+ npx campus players [--team <name>] [--name <name>]
212
+ npx campus player-stats [--competition <name>] [--match <id>] [--player <name>]
213
+ npx campus lineups [--match <id>] [--team <name>]
214
+ npx campus squad --competition <name> --team <name>
215
+ npx campus fantasy-points [--competition <name>] [--player <name>]
216
+ npx campus identities propose --competition <name>
217
+ ```
218
+
219
+ Paid extras (require `SB_USERNAME` / `SB_PASSWORD`):
220
+ `--with-paid-player-match-stats`, `--with-paid-team-match-stats`,
221
+ `--with-paid-player-season-stats`, `--with-paid-team-season-stats`,
222
+ `--with-paid-360`.
110
223
 
111
224
  ---
112
225
 
113
- ## Why this shape
226
+ ## Keep data fresh
114
227
 
115
- - **One canonical schema** (`Competition`, `Season`, `Team`, `Match`, …). Adapters translate; the app never sees provider field names.
116
- - **Provenance on every record** (`sources: [{ source, id }]`) so cross-source identity is possible.
117
- - **Local-first cache** (JSON by default, SQLite optional) no hosted database required for the library.
228
+ - **Pull a published snapshot:** `npx campus pull` or `CampusClient.fromBundle()`
229
+ (this repo can publish a daily `data-latest` release via GitHub Actions).
230
+ - **Sync yourself:** `npx campus sync --fantasy` or `npx campus update`.
118
231
 
119
232
  ---
120
233
 
121
- ## Docs & source
234
+ ## Design
122
235
 
123
- All public docs are in this README on GitHub:
124
- https://github.com/Minacava/campus-stats
236
+ - **One schema** your app never sees provider field names.
237
+ - **Provenance** — every record carries `sources: [{ source, id }]`.
238
+ - **Local-first** — JSON cache by default; SQLite optional (`--sqlite`).
125
239
 
126
240
  ---
127
241
 
128
- ## Data source & terms
242
+ ## Terms
129
243
 
130
244
  **Code is MIT. Data is not ours to relicense.**
131
245
 
132
- - [StatsBomb Open Data](https://github.com/statsbomb/open-data): free for research and genuine football analytics. Credit StatsBomb in published analysis ([media pack](https://statsbomb.com/media-pack/)).
133
- - FBref / Sports Reference: respect site terms, `robots.txt`, and rate limits.
246
+ - [StatsBomb Open Data](https://github.com/statsbomb/open-data) free for research and genuine football analytics; credit StatsBomb ([media pack](https://statsbomb.com/media-pack/)).
247
+ - StatsBomb paid API your Hudl StatsBomb contract; never commit `SB_USERNAME` / `SB_PASSWORD`.
248
+ - FBref / Sports Reference — respect site terms, `robots.txt`, and rate limits.
134
249
 
135
- Pass those requirements downstream.
250
+ Pass those requirements downstream to your users.
136
251
 
137
252
  ---
138
253
 
139
254
  ## License
140
255
 
141
- Code: MIT (see [`LICENSE`](./LICENSE)). Data: subject to each source's terms.
256
+ Code: MIT (see [`LICENSE`](./LICENSE)).
257
+ Data: subject to each upstream source’s terms.
258
+
259
+ Source: https://github.com/Minacava/campus-stats
package/dist/cache.js CHANGED
@@ -12,7 +12,6 @@ export function emptyCache() {
12
12
  players: [],
13
13
  playerMatchStats: [],
14
14
  lineups: [],
15
- injuries: [],
16
15
  };
17
16
  }
18
17
  export function cachePath(cwd = process.cwd()) {
@@ -31,7 +30,6 @@ export async function loadCache(filePath = cachePath()) {
31
30
  players: parsed.players ?? [],
32
31
  playerMatchStats: parsed.playerMatchStats ?? [],
33
32
  lineups: parsed.lineups ?? [],
34
- injuries: parsed.injuries ?? [],
35
33
  };
36
34
  }
37
35
  catch (err) {
@@ -64,6 +62,5 @@ export function mergeSyncResult(cache, result) {
64
62
  players: mergeById(cache.players, result.players ?? []),
65
63
  playerMatchStats: mergeById(cache.playerMatchStats, result.playerMatchStats ?? []),
66
64
  lineups: mergeById(cache.lineups, result.lineups ?? []),
67
- injuries: mergeById(cache.injuries, result.injuries ?? []),
68
65
  };
69
66
  }