vairified 0.3.2 → 0.6.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 +104 -3
- package/dist/index.cjs +419 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +401 -6
- package/dist/index.d.ts +401 -6
- package/dist/index.js +417 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ Every operation lives on a sub-resource that mirrors the REST path:
|
|
|
52
52
|
|
|
53
53
|
| Sub-resource | Operations |
|
|
54
54
|
|-------------------------|------------------------------------------------------------------|
|
|
55
|
-
| `client.members` | `get`, `getBulk`, `search`, `find`, `ratingUpdates`
|
|
55
|
+
| `client.members` | `get`, `getBulk`, `getByEmail`, `search`, `find`, `ratingUpdates` |
|
|
56
56
|
| `client.matches` | `submit`, `tournamentImport`, `testWebhook` |
|
|
57
57
|
| `client.oauth` | `authorize`, `exchangeToken`, `refresh`, `revoke` |
|
|
58
58
|
| `client.webhooks` | `deliveries` |
|
|
@@ -69,12 +69,13 @@ const member = await client.members.get('vair_mem_xxx');
|
|
|
69
69
|
console.log(member.name); // Full name
|
|
70
70
|
console.log(member.displayName); // "Mike B."
|
|
71
71
|
console.log(member.ratingFor('pickleball')); // 3.915
|
|
72
|
-
console.log(member.
|
|
72
|
+
console.log(member.sport.get('pickleball')?.isVairified); // true (per-sport)
|
|
73
73
|
|
|
74
74
|
// Dict-like access to rating splits for a specific sport
|
|
75
75
|
const pb = member.sport.get('pickleball');
|
|
76
76
|
if (pb) {
|
|
77
77
|
console.log(pb.rating, pb.abbr); // 3.915 VO
|
|
78
|
+
console.log(pb.isVairified, pb.isVairPro); // per-sport status flags
|
|
78
79
|
console.log(pb.get('overall-open')?.rating); // 3.915
|
|
79
80
|
console.log(pb.has('singles-open')); // true
|
|
80
81
|
for (const [key, split] of pb) {
|
|
@@ -99,6 +100,34 @@ const pb = await client.members.getBulk([4873327], { sport: 'pickleball' });
|
|
|
99
100
|
|
|
100
101
|
Unknown IDs are silently omitted — the returned array may be shorter than the input.
|
|
101
102
|
|
|
103
|
+
### Look members up by email
|
|
104
|
+
|
|
105
|
+
Resolve up to 100 members by their **exact** email address — useful for linking
|
|
106
|
+
your users to their VAIR identity when you hold their email but not their member
|
|
107
|
+
ID, instead of waiting for each player to complete SSO:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const result = await client.members.getByEmail(['ada@example.com', 'nobody@example.com']);
|
|
111
|
+
|
|
112
|
+
for (const match of result.matched) {
|
|
113
|
+
const member = match.sole; // null when the address is ambiguous
|
|
114
|
+
if (member) console.log(match.email, '->', member.memberId);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Read notFound directly — don't diff your input against the results
|
|
118
|
+
console.log('no VAIR account found for:', result.notFound);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Requires the **`key:player:lookup`** scope, granted per partner on approval —
|
|
122
|
+
holding `key:player:search` does not imply it. Ask VAIR to enable it for your app.
|
|
123
|
+
|
|
124
|
+
Unlike `getBulk`, **nothing is silently dropped**: every address you supply comes
|
|
125
|
+
back in either `matched` or `notFound`. Matching is exact and case-insensitive
|
|
126
|
+
(no partial or fuzzy matching), and `match.members` is an array because an email
|
|
127
|
+
is not a unique key in VAIR — use `.sole` or check `.isAmbiguous` rather than
|
|
128
|
+
assuming a single result. A `notFound` address is not proof the person has no VAIR
|
|
129
|
+
account: unclaimed imported records are excluded from this lookup.
|
|
130
|
+
|
|
102
131
|
### Filter ratings to specific sports
|
|
103
132
|
|
|
104
133
|
```ts
|
|
@@ -357,6 +386,60 @@ export VAIRIFIED_ENV="staging" # optional; default: production
|
|
|
357
386
|
const client = new Vairified(); // reads both env vars
|
|
358
387
|
```
|
|
359
388
|
|
|
389
|
+
## React Native
|
|
390
|
+
|
|
391
|
+
The SDK keeps its **zero-dependency** promise on React Native too — it doesn't bundle
|
|
392
|
+
any polyfills. Instead it feature-detects the platform primitives it needs and expects
|
|
393
|
+
your app to provide the two that Hermes lacks.
|
|
394
|
+
|
|
395
|
+
### Required polyfills
|
|
396
|
+
|
|
397
|
+
Install them and import each **once, at your app entry** (e.g. the top of `index.js`),
|
|
398
|
+
before any SDK call:
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
npm install react-native-get-random-values react-native-url-polyfill
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
// index.js — must run before `import { Vairified } from 'vairified'`
|
|
406
|
+
import 'react-native-get-random-values'; // Web Crypto for generateState()
|
|
407
|
+
import 'react-native-url-polyfill/auto'; // WHATWG URL / URLSearchParams
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
- **`react-native-get-random-values`** backs `crypto.getRandomValues`, which
|
|
411
|
+
`generateState()` uses for CSRF tokens. Without it, `generateState()` throws a
|
|
412
|
+
descriptive error (not a bare `ReferenceError`); you can also skip it and pass your
|
|
413
|
+
own high-entropy `state` string to `oauth.authorize()`.
|
|
414
|
+
- **`react-native-url-polyfill`** provides a complete `URL`/`URLSearchParams`. Hermes'
|
|
415
|
+
built-ins are incomplete, so request-URL and authorization-URL building need this.
|
|
416
|
+
|
|
417
|
+
### Pass `apiKey` and `env` explicitly
|
|
418
|
+
|
|
419
|
+
React Native has no `process.env`, so the `VAIRIFIED_API_KEY` / `VAIRIFIED_ENV` fallbacks
|
|
420
|
+
never resolve there (the SDK guards against the missing global rather than crashing).
|
|
421
|
+
Always construct the client explicitly:
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
const client = new Vairified({ apiKey: 'vair_pk_xxx', env: 'production' });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Token topology — never ship the API key in the app
|
|
428
|
+
|
|
429
|
+
The secret partner API key (`X-API-Key`) **must never ship in a mobile app binary**.
|
|
430
|
+
Split the OAuth flow across the app and your backend:
|
|
431
|
+
|
|
432
|
+
1. **App:** open the authorization URL using your public `client_id` (your `PartnerApp`
|
|
433
|
+
slug) — build it with `getAuthorizationUrl({ redirectUri, clientId })` — and capture
|
|
434
|
+
the `myapp://callback` deep link to read the `code` (and `state`).
|
|
435
|
+
2. **Your backend:** perform the code exchange there with a `Vairified` client that holds
|
|
436
|
+
the secret key — `client.oauth.exchangeToken({ code, redirectUri })`, and likewise
|
|
437
|
+
`refresh()` / `revoke()`. The SDK injects `X-API-Key` on these calls, so they belong
|
|
438
|
+
on the server, never in the app.
|
|
439
|
+
|
|
440
|
+
The app sends the captured `code` to your backend over your own authenticated channel;
|
|
441
|
+
the backend returns only the resulting access token (or a session) to the app.
|
|
442
|
+
|
|
360
443
|
## Error Handling
|
|
361
444
|
|
|
362
445
|
The SDK maps HTTP status codes to typed exceptions. All typed exceptions inherit from
|
|
@@ -410,7 +493,8 @@ member.displayName // "Mike B."
|
|
|
410
493
|
member.firstName / lastName
|
|
411
494
|
member.gender // 'MALE' | 'FEMALE' | 'OTHER' | 'UNKNOWN' | null
|
|
412
495
|
member.age / city / state / zip / country
|
|
413
|
-
member.status.
|
|
496
|
+
member.status.isWheelchair // global status flags
|
|
497
|
+
member.status.isAmbassador
|
|
414
498
|
member.status.isConnected
|
|
415
499
|
member.sport // MemberSportMap
|
|
416
500
|
member.sports // readonly string[] of sport codes
|
|
@@ -418,12 +502,19 @@ member.ratingFor('pickleball') // number | null
|
|
|
418
502
|
member.split('overall-open') // RatingSplitWire | null
|
|
419
503
|
```
|
|
420
504
|
|
|
505
|
+
VAIRification & VAIR-Pro status are **per-sport** — read them off each
|
|
506
|
+
`member.sport` entry, not `member.status` (see `SportRating` below).
|
|
507
|
+
|
|
421
508
|
### `SportRating` (dict-like)
|
|
422
509
|
|
|
423
510
|
```ts
|
|
424
511
|
const pb = member.sport.get('pickleball');
|
|
425
512
|
pb?.rating // Primary rating for this sport
|
|
426
513
|
pb?.abbr // "VO", "VG", etc.
|
|
514
|
+
pb?.isVairified // per-sport VAIRified flag (Vairified#783)
|
|
515
|
+
pb?.isRater // per-sport rater flag
|
|
516
|
+
pb?.isVairPro // per-sport VAIR-Pro flag
|
|
517
|
+
pb?.isVairProStatus // 'PENDING' | 'ACTIVE' | null
|
|
427
518
|
pb?.get('overall-open') // Any split key
|
|
428
519
|
pb?.size // Number of splits
|
|
429
520
|
pb?.has('singles-40+') // Membership check
|
|
@@ -432,6 +523,16 @@ for (const [key, split] of pb ?? []) { /* iterate */ }
|
|
|
432
523
|
|
|
433
524
|
## Migrating
|
|
434
525
|
|
|
526
|
+
**From 0.3.x → 0.4.0 (breaking):** VAIRification & VAIR-Pro status are now
|
|
527
|
+
**per-sport** (Vairified#783). `isVairified`, `isRater`, `isVairPro`, and
|
|
528
|
+
`isVairProStatus` moved off the member `status` object onto each per-sport
|
|
529
|
+
entry — read them via `member.sport.get(code)?.isVairified` instead of
|
|
530
|
+
`member.status.isVairified`. The `status` object keeps only the genuinely
|
|
531
|
+
global flags (`isWheelchair`, `isAmbassador`, `isConnected`). This mirrors
|
|
532
|
+
the backend: a player can be VAIRified / a VAIR Pro in one sport but not
|
|
533
|
+
another. Publish only after the backend #788 reaches production — against
|
|
534
|
+
the old prod shape the per-sport flags default to `false`/`null`.
|
|
535
|
+
|
|
435
536
|
**From 0.2.x → 0.3.0:** All OAuth scope strings gained a `user:` prefix
|
|
436
537
|
(`profile:read` → `user:profile:read`). Update any hardcoded scope arrays.
|
|
437
538
|
New: `members.getBulk()`, `matches.tournamentImport()`, `webhooks.deliveries()`.
|