nhle-api 0.5.3 → 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
@@ -5,6 +5,84 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-10-03
9
+
10
+ ### Added
11
+
12
+ - `stats` namespace for the Stats API (`api.nhle.com/stats/rest`): skater, goalie and team reports, leaders, milestones, teams, seasons, games, shift charts, drafts, config, countries, glossary and franchises
13
+ - `getStats(report, params)` for skaters, goalies and teams, with `SkaterReport`, `GoalieReport` and `TeamReport` unions of the report names
14
+ - Response types for every `adv` function (`EdgeSkaterDetail`, `EdgeTeamZoneTime`, ...) and `LocalizedText`
15
+ - `video.metadata(videoId)` for Brightcove video titles, descriptions and sources
16
+ - Root exports: `APIResult`, `NHLClient`, `createNHLClient`, `BASE_URLS`, the error classes (`NHLError`, `NotFoundError`, `ValidationError`...), `ErrorCategory`, `ErrorLogLevel`, and the Cayenne query builder
17
+ - `nhle-api/toolkit` entry point with constants, date helpers, `resolvePath`, `normalizeAbbrev`, the Cayenne query builder, and team branding (colors and logo URLs)
18
+ - `NHLClient` accepts an options object (`baseUrl`, `timeout`, `language`, `headers`, `errorConfig`)
19
+ - Level-aware `logger` and `writeLog`
20
+ - Daily API drift check (`bun run drift`, GitHub workflow "API drift") covering every public function
21
+ - CI on pull requests (lint, typecheck, tests, build, publint and are-the-types-wrong) and a tag-triggered release workflow that publishes with npm trusted publishing
22
+
23
+ ### Changed
24
+
25
+ - `config` is read on every request, so changing it at runtime takes effect; environment variable configuration was removed
26
+ - Invalid `adv` parameters resolve to a failed result with a `ValidationError` instead of rejecting
27
+ - Date parameters use the NHL's timezone (America/New_York)
28
+ - `require` resolves `.d.cts` type declarations; the package declares `sideEffects: false` and `engines.node >= 18`
29
+ - Stats API: `getPlayerInfo`, `getLeaders`, `getGames`, `teams.getAll` and `teams.getById` take query params before `lang`; `teams.getById` returns a list, matching the API
30
+
31
+ ### Fixed
32
+
33
+ - The build failed under TypeScript 7; builds use TypeScript 5.9
34
+ - Published types left out `config`, `logConfig`, `logger` and `writeLog`
35
+ - Edge skater and team paths, goalie top-10 save percentage paths and top-10 filter codes (23 endpoints)
36
+ - `postalLookup` sends Canadian postal codes in the only form the API matches
37
+ - `stats.skaters.getStats()` requested an unresolved `{report}` path, report functions sent no `cayenneExp` (HTTP 500), and `getStatsWithFilters` dropped most filters
38
+ - Functions that default to the current season used 2025-26 (the last hardcoded season); the default now follows today's date
39
+
40
+ ### Deprecated
41
+
42
+ - `gc.game.whereToWatch()`: the endpoint now returns 404
43
+
44
+ ### Removed
45
+
46
+ - The unused `NHLScoreboard` type
47
+
48
+ ## [0.5.4] - 2026-02-10
49
+
50
+ ### Added
51
+
52
+ - Added game report tooling scripts for downloading and parsing summaries to support data collection workflows
53
+ - Added PlayerSearchResult response typing for Game Center player search
54
+ - Added test utilities for API response validation
55
+
56
+ ### Changed
57
+
58
+ - Reworked API response handling to use a `success` field instead of `status`, with simpler error handling
59
+ - Exported `nhlClient` as a named export and updated consumers
60
+ - Renamed the route helper to `resolvePath` and refactored Game Center/Edge Advanced Stats path builders
61
+ - Renamed Game Center and Edge Advanced Stats path modules from `_paths` to `paths` and cleaned up path object naming
62
+ - Restructured Edge Advanced Stats exports and converted module methods to standalone async functions
63
+ - Updated Edge Advanced Stats top10 schema validators to match the new path naming
64
+ - Overloaded game log and playoff series schedule helpers for flexible parameter handling
65
+ - Updated README usage examples and badges
66
+ - Simplified base URL handling in NHL client configuration
67
+ - Enhanced endpoint paths documentation for clarity and completeness across edge-adv module
68
+
69
+ ### Fixed
70
+
71
+ - Corrected rankings response handling to align with `success` response payloads
72
+ - Removed a stray console log from `getSeason`
73
+
74
+ ### Deprecated
75
+
76
+ - Marked the player search endpoint as deprecated; use the team roster endpoints for player information instead
77
+
78
+ ### Technical
79
+
80
+ - Reorganized edge-stats tests and added edge-adv unit tests for goalies, skaters, summary, and teams
81
+ - Updated Game Center test utilities and imports for shared validation helpers
82
+ - Updated test coverage to handle error result objects
83
+ - Removed an unused Game Center index export
84
+ - Updated dependencies and devDependencies (including cheerio and node-html-parser)
85
+
8
86
  ## [0.5.3] - 2025-12-03
9
87
 
10
88
  ### Added
package/README.md CHANGED
@@ -1,153 +1,319 @@
1
- # NHLe API Library
2
-
3
- A TypeScript wrapper for the NHLe API with simple, composable functions and full type safety.
4
-
5
- ## Overview
6
-
7
- Access NHLe game data, player statistics, and team information through a clean, functional API. Built with TypeScript for complete type safety and designed for ease of use.
8
-
9
- ## Installation
10
-
11
- To install the package, run:
12
-
13
- ```bash
14
- npm install nhle-api
15
- ```
16
-
17
- ## Usage
18
-
19
- Here are some basic examples of how to use the NHLe API library:
20
-
21
- ```typescript
22
- import { gc } from "nhle-api";
23
-
24
- // Get game landing page data
25
- const gameInfo = await gc.game.landing(2023020001);
26
- console.log(gameInfo);
27
-
28
- // Get live play-by-play
29
- const playByPlay = await gc.game.playByPlay(2023020001);
30
- console.log(playByPlay);
31
-
32
- // Get boxscore
33
- const boxscore = await gc.game.boxscore(2023020001);
34
- console.log(boxscore);
35
-
36
- // Get today's scores
37
- const scores = await gc.score(new Date());
38
- console.log(scores);
39
-
40
- // Get current scoreboard
41
- const scoreboard = await gc.scoreboard();
42
- console.log(scoreboard);
43
-
44
- // Get team roster
45
- const roster = await gc.team.roster("TOR");
46
- console.log(roster);
47
-
48
- // Get player information
49
- const player = await gc.player.landing(8478402); // Connor McDavid
50
- console.log(player);
51
- ```
52
-
53
- ## Configuration
54
-
55
- The library supports simple configuration through environment variables:
56
-
57
- ```bash
58
- # Set request timeout (in milliseconds, default: 5000)
59
- export NHLE_API_TIMEOUT=10000
60
-
61
- # Set language for responses (en or fr, default: en)
62
- export NHLE_API_LANGUAGE=en
63
-
64
- # Set logging level (silent, error, warn, info, debug; default: warn)
65
- export NHLE_API_LOGLEVEL=debug
66
- ```
67
-
68
- For more details, see [Environment Configuration Documentation](docs/ENVIRONMENT_CONFIG.md).
69
-
70
- ## API Reference
71
-
72
- ### Gamecenter API (`gc`)
73
-
74
- The primary API for accessing NHL game data, scores, team and player information (live and historical).
75
-
76
- #### `gc.game` - Game Data
77
-
78
- - `playByPlay(gameId)` - Get play-by-play data for a game
79
- - `boxscore(gameId)` - Get boxscore for a game
80
- - `landing(gameId)` - Get landing page data for a game
81
- - `reports(gameId)` - Get game reports (right rail data)
82
- - `schedule(date?)` - Get schedule for a specific date (defaults to today)
83
- - `scheduleCalendar(date?)` - Get schedule calendar for a specific date
84
- - `playoffBracket(year?)` - Get playoff bracket for a year
85
- - `playoffSeries(season?)` - Get playoff series for a season
86
- - `playoffSeriesSchedule(season, seriesLetter)` - Get schedule for a playoff series
87
- - `whereToWatch()` - Get where to watch information
88
- - `networkTVSchedule(date?)` - Get network TV schedule
89
- - `wsc.gameStory(gameId)` - Get game story (web service)
90
- - `wsc.playByPlay(gameId)` - Get play-by-play (web service)
91
- - `pptReplay.goal(gameId, eventId)` - Get goal replay data
92
- - `pptReplay.event(date?)` - Get replay events for a date
93
-
94
- #### `gc.score` - Scores & Scoreboards
95
-
96
- - `score(date?)` - Get scores for a specific date (defaults to today)
97
- - `scoreboard()` - Get current scoreboard
98
-
99
- #### `gc.team` - Team Data
100
-
101
- - `roster(teamCode, season?)` - Get team roster
102
- - `rosterSeasons(teamCode)` - Get available roster seasons
103
- - `prospects(teamCode)` - Get team prospects
104
- - `clubStats(teamCode, season?)` - Get club stats
105
- - `clubStatsSeason(teamCode)` - Get club stats season
106
- - `standings(date?)` - Get standings for a date
107
- - `standingsSeason()` - Get standings by season
108
- - `schedule.now(teamCode)` - Get team's current schedule
109
- - `schedule.month(teamCode, month)` - Get team's schedule for a month
110
- - `schedule.season(teamCode, season)` - Get team's schedule for a season
111
-
112
- #### `gc.player` - Player Data
113
-
114
- - `landing(playerId)` - Get player landing page data
115
- - `gameLog(playerId, season?, gameType?)` - Get player game log
116
- - `spotlight()` - Get featured players
117
- - `search(query)` - Search for players
118
- - `statsLeaders.season(season?, gameType?, category?, limit?)` - Get season stats leaders
119
- - `statsLeaders.current(gameType?, category?, limit?)` - Get current stats leaders
120
-
121
- #### `gc.draft` - Draft Data
122
-
123
- - `picks(year?)` - Get draft picks for a year
124
- - `tracker()` - Get draft tracker
125
- - `rankings(year?)` - Get draft rankings
126
-
127
- #### `gc.misc` - Miscellaneous
128
-
129
- - `seasons()` - Get all NHL seasons
130
- - `meta.game(gameId)` - Get game metadata
131
- - `meta.gameVideo(gameId)` - Get game video metadata
132
- - `postalLookup(postalCode)` - Lookup location by postal code
133
- - `location()` - Get current location info
134
- - `partnerGame(country, date?)` - Get partner game information
135
-
136
- ### Edge Stats API (`stats`) - _In Development_
137
-
138
- Historical and current NHL statistics (functionality being finalized)
139
-
140
- ### Edge Advanced API (`adv`) - _In Development_
141
-
142
- Advanced analytics endpoints (functionality being finalized)
143
-
144
- ## Docs
145
-
146
- - [Environment Configuration](docs/ENVIRONMENT_CONFIG.md) - Configure timeouts, language, and base URLs
147
- - [Query Builder Guide](docs/QUERY_BUILDER_GUIDE.md) - Advanced query building for stats API
148
-
149
- ## Support
150
-
151
- If you find this library helpful, consider supporting its development:
152
-
153
- [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R01DV0JD)
1
+ # NHLe API Library
2
+
3
+ [![npm version](https://img.shields.io/npm/v/nhle-api.svg)](https://www.npmjs.com/package/nhle-api)
4
+ [![npm downloads](https://img.shields.io/npm/dm/nhle-api.svg)](https://www.npmjs.com/package/nhle-api)
5
+ [![license](https://img.shields.io/github/license/spiflicate/nhle-api.svg)](LICENSE)
6
+
7
+ A modern TypeScript wrapper around the public NHL GameCenter and EdgeStats APIs with simple, composable functions and strong TypeScript typing.
8
+
9
+ ## Overview
10
+
11
+ The library exposes a small, functional surface over the NHL "Game Center", Edge and Stats APIs and ships with response types for every endpoint.
12
+
13
+ - Written in TypeScript and published as ESM/CJS
14
+ - Thin functional wrappers over official NHL API routes
15
+ - Typed responses for Game Center (`gc`), Edge advanced stats (`adv`), the Stats API (`stats`) and video metadata (`video`)
16
+ - Every function resolves to an `APIResult` instead of throwing
17
+ - Runtime configuration for timeouts, language and logging
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install nhle-api
23
+ ```
24
+
25
+ The package ships both ESM and CJS builds; use standard `import`/`require` according to your toolchain.
26
+
27
+ ## Quick Start
28
+
29
+ The primary entrypoint is the Game Center API namespace `gc`:
30
+
31
+ ```ts
32
+ import { gc } from 'nhle-api';
33
+
34
+ // Get game landing page data
35
+ const gameInfo = await gc.game.landing(2023020001);
36
+
37
+ // Get live play-by-play
38
+ const playByPlay = await gc.game.playByPlay(2023020001);
39
+
40
+ // Get boxscore
41
+ const boxscore = await gc.game.boxscore(2023020001);
42
+
43
+ // Get today's scores
44
+ const scores = await gc.score(new Date());
45
+
46
+ // Get current scoreboard
47
+ const scoreboard = await gc.scoreboard();
48
+
49
+ // Get team roster
50
+ const roster = await gc.team.roster('TOR');
51
+
52
+ // Get player landing page (Connor McDavid)
53
+ const player = await gc.player.landing(8478402);
54
+ ```
55
+
56
+ ## Configuration
57
+
58
+ `config` holds the shared defaults. Change it at runtime; every request reads it, so changes apply even after the API modules are loaded:
59
+
60
+ ```ts
61
+ import { config, logConfig } from 'nhle-api';
62
+
63
+ config.timeout = 10000; // ms, default 5000
64
+ config.language = 'fr'; // 'en' (default) or 'fr'
65
+ config.logLevel = 'error'; // 'silent' | 'error' | 'warn' (default) | 'info' | 'debug'
66
+
67
+ logConfig();
68
+ ```
69
+
70
+ See `docs/CONFIGURATION.md` for per-client settings.
71
+
72
+ ## Top-Level Exports
73
+
74
+ The package root `nhle-api` re-exports the API namespaces, configuration, the HTTP client, error classes, constants and types:
75
+
76
+ ```ts
77
+ // API namespaces
78
+ import { adv, gc, stats, video } from 'nhle-api';
79
+
80
+ // Configuration and logging
81
+ import { config, logConfig, logger } from 'nhle-api';
82
+
83
+ // Result type and errors
84
+ import { NHLError, NotFoundError, ValidationError } from 'nhle-api';
85
+ import type { APIResult } from 'nhle-api';
86
+
87
+ // Your own client (custom base URL, headers, timeout, error handling)
88
+ import { NHLClient, createNHLClient } from 'nhle-api';
89
+
90
+ // Cayenne filter builder for `stats`
91
+ import { CayenneQueryBuilder } from 'nhle-api';
92
+
93
+ // Response and parameter types
94
+ import type { EdgeSkaterDetail, GamecenterLanding, Season } from 'nhle-api';
95
+ ```
96
+
97
+ ## Toolkit Entry Point
98
+
99
+ Use the `nhle-api/toolkit` subpath when you only need NHL constants and shared helpers. It does not load the API clients or configuration:
100
+
101
+ ```ts
102
+ import {
103
+ NHL,
104
+ getCurrentSeason,
105
+ normalizeAbbrev,
106
+ resolvePath,
107
+ } from 'nhle-api/toolkit';
108
+
109
+ const season = getCurrentSeason();
110
+ const team = normalizeAbbrev('TB'); // 'TBL'
111
+ const teamPath = resolvePath('/teams/{team}', { team });
112
+ ```
113
+
114
+ This entry point exports `NHL`, date helpers, `normalizeAbbrev`, `resolvePath`,
115
+ the Cayenne query builder, team branding data, historical team palettes, and
116
+ official logo URL helpers.
117
+
118
+ ## Game Center API (`gc`)
119
+
120
+ `gc` is the primary namespace for accessing live and historical NHL data.
121
+
122
+ ### `gc.game` – Game Data
123
+
124
+ - `playByPlay(gameId)` – Full live play-by-play event stream
125
+ - `boxscore(gameId)` – Boxscore and team/player stats for a game
126
+ - `landing(gameId)` – Game landing page data (summary, lines, etc.)
127
+ - `reports(gameId)` – Game reports / right-rail data
128
+ - `schedule(date?)` – Schedule for a given date (defaults to today)
129
+ - `scheduleCalendar(date?)` – Calendar-style schedule for a date
130
+ - `playoffBracket(year?)` – Playoff bracket for a given year
131
+ - `playoffSeries(season?)` – Playoff series information for a season
132
+ - `playoffSeriesSchedule(season, seriesLetter)` – Schedule for a playoff series
133
+ - `whereToWatch()` – Regional broadcast / streaming info (deprecated: the NHL retired this endpoint and it returns 404)
134
+ - `networkTVSchedule(date?)` – National TV schedule
135
+ - `wsc.gameStory(gameId)` – Game story from the web service collection
136
+ - `wsc.playByPlay(gameId)` – Play-by-play from the web service collection
137
+ - `pptReplay.goal(gameId, eventId)` – Goal replay data for a specific event
138
+ - `pptReplay.event(date?)` – Replay events for a given date (does not appear to be functional at this time)
139
+
140
+ ### `gc.score` – Scores & Scoreboards
141
+
142
+ - `score(date?)` – Scores for a given date (defaults to today)
143
+ - `scoreboard()` – Current live scoreboard
144
+
145
+ ### `gc.team` – Team Data
146
+
147
+ - `roster(teamCode, season?)` – Team roster for a given season
148
+ - `rosterSeasons(teamCode)` – Available roster seasons for a team
149
+ - `prospects(teamCode)` – Team prospects
150
+ - `clubStats(teamCode, season?)` – Club stats for a team/season
151
+ - `clubStatsSeason(teamCode)` – Season-level club stats
152
+ - `standings(date?)` – Standings for a given date
153
+ - `standingsSeason()` – Season standings
154
+ - `schedule.now(teamCode)` – Current schedule segment for a team
155
+ - `schedule.month(teamCode, month)` – Team schedule for a given month
156
+ - `schedule.season(teamCode, season)` – Full season schedule for a team
157
+
158
+ ### `gc.player` – Player Data
159
+
160
+ - `landing(playerId)` – Player landing page data
161
+ - `gameLog(playerId, season?, gameType?)` – Game log for a player
162
+ - `spotlight()` – Featured players (spotlight carousel)
163
+ - `search(query)` – Player search
164
+ - `statsLeaders.season(season?, gameType?, category?, limit?)` – Season stat leaders
165
+ - `statsLeaders.current(gameType?, category?, limit?)` – Current stat leaders
166
+
167
+ ### `gc.draft` – Draft Data
168
+
169
+ - `picks(year?)` – Draft picks for a given year
170
+ - `tracker()` – Draft tracker
171
+ - `rankings(year?)` – Draft rankings (all, NA/International, skaters/goalies)
172
+
173
+ ### `gc.misc` – Miscellaneous
174
+
175
+ - `seasons()` – All NHL seasons
176
+ - `meta.game(gameId)` – Game metadata
177
+ - `meta.gameVideo(gameId)` – Game video metadata
178
+ - `postalLookup(postalCode)` – Postal/ZIP lookup (location info)
179
+ - `location()` – Location info for current context
180
+ - `partnerGame(country, date?)` – Partner game information
181
+
182
+ ## Edge Advanced Stats (`adv`)
183
+
184
+ NHL Edge puck and player tracking data (`api-web.nhle.com/v1/edge`), with response types for every function (`EdgeSkaterDetail`, `EdgeTeamZoneTime`, ...).
185
+
186
+ - `adv.skaters`: `detail`, `comparison`, `leaders`, `shotLocation`, `shotSpeed`, `skatingDistance`, `skatingSpeed`, `zoneTime`, and `top10.{distance, shotLocation, shotSpeed, speed, zoneTime}`
187
+ - `adv.goalies`: `player`, `compare`, `leaders`, `savePercentage`, `savePercentage5v5`, `saveLocation`, and `top10.{savePercentage, savePercentage5v5, saveLocation}`
188
+ - `adv.teams`: `stats`, `compare`, `leaders`, `shotLocation`, `shotSpeed`, `skatingDistance`, `skatingSpeed`, `zoneTime`, and `top10.{shotLocation, shotSpeed, skatingDistance, skatingSpeed, zoneTime}`
189
+ - `adv.byTheNumbers()`: the Edge home page's daily highlights
190
+
191
+ ```ts
192
+ const result = await adv.skaters.detail(8478402, 20242025, 2);
193
+ if (result.success) console.log(result.data.skatingSpeed.speedMax.imperial);
194
+ ```
195
+
196
+ ## Stats API (`stats`)
197
+
198
+ The stats.nhl.com reports (`api.nhle.com/stats/rest`). List endpoints answer `{ data, total }` and accept `cayenneExp`, `sort`, `dir`, `limit` and `start`.
199
+
200
+ - `stats.skaters` / `stats.goalies` / `stats.teams`: `getStats(report, params)` for any report (`summary`, `realtime`, `powerplay`...; `SkaterReport`, `GoalieReport` and `TeamReport` list them), plus `getStatsWithParams`, `getStatsWithBuilder` and `getStatsWithFilters`
201
+ - `stats.skaters.getPlayerInfo(params)`, `getLeaders(category, params)`, `getMilestones()`; the same leaders and milestones for goalies
202
+ - `stats.teams.getAll(params)`, `getById(teamId, params)`
203
+ - `stats.season`: `getSeasons()`, `getGames(params)`, `getShiftChart(gameId)`, `getDraft()`
204
+ - `stats.misc`: `getConfig()` (every report's columns), `getCountries()`, `getGlossary()`, `getFranchises()`
205
+
206
+ ```ts
207
+ import { stats } from 'nhle-api';
208
+
209
+ const result = await stats.skaters.getStats('summary', {
210
+ cayenneExp: 'seasonId=20242025 and gameTypeId=2',
211
+ sort: 'points',
212
+ dir: 'desc',
213
+ limit: 10,
214
+ });
215
+
216
+ // Or with the query builder
217
+ const goalies = await stats.goalies.getStatsWithBuilder('summary', (q) => ({
218
+ cayenneExp: q.equals('seasonId', 20242025).greaterThan('gamesPlayed', 20).build(),
219
+ sort: 'savePct',
220
+ dir: 'desc',
221
+ }));
222
+ ```
223
+
224
+ See [docs/QUERY_BUILDER_GUIDE.md](docs/QUERY_BUILDER_GUIDE.md) for the builder.
225
+
226
+ ## Video (`video`)
227
+
228
+ - `video.metadata(videoId)`: title, descriptions, duration, poster and playable sources (with the best MP4 as `url`) for a Brightcove video id, such as a goal's `highlightClip` in `gc.game.landing`
229
+
230
+ ## Types
231
+
232
+ Response types are exported for every endpoint: Game Center (`GamecenterLanding`, `TeamRoster`...), Edge (`Edge*`) and the Stats API (`stats.SkaterStats`, `stats.Team`...), along with parameter types (`Season`, `GameId`, `TeamAbbrev`...) and shared shapes such as `LocalizedText`.
233
+
234
+ ### Results and errors
235
+
236
+ Functions never throw for API failures. Each resolves to an `APIResult<T>`:
237
+
238
+ - `{ success: true, data }` on success.
239
+ - `{ success: false, error }` otherwise, where `error` is an `NHLError` subclass: `NotFoundError` (404), `ClientError` (other 4xx), `ServerError` (5xx), `RateLimitError` (429), `NetworkError`, or `ValidationError` for parameters rejected before any request.
240
+ - `error.category` and `error.context` (endpoint, status code, response body) carry the details.
241
+
242
+ ```ts
243
+ import { gc, NotFoundError } from 'nhle-api';
244
+ import type { APIResult, GamecenterLanding } from 'nhle-api';
245
+
246
+ const result: APIResult<GamecenterLanding> = await gc.game.landing(2023020001);
247
+
248
+ if (result.success) {
249
+ console.log(result.data.venue.default);
250
+ } else if (result.error instanceof NotFoundError) {
251
+ console.log('No such game');
252
+ } else {
253
+ console.error(result.error.category, result.error.message);
254
+ }
255
+ ```
256
+
257
+ ## Roadmap / Status
258
+
259
+ - ✅ Game Center endpoints with response types
260
+ - ✅ Edge Advanced (`adv`) endpoints with response types
261
+ - ✅ Stats API (`stats`) endpoints with typed reports
262
+ - ✅ Runtime configuration (`config`) and per-client settings (`NHLClient`)
263
+ - ✅ Error classes and `APIResult` exported
264
+ - ✅ Daily drift check against the live APIs
265
+
266
+ For a detailed list of changes, see `CHANGELOG.md`.
267
+
268
+ ## Docs
269
+
270
+ - [Configuration](docs/CONFIGURATION.md) - Code-defined configuration and default values
271
+ - [Query Builder Guide](docs/QUERY_BUILDER_GUIDE.md) - Cayenne query builder for stats APIs
272
+
273
+ ## Contributing
274
+
275
+ This project is still evolving and feedback is very welcome.
276
+
277
+ - **Bug reports / issues:** If an endpoint returns unexpected data, response parsing fails, or types don’t match the real API, please open a GitHub issue with the endpoint, parameters, and (sanitized) sample payloads if possible.
278
+ - **Type improvements:** PRs or issues that improve response coverage, fix missing/incorrect fields, or refine enums and unions are particularly helpful.
279
+ - **JSDoc / docs:** Additions or corrections to JSDoc on public types and functions, or clarifications to the guides in `docs/`, are also very much appreciated.
280
+
281
+ Before filing an issue, check the `CHANGELOG.md` for recent changes and breaking notes, and include the library version you’re using.
282
+
283
+ ## API drift check
284
+
285
+ The NHL APIs are undocumented and change without notice. `bun run drift` calls every public function against the live APIs (with a finished season, game and draft) and compares each response's structure with `scripts/drift/baseline.json`. It fails when an endpoint stops answering, an error response changes, or a field is added, removed or changes type. Values are ignored, and so are fields that turn `null`. Translations in localized strings (`{ default, fr, cs, ... }`) count as one field.
286
+
287
+ The **API drift** workflow runs it daily and on demand (Actions → API drift → Run workflow). A failure opens one `api-drift` issue with the report, which the next passing run closes. It never runs on PRs, so upstream changes don't block unrelated work.
288
+
289
+ When an API really has changed:
290
+
291
+ 1. Update the response types in `src/types/responses/`.
292
+ 2. Accept the new shapes with `bun run drift --update` and commit the baseline.
293
+
294
+ Checks for date- or location-dependent data (scoreboard, draft tracker) are marked `volatile`: a field missing today is only noted, and `--update` adds to their baseline instead of replacing it. When you add a library function, add a check in `scripts/drift/checks.ts`; `test/unit/drift.test.ts` fails if a public function has none.
295
+
296
+ ## Releasing
297
+
298
+ 1. Move the `Unreleased` notes in `CHANGELOG.md` under the new version.
299
+ 2. Bump the version and tag it: `npm version patch` (or `minor`/`major`).
300
+ 3. Push the commit and tag: `git push --follow-tags`.
301
+
302
+ 4. Approve the staged version with 2FA, on npmjs.com (the package's **Staged Packages** tab) or with `npm stage approve <stage-id>`.
303
+
304
+ The **Release** workflow checks that the tag matches `package.json`, runs the full CI suite and `check:package`, stages the version on npm with provenance, and creates a GitHub release. It uses [npm trusted publishing](https://docs.npmjs.com/trusted-publishers), so there is no npm token in the repo, and [staged publishing](https://docs.npmjs.com/staged-publishing), so nothing goes live until a maintainer approves it.
305
+
306
+ One-time setup on npmjs.com:
307
+
308
+ 1. Open the `nhle-api` package's **Settings → Trusted publishing** and add GitHub Actions with user `spiflicate`, repository `nhle-api` and workflow `release.yml`. Leave "Allow npm publish" and "Allow npm dist-tag" unchecked, so CI can only stage.
309
+ 2. Under **Publishing access**, choose "Require two-factor authentication and disallow tokens".
310
+
311
+ ## Support
312
+
313
+ If you find this library helpful, consider supporting its development:
314
+
315
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R01DV0JD)
316
+
317
+ ## License
318
+
319
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.