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 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>