@braintwopoint0/playback-commons 0.1.19 → 0.1.20
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/dist/playerdata/index.d.ts +171 -0
- package/dist/security/index.d.ts +21 -0
- package/package.json +2 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
declare const PLAYERDATA_BASE_URL = "https://app.playerdata.co.uk";
|
|
2
|
+
declare const PLAYERDATA_AUTH_URL = "https://app.playerdata.co.uk/api/oauth/authorize";
|
|
3
|
+
declare const PLAYERDATA_TOKEN_URL = "https://app.playerdata.co.uk/api/oauth/token";
|
|
4
|
+
declare const PLAYERDATA_GRAPHQL_URL = "https://app.playerdata.co.uk/api/graphql";
|
|
5
|
+
|
|
6
|
+
interface PlayerDataCredentials {
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
}
|
|
10
|
+
interface PlayerDataTokenResponse {
|
|
11
|
+
access_token: string;
|
|
12
|
+
refresh_token: string;
|
|
13
|
+
token_type: string;
|
|
14
|
+
expires_in: number;
|
|
15
|
+
scope?: string;
|
|
16
|
+
}
|
|
17
|
+
interface PlayerDataToken {
|
|
18
|
+
accessToken: string;
|
|
19
|
+
refreshToken: string;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
}
|
|
22
|
+
interface GraphQLResponse<T = unknown> {
|
|
23
|
+
data?: T;
|
|
24
|
+
errors?: GraphQLError[];
|
|
25
|
+
}
|
|
26
|
+
interface GraphQLError {
|
|
27
|
+
message: string;
|
|
28
|
+
locations?: {
|
|
29
|
+
line: number;
|
|
30
|
+
column: number;
|
|
31
|
+
}[];
|
|
32
|
+
path?: string[];
|
|
33
|
+
extensions?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
interface PDOrganisation {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
clubs: PDClub[];
|
|
39
|
+
}
|
|
40
|
+
interface PDClub {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
initials?: string;
|
|
44
|
+
sportDefinition?: PDSportDefinition;
|
|
45
|
+
athletes?: PDAthlete[];
|
|
46
|
+
}
|
|
47
|
+
interface PDSportDefinition {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
positionDefinitions?: PDPositionDefinition[];
|
|
51
|
+
}
|
|
52
|
+
interface PDPositionDefinition {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
initial?: string;
|
|
56
|
+
}
|
|
57
|
+
interface PDAthlete {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
initials: string;
|
|
61
|
+
customId?: string;
|
|
62
|
+
topSpeedKph?: number;
|
|
63
|
+
autoTopSpeedKph?: number;
|
|
64
|
+
defaultPosition?: PDPositionDefinition;
|
|
65
|
+
profilePicture?: PDProfilePicture;
|
|
66
|
+
}
|
|
67
|
+
interface PDProfilePicture {
|
|
68
|
+
thumbnailUrl?: string;
|
|
69
|
+
}
|
|
70
|
+
interface PDPerson {
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
email?: string;
|
|
74
|
+
athletes?: PDAthlete[];
|
|
75
|
+
clubs?: PDClub[];
|
|
76
|
+
}
|
|
77
|
+
interface PDSessionBase {
|
|
78
|
+
id: string;
|
|
79
|
+
startTime?: string;
|
|
80
|
+
endTime?: string;
|
|
81
|
+
description?: string;
|
|
82
|
+
club?: {
|
|
83
|
+
id: string;
|
|
84
|
+
name: string;
|
|
85
|
+
};
|
|
86
|
+
sessionParticipations?: PDSessionParticipation[];
|
|
87
|
+
}
|
|
88
|
+
interface PDTrainingSession extends PDSessionBase {
|
|
89
|
+
__typename: 'TrainingSession';
|
|
90
|
+
}
|
|
91
|
+
interface PDMatchSession extends PDSessionBase {
|
|
92
|
+
__typename: 'MatchSession';
|
|
93
|
+
opponent?: string;
|
|
94
|
+
opponentScore?: number;
|
|
95
|
+
ourTeam: string;
|
|
96
|
+
}
|
|
97
|
+
type PDSession = PDTrainingSession | PDMatchSession;
|
|
98
|
+
interface PDSessionParticipation {
|
|
99
|
+
id: string;
|
|
100
|
+
athlete: {
|
|
101
|
+
id: string;
|
|
102
|
+
name: string;
|
|
103
|
+
initials: string;
|
|
104
|
+
};
|
|
105
|
+
configuredMetrics?: PDConfiguredMetrics;
|
|
106
|
+
}
|
|
107
|
+
interface PDConfiguredMetrics {
|
|
108
|
+
data?: PDGenericMetric[];
|
|
109
|
+
}
|
|
110
|
+
interface PDConfiguredAggMetrics {
|
|
111
|
+
data?: PDGenericMetric[];
|
|
112
|
+
}
|
|
113
|
+
interface PDGenericMetric {
|
|
114
|
+
key: string;
|
|
115
|
+
label?: string;
|
|
116
|
+
shortLabel?: string;
|
|
117
|
+
localValue?: number | string | null;
|
|
118
|
+
localUnitLabel?: string;
|
|
119
|
+
category?: string;
|
|
120
|
+
precision?: number;
|
|
121
|
+
}
|
|
122
|
+
interface PDAthleteMetricsSummary {
|
|
123
|
+
athlete: {
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
};
|
|
127
|
+
configuredAggMetrics?: PDConfiguredAggMetrics;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate the OAuth authorization URL for user-level access.
|
|
132
|
+
* Redirect the user's browser to this URL to start the OAuth flow.
|
|
133
|
+
*/
|
|
134
|
+
declare function generateAuthUrl(credentials: PlayerDataCredentials, redirectUri: string, state?: string): string;
|
|
135
|
+
/**
|
|
136
|
+
* Exchange an authorization code for access + refresh tokens.
|
|
137
|
+
* Used after the user is redirected back from PlayerData.
|
|
138
|
+
*/
|
|
139
|
+
declare function exchangeCode(credentials: PlayerDataCredentials, code: string, redirectUri: string): Promise<PlayerDataToken>;
|
|
140
|
+
/**
|
|
141
|
+
* Refresh an expired access token using a refresh token.
|
|
142
|
+
* IMPORTANT: PlayerData refresh tokens are single-use.
|
|
143
|
+
* The response contains a NEW refresh token that must replace the old one.
|
|
144
|
+
*/
|
|
145
|
+
declare function refreshToken(credentials: PlayerDataCredentials, currentRefreshToken: string): Promise<PlayerDataToken>;
|
|
146
|
+
/**
|
|
147
|
+
* Get a service-level token using client credentials grant.
|
|
148
|
+
* Used for org-wide data access without user interaction.
|
|
149
|
+
*/
|
|
150
|
+
declare function getServiceToken(credentials: PlayerDataCredentials): Promise<PlayerDataToken>;
|
|
151
|
+
|
|
152
|
+
/** Verify auth and get the current user's identity */
|
|
153
|
+
declare const GET_CURRENT_PERSON = "\n query GetCurrentPerson {\n currentPerson {\n id\n name\n email\n athletes {\n id\n name\n club {\n id\n name\n }\n }\n clubs {\n id\n name\n }\n }\n }\n";
|
|
154
|
+
/** Get club info and its athlete roster */
|
|
155
|
+
declare const GET_CLUB = "\n query GetClub($clubId: ID!) {\n club(id: $clubId) {\n id\n name\n sportDefinition {\n id\n name\n positionDefinitions {\n id\n name\n initial\n }\n }\n athletes {\n id\n name\n initials\n customId\n topSpeedKph\n autoTopSpeedKph\n defaultPosition {\n id\n name\n initial\n }\n profilePicture {\n thumbnailUrl\n }\n }\n }\n }\n";
|
|
156
|
+
/** List sessions for a club with optional date filters */
|
|
157
|
+
declare const GET_CLUB_SESSIONS = "\n query GetClubSessions($clubId: ID!, $startTimeGteq: ISO8601DateTime, $startTimeLteq: ISO8601DateTime, $limit: Int, $offset: Int!) {\n club(id: $clubId) {\n id\n sessions(\n filter: { startTimeGteq: $startTimeGteq, startTimeLteq: $startTimeLteq }\n limit: $limit\n offset: $offset\n ) {\n id\n startTime\n endTime\n description\n ... on TrainingSession {\n __typename\n }\n ... on MatchSession {\n __typename\n opponent\n opponentScore\n ourTeam\n }\n }\n }\n }\n";
|
|
158
|
+
/** Get an athlete's sessions */
|
|
159
|
+
declare const GET_ATHLETE_SESSIONS = "\n query GetAthleteSessions($athleteId: ID!, $limit: Int, $offset: Int) {\n athlete(id: $athleteId) {\n id\n name\n sessions(limit: $limit, offset: $offset) {\n id\n startTime\n endTime\n description\n ... on TrainingSession {\n __typename\n }\n ... on MatchSession {\n __typename\n opponent\n opponentScore\n ourTeam\n }\n }\n }\n }\n";
|
|
160
|
+
/** Get a session with per-athlete metrics via participations */
|
|
161
|
+
declare const GET_SESSION_DETAILS = "\n query GetSessionDetails($sessionId: ID!) {\n session(id: $sessionId) {\n id\n startTime\n endTime\n description\n club {\n id\n name\n }\n ... on TrainingSession {\n __typename\n sessionParticipations {\n id\n athlete {\n id\n name\n initials\n }\n configuredMetrics {\n data {\n key\n label\n shortLabel\n localValue\n localUnitLabel\n category\n precision\n }\n }\n }\n }\n ... on MatchSession {\n __typename\n opponent\n opponentScore\n ourTeam\n sessionParticipations {\n id\n athlete {\n id\n name\n initials\n }\n configuredMetrics {\n data {\n key\n label\n shortLabel\n localValue\n localUnitLabel\n category\n precision\n }\n }\n }\n }\n }\n }\n";
|
|
162
|
+
/** Get aggregated metrics for an athlete over a date range */
|
|
163
|
+
declare const GET_ATHLETE_SUMMARY = "\n query GetAthleteSummary($athleteId: ID!, $startTime: ISO8601DateTime!, $endTime: ISO8601DateTime) {\n athlete(id: $athleteId) {\n id\n name\n timeSafeMetricsSummary(startTime: $startTime, endTime: $endTime) {\n athlete {\n id\n name\n }\n configuredAggMetrics {\n data {\n key\n label\n shortLabel\n localValue\n localUnitLabel\n category\n precision\n }\n }\n }\n }\n }\n";
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Execute a GraphQL query against the PlayerData API.
|
|
167
|
+
* Stateless — caller provides the access token.
|
|
168
|
+
*/
|
|
169
|
+
declare function executeQuery<T = unknown>(token: string, query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
170
|
+
|
|
171
|
+
export { GET_ATHLETE_SESSIONS, GET_ATHLETE_SUMMARY, GET_CLUB, GET_CLUB_SESSIONS, GET_CURRENT_PERSON, GET_SESSION_DETAILS, type GraphQLError, type GraphQLResponse, type PDAthlete, type PDAthleteMetricsSummary, type PDClub, type PDConfiguredAggMetrics, type PDConfiguredMetrics, type PDGenericMetric, type PDMatchSession, type PDOrganisation, type PDPerson, type PDPositionDefinition, type PDProfilePicture, type PDSession, type PDSessionBase, type PDSessionParticipation, type PDSportDefinition, type PDTrainingSession, PLAYERDATA_AUTH_URL, PLAYERDATA_BASE_URL, PLAYERDATA_GRAPHQL_URL, PLAYERDATA_TOKEN_URL, type PlayerDataCredentials, type PlayerDataToken, type PlayerDataTokenResponse, exchangeCode, executeQuery, generateAuthUrl, getServiceToken, refreshToken };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize a redirect path to prevent open redirect attacks.
|
|
3
|
+
* Returns '/' if the path is unsafe.
|
|
4
|
+
*/
|
|
5
|
+
declare function sanitizeRedirect(raw: string | null | undefined): string;
|
|
6
|
+
/**
|
|
7
|
+
* Escape HTML special characters to prevent injection in templates.
|
|
8
|
+
*/
|
|
9
|
+
declare function escapeHtml(s: string): string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Verify an API key from a request header using timing-safe comparison.
|
|
13
|
+
* Prevents timing side-channel attacks on key verification.
|
|
14
|
+
*
|
|
15
|
+
* @param request - The incoming request
|
|
16
|
+
* @param expectedKey - The expected API key value
|
|
17
|
+
* @param headerName - The header to read (default: 'x-api-key')
|
|
18
|
+
*/
|
|
19
|
+
declare function verifyApiKey(request: Request, expectedKey: string, headerName?: string): boolean;
|
|
20
|
+
|
|
21
|
+
export { escapeHtml, sanitizeRedirect, verifyApiKey };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braintwopoint0/playback-commons",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Shared UI components, auth utilities, and Supabase client for the PLAYBACK Sports ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"fonts"
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
|
-
"build": "tsup",
|
|
39
|
+
"build": "rm -rf dist && tsup",
|
|
40
40
|
"dev": "tsup --watch",
|
|
41
41
|
"test": "vitest run"
|
|
42
42
|
},
|