nhl-api-ts 0.2.0 → 0.3.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 (51) hide show
  1. package/README.md +81 -9
  2. package/dist/api/draft.d.mts +61 -0
  3. package/dist/api/draft.mjs +68 -0
  4. package/dist/api/edge.d.mts +221 -24
  5. package/dist/api/edge.mjs +276 -27
  6. package/dist/api/game-center.d.mts +63 -15
  7. package/dist/api/game-center.mjs +67 -8
  8. package/dist/api/helpers.d.mts +6 -6
  9. package/dist/api/helpers.mjs +6 -6
  10. package/dist/api/misc.d.mts +120 -6
  11. package/dist/api/misc.mjs +162 -7
  12. package/dist/api/players.d.mts +59 -7
  13. package/dist/api/players.mjs +85 -4
  14. package/dist/api/query/cayenne.d.mts +4 -0
  15. package/dist/api/query/cayenne.mjs +13 -0
  16. package/dist/api/query/sorting/sorting-options.d.mts +8 -0
  17. package/dist/api/query/sorting/sorting-options.mjs +30 -0
  18. package/dist/api/schedule.d.mts +36 -23
  19. package/dist/api/schedule.mjs +40 -16
  20. package/dist/api/standings.d.mts +3 -3
  21. package/dist/api/standings.mjs +4 -4
  22. package/dist/api/stats.d.mts +105 -16
  23. package/dist/api/stats.mjs +145 -41
  24. package/dist/api/teams.d.mts +48 -8
  25. package/dist/api/teams.mjs +59 -7
  26. package/dist/cli/colors.d.mts +2 -2
  27. package/dist/cli/colors.mjs +6 -2
  28. package/dist/cli/commands.mjs +521 -46
  29. package/dist/cli/run.mjs +7 -5
  30. package/dist/http-client.d.mts +2 -1
  31. package/dist/http-client.mjs +10 -3
  32. package/dist/index.d.mts +8 -2
  33. package/dist/index.mjs +3 -0
  34. package/dist/nhl-client.d.mts +70 -15
  35. package/dist/nhl-client.mjs +40 -17
  36. package/dist/types/common.d.mts +0 -1
  37. package/dist/types/draft.d.mts +97 -0
  38. package/dist/types/draft.mjs +1 -0
  39. package/dist/types/edge.d.mts +301 -5
  40. package/dist/types/game-center.d.mts +277 -119
  41. package/dist/types/index.d.mts +1 -0
  42. package/dist/types/misc.d.mts +101 -0
  43. package/dist/types/misc.mjs +0 -1
  44. package/dist/types/players.d.mts +23 -2
  45. package/dist/types/schedule.d.mts +50 -62
  46. package/dist/types/standings.d.mts +1 -2
  47. package/dist/types/stats.d.mts +313 -111
  48. package/dist/types/teams.d.mts +29 -1
  49. package/dist/utils.d.mts +1 -1
  50. package/dist/utils.mjs +3 -1
  51. package/package.json +7 -28
package/README.md CHANGED
@@ -73,8 +73,12 @@ optional.
73
73
  ### teams
74
74
 
75
75
  - `teams({ [date] })` — all teams with conference, division and `franchise_id`
76
- - `teamRoster({ teamAbbr, season })`
76
+ - `teamRoster({ teamAbbr, [season] })` — no `season` → current roster
77
+ - `rosterSeasons({ teamAbbr })` — every season the team has a roster for
78
+ - `teamScoreboard({ teamAbbr })` — recent and upcoming games
77
79
  - `franchises()`
80
+ - `statsTeams()` — every team in the stats API, past and present (raw rows with `franchiseId`, `triCode`)
81
+ - `teamById({ teamId })` — one stats API team row; an unknown id throws `ResourceNotFoundException`
78
82
 
79
83
  ### standings
80
84
 
@@ -87,10 +91,11 @@ optional.
87
91
  - `weeklySchedule({ [date] })`
88
92
  - `teamMonthlySchedule({ teamAbbr, [month] })`
89
93
  - `teamWeeklySchedule({ teamAbbr, [date] })`
90
- - `teamSeasonSchedule({ teamAbbr, season })`
91
- - `calendarSchedule({ date })`
94
+ - `teamSeasonSchedule({ teamAbbr, [season] })`
95
+ - `calendarSchedule({ [date] })`
92
96
  - `playoffCarousel({ season })`
93
97
  - `playoffSeriesSchedule({ season, series })`
98
+ - `playoffSeriesMeta({ year, series })` — round title and the two seeded teams
94
99
  - `playoffBracket({ year })`
95
100
 
96
101
  ### gameCenter
@@ -102,16 +107,26 @@ optional.
102
107
  - `shiftChartData({ gameId, [excludes] })`
103
108
  - `seasonSeriesMatchup({ gameId })`
104
109
  - `gameStory({ gameId })`
110
+ - `scoreboard({ [date] })` — every game in the week around a date, grouped by date
111
+ - `wscPlayByPlay({ gameId })` — flat play array (a different shape from `playByPlay`)
112
+ - `playReplay({ gameId, eventNumber })` — replay card for one event; `goal` only for goals (experimental)
113
+ - `gameMeta({ gameId })` — teams, season state and game state
105
114
 
106
115
  ### stats
107
116
 
108
117
  - `gametypesPerSeasonDirectoryByTeam({ teamAbbr })`
109
118
  - `playerCareerStats({ playerId })`
110
- - `playerGameLog({ playerId, seasonId, gameType })`
119
+ - `playerGameLog({ playerId, [seasonId], [gameType] })`
120
+ - `skaterStatsLeaders({ [season], [gameType], [categories], [limit] })` — leaders keyed by category (`goals`, `points`, `toi`, ...); `limit: -1` returns every player
121
+ - `goalieStatsLeaders({ [season], [gameType], [categories], [limit] })` — `wins`, `shutouts`, `savePctg`, `goalsAgainstAverage`
122
+ - `clubStats({ teamAbbr, [season], [gameType] })` — a team's skater and goalie stats
111
123
  - `teamSummary({ startSeason, endSeason, [gameTypeId], [isGame], [isAggregate], [sortExpr], [start], [limit], [factCayenneExp], [defaultCayenneExp] })`
112
124
  - `skaterStatsSummary({ startSeason, endSeason, [franchiseId], [gameTypeId], [aggregate], [sortExpr], [start], [limit], [factCayenneExp], [defaultCayenneExp] })`
113
125
  - `skaterStatsWithQueryContext({ queryContext, reportType, [sortExpr], [aggregate], [start], [limit] })`
114
126
  - `goalieStatsSummary({ startSeason, [endSeason], [statsType], [gameTypeId], [franchiseId], [aggregate], [sortExpr], [start], [limit], [factCayenneExp], [defaultCayenneExp] })`
127
+ - `skaterLeaders({ attribute, [season] })` — top 10 single-season records, all time or for one `season`; `attribute` is `points`, `goals`, `assists` or `gamesPlayed`
128
+ - `goalieLeaders({ attribute, [season] })` — same, for `gaa`, `savePctg`, `shutouts`, `timeOnIce` or `gamesPlayed`. `gaa` / `savePctg` have no minimum games, so the top rows are goalies with a few minutes played (all-time `gaa` is `null`)
129
+ - `skaterMilestones()`, `goalieMilestones()` — career milestones active players are approaching
115
130
 
116
131
  ### edge
117
132
 
@@ -129,16 +144,68 @@ default; 3 = playoffs). Without `season` the current season (`/now`) is used.
129
144
  `teamZoneTimeDetails`, `teamShotLocationDetail`, `teamShotSpeedDetail`,
130
145
  `teamSkatingSpeedDetail`
131
146
  - Landing pages (no id): `skaterLanding`, `goalieLanding`, `teamLanding`
147
+ - Team comparison (`{ teamId }`): `teamComparison`
148
+ - League highlights: `byTheNumbers({ [season], [gameType] })`. Without `season`
149
+ it covers the latest game night; with one, the season bests
150
+ - Leaderboards (top 10, no id). Each returns a bare array of 10 rows that carry
151
+ every metric of that leaderboard; `sortBy` (required) only picks the order.
152
+ `positions` (`'all'`, `'forwards'`, `'defense'`; see `EdgePositions`),
153
+ `strength` (`'all'`, `'es'`, `'pp'`, `'pk'`; see `EdgeStrengths`) and the
154
+ shot-location `category` (`'all'`, `'high'`, `'mid'`, `'long'`) default to
155
+ `'all'`:
156
+ - Skaters: `skaterDistanceTop10({ [positions], [strength], sortBy })`,
157
+ `skaterSpeedTop10({ [positions], sortBy })`,
158
+ `skaterZoneTimeTop10({ [positions], [strength], sortBy })`,
159
+ `skaterShotSpeedTop10({ [positions], sortBy })`,
160
+ `skaterShotLocationTop10({ [positions], sortBy, [category] })`
161
+ - Goalies: `goalie5v5Top10({ sortBy })`,
162
+ `goalieShotLocationTop10({ sortBy, [category] })`,
163
+ `goalieSavePercentageTop10({ sortBy })`
164
+ - Teams: `teamSkatingDistanceTop10({ [positions], [strength], sortBy })`,
165
+ `teamSkatingSpeedTop10({ [positions], sortBy })`,
166
+ `teamZoneTimeTop10({ [strength], sortBy })`,
167
+ `teamShotSpeedTop10({ [positions], sortBy })`,
168
+ `teamShotLocationTop10({ [positions], sortBy, [category] })`
169
+ - Zone-time `sortBy` is the zone (`EdgeZones`: `'offensive'`, `'neutral'`,
170
+ `'defensive'`); the other `sortBy` values are typed per method (e.g.
171
+ `EdgeSkatingDistanceSortBy`: `'total'`, `'per-60'`, `'max-game'`,
172
+ `'max-period'`)
132
173
 
133
174
  ### players
134
175
 
135
176
  - `prospectsByTeam({ teamAbbr })`
136
- - `playersByTeam({ teamAbbr, season })`
177
+ - `playersByTeam({ teamAbbr, [season] })`
178
+ - `playerSpotlight()`
179
+ - `playerSearch({ [name], [teamId], [position], [cayenneExp], [sort], [dir], [start], [include], [exclude] })` — search every player on record, e.g. `{ name: 'matth', position: 'C' }`. `name` is a case-insensitive part of the full name; `position: 'F'` matches every forward; at least one filter is required. Returns `{ data, total }`; the API sends **at most 5 rows** per request, so page with `start`
180
+
181
+ ### draft
182
+
183
+ Draft years are **YYYY** (`2023`), not `YYYYYYYY`. Without `season` the
184
+ current draft (`/now`) is used.
185
+
186
+ - `rankings({ [season], [category] })` — Central Scouting rankings.
187
+ `category` is a `ProspectCategory` (1 NA skater, the default; 2 Intl skater;
188
+ 3 NA goalie; 4 Intl goalie)
189
+ - `tracker()` — the live draft tracker for the current draft
190
+ - `picks({ [season], [round] })` — picks for round `1`-`7` or `'all'` (the
191
+ default). `picks()` without a season returns round **1** of the current
192
+ draft, since the API redirects `draft/picks/now` there
193
+
194
+ Rounds per draft year live in `misc.draftYearAndRounds()`.
137
195
 
138
196
  ### misc
139
197
 
140
198
  - `glossary()`, `config()`, `countries()`, `seasonSpecificRulesAndInfo()`,
141
199
  `draftYearAndRounds()`
200
+ - `componentSeasons()` — the season and game type the stats API serves by default
201
+ - `games({ [season], [gameType], [teamId], [startDate], [endDate], [cayenneExp] })` — stats API games, e.g. `{ season: 20232024, teamId: 10 }`. At least one filter is required (unfiltered is every game ever, about 19 MB)
202
+ - `ping()` — stats API health check; resolves `true`, and throws on any failure (it never returns `false`)
203
+ - `seasonIds()` — every season id (`19171918` ... current)
204
+ - `tvSchedule({ [date] })` — national TV broadcasts for the week around a date
205
+ - `meta({ [players], [teams] })` — display metadata (names, slugs, headshots) for player ids and team abbreviations
206
+ - `partnerGameOdds({ countryCode })` — today's betting odds (experimental)
207
+ - `location()` — the caller's country from their IP (experimental)
208
+ - `postalLookup({ postalCode })` — teams whose broadcast market covers a postal code (experimental)
142
209
 
143
210
  ### helpers
144
211
 
@@ -290,21 +357,26 @@ nhl-api-ts teams teams --compact | jq -r '.[].abbr'
290
357
 
291
358
  ## Development
292
359
 
360
+ This package lives in a pnpm workspace. Run these from the
361
+ [repository root](https://github.com/andrewangelle/nhl-api-ts):
362
+
293
363
  ```bash
294
364
  pnpm install
295
365
  pnpm test # node:test, runs the TypeScript sources directly
296
366
  pnpm typecheck
297
367
  pnpm lint:check
298
- pnpm build # emits dist/
368
+ pnpm build # emits packages/nhl-api-ts/dist/
299
369
  ```
300
370
 
301
- See [CONTRIBUTING.md](CONTRIBUTING.md) for the full set of checks, and
302
- [AGENTS.md](AGENTS.md) for repository conventions.
371
+ See [CONTRIBUTING.md](https://github.com/andrewangelle/nhl-api-ts/blob/main/CONTRIBUTING.md)
372
+ for the full set of checks, and
373
+ [AGENTS.md](https://github.com/andrewangelle/nhl-api-ts/blob/main/AGENTS.md) for
374
+ repository conventions.
303
375
 
304
376
  Try the CLI from source:
305
377
 
306
378
  ```bash
307
- node --conditions=source src/cli.mts --help
379
+ node --conditions=source packages/nhl-api-ts/src/cli.mts --help
308
380
  ```
309
381
 
310
382
  ## Credits
@@ -0,0 +1,61 @@
1
+ import type { Context } from '#src/context.mjs';
2
+ import type { DraftPicks, DraftRankings, DraftTracker } from '#src/types/index.mjs';
3
+ import type { Id } from '#src/utils.mjs';
4
+ /** Prospect list of the NHL Central Scouting draft rankings. */
5
+ export declare const ProspectCategory: {
6
+ readonly NORTH_AMERICAN_SKATER: 1;
7
+ readonly INTERNATIONAL_SKATER: 2;
8
+ readonly NORTH_AMERICAN_GOALIE: 3;
9
+ readonly INTERNATIONAL_GOALIE: 4;
10
+ };
11
+ export type ProspectCategory = (typeof ProspectCategory)[keyof typeof ProspectCategory];
12
+ /** Draft round 1-7, or 'all' for every round. */
13
+ export type DraftRound = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 'all';
14
+ export interface DraftRankingsParams {
15
+ /** Draft year in YYYY format, e.g. 2023. Defaults to the current draft ("now"). */
16
+ season?: Id;
17
+ /** 1=NA skater (default), 2=Intl skater, 3=NA goalie, 4=Intl goalie. Ignored when `season` is omitted. */
18
+ category?: ProspectCategory;
19
+ }
20
+ export interface DraftPicksParams {
21
+ /** Draft year in YYYY format, e.g. 2023. Defaults to the current draft ("now"). */
22
+ season?: Id;
23
+ /** Round 1-7 or 'all' (default). Ignored when `season` is omitted. */
24
+ round?: DraftRound;
25
+ }
26
+ /**
27
+ * Gets the NHL Central Scouting rankings for one prospect category. The API
28
+ * has no season-only route, so `category` defaults to 1 when `season` is
29
+ * given; without `season` the current draft's North American skaters come
30
+ * back and `category` is ignored.
31
+ *
32
+ * @param params.season Draft year in YYYY format (not YYYYYYYY), e.g. 2023. Defaults to "now".
33
+ * @param params.category 1=NA skater, 2=Intl skater, 3=NA goalie, 4=Intl goalie. See `ProspectCategory`.
34
+ * @throws RangeError If `category` is not 1-4
35
+ * @example https://api-web.nhle.com/v1/draft/rankings/2023/1
36
+ */
37
+ export declare function rankings(ctx: Context, { season, category, }?: DraftRankingsParams): Promise<DraftRankings>;
38
+ /**
39
+ * Gets the live draft tracker: the current draft's broadcast info, state
40
+ * and the picks of the round in progress (or the last round, once the draft
41
+ * is over).
42
+ *
43
+ * @example https://api-web.nhle.com/v1/draft-tracker/picks/now
44
+ */
45
+ export declare function tracker(ctx: Context): Promise<DraftTracker>;
46
+ /**
47
+ * Gets the picks of a draft. Note the defaults differ: `picks()` hits
48
+ * `draft/picks/now`, which the API redirects to round **1** of the current
49
+ * draft, while `picks({ season })` returns every round.
50
+ *
51
+ * @param params.season Draft year in YYYY format (not YYYYYYYY), e.g. 2023. Defaults to "now".
52
+ * @param params.round Round 1-7 or 'all'. Defaults to 'all' when `season` is given.
53
+ * @throws RangeError If `round` is not 1-7 or 'all'
54
+ * @example https://api-web.nhle.com/v1/draft/picks/2023/all
55
+ */
56
+ export declare function picks(ctx: Context, { season, round }?: DraftPicksParams): Promise<DraftPicks>;
57
+ export declare const draftApi: {
58
+ readonly rankings: typeof rankings;
59
+ readonly tracker: typeof tracker;
60
+ readonly picks: typeof picks;
61
+ };
@@ -0,0 +1,68 @@
1
+ import { Endpoint } from '#src/http-client.mjs';
2
+ /** Prospect list of the NHL Central Scouting draft rankings. */
3
+ export const ProspectCategory = {
4
+ NORTH_AMERICAN_SKATER: 1,
5
+ INTERNATIONAL_SKATER: 2,
6
+ NORTH_AMERICAN_GOALIE: 3,
7
+ INTERNATIONAL_GOALIE: 4,
8
+ };
9
+ function inRange(value, min, max) {
10
+ return (Number.isInteger(value) && Number(value) >= min && Number(value) <= max);
11
+ }
12
+ async function getWeb(ctx, resource) {
13
+ return (await ctx.get(Endpoint.API_WEB_V1, resource)).json();
14
+ }
15
+ /**
16
+ * Gets the NHL Central Scouting rankings for one prospect category. The API
17
+ * has no season-only route, so `category` defaults to 1 when `season` is
18
+ * given; without `season` the current draft's North American skaters come
19
+ * back and `category` is ignored.
20
+ *
21
+ * @param params.season Draft year in YYYY format (not YYYYYYYY), e.g. 2023. Defaults to "now".
22
+ * @param params.category 1=NA skater, 2=Intl skater, 3=NA goalie, 4=Intl goalie. See `ProspectCategory`.
23
+ * @throws RangeError If `category` is not 1-4
24
+ * @example https://api-web.nhle.com/v1/draft/rankings/2023/1
25
+ */
26
+ export async function rankings(ctx, { season, category = ProspectCategory.NORTH_AMERICAN_SKATER, } = {}) {
27
+ if (!inRange(category, 1, 4)) {
28
+ throw new RangeError(`Invalid prospect category ${category}. Use 1-4 (see ProspectCategory).`);
29
+ }
30
+ if (season === undefined || season === null) {
31
+ return getWeb(ctx, 'draft/rankings/now');
32
+ }
33
+ return getWeb(ctx, `draft/rankings/${season}/${category}`);
34
+ }
35
+ /**
36
+ * Gets the live draft tracker: the current draft's broadcast info, state
37
+ * and the picks of the round in progress (or the last round, once the draft
38
+ * is over).
39
+ *
40
+ * @example https://api-web.nhle.com/v1/draft-tracker/picks/now
41
+ */
42
+ export async function tracker(ctx) {
43
+ return getWeb(ctx, 'draft-tracker/picks/now');
44
+ }
45
+ /**
46
+ * Gets the picks of a draft. Note the defaults differ: `picks()` hits
47
+ * `draft/picks/now`, which the API redirects to round **1** of the current
48
+ * draft, while `picks({ season })` returns every round.
49
+ *
50
+ * @param params.season Draft year in YYYY format (not YYYYYYYY), e.g. 2023. Defaults to "now".
51
+ * @param params.round Round 1-7 or 'all'. Defaults to 'all' when `season` is given.
52
+ * @throws RangeError If `round` is not 1-7 or 'all'
53
+ * @example https://api-web.nhle.com/v1/draft/picks/2023/all
54
+ */
55
+ export async function picks(ctx, { season, round = 'all' } = {}) {
56
+ if (round !== 'all' && !inRange(round, 1, 7)) {
57
+ throw new RangeError(`Invalid draft round ${round}. Use 1-7 or 'all'.`);
58
+ }
59
+ if (season === undefined || season === null) {
60
+ return getWeb(ctx, 'draft/picks/now');
61
+ }
62
+ return getWeb(ctx, `draft/picks/${season}/${round}`);
63
+ }
64
+ export const draftApi = {
65
+ rankings,
66
+ tracker,
67
+ picks,
68
+ };