vairified 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.
- package/LICENSE +21 -0
- package/README.md +399 -0
- package/dist/index.cjs +1042 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1015 -0
- package/dist/index.d.ts +1015 -0
- package/dist/index.js +994 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vairified Corp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
<h1 align="center">Vairified JavaScript SDK</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Official TypeScript/JavaScript SDK for the Vairified Partner API</strong><br>
|
|
5
|
+
Player ratings, search, and match submission
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://github.com/Vairified/vairified.js/actions/workflows/ci.yml"><img src="https://github.com/Vairified/vairified.js/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/vairified"><img src="https://img.shields.io/npm/v/vairified.svg" alt="npm"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
TypeScript/JavaScript SDK for integrating with the [Vairified](https://vairified.com) player rating platform. Zero dependencies, works in Node.js and browsers.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install vairified
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or with other package managers:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add vairified
|
|
27
|
+
pnpm add vairified
|
|
28
|
+
bun add vairified
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Vairified } from 'vairified';
|
|
35
|
+
|
|
36
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
37
|
+
|
|
38
|
+
// Get a member - automatically subscribes to their rating updates
|
|
39
|
+
const member = await client.getMember('clerk_user_123');
|
|
40
|
+
console.log(`${member.name}: ${member.rating}`);
|
|
41
|
+
console.log(`Verified: ${member.isVairified}`);
|
|
42
|
+
console.log(`Best rating: ${member.ratingSplits.best}`);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
### Search for Players
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
51
|
+
|
|
52
|
+
// Search with filters
|
|
53
|
+
const results = await client.search({
|
|
54
|
+
city: 'Austin',
|
|
55
|
+
state: 'TX',
|
|
56
|
+
ratingMin: 3.5,
|
|
57
|
+
ratingMax: 4.5,
|
|
58
|
+
vairifiedOnly: true,
|
|
59
|
+
limit: 20,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Iterate over results
|
|
63
|
+
for (const player of results) {
|
|
64
|
+
console.log(`${player.name}: ${player.rating}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Pagination
|
|
68
|
+
console.log(`Page ${results.page} of ${results.pages}`);
|
|
69
|
+
if (results.hasMore) {
|
|
70
|
+
const nextPage = await results.nextPage();
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Find a Player by Name
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const player = await client.findPlayer('John Smith');
|
|
78
|
+
if (player) {
|
|
79
|
+
console.log(`Found: ${player.name} (${player.rating})`);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Submit Match Results
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Vairified, Match } from 'vairified';
|
|
87
|
+
|
|
88
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
89
|
+
|
|
90
|
+
// Doubles match: 11-9, 11-7
|
|
91
|
+
const match = new Match({
|
|
92
|
+
event: 'Weekly League',
|
|
93
|
+
bracket: '4.0 Doubles',
|
|
94
|
+
date: new Date(),
|
|
95
|
+
team1: ['player1_id', 'player2_id'],
|
|
96
|
+
team2: ['player3_id', 'player4_id'],
|
|
97
|
+
scores: [[11, 9], [11, 7]],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const result = await client.submitMatch(match);
|
|
101
|
+
if (result.ok) {
|
|
102
|
+
console.log(`Submitted ${result.numGames} games`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Singles match
|
|
106
|
+
const singles = new Match({
|
|
107
|
+
event: 'Club Singles',
|
|
108
|
+
bracket: 'Open Singles',
|
|
109
|
+
date: new Date(),
|
|
110
|
+
team1: ['player1_id'],
|
|
111
|
+
team2: ['player2_id'],
|
|
112
|
+
scores: [[11, 8], [9, 11], [11, 6]],
|
|
113
|
+
});
|
|
114
|
+
await client.submitMatch(singles);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Get Rating Updates
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// First, look up members to subscribe to their updates
|
|
121
|
+
await client.getMember('user_1');
|
|
122
|
+
await client.getMember('user_2');
|
|
123
|
+
|
|
124
|
+
// Later, check for rating changes
|
|
125
|
+
const updates = await client.getRatingUpdates();
|
|
126
|
+
for (const update of updates) {
|
|
127
|
+
const direction = update.improved ? 'improved' : 'dropped';
|
|
128
|
+
console.log(`${update.memberId} ${direction}: ${update.previousRating} -> ${update.newRating}`);
|
|
129
|
+
|
|
130
|
+
// Get the full member profile
|
|
131
|
+
const member = await update.getMember();
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### OAuth Connect Flow
|
|
136
|
+
|
|
137
|
+
Connect players to your application using OAuth to access their profile and rating data.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { Vairified, generateState, OAuthError } from 'vairified';
|
|
141
|
+
|
|
142
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
143
|
+
|
|
144
|
+
// Step 1: Start authorization
|
|
145
|
+
const state = generateState(); // CSRF protection
|
|
146
|
+
const auth = await client.startOAuth(
|
|
147
|
+
'https://myapp.com/oauth/callback',
|
|
148
|
+
['profile:read', 'rating:read', 'match:submit'],
|
|
149
|
+
state,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Redirect user to auth.authorizationUrl
|
|
153
|
+
window.location.href = auth.authorizationUrl;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Step 2: Handle callback (in your /oauth/callback route)
|
|
158
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
159
|
+
|
|
160
|
+
// Exchange code for tokens
|
|
161
|
+
const code = new URL(window.location.href).searchParams.get('code')!;
|
|
162
|
+
const tokens = await client.exchangeToken(code, 'https://myapp.com/oauth/callback');
|
|
163
|
+
|
|
164
|
+
// Store tokens securely
|
|
165
|
+
const { playerId, accessToken, refreshToken } = tokens;
|
|
166
|
+
|
|
167
|
+
// Now you can access the player's data
|
|
168
|
+
const member = await client.getMember(playerId);
|
|
169
|
+
console.log(`Connected: ${member.name} (${member.rating})`);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Step 3: Refresh expired tokens
|
|
174
|
+
try {
|
|
175
|
+
const newTokens = await client.refreshAccessToken(storedRefreshToken);
|
|
176
|
+
// Update stored tokens
|
|
177
|
+
} catch (e) {
|
|
178
|
+
if (e instanceof OAuthError && e.errorCode === 'invalid_grant') {
|
|
179
|
+
// Token revoked, user needs to re-authorize
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Available OAuth Scopes
|
|
185
|
+
|
|
186
|
+
| Scope | Description |
|
|
187
|
+
|-------|-------------|
|
|
188
|
+
| `profile:read` | Name, location, verification status |
|
|
189
|
+
| `profile:email` | Email address |
|
|
190
|
+
| `rating:read` | Current rating and rating splits |
|
|
191
|
+
| `rating:history` | Complete rating history |
|
|
192
|
+
| `match:submit` | Submit matches on behalf of user |
|
|
193
|
+
| `webhook:subscribe` | Rating change notifications |
|
|
194
|
+
|
|
195
|
+
### Revoke Connection
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
await client.revokeConnection('vair_mem_xxx');
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Models
|
|
202
|
+
|
|
203
|
+
### Player
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
player.id // UUID or member ID
|
|
207
|
+
player.memberId // Legacy member ID
|
|
208
|
+
player.name // "John Smith"
|
|
209
|
+
player.firstName // "John"
|
|
210
|
+
player.lastName // "Smith"
|
|
211
|
+
player.rating // 4.25
|
|
212
|
+
player.isVairified // true/false
|
|
213
|
+
player.ratingSplits // RatingSplits object
|
|
214
|
+
player.city // "Austin"
|
|
215
|
+
player.state // "TX"
|
|
216
|
+
player.verifiedRating // Best verified rating
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Member (extends Player)
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
member.email // Email address
|
|
223
|
+
await member.refresh() // Refresh data from API
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### RatingSplits
|
|
227
|
+
|
|
228
|
+
Access ratings by category:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const splits = member.ratingSplits;
|
|
232
|
+
splits.open // Open division rating
|
|
233
|
+
splits.gender // Same-gender doubles rating
|
|
234
|
+
splits.mixed // Mixed doubles rating
|
|
235
|
+
splits.recreational // Recreational rating
|
|
236
|
+
splits.singles // Singles rating
|
|
237
|
+
splits.best // Best available rating
|
|
238
|
+
splits.get('50_and_up') // Age bracket rating
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Match
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const match = new Match({
|
|
245
|
+
event: 'Weekly League',
|
|
246
|
+
bracket: '4.0 Doubles',
|
|
247
|
+
date: new Date(),
|
|
248
|
+
team1: ['id1', 'id2'], // Player IDs for team 1
|
|
249
|
+
team2: ['id3', 'id4'], // Player IDs for team 2
|
|
250
|
+
scores: [[11, 9], [11, 7]], // Game scores
|
|
251
|
+
location: 'Austin Club', // Optional
|
|
252
|
+
matchType: 'SIDEOUT', // Default: "SIDEOUT"
|
|
253
|
+
source: 'PARTNER', // Default: "PARTNER"
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
match.format // "DOUBLES" or "SINGLES"
|
|
257
|
+
match.winner // 1 or 2 (0 if tie)
|
|
258
|
+
match.scoreSummary // "11-9, 11-7"
|
|
259
|
+
match.identifier // Auto-generated unique ID
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### MatchResult
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const result = await client.submitMatches([match1, match2]);
|
|
266
|
+
result.success // true/false
|
|
267
|
+
result.numMatches // Number processed
|
|
268
|
+
result.numGames // Games recorded
|
|
269
|
+
result.dryRun // true if validation only
|
|
270
|
+
result.message // Human-readable message
|
|
271
|
+
result.errors // Array of errors
|
|
272
|
+
result.ok // true if successful
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### SearchResults
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
results.players // Array of Player objects
|
|
279
|
+
results.total // Total matching players
|
|
280
|
+
results.page // Current page
|
|
281
|
+
results.pages // Total pages
|
|
282
|
+
results.hasMore // More pages available
|
|
283
|
+
await results.nextPage() // Get next page
|
|
284
|
+
results.length // Players on current page
|
|
285
|
+
results.at(0) // Get by index
|
|
286
|
+
for (const p of results) { } // Iterable
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Configuration
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { Vairified } from 'vairified';
|
|
293
|
+
|
|
294
|
+
// Basic usage
|
|
295
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
296
|
+
|
|
297
|
+
// Use staging for development/testing
|
|
298
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx', env: 'staging' });
|
|
299
|
+
|
|
300
|
+
// Custom configuration
|
|
301
|
+
const client = new Vairified({
|
|
302
|
+
apiKey: 'vair_pk_xxx',
|
|
303
|
+
timeout: 30000,
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Environment Variables
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
export VAIRIFIED_API_KEY="vair_pk_xxx"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// API key read from environment (Node.js only)
|
|
315
|
+
const client = new Vairified();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Dry-Run Mode (Dev Keys)
|
|
319
|
+
|
|
320
|
+
If your API key has the `dry-run` scope, match submissions are **validated but not persisted**. This is useful for testing integrations without affecting production data.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// With a dry-run API key
|
|
324
|
+
const client = new Vairified({ apiKey: 'vair_pk_dev_xxx' });
|
|
325
|
+
const result = await client.submitMatches([match1, match2]);
|
|
326
|
+
|
|
327
|
+
if (result.dryRun) {
|
|
328
|
+
console.log(`Validation passed: ${result.numGames} games would be created`);
|
|
329
|
+
console.log(result.message);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Request a dry-run API key from your Vairified partner contact for integration testing.
|
|
334
|
+
|
|
335
|
+
## Error Handling
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import {
|
|
339
|
+
Vairified,
|
|
340
|
+
VairifiedError,
|
|
341
|
+
RateLimitError,
|
|
342
|
+
AuthenticationError,
|
|
343
|
+
NotFoundError,
|
|
344
|
+
OAuthError,
|
|
345
|
+
} from 'vairified';
|
|
346
|
+
|
|
347
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx' });
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const member = await client.getMember('user_123');
|
|
351
|
+
} catch (error) {
|
|
352
|
+
if (error instanceof RateLimitError) {
|
|
353
|
+
console.log(`Rate limited. Retry after ${error.retryAfter} seconds`);
|
|
354
|
+
} else if (error instanceof AuthenticationError) {
|
|
355
|
+
console.log('Invalid API key');
|
|
356
|
+
} else if (error instanceof NotFoundError) {
|
|
357
|
+
console.log('Member not found');
|
|
358
|
+
} else if (error instanceof OAuthError) {
|
|
359
|
+
console.log(`OAuth error: ${error.message} (code: ${error.errorCode})`);
|
|
360
|
+
} else if (error instanceof VairifiedError) {
|
|
361
|
+
console.log(`API error: ${error.message} (status: ${error.statusCode})`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## TypeScript
|
|
367
|
+
|
|
368
|
+
Full TypeScript support with exported types:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import type {
|
|
372
|
+
MatchInput,
|
|
373
|
+
MatchResultData,
|
|
374
|
+
PlayerData,
|
|
375
|
+
SearchFilters,
|
|
376
|
+
VairifiedOptions,
|
|
377
|
+
} from 'vairified';
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Development
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
git clone https://github.com/Vairified/vairified.js.git
|
|
384
|
+
cd vairified.js
|
|
385
|
+
npm install
|
|
386
|
+
npm test
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## License
|
|
390
|
+
|
|
391
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
<p align="center">
|
|
396
|
+
<a href="https://vairified.com">vairified.com</a> ·
|
|
397
|
+
<a href="https://vairified.github.io/vairified.js">Documentation</a> ·
|
|
398
|
+
<a href="mailto:support@vairified.com">Support</a>
|
|
399
|
+
</p>
|