campus-stats 0.4.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 (58) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +163 -0
  4. package/dist/cache.d.ts +10 -0
  5. package/dist/cache.js +69 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +537 -0
  8. package/dist/client.d.ts +88 -0
  9. package/dist/client.js +273 -0
  10. package/dist/data-bundle.d.ts +11 -0
  11. package/dist/data-bundle.js +17 -0
  12. package/dist/db/migrate.d.ts +6 -0
  13. package/dist/db/migrate.js +22 -0
  14. package/dist/db/sqlite-store.d.ts +12 -0
  15. package/dist/db/sqlite-store.js +344 -0
  16. package/dist/db/store-api.d.ts +9 -0
  17. package/dist/db/store-api.js +70 -0
  18. package/dist/fantasy.d.ts +36 -0
  19. package/dist/fantasy.js +152 -0
  20. package/dist/http.d.ts +25 -0
  21. package/dist/http.js +63 -0
  22. package/dist/identity/match.d.ts +22 -0
  23. package/dist/identity/match.js +106 -0
  24. package/dist/identity/propose.d.ts +8 -0
  25. package/dist/identity/propose.js +45 -0
  26. package/dist/identity/rules.d.ts +8 -0
  27. package/dist/identity/rules.js +17 -0
  28. package/dist/identity/store.d.ts +5 -0
  29. package/dist/identity/store.js +20 -0
  30. package/dist/identity/types.d.ts +32 -0
  31. package/dist/identity/types.js +8 -0
  32. package/dist/ids.d.ts +2 -0
  33. package/dist/ids.js +4 -0
  34. package/dist/index.d.ts +16 -0
  35. package/dist/index.js +9 -0
  36. package/dist/injuries.d.ts +12 -0
  37. package/dist/injuries.js +15 -0
  38. package/dist/refs.d.ts +7 -0
  39. package/dist/refs.js +1 -0
  40. package/dist/scoring.d.ts +37 -0
  41. package/dist/scoring.js +56 -0
  42. package/dist/sources/fbref-map.d.ts +14 -0
  43. package/dist/sources/fbref-map.js +56 -0
  44. package/dist/sources/fbref-parse.d.ts +17 -0
  45. package/dist/sources/fbref-parse.js +87 -0
  46. package/dist/sources/fbref.d.ts +27 -0
  47. package/dist/sources/fbref.js +72 -0
  48. package/dist/sources/index.d.ts +3 -0
  49. package/dist/sources/index.js +2 -0
  50. package/dist/sources/statsbomb-player-stats.d.ts +63 -0
  51. package/dist/sources/statsbomb-player-stats.js +107 -0
  52. package/dist/sources/statsbomb.d.ts +50 -0
  53. package/dist/sources/statsbomb.js +170 -0
  54. package/dist/sources/types.d.ts +23 -0
  55. package/dist/sources/types.js +1 -0
  56. package/dist/types.d.ts +114 -0
  57. package/dist/types.js +1 -0
  58. package/package.json +57 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ - **Rename (npm):** package **`campus`** → **`campus-stats`** so public
6
+ `npm install campus-stats` works (unscoped `campus` is taken on npmjs.com)
7
+ - Publish target: **npmjs.com** primary; GitLab Package Registry as mirror
8
+ - CLI bins: `campus` and `campus-stats` (same entrypoint)
9
+ - Import path: `import { CampusClient } from "campus-stats"`
10
+ - Product brand, `CampusClient`, `.campus/` cache, and `campus-data` unchanged
11
+
12
+ ## 0.3.0
13
+
14
+ - **Rename:** package/CLI `campo-stats` → **`campus`** (brand alignment)
15
+ - API rename: `CampoClient` → `CampusClient`, `CampoCache` → `CampusCache`
16
+ - Local cache directory: `.campo-stats/` → `.campus/`
17
+ - Data bundle package: `campo-stats-data` → `campus-data`
18
+ - Smoke test for package identity (`name`, bin, cache paths)
19
+
20
+ ## 0.2.0
21
+
22
+ - App-facing `CampusClient` API (`open` / `fromBundle` / `syncFantasy` / queries)
23
+ - `sync --fantasy` / `--all`, `update`, `pull`, `available`
24
+ - Lineups + season squad queries; default fantasy scoring
25
+ - Injuries API stub (empty — no open source yet)
26
+ - GitLab CI cron job `refresh_fantasy_data` + `docs/cron.md`
27
+ - Competition `international` and team `kind` (`club` | `national`)
28
+
29
+ ## 0.1.0
30
+
31
+ - Initial package layout (distributed via GitLab Package Registry, not npmjs)
32
+ - StatsBomb + FBref (schedule) adapters
33
+ - JSON cache and optional SQLite persistence
34
+ - Player match stats (StatsBomb, capped)
35
+ - Cross-source team identity proposals
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Campus / campus contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # Campus (`campus-stats`)
2
+
3
+ **Open, normalized, queryable data for women's football — installable in your app.**
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.
9
+
10
+ Requires **Node.js ≥ 22**.
11
+
12
+ ---
13
+
14
+ ## Install
15
+
16
+ Published on **npmjs.com**:
17
+
18
+ ```bash
19
+ npm install campus-stats
20
+
21
+ npx campus sync --competition "Liga F"
22
+ npx campus teams --competition "Liga F"
23
+ npx campus matches --competition "Liga F" --team "Barcelona"
24
+ ```
25
+
26
+ The CLI binaries are `campus` and `campus-stats` (same entrypoint).
27
+
28
+ Maintainer publish setup: [`docs/npm.md`](./docs/npm.md).
29
+ Optional GitLab mirror: [`docs/gitlab-package.md`](./docs/gitlab-package.md).
30
+
31
+ ---
32
+
33
+ ## Use as a library (recommended for apps)
34
+
35
+ ```ts
36
+ import { CampusClient } from "campus-stats";
37
+
38
+ const client = await CampusClient.open();
39
+ await client.syncFantasy({ includePlayerStats: true });
40
+ // or: const client = await CampusClient.fromBundle();
41
+
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" });
47
+
48
+ return Response.json({ clubs, nations, matches, squad, points });
49
+ ```
50
+
51
+ ### What you get from the package
52
+
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 |
61
+
62
+ ---
63
+
64
+ ## Keep data fresh (cron)
65
+
66
+ 1. GitLab **Pipeline schedule** on `main` (e.g. `0 6 * * *`) runs
67
+ `refresh_fantasy_data` and publishes `campus-data/latest/cache.json`.
68
+ 2. Apps call `CampusClient.fromBundle()` or `npx campus pull`.
69
+
70
+ Details: [`docs/cron.md`](./docs/cron.md).
71
+
72
+ ---
73
+
74
+ ## CLI quick start
75
+
76
+ After `npm install campus-stats`:
77
+
78
+ ```bash
79
+ npx campus sync --fantasy # all women's comps (clubs + selecciones)
80
+ npx campus sync --fantasy --with-players # + lineups + player stats (capped)
81
+ npx campus pull # download cron bundle
82
+ npx campus competitions
83
+ npx campus matches --competition "Liga F" --team "Barcelona"
84
+ npx campus squad --competition "Liga F" --team "Barcelona"
85
+ npx campus fantasy-points --competition "Liga F"
86
+ npx campus injuries # empty + documented deferral
87
+ ```
88
+
89
+ Store: `.campus/cache.json` (or `--sqlite` / `--db <path>`).
90
+
91
+ Single-competition sync and identities still work:
92
+
93
+ ```bash
94
+ npx campus sync --competition "Liga F"
95
+ npx campus sync --source fbref --competition "WSL"
96
+ npx campus identities propose --competition "Liga F"
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Supported data (open sources)
102
+
103
+ | Source | Coverage | Notes |
104
+ |--------|----------|-------|
105
+ | **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 |
106
+ | **FBref** | WSL + Liga F schedules (pilot) | Live HTML may hit Cloudflare |
107
+
108
+ Injuries are **not** in these open feeds yet — the API is ready, the list is empty.
109
+
110
+ ---
111
+
112
+ ## Why this shape
113
+
114
+ - **One canonical schema** (`Competition`, `Season`, `Team`, `Match`, …). Adapters translate; the app never sees provider field names.
115
+ - **Provenance on every record** (`sources: [{ source, id }]`) so cross-source identity is possible.
116
+ - **Local-first cache** (JSON by default, SQLite optional) — no hosted database required for the library.
117
+
118
+ ---
119
+
120
+ ## Develop from a clone
121
+
122
+ For contributors working on this repo:
123
+
124
+ ```bash
125
+ git clone https://gitlab.com/marina34/campus.git
126
+ cd campus
127
+ npm install
128
+ npm run build
129
+ npm test
130
+ npx campus --help
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Project plan & docs
136
+
137
+ | Epic | Description |
138
+ |------|-------------|
139
+ | [00 — Foundation](./docs/epics/00-fundacion-v0.md) | Schema, StatsBomb, CLI, JSON cache |
140
+ | [01 — FBref](./docs/epics/01-adapter-fbref.md) | Second public source |
141
+ | [02 — Identity](./docs/epics/02-resolucion-identidad.md) | Cross-source team resolution |
142
+ | [03 — Player stats](./docs/epics/03-stats-jugadora.md) | Per-match player stats |
143
+ | [04 — SQLite](./docs/epics/04-persistencia-sqlite.md) | Persistence beyond JSON |
144
+ | [05 — Package](./docs/epics/05-publicacion-npm.md) | npmjs + GitLab package distribution |
145
+
146
+ More: [`docs/cron.md`](./docs/cron.md) · [`docs/injuries.md`](./docs/injuries.md) · [`docs/npm.md`](./docs/npm.md) · [`docs/gitlab-package.md`](./docs/gitlab-package.md) · [`CHANGELOG.md`](./CHANGELOG.md)
147
+
148
+ ---
149
+
150
+ ## Data source & terms
151
+
152
+ **Code is MIT. Data is not ours to relicense.**
153
+
154
+ - [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/)).
155
+ - FBref / Sports Reference: respect site terms, `robots.txt`, and rate limits.
156
+
157
+ Pass those requirements downstream.
158
+
159
+ ---
160
+
161
+ ## License
162
+
163
+ Code: MIT (see [`LICENSE`](./LICENSE)). Data: subject to each source's terms.
@@ -0,0 +1,10 @@
1
+ import type { CampusCache } from "./types.js";
2
+ import type { SyncResult } from "./sources/types.js";
3
+ export declare const DEFAULT_CACHE_DIR = ".campus";
4
+ export declare const DEFAULT_CACHE_FILE = "cache.json";
5
+ export declare function emptyCache(): CampusCache;
6
+ export declare function cachePath(cwd?: string): string;
7
+ export declare function loadCache(filePath?: string): Promise<CampusCache>;
8
+ export declare function saveCache(cache: CampusCache, filePath?: string): Promise<void>;
9
+ /** Merge a sync result into the cache without wiping unrelated competitions. */
10
+ export declare function mergeSyncResult(cache: CampusCache, result: SyncResult): CampusCache;
package/dist/cache.js ADDED
@@ -0,0 +1,69 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export const DEFAULT_CACHE_DIR = ".campus";
4
+ export const DEFAULT_CACHE_FILE = "cache.json";
5
+ export function emptyCache() {
6
+ return {
7
+ competitions: [],
8
+ seasons: [],
9
+ teams: [],
10
+ matches: [],
11
+ identities: [],
12
+ players: [],
13
+ playerMatchStats: [],
14
+ lineups: [],
15
+ injuries: [],
16
+ };
17
+ }
18
+ export function cachePath(cwd = process.cwd()) {
19
+ return path.join(cwd, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE);
20
+ }
21
+ export async function loadCache(filePath = cachePath()) {
22
+ try {
23
+ const raw = await readFile(filePath, "utf8");
24
+ const parsed = JSON.parse(raw);
25
+ return {
26
+ competitions: parsed.competitions ?? [],
27
+ seasons: parsed.seasons ?? [],
28
+ teams: parsed.teams ?? [],
29
+ matches: parsed.matches ?? [],
30
+ identities: parsed.identities ?? [],
31
+ players: parsed.players ?? [],
32
+ playerMatchStats: parsed.playerMatchStats ?? [],
33
+ lineups: parsed.lineups ?? [],
34
+ injuries: parsed.injuries ?? [],
35
+ };
36
+ }
37
+ catch (err) {
38
+ const code = err.code;
39
+ if (code === "ENOENT")
40
+ return emptyCache();
41
+ throw err;
42
+ }
43
+ }
44
+ export async function saveCache(cache, filePath = cachePath()) {
45
+ await mkdir(path.dirname(filePath), { recursive: true });
46
+ await writeFile(filePath, JSON.stringify(cache, null, 2) + "\n", "utf8");
47
+ }
48
+ function mergeById(existing, incoming) {
49
+ const map = new Map();
50
+ for (const item of existing)
51
+ map.set(item.id, item);
52
+ for (const item of incoming)
53
+ map.set(item.id, item);
54
+ return [...map.values()];
55
+ }
56
+ /** Merge a sync result into the cache without wiping unrelated competitions. */
57
+ export function mergeSyncResult(cache, result) {
58
+ return {
59
+ competitions: mergeById(cache.competitions, result.competitions),
60
+ seasons: mergeById(cache.seasons, result.seasons),
61
+ teams: mergeById(cache.teams, result.teams),
62
+ matches: mergeById(cache.matches, result.matches),
63
+ identities: cache.identities,
64
+ players: mergeById(cache.players, result.players ?? []),
65
+ playerMatchStats: mergeById(cache.playerMatchStats, result.playerMatchStats ?? []),
66
+ lineups: mergeById(cache.lineups, result.lineups ?? []),
67
+ injuries: mergeById(cache.injuries, result.injuries ?? []),
68
+ };
69
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};