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.
- package/README.md +81 -9
- package/dist/api/draft.d.mts +61 -0
- package/dist/api/draft.mjs +68 -0
- package/dist/api/edge.d.mts +221 -24
- package/dist/api/edge.mjs +276 -27
- package/dist/api/game-center.d.mts +63 -15
- package/dist/api/game-center.mjs +67 -8
- package/dist/api/helpers.d.mts +6 -6
- package/dist/api/helpers.mjs +6 -6
- package/dist/api/misc.d.mts +120 -6
- package/dist/api/misc.mjs +162 -7
- package/dist/api/players.d.mts +59 -7
- package/dist/api/players.mjs +85 -4
- package/dist/api/query/cayenne.d.mts +4 -0
- package/dist/api/query/cayenne.mjs +13 -0
- package/dist/api/query/sorting/sorting-options.d.mts +8 -0
- package/dist/api/query/sorting/sorting-options.mjs +30 -0
- package/dist/api/schedule.d.mts +36 -23
- package/dist/api/schedule.mjs +40 -16
- package/dist/api/standings.d.mts +3 -3
- package/dist/api/standings.mjs +4 -4
- package/dist/api/stats.d.mts +105 -16
- package/dist/api/stats.mjs +145 -41
- package/dist/api/teams.d.mts +48 -8
- package/dist/api/teams.mjs +59 -7
- package/dist/cli/colors.d.mts +2 -2
- package/dist/cli/colors.mjs +6 -2
- package/dist/cli/commands.mjs +521 -46
- package/dist/cli/run.mjs +7 -5
- package/dist/http-client.d.mts +2 -1
- package/dist/http-client.mjs +10 -3
- package/dist/index.d.mts +8 -2
- package/dist/index.mjs +3 -0
- package/dist/nhl-client.d.mts +70 -15
- package/dist/nhl-client.mjs +40 -17
- package/dist/types/common.d.mts +0 -1
- package/dist/types/draft.d.mts +97 -0
- package/dist/types/draft.mjs +1 -0
- package/dist/types/edge.d.mts +301 -5
- package/dist/types/game-center.d.mts +277 -119
- package/dist/types/index.d.mts +1 -0
- package/dist/types/misc.d.mts +101 -0
- package/dist/types/misc.mjs +0 -1
- package/dist/types/players.d.mts +23 -2
- package/dist/types/schedule.d.mts +50 -62
- package/dist/types/standings.d.mts +1 -2
- package/dist/types/stats.d.mts +313 -111
- package/dist/types/teams.d.mts +29 -1
- package/dist/utils.d.mts +1 -1
- package/dist/utils.mjs +3 -1
- 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)
|
|
302
|
-
|
|
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
|
+
};
|