dupr-js-client 0.1.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.
@@ -0,0 +1,516 @@
1
+ type AuthMode = {
2
+ type: "clientCredentials";
3
+ clientKey: string;
4
+ clientSecret: string;
5
+ } | {
6
+ type: "staticBearer";
7
+ bearerToken: string;
8
+ } | {
9
+ type: "none";
10
+ };
11
+ interface RequestInfo {
12
+ method: string;
13
+ url: string;
14
+ headers: Record<string, string>;
15
+ }
16
+ interface ResponseInfo {
17
+ status: number;
18
+ url: string;
19
+ durationMs: number;
20
+ }
21
+ interface RetryOptions {
22
+ /** Max number of retry attempts after the initial failure. @default 3 */
23
+ maxRetries?: number;
24
+ /** Base delay in ms before the first retry. Doubles on each attempt. @default 1_000 */
25
+ baseDelayMs?: number;
26
+ /** Max delay cap in ms (prevents infinite backoff). @default 30_000 */
27
+ maxDelayMs?: number;
28
+ }
29
+ interface DuprClientOptions {
30
+ /** @default "https://uat.mydupr.com/api" */
31
+ baseUrl?: string;
32
+ /** API version prefix used in all paths. @default "v1.0" */
33
+ version?: string;
34
+ /** Request timeout in milliseconds. @default 30_000 */
35
+ timeoutMs?: number;
36
+ auth?: AuthMode;
37
+ /** Custom User-Agent header. */
38
+ userAgent?: string;
39
+ /** Inject a custom fetch implementation (useful for testing or polyfilling). */
40
+ customFetch?: typeof fetch;
41
+ /** Called before every request. Useful for logging or tracing. */
42
+ onRequest?: (info: RequestInfo) => void;
43
+ /** Called after every response (including errors). */
44
+ onResponse?: (info: ResponseInfo) => void;
45
+ /** Retry behaviour for transient errors (5xx, network failures, 429). Pass `false` to disable. */
46
+ retry?: RetryOptions | false;
47
+ }
48
+ interface ResolvedRetryOptions {
49
+ maxRetries: number;
50
+ baseDelayMs: number;
51
+ maxDelayMs: number;
52
+ }
53
+ interface ResolvedConfig {
54
+ baseUrl: string;
55
+ version: string;
56
+ timeoutMs: number;
57
+ auth: AuthMode;
58
+ userAgent: string;
59
+ customFetch: typeof fetch;
60
+ retry: ResolvedRetryOptions | false;
61
+ onRequest?: (info: RequestInfo) => void;
62
+ onResponse?: (info: ResponseInfo) => void;
63
+ /** Runtime override for the bearer token — set via `client.setBearerToken()`. */
64
+ overrideBearerToken?: string;
65
+ }
66
+
67
+ declare class HttpClient {
68
+ private readonly config;
69
+ private token;
70
+ private tokenExpiresAt;
71
+ constructor(config: ResolvedConfig);
72
+ private getBearerToken;
73
+ get<T>(path: string, init?: RequestInit): Promise<T>;
74
+ post<T>(path: string, body?: unknown, init?: RequestInit): Promise<T>;
75
+ delete<T>(path: string, body?: unknown, init?: RequestInit): Promise<T>;
76
+ request<T>(method: string, path: string, init?: RequestInit): Promise<T>;
77
+ private rawRequest;
78
+ }
79
+
80
+ type MatchFormat = "SINGLES" | "DOUBLES";
81
+ type MatchSource = "PARTNER" | "CLUB";
82
+ type Gender = "MALE" | "FEMALE";
83
+ type RatingType = "SINGLES" | "DOUBLES";
84
+ type WebhookTopic = "RATING";
85
+ type ApiStatus = "SUCCESS" | "FAILURE";
86
+ interface ApiWrapper<T = unknown> {
87
+ status: ApiStatus;
88
+ message?: string;
89
+ result?: T;
90
+ }
91
+ interface TokenResponse {
92
+ token?: string;
93
+ accessToken?: string;
94
+ expiresIn?: number;
95
+ }
96
+ interface UserInfo {
97
+ duprId: string;
98
+ fullName: string;
99
+ singlesRating?: number;
100
+ doublesRating?: number;
101
+ singlesProvisional?: boolean;
102
+ doublesProvisional?: boolean;
103
+ }
104
+ interface ExtendedUserInfo extends UserInfo {
105
+ email?: string;
106
+ }
107
+ interface ClubMembership {
108
+ clubId: string;
109
+ clubName: string;
110
+ role: string;
111
+ }
112
+ interface ClubMembershipResult {
113
+ clubs: ClubMembership[];
114
+ }
115
+ type ClubMembershipResponse = ApiWrapper<ClubMembershipResult>;
116
+ interface ExternalSearchFilter {
117
+ gender?: Gender;
118
+ location?: ExternalFilterLocation;
119
+ rating?: ExternalRatingFilter;
120
+ age?: ExternalAgeRangeFilter;
121
+ }
122
+ interface ExternalFilterLocation {
123
+ lat: number;
124
+ lng: number;
125
+ address?: string;
126
+ radiusInMeters?: number;
127
+ }
128
+ interface ExternalRatingFilter {
129
+ min?: number;
130
+ max?: number;
131
+ type?: RatingType;
132
+ reliable?: boolean;
133
+ }
134
+ interface ExternalAgeRangeFilter {
135
+ min?: number;
136
+ max?: number;
137
+ }
138
+ interface ExternalSearchRequest {
139
+ query: string;
140
+ offset: number;
141
+ limit: number;
142
+ filters?: ExternalSearchFilter;
143
+ }
144
+ interface ExternalBatchUserDetailRequest {
145
+ duprIds: string[];
146
+ }
147
+ interface ExternalInviteRequest {
148
+ firstName: string;
149
+ lastName: string;
150
+ email: string;
151
+ }
152
+ interface RatingCoach {
153
+ id: string;
154
+ metadata: Record<string, string>;
155
+ }
156
+ interface GetProvisionalRatingRequest {
157
+ duprId: string;
158
+ }
159
+ interface CreateProvisionalRatingRequest {
160
+ duprId: string;
161
+ provisionalSinglesRating?: number;
162
+ provisionalDoublesRating?: number;
163
+ coach?: RatingCoach;
164
+ }
165
+ interface UpdateProvisionalRatingRequest {
166
+ duprId: string;
167
+ provisionalSinglesRating?: number;
168
+ provisionalDoublesRating?: number;
169
+ }
170
+ interface DeleteProvisionalRatingRequest {
171
+ duprId: string;
172
+ }
173
+ interface ExternalMatchPlayer {
174
+ duprId: string;
175
+ }
176
+ interface ExternalMatchTeam {
177
+ /** 1 player for SINGLES, 2 for DOUBLES */
178
+ players: ExternalMatchPlayer[];
179
+ /** Scores per game, aligned with the opposing team's scores array */
180
+ scores: number[];
181
+ }
182
+ interface ExternalMatchRequest {
183
+ /** Your universally unique identifier — must not be reused across matches */
184
+ identifier: string;
185
+ /** yyyy-MM-dd */
186
+ matchDate: string;
187
+ matchFormat: MatchFormat;
188
+ teams: [ExternalMatchTeam, ExternalMatchTeam];
189
+ source: MatchSource;
190
+ eventId?: number;
191
+ clubId?: number;
192
+ }
193
+ interface ExternalUpdateMatchRequest {
194
+ matchId: string;
195
+ matchDate?: string;
196
+ teams?: [ExternalMatchTeam, ExternalMatchTeam];
197
+ }
198
+ interface ExternalDeleteMatchRequest {
199
+ matchCode: string;
200
+ identifier: string;
201
+ }
202
+ interface ExternalMatchSearchRequest {
203
+ duprId: string;
204
+ offset?: number;
205
+ limit?: number;
206
+ }
207
+ interface MatchResponse {
208
+ matchCode?: string;
209
+ hashedMatchCode?: string;
210
+ identifier?: string;
211
+ matchDate?: string;
212
+ matchFormat?: MatchFormat;
213
+ teams?: ExternalMatchTeam[];
214
+ }
215
+ interface SubscriptionItemRequest {
216
+ productId: string;
217
+ promotionId: string;
218
+ }
219
+ interface TargetUserRequest {
220
+ duprId?: string;
221
+ email?: string;
222
+ }
223
+ interface AttributionRequest {
224
+ clientKeyId: number;
225
+ }
226
+ interface GrantExternalSubscriptionRequest {
227
+ user: TargetUserRequest;
228
+ item: SubscriptionItemRequest;
229
+ attribution: AttributionRequest;
230
+ metadata?: Record<string, string>;
231
+ }
232
+ interface PlayerRatingSubscribeRequest {
233
+ duprIds: string[];
234
+ }
235
+ interface ExternalClubMemberRequest {
236
+ clubId: string;
237
+ }
238
+ interface ExternalClubMatchSearchRequest {
239
+ clubId: string;
240
+ offset?: number;
241
+ limit?: number;
242
+ }
243
+ interface CreateEventRequestV1 {
244
+ name: string;
245
+ description?: string;
246
+ startDate: string;
247
+ endDate: string;
248
+ location?: string;
249
+ clubId?: number;
250
+ }
251
+ interface UpdateEventRequestV1 {
252
+ eventId: string;
253
+ name?: string;
254
+ description?: string;
255
+ startDate?: string;
256
+ endDate?: string;
257
+ }
258
+ interface GetEventRequestV1 {
259
+ eventIds: string[];
260
+ }
261
+ interface DeleteEventRequestV1 {
262
+ eventIds: string[];
263
+ }
264
+ interface CreateEventResponseV1 {
265
+ eventId?: string;
266
+ name?: string;
267
+ }
268
+ interface UpdateEventResponseV1 {
269
+ eventId?: string;
270
+ }
271
+ interface GetEventResponseV1 {
272
+ events?: Array<{
273
+ eventId: string;
274
+ name: string;
275
+ description?: string;
276
+ startDate?: string;
277
+ endDate?: string;
278
+ location?: string;
279
+ }>;
280
+ }
281
+ interface DeleteEventResponseV1 {
282
+ deleted?: string[];
283
+ }
284
+ interface ClientHookRequest {
285
+ webhookUrl: string;
286
+ topics: WebhookTopic[];
287
+ }
288
+ interface UserWebhookRequest {
289
+ duprIds: string[];
290
+ topic: WebhookTopic;
291
+ }
292
+ interface WebhookSchema {
293
+ topic: string;
294
+ schema: Record<string, unknown>;
295
+ }
296
+
297
+ interface AuthCodeTokenResponse extends TokenResponse {
298
+ refresh_token?: string;
299
+ token_type?: string;
300
+ scope?: string;
301
+ }
302
+ interface AuthorizationUrlParams {
303
+ /** Your OAuth redirect URI, must match what is registered with DUPR. */
304
+ redirectUri: string;
305
+ /** OAuth scopes to request (e.g. ["user.read"]). Defaults to empty. */
306
+ scopes?: string[];
307
+ /** Opaque value passed through the flow to prevent CSRF. Recommended. */
308
+ state?: string;
309
+ }
310
+ declare class AuthModule {
311
+ private readonly http;
312
+ private readonly config;
313
+ constructor(http: HttpClient, config: ResolvedConfig);
314
+ /**
315
+ * Explicitly request a new access token using client credentials.
316
+ * Normally you don't need to call this — the HTTP client handles token refresh automatically.
317
+ */
318
+ getToken(): Promise<TokenResponse>;
319
+ /**
320
+ * Build the DUPR authorization URL to redirect the user to for login.
321
+ * The `authorizationUrl` config option must be set (see above note).
322
+ */
323
+ getAuthorizationUrl(params: AuthorizationUrlParams): string;
324
+ /**
325
+ * Exchange an authorization code (from the OAuth callback) for access + refresh tokens.
326
+ * Uses `POST /auth/{version}/token` with `grant_type=authorization_code`.
327
+ */
328
+ exchangeCode(params: {
329
+ code: string;
330
+ redirectUri: string;
331
+ }): Promise<AuthCodeTokenResponse>;
332
+ /**
333
+ * Obtain a new access token using a refresh token from a previous Authorization Code flow.
334
+ * Uses `POST /auth/{version}/token` with `grant_type=refresh_token`.
335
+ */
336
+ refreshToken(refreshToken: string): Promise<AuthCodeTokenResponse>;
337
+ }
338
+
339
+ declare class UsersModule {
340
+ private readonly http;
341
+ private readonly cfg;
342
+ constructor(http: HttpClient, cfg: ResolvedConfig);
343
+ /** GET /user/{version}/{id} — basic player profile */
344
+ getUser(duprId: string): Promise<ApiWrapper<UserInfo>>;
345
+ /** GET /user/{version}/{id}/details — extended profile (requires USER_EMAIL::VIEW permission) */
346
+ getExtendedUser(duprId: string): Promise<ApiWrapper<ExtendedUserInfo>>;
347
+ /** GET /user/{version}/{id}/clubs — club memberships for a player */
348
+ getClubMemberships(duprId: string): Promise<ClubMembershipResponse>;
349
+ /** POST /user/{version}/search — full-text player search with optional filters */
350
+ search(req: ExternalSearchRequest): Promise<ApiWrapper<UserInfo[]>>;
351
+ /** POST /user/{version}/batch — fetch multiple players by DUPR ID in one request */
352
+ getBatch(req: ExternalBatchUserDetailRequest): Promise<ApiWrapper<UserInfo[]>>;
353
+ /** POST /user/{version}/invite — pre-generate a DUPR ID and send an invite email */
354
+ invite(req: ExternalInviteRequest): Promise<ApiWrapper<{
355
+ duprId: string;
356
+ }>>;
357
+ /** POST /user/{version}/subscription/grants — grant a subscription to a user */
358
+ grantSubscription(req: GrantExternalSubscriptionRequest): Promise<ApiWrapper>;
359
+ /** POST /user/{version}/provisional_rating — get provisional ratings for a player */
360
+ getProvisionalRating(req: GetProvisionalRatingRequest): Promise<ApiWrapper>;
361
+ /** POST /user/{version}/provisional_rating/create */
362
+ createProvisionalRating(req: CreateProvisionalRatingRequest): Promise<ApiWrapper>;
363
+ /** POST /user/{version}/provisional_rating/update */
364
+ updateProvisionalRating(req: UpdateProvisionalRatingRequest): Promise<ApiWrapper>;
365
+ /** DELETE /user/{version}/provisional_rating/delete */
366
+ deleteProvisionalRating(req: DeleteProvisionalRatingRequest): Promise<ApiWrapper>;
367
+ }
368
+
369
+ interface GetDuprIdByEmailRequest {
370
+ email: string;
371
+ }
372
+ interface DuprIdByEmailResponse {
373
+ duprId?: string;
374
+ }
375
+ declare class PlayersModule {
376
+ private readonly http;
377
+ private readonly cfg;
378
+ constructor(http: HttpClient, cfg: ResolvedConfig);
379
+ /** POST /{version}/player/duprid-by-email — resolve a DUPR ID from an email address */
380
+ getDuprIdByEmail(req: GetDuprIdByEmailRequest): Promise<ApiWrapper<DuprIdByEmailResponse>>;
381
+ }
382
+
383
+ interface PlayerRatingHistoryRequest {
384
+ duprId: string;
385
+ offset?: number;
386
+ limit?: number;
387
+ }
388
+ interface PlayerRatingHistoryEntry {
389
+ date?: string;
390
+ singlesRating?: number;
391
+ doublesRating?: number;
392
+ }
393
+ declare class PlayerRatingModule {
394
+ private readonly http;
395
+ private readonly cfg;
396
+ constructor(http: HttpClient, cfg: ResolvedConfig);
397
+ /** POST /history — get rating history for a player */
398
+ getHistory(req: PlayerRatingHistoryRequest): Promise<ApiWrapper<PlayerRatingHistoryEntry[]>>;
399
+ /** GET /{version}/subscribe/rating-changes — list currently subscribed DUPR IDs */
400
+ getSubscriptions(): Promise<ApiWrapper<{
401
+ duprIds: string[];
402
+ }>>;
403
+ /** POST /{version}/subscribe/rating-changes — subscribe to rating-change events for a list of players */
404
+ subscribe(req: PlayerRatingSubscribeRequest): Promise<ApiWrapper>;
405
+ /** DELETE /{version}/subscribe/rating-changes — unsubscribe from rating-change events */
406
+ unsubscribe(req: PlayerRatingSubscribeRequest): Promise<ApiWrapper>;
407
+ }
408
+
409
+ interface AnnotateMatchRequest {
410
+ matchId: string;
411
+ annotation: Record<string, unknown>;
412
+ }
413
+ declare class MatchesModule {
414
+ private readonly http;
415
+ private readonly cfg;
416
+ constructor(http: HttpClient, cfg: ResolvedConfig);
417
+ /** GET /match/{version}/{id} — view a single match */
418
+ get(matchId: string): Promise<ApiWrapper<MatchResponse>>;
419
+ /** POST /match/{version}/create — submit a new match result */
420
+ create(match: ExternalMatchRequest): Promise<ApiWrapper<MatchResponse>>;
421
+ /** POST /match/{version}/batch — submit multiple match results in one call */
422
+ createBulk(matches: ExternalMatchRequest[]): Promise<ApiWrapper<MatchResponse[]>>;
423
+ /** POST /match/{version}/update — update an existing match */
424
+ update(req: ExternalUpdateMatchRequest): Promise<ApiWrapper<MatchResponse>>;
425
+ /** DELETE /match/{version}/delete — delete a match */
426
+ delete(req: ExternalDeleteMatchRequest): Promise<ApiWrapper>;
427
+ /** POST /match/{version}/annotate — attach vendor-specific metadata to a match */
428
+ annotate(req: AnnotateMatchRequest): Promise<ApiWrapper>;
429
+ /** DELETE /match/{version}/annotate/{matchId} — remove a match annotation */
430
+ deleteAnnotation(matchId: string): Promise<ApiWrapper>;
431
+ /** POST /match/history/search — search a player's match history */
432
+ searchHistory(req: ExternalMatchSearchRequest): Promise<ApiWrapper<MatchResponse[]>>;
433
+ }
434
+
435
+ declare class ClubsModule {
436
+ private readonly http;
437
+ private readonly cfg;
438
+ constructor(http: HttpClient, cfg: ResolvedConfig);
439
+ /** POST /club/{version}/members — get ratings for all members of a club */
440
+ membersRating(req: ExternalClubMemberRequest): Promise<ApiWrapper<UserInfo[]>>;
441
+ /** POST /club/{version}/match/search — search matches associated with a club */
442
+ searchMatches(req: ExternalClubMatchSearchRequest): Promise<ApiWrapper<MatchResponse[]>>;
443
+ }
444
+
445
+ declare class EventsModule {
446
+ private readonly http;
447
+ private readonly cfg;
448
+ constructor(http: HttpClient, cfg: ResolvedConfig);
449
+ /** POST /events/{version}/create */
450
+ create(req: CreateEventRequestV1): Promise<ApiWrapper<CreateEventResponseV1>>;
451
+ /** GET /events/{version}/get */
452
+ get(req: GetEventRequestV1): Promise<ApiWrapper<GetEventResponseV1>>;
453
+ /** POST /events/{version}/update */
454
+ update(req: UpdateEventRequestV1): Promise<ApiWrapper<UpdateEventResponseV1>>;
455
+ /** POST /events/{version}/delete */
456
+ delete(req: DeleteEventRequestV1): Promise<ApiWrapper<DeleteEventResponseV1>>;
457
+ }
458
+
459
+ declare class WebhooksModule {
460
+ private readonly http;
461
+ private readonly cfg;
462
+ constructor(http: HttpClient, cfg: ResolvedConfig);
463
+ /** POST /{version}/webhook — register a webhook endpoint */
464
+ register(req: ClientHookRequest): Promise<ApiWrapper>;
465
+ /** GET /{version}/topic — list all available webhook topics */
466
+ getTopics(): Promise<ApiWrapper<string[]>>;
467
+ /** GET /{version}/webhook/schema — list available webhook schemas */
468
+ listSchemas(): Promise<ApiWrapper<WebhookSchema[]>>;
469
+ /** GET /{version}/webhook/schema/{topic} — get the JSON schema for a specific topic */
470
+ getSchema(topic: string): Promise<ApiWrapper<WebhookSchema>>;
471
+ /** POST /user/{version}/subscribe/webhook-event — subscribe specific users to a webhook topic */
472
+ subscribeUsers(req: UserWebhookRequest): Promise<ApiWrapper>;
473
+ /** DELETE /user/{version}/subscribe/webhook-event — unsubscribe specific users from a webhook topic */
474
+ unsubscribeUsers(req: UserWebhookRequest): Promise<ApiWrapper>;
475
+ }
476
+
477
+ declare class DuprClient {
478
+ readonly config: ResolvedConfig;
479
+ readonly auth: AuthModule;
480
+ readonly users: UsersModule;
481
+ readonly players: PlayersModule;
482
+ readonly playerRating: PlayerRatingModule;
483
+ readonly matches: MatchesModule;
484
+ readonly clubs: ClubsModule;
485
+ readonly events: EventsModule;
486
+ readonly webhooks: WebhooksModule;
487
+ constructor(opts?: DuprClientOptions);
488
+ /** Override the bearer token at runtime — useful when your backend handles auth separately. */
489
+ setBearerToken(token: string): void;
490
+ /** Clear the runtime bearer token override and fall back to configured auth. */
491
+ clearBearerToken(): void;
492
+ }
493
+
494
+ declare class DuprApiError extends Error {
495
+ readonly statusCode: number;
496
+ readonly details?: unknown | undefined;
497
+ readonly duprRequestId?: string | undefined;
498
+ constructor(message: string, statusCode: number, details?: unknown | undefined, duprRequestId?: string | undefined);
499
+ }
500
+ declare class AuthenticationError extends DuprApiError {
501
+ constructor(message: string, statusCode: number, details?: unknown, duprRequestId?: string);
502
+ }
503
+ declare class ValidationError extends DuprApiError {
504
+ constructor(message: string, statusCode: number, details?: unknown, duprRequestId?: string);
505
+ }
506
+ declare class NotFoundError extends DuprApiError {
507
+ constructor(message: string, statusCode: number, details?: unknown, duprRequestId?: string);
508
+ }
509
+ declare class RateLimitError extends DuprApiError {
510
+ constructor(message: string, statusCode: number, details?: unknown, duprRequestId?: string);
511
+ }
512
+ declare class ServerError extends DuprApiError {
513
+ constructor(message: string, statusCode: number, details?: unknown, duprRequestId?: string);
514
+ }
515
+
516
+ export { type ApiStatus, type ApiWrapper, type AttributionRequest, type AuthCodeTokenResponse, type AuthMode, AuthModule, AuthenticationError, type AuthorizationUrlParams, type ClientHookRequest, type ClubMembership, type ClubMembershipResponse, ClubsModule, type CreateEventRequestV1, type CreateEventResponseV1, type CreateProvisionalRatingRequest, type DeleteEventRequestV1, type DeleteEventResponseV1, type DeleteProvisionalRatingRequest, DuprApiError, DuprClient, type DuprClientOptions, EventsModule, type ExtendedUserInfo, type ExternalAgeRangeFilter, type ExternalBatchUserDetailRequest, type ExternalClubMatchSearchRequest, type ExternalClubMemberRequest, type ExternalDeleteMatchRequest, type ExternalFilterLocation, type ExternalInviteRequest, type ExternalMatchPlayer, type ExternalMatchRequest, type ExternalMatchSearchRequest, type ExternalMatchTeam, type ExternalRatingFilter, type ExternalSearchFilter, type ExternalSearchRequest, type ExternalUpdateMatchRequest, type Gender, type GetEventRequestV1, type GetEventResponseV1, type GetProvisionalRatingRequest, type GrantExternalSubscriptionRequest, type MatchFormat, type MatchResponse, type MatchSource, MatchesModule, NotFoundError, PlayerRatingModule, type PlayerRatingSubscribeRequest, PlayersModule, RateLimitError, type RatingCoach, type RatingType, type RequestInfo, type ResolvedConfig, type ResolvedRetryOptions, type ResponseInfo, type RetryOptions, ServerError, type SubscriptionItemRequest, type TargetUserRequest, type TokenResponse, type UpdateEventRequestV1, type UpdateEventResponseV1, type UpdateProvisionalRatingRequest, type UserInfo, type UserWebhookRequest, UsersModule, ValidationError, type WebhookSchema, type WebhookTopic, WebhooksModule };