vairified 0.6.0 → 0.8.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 +145 -1
- package/dist/index.cjs +227 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +496 -4
- package/dist/index.d.ts +496 -4
- package/dist/index.js +216 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/leaderboard.ts","../src/models/match-batch-result.ts","../src/models/tournament-import-result.ts","../src/resources/matches.ts","../src/models/sport-rating.ts","../src/models/member.ts","../src/models/members-by-email-result.ts","../src/models/rating-update.ts","../src/resources/members.ts","../src/oauth.ts","../src/resources/oauth.ts","../src/models/attribution.ts","../src/resources/referrals.ts","../src/models/webhook-delivery.ts","../src/resources/webhooks.ts","../src/client.ts"],"sourcesContent":["/**\n * Vairified SDK — error hierarchy.\n *\n * All SDK errors inherit from {@link VairifiedError}, so a single\n * `catch (err: unknown) { if (err instanceof VairifiedError) ... }`\n * covers everything. HTTP-status-specific subclasses (auth, not found,\n * rate limit, validation) are thrown automatically by the HTTP layer.\n *\n * @module\n */\n\n/**\n * Base class for every error thrown by the Vairified SDK.\n *\n * @category Errors\n */\nexport class VairifiedError extends Error {\n /** HTTP status code (if the error came from an API response). */\n readonly statusCode?: number;\n /** Raw response body parsed as JSON when available. */\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = 'VairifiedError';\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/**\n * Thrown on HTTP 429 responses. Carries the `Retry-After` header value\n * when the server provides one.\n *\n * @category Errors\n */\nexport class RateLimitError extends VairifiedError {\n /** Seconds to wait before retrying, or `undefined` if the server didn't say. */\n readonly retryAfter?: number;\n\n constructor(message = 'Rate limit exceeded', retryAfter?: number, response?: unknown) {\n super(message, 429, response);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown on HTTP 401 — invalid or missing API key.\n *\n * @category Errors\n */\nexport class AuthenticationError extends VairifiedError {\n constructor(message = 'Invalid API key', response?: unknown) {\n super(message, 401, response);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown on HTTP 404 — the resource doesn't exist.\n *\n * @category Errors\n */\nexport class NotFoundError extends VairifiedError {\n constructor(message = 'Resource not found', response?: unknown) {\n super(message, 404, response);\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Thrown on HTTP 400 — the server rejected the request payload.\n *\n * Inspect {@link VairifiedError.response} for field-level details.\n *\n * @category Errors\n */\nexport class ValidationError extends VairifiedError {\n constructor(message = 'Validation error', response?: unknown) {\n super(message, 400, response);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Thrown by {@link OAuthResource} methods when authorization, token\n * exchange, refresh, or revocation fails.\n *\n * @category Errors\n */\nexport class OAuthError extends VairifiedError {\n /**\n * OAuth error code such as `'invalid_grant'`, `'invalid_scope'`,\n * or `'expired_token'`. Check this to branch on the specific failure.\n */\n readonly errorCode?: string;\n\n constructor(message = 'OAuth error', errorCode?: string, response?: unknown) {\n super(message, undefined, response);\n this.name = 'OAuthError';\n this.errorCode = errorCode;\n }\n}\n","/**\n * HTTP transport — internal. Not part of the public API.\n *\n * The transport is a thin wrapper around native `fetch` that:\n *\n * - Injects the API key header on every request.\n * - Serializes query params (dropping `undefined`) and JSON bodies.\n * - Applies a request timeout via `AbortController`.\n * - Maps non-2xx responses to the right typed exception.\n *\n * @internal\n * @module\n */\n\nimport {\n AuthenticationError,\n NotFoundError,\n RateLimitError,\n VairifiedError,\n ValidationError,\n} from './errors.js';\nimport type { ApiErrorResponse } from './types.js';\n\n/**\n * Values accepted as query parameters.\n *\n * `null` and `undefined` are silently dropped. Arrays are joined with\n * `','`. Everything else is stringified.\n *\n * @internal\n */\nexport type QueryParams = Readonly<\n Record<string, string | number | boolean | readonly (string | number)[] | null | undefined>\n>;\n\n/**\n * Options passed to the internal HTTP layer.\n *\n * @internal\n */\nexport interface RequestOptions {\n readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n readonly path: string;\n readonly query?: QueryParams;\n readonly body?: unknown;\n}\n\n/**\n * Transport configuration.\n *\n * @internal\n */\nexport interface TransportConfig {\n readonly baseUrl: string;\n readonly apiKey: string;\n readonly timeoutMs: number;\n readonly fetch: typeof fetch;\n}\n\n/**\n * Internal HTTP client. Holds no state between requests — each call\n * builds a fresh `Request`, runs it, and returns the parsed body.\n *\n * @internal\n */\nexport class HttpTransport {\n readonly #config: TransportConfig;\n\n constructor(config: TransportConfig) {\n this.#config = config;\n }\n\n async request<T>(options: RequestOptions): Promise<T> {\n const url = buildUrl(this.#config.baseUrl, options.path, options.query);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error(`Request timed out after ${this.#config.timeoutMs}ms`)),\n this.#config.timeoutMs,\n );\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.#config.apiKey,\n Accept: 'application/json',\n };\n\n const init: RequestInit = {\n method: options.method,\n headers,\n signal: controller.signal,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await this.#config.fetch(url, init);\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n await throwFromResponse(response);\n }\n\n if (response.status === 204 || response.headers.get('content-length') === '0') {\n return undefined as T;\n }\n\n const text = await response.text();\n if (text.length === 0) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VairifiedError(`Unable to parse response as JSON: ${text}`, response.status);\n }\n }\n}\n\n/**\n * Build a full URL from a base and relative path, appending query params.\n *\n * @internal\n */\nexport function buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const cleanBase = baseUrl.replace(/\\/+$/, '');\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(cleanBase + cleanPath);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue;\n if (Array.isArray(value)) {\n if (value.length === 0) continue;\n url.searchParams.set(key, value.join(','));\n } else {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n}\n\n/**\n * Parse an error response and throw the right typed exception.\n *\n * @internal\n */\nasync function throwFromResponse(response: Response): Promise<never> {\n const status = response.status;\n const text = await response.text().catch(() => '');\n\n let body: ApiErrorResponse | unknown = null;\n if (text.length > 0) {\n try {\n body = JSON.parse(text);\n } catch {\n body = null;\n }\n }\n\n let message: string;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n const apiBody = body as ApiErrorResponse;\n message = apiBody.message || apiBody.error || text || `HTTP ${status}`;\n } else {\n message = text || `HTTP ${status}`;\n }\n\n switch (status) {\n case 400:\n throw new ValidationError(message, body);\n case 401:\n throw new AuthenticationError(message, body);\n case 404:\n throw new NotFoundError(message, body);\n case 429: {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : undefined;\n throw new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : undefined, body);\n }\n default:\n throw new VairifiedError(message, status, body);\n }\n}\n","/**\n * {@link LeaderboardResource} — read-only leaderboard queries.\n *\n * @module\n */\n\nimport type { HttpTransport, QueryParams } from '../http.js';\nimport type { LeaderboardOptions, PlayerRankOptions } from '../types.js';\n\n/**\n * Read-only leaderboard queries.\n *\n * @category Resources\n */\nexport class LeaderboardResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /** Fetch a leaderboard page with optional filters. */\n async list(options: LeaderboardOptions = {}): Promise<Record<string, unknown>> {\n const query: QueryParams = {\n limit: options.limit ?? 50,\n offset: options.offset ?? 0,\n category: options.category,\n ageBracket: options.ageBracket,\n scope: options.scope,\n state: options.state,\n city: options.city,\n clubId: options.clubId,\n gender: options.gender?.toUpperCase(),\n minGames: options.minGames,\n search: options.search,\n verifiedOnly: options.verifiedOnly === true ? true : undefined,\n };\n\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/leaderboard',\n query,\n });\n return data ?? {};\n }\n\n /** Fetch a specific player's rank plus nearby players. */\n async rank(playerId: string, options: PlayerRankOptions = {}): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {\n playerId,\n category: options.category ?? 'doubles',\n ageBracket: options.ageBracket ?? 'open',\n scope: options.scope ?? 'global',\n contextSize: options.contextSize ?? 5,\n };\n if (options.state !== undefined) body.state = options.state;\n if (options.city !== undefined) body.city = options.city;\n if (options.clubId !== undefined) body.clubId = options.clubId;\n\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/leaderboard/rank',\n body,\n });\n return data ?? {};\n }\n\n /** List available leaderboard categories, brackets, and scopes. */\n async categories(): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/leaderboard/categories',\n });\n return data ?? {};\n }\n}\n","/**\n * {@link MatchBatchResult} — result of a batch match submission.\n *\n * @module\n */\n\nimport type { MatchBatchResultWire } from '../types.js';\n\n/**\n * Result of a {@link MatchesResource.submit} call.\n *\n * {@link success} is `true` only when every match in the batch was\n * accepted. Check {@link errors} for per-match validation failures.\n *\n * @category Matches\n */\nexport class MatchBatchResult {\n readonly success: boolean;\n readonly numMatches: number;\n readonly numGames: number;\n readonly dryRun: boolean | null;\n readonly message: string | null;\n readonly errors: readonly string[] | null;\n\n constructor(wire: MatchBatchResultWire) {\n this.success = wire.success;\n this.numMatches = wire.numMatches;\n this.numGames = wire.numGames;\n this.dryRun = wire.dryRun ?? null;\n this.message = wire.message ?? null;\n this.errors = wire.errors ? Object.freeze([...wire.errors]) : null;\n Object.freeze(this);\n }\n\n /** Shorthand: successful submission with zero errors. */\n get ok(): boolean {\n return this.success && (this.errors === null || this.errors.length === 0);\n }\n\n /** Whether this was a dry-run (validation only, nothing persisted). */\n get isDryRun(): boolean {\n return this.dryRun === true;\n }\n\n toString(): string {\n const mode = this.isDryRun ? ' [dry-run]' : '';\n const errs = this.errors && this.errors.length > 0 ? ` errors=${this.errors.length}` : '';\n const status = this.ok ? 'ok' : 'FAILED';\n return `MatchBatchResult ${status}${mode} matches=${this.numMatches} games=${this.numGames}${errs}`;\n }\n}\n","/**\n * {@link TournamentImportResult} — result of a tournament import submission.\n *\n * @module\n */\n\nimport type { TournamentImportResultWire } from '../types.js';\n\n/**\n * One ghost player created by a tournament import.\n *\n * @category Matches\n */\nexport interface TournamentImportCreatedGhost {\n /** The email or phone you supplied for this person in `ghostMembers[]`. */\n readonly ref: string;\n /** The public member id allocated to them, usable in `matches.submit()`. */\n readonly memberId: number;\n}\n\n/**\n * Result of a tournament import submission.\n *\n * @category Matches\n */\nexport class TournamentImportResult {\n readonly success: boolean;\n readonly matchesImported: number;\n readonly gamesRecorded: number;\n readonly ghostPlayersCreated: number;\n readonly existingPlayersMatched: number;\n readonly dryRun: boolean;\n readonly message: string | undefined;\n readonly errors: readonly string[];\n /**\n * Public member ids for the ghost players THIS import created, keyed by the\n * `ref` supplied in `ghostMembers[]`.\n *\n * Empty on a dry-run, and empty for entries the import matched to a player who\n * already existed — resolving an existing email to a member requires the\n * `key:player:lookup` scope and `members.getByEmail()`.\n *\n * Use these ids directly in a follow-up `matches.submit()`; without them an\n * import reports only how many accounts it caused and you cannot address any\n * of them.\n */\n readonly createdGhostMembers: readonly TournamentImportCreatedGhost[];\n\n /** @internal */\n constructor(wire: TournamentImportResultWire) {\n this.success = wire.success;\n this.matchesImported = wire.matchesImported;\n this.gamesRecorded = wire.gamesRecorded;\n this.ghostPlayersCreated = wire.ghostPlayersCreated;\n this.existingPlayersMatched = wire.existingPlayersMatched;\n this.dryRun = wire.dryRun ?? false;\n this.message = wire.message;\n this.errors = Object.freeze(wire.errors ?? []);\n this.createdGhostMembers = Object.freeze(\n (wire.createdGhostMembers ?? []).map((entry) =>\n Object.freeze({ ref: entry.ref, memberId: entry.memberId }),\n ),\n );\n Object.freeze(this);\n }\n\n /** True when the import succeeded without errors. */\n get ok(): boolean {\n return this.success && this.errors.length === 0;\n }\n}\n","/**\n * {@link MatchesResource} — bulk match submission.\n *\n * @module\n */\n\nimport type { HttpTransport } from '../http.js';\nimport { MatchBatchResult } from '../models/match-batch-result.js';\nimport { TournamentImportResult } from '../models/tournament-import-result.js';\nimport type { MatchBatch, MatchBatchResultWire, TournamentImportResultWire } from '../types.js';\n\n/**\n * Match submission — one call submits a full batch.\n *\n * @category Resources\n */\nexport class MatchesResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Submit a {@link MatchBatch} for rating calculation.\n *\n * All players in every match must have granted the `user:match:submit`\n * scope via OAuth (unless your API key has the\n * `user:match:submit:trusted` scope, which skips per-player consent).\n *\n * Set `batch.dryRun = true` to validate without persisting.\n *\n * ```ts\n * const result = await client.matches.submit({\n * sport: 'pickleball',\n * winScore: 11,\n * winBy: 2,\n * bracket: '4.0 Doubles',\n * event: 'Weekly League',\n * matchDate: '2026-04-11T14:00:00Z',\n * matches: [\n * {\n * identifier: 'm1',\n * teams: [['vair_mem_aaa', 'vair_mem_bbb'],\n * ['vair_mem_ccc', 'vair_mem_ddd']],\n * games: [{ scores: [11, 8] }, { scores: [11, 5] }],\n * },\n * ],\n * });\n * if (result.ok) {\n * console.log(`Submitted ${result.numGames} games`);\n * }\n * ```\n */\n async submit(batch: MatchBatch): Promise<MatchBatchResult> {\n const wire = await this.#http.request<MatchBatchResultWire>({\n method: 'POST',\n path: '/partner/matches',\n body: batch,\n });\n return new MatchBatchResult(wire);\n }\n\n /**\n * Import tournament results.\n *\n * The request body is a free-form JSON object whose structure is\n * defined by the Vairified tournament import schema. Set\n * `body.dryRun = true` to validate without persisting.\n *\n * @param body - Tournament import payload.\n * @returns {@link TournamentImportResult} with match/game counts.\n * @category Matches\n *\n * @example\n * ```ts\n * const result = await client.matches.tournamentImport({\n * sport: 'pickleball',\n * tournamentName: 'Spring Classic',\n * matches: [...],\n * });\n * if (result.ok) {\n * console.log(`Imported ${result.matchesImported} matches`);\n * }\n * ```\n */\n async tournamentImport(body: Record<string, unknown>): Promise<TournamentImportResult> {\n const wire = await this.#http.request<TournamentImportResultWire>({\n method: 'POST',\n path: '/partner/tournament-import',\n body,\n });\n return new TournamentImportResult(wire);\n }\n\n /** Send a test payload to a webhook URL. */\n async testWebhook(webhookUrl: string): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/partner/webhook-test',\n body: { webhookUrl },\n });\n return data ?? {};\n }\n}\n","/**\n * {@link SportRating} — a player's ratings for one sport.\n *\n * @module\n */\n\nimport type { RatingSplitWire, SportRatingWire, VairProStatus } from '../types.js';\n\n/**\n * A player's ratings for a single sport.\n *\n * The top-level `rating` / `abbr` is the primary rating for that sport\n * (conventionally the overall-open bracket). Every category × age\n * bracket the player has played is also available via {@link get},\n * {@link has}, subscript-style access, and iteration.\n *\n * ```ts\n * const pb = member.sport.get('pickleball');\n * if (pb) {\n * console.log(pb.rating, pb.abbr); // 3.915 VO\n * console.log(pb.get('overall-open')?.rating); // 3.915\n * console.log(pb.has('singles-40+')); // false\n * console.log(pb.size); // 3\n * for (const [key, split] of pb) {\n * console.log(key, split.rating);\n * }\n * }\n * ```\n *\n * @category Members\n */\nexport class SportRating {\n /** Primary rating for this sport. */\n readonly rating: number;\n /** Category abbreviation for the primary rating (e.g. `'VO'`). */\n readonly abbr: string;\n\n /** Player is VAIRified in this sport (has a verified, non-recreational rating). */\n readonly isVairified: boolean;\n /** Player is an active VAIR Pro (can rate) in this sport. Alias of {@link isVairPro}. */\n readonly isRater: boolean;\n /** Player is an active VAIR Pro (can rate) in this sport. */\n readonly isVairPro: boolean;\n /** VAIR-Pro lifecycle status in this sport (`'ACTIVE'` / `'PENDING'` / `null`). */\n readonly isVairProStatus: VairProStatus;\n\n readonly #splits: ReadonlyMap<string, RatingSplitWire>;\n\n constructor(wire: SportRatingWire) {\n this.rating = wire.rating;\n this.abbr = wire.abbr;\n this.isVairified = wire.isVairified ?? false;\n this.isRater = wire.isRater ?? false;\n this.isVairPro = wire.isVairPro ?? false;\n this.isVairProStatus = wire.isVairProStatus ?? null;\n this.#splits = new Map(Object.entries(wire.ratingSplits ?? {}));\n Object.freeze(this);\n }\n\n /**\n * Look up a rating split by key (e.g. `'overall-open'`,\n * `'singles-12-13'`, `'gender-40+'`). Returns `undefined` if the\n * player has no rating for that bracket.\n */\n get(key: string): RatingSplitWire | undefined {\n return this.#splits.get(key);\n }\n\n /** Whether the player has a rating for the given split key. */\n has(key: string): boolean {\n return this.#splits.has(key);\n }\n\n /** Number of rating splits. */\n get size(): number {\n return this.#splits.size;\n }\n\n /** All split keys the player has ratings for. */\n keys(): IterableIterator<string> {\n return this.#splits.keys();\n }\n\n /** All rating splits the player has. */\n values(): IterableIterator<RatingSplitWire> {\n return this.#splits.values();\n }\n\n /** `[key, split]` pairs for every rating split. */\n entries(): IterableIterator<[string, RatingSplitWire]> {\n return this.#splits.entries();\n }\n\n /**\n * `for (const [key, split] of sportRating) { ... }` — iterate every\n * rating split the player has in this sport.\n */\n [Symbol.iterator](): IterableIterator<[string, RatingSplitWire]> {\n return this.#splits.entries();\n }\n}\n","/**\n * {@link Member} — partner-facing player record.\n *\n * @module\n */\n\nimport type {\n Gender,\n MemberStatusWire,\n PartnerMemberWire,\n RatingSplitWire,\n SportRatingWire,\n} from '../types.js';\nimport { SportRating } from './sport-rating.js';\n\n/**\n * Map-like wrapper around a player's sport → {@link SportRating}.\n *\n * Supports `.get(code)`, `.has(code)`, `.size`, and iteration so the\n * shape feels native:\n *\n * ```ts\n * member.sport.get('pickleball')?.rating\n * for (const [code, rating] of member.sport) { ... }\n * ```\n *\n * @category Members\n */\nexport class MemberSportMap {\n readonly #sports: ReadonlyMap<string, SportRating>;\n\n constructor(wire: Readonly<Record<string, SportRatingWire>> | undefined) {\n const entries = Object.entries(wire ?? {}).map(\n ([code, w]) => [code, new SportRating(w)] as const,\n );\n this.#sports = new Map(entries);\n Object.freeze(this);\n }\n\n get(sport: string): SportRating | undefined {\n return this.#sports.get(sport);\n }\n\n has(sport: string): boolean {\n return this.#sports.has(sport);\n }\n\n get size(): number {\n return this.#sports.size;\n }\n\n keys(): IterableIterator<string> {\n return this.#sports.keys();\n }\n\n values(): IterableIterator<SportRating> {\n return this.#sports.values();\n }\n\n entries(): IterableIterator<[string, SportRating]> {\n return this.#sports.entries();\n }\n\n [Symbol.iterator](): IterableIterator<[string, SportRating]> {\n return this.#sports.entries();\n }\n}\n\n/**\n * A partner-facing player record.\n *\n * Returned by {@link MembersResource.get} (full detail, requires an\n * active OAuth connection) and {@link MembersResource.search} (limited\n * detail for public search).\n *\n * Rating data lives under {@link sport} — keyed by sport code. The\n * backend returns only the sports the player has ratings in, or only\n * the sports requested via the `sport=` query filter. Use\n * {@link ratingFor} to fetch the primary rating for a specific sport\n * with a sensible default.\n *\n * @category Members\n */\nexport class Member {\n readonly memberId: number;\n readonly id: string | null;\n readonly firstName: string;\n readonly lastName: string;\n readonly fullName: string;\n readonly displayName: string;\n readonly age: number | null;\n readonly city: string | null;\n readonly state: string | null;\n readonly zip: string | null;\n readonly country: string | null;\n /**\n * The DATE this member's VAIR account was created (`YYYY-MM-DD`, UTC), or\n * `null` when the endpoint does not supply it.\n *\n * Present on `members.get()`, `members.getBulk()` and `members.getByEmail()` —\n * the calls where you already know which member you asked about. **Never on\n * `members.search()`**, which is discovery: account age is not something you\n * can browse strangers by.\n *\n * Deliberately a date, not a timestamp. It exists so you can apply a\n * new-accounts-only referral rule — crediting an ambassador only for accounts\n * created because of their event.\n */\n readonly memberSince: string | null;\n readonly gender: Gender | null;\n readonly status: MemberStatusWire;\n readonly sport: MemberSportMap;\n readonly activeLeagues: readonly string[] | null;\n readonly email: string | null;\n readonly grantedScopes: readonly string[] | null;\n\n constructor(wire: PartnerMemberWire) {\n this.memberId = wire.memberId;\n this.id = wire.id ?? null;\n this.firstName = wire.firstName;\n this.lastName = wire.lastName;\n this.fullName = wire.fullName;\n this.displayName = wire.displayName;\n this.age = wire.age ?? null;\n this.city = wire.city ?? null;\n this.state = wire.state ?? null;\n this.zip = wire.zip ?? null;\n this.country = wire.country ?? null;\n this.memberSince = wire.memberSince ?? null;\n this.gender = wire.gender ?? null;\n this.status = Object.freeze({ ...wire.status });\n this.sport = new MemberSportMap(wire.sport);\n this.activeLeagues = wire.activeLeagues ? Object.freeze([...wire.activeLeagues]) : null;\n this.email = wire.email ?? null;\n this.grantedScopes = wire.grantedScopes ? Object.freeze([...wire.grantedScopes]) : null;\n Object.freeze(this);\n }\n\n /** Full name — alias for {@link fullName}, matching common usage. */\n get name(): string {\n return this.fullName;\n }\n\n /** The list of sport codes this player has ratings in. */\n get sports(): readonly string[] {\n return [...this.sport.keys()];\n }\n\n /**\n * Primary rating for a given sport.\n *\n * @param sport Sport code — defaults to `'pickleball'`.\n * @returns The primary rating value, or `null` if the player has no\n * ratings for that sport.\n */\n ratingFor(sport = 'pickleball'): number | null {\n return this.sport.get(sport)?.rating ?? null;\n }\n\n /**\n * Get a specific rating split for a sport.\n *\n * @param key Split key (e.g. `'overall-open'`).\n * @param sport Sport code — defaults to `'pickleball'`.\n */\n split(key: string, sport = 'pickleball'): RatingSplitWire | null {\n return this.sport.get(sport)?.get(key) ?? null;\n }\n\n /** Compact summary for console output. */\n toString(): string {\n const primary = this.sport.values().next().value;\n if (primary) {\n return `Member #${this.memberId} '${this.displayName}' rating=${primary.rating.toFixed(\n 3,\n )} ${primary.abbr}`;\n }\n return `Member #${this.memberId} '${this.displayName}'`;\n }\n}\n","/**\n * {@link MembersByEmailResult} — result of an email → member lookup.\n *\n * @module\n */\n\nimport type { MembersByEmailResultWire, PartnerMemberEmailMatchWire } from '../types.js';\nimport { Member } from './member.js';\n\n/**\n * One requested address that resolved to at least one member.\n *\n * {@link members} is always an array. An email address is not a unique\n * key in VAIR — an unclaimed imported record can share an address with a\n * claimed account — so never assume a single element without checking.\n *\n * @category Members\n */\nexport class MemberEmailMatch {\n /** The address exactly as you supplied it, not as stored. */\n readonly email: string;\n /** Every member holding this address. Never empty. */\n readonly members: readonly Member[];\n\n /** @internal */\n constructor(wire: PartnerMemberEmailMatchWire) {\n this.email = wire.email;\n this.members = Object.freeze(wire.members.map((m) => new Member(m)));\n Object.freeze(this);\n }\n\n /**\n * The single member for this address, or `null` when the address is\n * ambiguous (more than one match).\n *\n * Use this rather than `members[0]` when a wrong link is worse than no\n * link — it refuses to guess instead of silently picking one.\n */\n get sole(): Member | null {\n return this.members.length === 1 ? (this.members[0] ?? null) : null;\n }\n\n /** Whether this address resolved to more than one member. */\n get isAmbiguous(): boolean {\n return this.members.length > 1;\n }\n\n toString(): string {\n return `MemberEmailMatch ${this.email} -> ${this.members.length} member(s)`;\n }\n}\n\n/**\n * Result of a {@link MembersResource.getByEmail} call.\n *\n * Every address you supplied appears in exactly one of {@link matched} or\n * {@link notFound} — the endpoint never silently drops one, so consume\n * {@link notFound} directly rather than diffing your input against the\n * results.\n *\n * A `notFound` address is not proof the person has no VAIR account:\n * unclaimed imported records are deliberately excluded from this lookup.\n *\n * @category Members\n */\nexport class MembersByEmailResult {\n readonly matched: readonly MemberEmailMatch[];\n /** Addresses that resolved to nothing, echoed as you supplied them. */\n readonly notFound: readonly string[];\n\n /** @internal */\n constructor(wire: MembersByEmailResultWire) {\n this.matched = Object.freeze(wire.matched.map((m) => new MemberEmailMatch(m)));\n this.notFound = Object.freeze([...wire.notFound]);\n Object.freeze(this);\n }\n\n /**\n * Look up one address's match, case-insensitively.\n *\n * Saves callers a linear scan and, more importantly, saves them from\n * matching case-sensitively against an address the server echoed back\n * in whatever case they originally sent.\n */\n get(email: string): MemberEmailMatch | null {\n const needle = email.trim().toLowerCase();\n return this.matched.find((m) => m.email.toLowerCase() === needle) ?? null;\n }\n\n /** Whether every requested address resolved to at least one member. */\n get allResolved(): boolean {\n return this.notFound.length === 0;\n }\n\n /** Total number of members across every matched address. */\n get memberCount(): number {\n return this.matched.reduce((n, m) => n + m.members.length, 0);\n }\n\n toString(): string {\n return `MembersByEmailResult matched=${this.matched.length} notFound=${this.notFound.length}`;\n }\n}\n","/**\n * {@link RatingUpdate} — a single rating change notification.\n *\n * @module\n */\n\nimport type { PartnerRatingUpdateWire, RatingSplitWire } from '../types.js';\n\n/**\n * A single rating change notification.\n *\n * Returned by {@link MembersResource.ratingUpdates} (polling) and\n * delivered via webhook callbacks to partners that have registered a\n * webhook URL and have subscribers.\n *\n * @category Members\n */\nexport class RatingUpdate {\n readonly memberId: number;\n readonly id: string | null;\n readonly displayName: string | null;\n readonly sport: string | null;\n readonly previousRating: number | null;\n readonly newRating: number | null;\n readonly changedAt: string | null;\n readonly ratingSplits: Readonly<Record<string, RatingSplitWire>> | null;\n\n constructor(wire: PartnerRatingUpdateWire) {\n this.memberId = wire.memberId;\n this.id = wire.id ?? null;\n this.displayName = wire.displayName ?? null;\n this.sport = wire.sport ?? null;\n this.previousRating = wire.previousRating ?? null;\n this.newRating = wire.newRating ?? null;\n this.changedAt = wire.changedAt ?? null;\n this.ratingSplits = wire.ratingSplits ? Object.freeze({ ...wire.ratingSplits }) : null;\n Object.freeze(this);\n }\n\n /**\n * Rating change amount — `newRating - previousRating`. Returns `null`\n * if either rating is missing from the update payload.\n */\n get delta(): number | null {\n if (this.previousRating === null || this.newRating === null) {\n return null;\n }\n return this.newRating - this.previousRating;\n }\n\n /** `true` when the new rating is strictly higher than the previous. */\n get improved(): boolean {\n const delta = this.delta;\n return delta !== null && delta > 0;\n }\n\n toString(): string {\n const arrow = this.improved ? '↑' : '↓';\n const prev = this.previousRating !== null ? this.previousRating.toFixed(3) : '?';\n const next = this.newRating !== null ? this.newRating.toFixed(3) : '?';\n const name = this.displayName ? ` '${this.displayName}'` : '';\n return `RatingUpdate #${this.memberId}${name} ${prev} ${arrow} ${next}`;\n }\n}\n","/**\n * {@link MembersResource} — member lookups, search, and rating updates.\n *\n * @module\n */\n\nimport { ValidationError } from '../errors.js';\nimport type { HttpTransport, QueryParams } from '../http.js';\nimport { Member } from '../models/member.js';\nimport { MembersByEmailResult } from '../models/members-by-email-result.js';\nimport { RatingUpdate } from '../models/rating-update.js';\nimport type {\n MembersByEmailResultWire,\n PartnerMemberWire,\n PartnerRatingUpdateWire,\n SearchFilters,\n} from '../types.js';\n\nconst DEFAULT_PAGE_SIZE = 20;\nconst MAX_PAGE_SIZE = 100;\nconst MAX_EMAILS_PER_LOOKUP = 100;\n\n/**\n * Member operations — get a single member, auto-paginating search,\n * find by name, and polling for rating change notifications.\n *\n * @category Resources\n */\nexport class MembersResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Get a connected member by external ID.\n *\n * **Requires an active OAuth connection** between your partner app\n * and the player. Use the OAuth flow on `client.oauth` first.\n *\n * @param playerId External player ID in `vair_mem_xxx` format.\n * @param options.sport Optional sport filter — single code or list.\n * When omitted, the response contains every sport the player has\n * ratings in.\n * @throws {@link NotFoundError} if the external ID is unknown.\n * @throws {@link VairifiedError} if the player has not connected to\n * your app (403) or the request otherwise fails.\n *\n * @example\n * ```ts\n * const member = await client.members.get('vair_mem_xxx');\n * console.log(member.name, member.ratingFor('pickleball'));\n *\n * // Just pickleball\n * const member2 = await client.members.get('vair_mem_xxx', { sport: 'pickleball' });\n *\n * // Multiple sports\n * const member3 = await client.members.get('vair_mem_xxx', {\n * sport: ['pickleball', 'padel'],\n * });\n * ```\n */\n async get(\n playerId: string,\n options: { sport?: string | readonly string[] } = {},\n ): Promise<Member> {\n const query: Record<string, string> = { memberId: playerId };\n if (options.sport !== undefined) {\n query.sport = Array.isArray(options.sport)\n ? (options.sport as readonly string[]).join(',')\n : (options.sport as string);\n }\n const wire = await this.#http.request<PartnerMemberWire>({\n method: 'GET',\n path: '/partner/member',\n query,\n });\n return new Member(wire);\n }\n\n /**\n * Search for members, yielding each match as a {@link Member}.\n *\n * This is an **auto-paginating async iterator** — it fetches pages\n * from the server lazily as you iterate, so you can stream through\n * thousands of results without holding them all in memory:\n *\n * ```ts\n * for await (const m of client.members.search({ city: 'Austin' })) {\n * console.log(m.name, m.ratingFor('pickleball'));\n * }\n * ```\n *\n * Stop early by `break`-ing out of the loop, or cap the total with\n * `maxResults`.\n */\n async *search(filters: SearchFilters = {}): AsyncGenerator<Member, void, void> {\n const pageSize = Math.min(filters.pageSize ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);\n const maxResults = filters.maxResults;\n\n const baseQuery = buildSearchQuery(filters, pageSize);\n\n let offset = 0;\n let yielded = 0;\n\n while (true) {\n const query: QueryParams = { ...baseQuery, offset };\n const data = await this.#http.request<\n PartnerMemberWire[] | { players?: PartnerMemberWire[] }\n >({ method: 'GET', path: '/partner/search', query });\n\n const batch: PartnerMemberWire[] = Array.isArray(data) ? data : (data?.players ?? []);\n\n if (batch.length === 0) {\n return;\n }\n\n for (const wire of batch) {\n yield new Member(wire);\n yielded += 1;\n if (maxResults !== undefined && yielded >= maxResults) {\n return;\n }\n }\n\n if (batch.length < pageSize) {\n return;\n }\n offset += pageSize;\n }\n }\n\n /**\n * Return the first search hit for a name, or `null`.\n *\n * Convenience for the common \"look up by name\" case:\n *\n * ```ts\n * const mike = await client.members.find('Mike Barker');\n * if (mike) {\n * console.log(mike.ratingFor('pickleball'));\n * }\n * ```\n */\n async find(name: string): Promise<Member | null> {\n for await (const member of this.search({ name, pageSize: 1, maxResults: 1 })) {\n return member;\n }\n return null;\n }\n\n /**\n * Fetch up to 100 members by their member IDs in one call.\n *\n * Unknown IDs are silently omitted — the returned array may be\n * shorter than the input. Results are returned in the same order\n * as the input IDs.\n *\n * @param ids - Array of integer member IDs (max 100).\n * @param options - Optional filters.\n * @param options.sport - Sport code to scope ratings (e.g. `'pickleball'`).\n * @returns Array of {@link Member} instances.\n * @throws {@link ValidationError} If more than 100 IDs are provided.\n * @category Members\n *\n * @example\n * ```ts\n * const members = await client.members.getBulk([4873327, 4873328]);\n * for (const m of members) {\n * console.log(m.name, m.ratingFor('pickleball'));\n * }\n * ```\n */\n async getBulk(ids: number[], options?: { sport?: string }): Promise<Member[]> {\n if (ids.length > 100) {\n throw new ValidationError('Maximum 100 member IDs per request');\n }\n const query: Record<string, string> = { ids: ids.join(',') };\n if (options?.sport) query.sport = options.sport;\n const rows = await this.#http.request<PartnerMemberWire[]>({\n method: 'GET',\n path: '/partner/members',\n query,\n });\n return rows.map((row) => new Member(row));\n }\n\n /**\n * Resolve up to 100 members by their **exact** email address.\n *\n * Use this to link your users to their VAIR identity when you hold\n * their email but not their member ID — e.g. resolving a tournament\n * roster at registration instead of waiting for each player to\n * complete SSO.\n *\n * **Requires the `key:player:lookup` scope**, which is granted per\n * partner on approval. Holding `key:player:search` does not imply it.\n *\n * Matching is exact and case-insensitive; there is deliberately no\n * partial, prefix or fuzzy matching. Every address you supply comes\n * back in either `matched` or `notFound`, so read `notFound` directly\n * instead of diffing your input against the results.\n *\n * A `notFound` address is **not** proof the person has no VAIR\n * account — unclaimed imported records are excluded from this lookup.\n *\n * @param emails - Email addresses to resolve (max 100).\n * @param options - Optional filters.\n * @param options.sport - Sport code to scope ratings (e.g. `'pickleball'`).\n * @throws {@link ValidationError} If more than 100 addresses are\n * provided, or the list is empty.\n * @category Members\n *\n * @example\n * ```ts\n * const result = await client.members.getByEmail([\n * 'ada@example.com',\n * 'nobody@example.com',\n * ]);\n *\n * for (const match of result.matched) {\n * const member = match.sole; // null when the address is ambiguous\n * if (member) console.log(match.email, '->', member.memberId);\n * }\n *\n * console.log('no VAIR account found for:', result.notFound);\n * ```\n */\n async getByEmail(\n emails: readonly string[],\n options?: { sport?: string },\n ): Promise<MembersByEmailResult> {\n // Validate before the round trip so the caller gets a typed error\n // rather than a 400 they have to interpret.\n if (emails.length === 0) {\n throw new ValidationError('At least one email address is required');\n }\n if (emails.length > MAX_EMAILS_PER_LOOKUP) {\n throw new ValidationError(`Maximum ${MAX_EMAILS_PER_LOOKUP} email addresses per request`);\n }\n // A comma inside an entry would split into two addresses server-side\n // and silently shift every result — reject it rather than send it.\n const offender = emails.find((e) => e.includes(','));\n if (offender !== undefined) {\n throw new ValidationError(`Email address must not contain a comma: ${offender}`);\n }\n\n const query: Record<string, string> = { emails: emails.join(',') };\n if (options?.sport) query.sport = options.sport;\n const wire = await this.#http.request<MembersByEmailResultWire>({\n method: 'GET',\n path: '/partner/members/by-email',\n query,\n });\n return new MembersByEmailResult({\n matched: wire?.matched ?? [],\n notFound: wire?.notFound ?? [],\n });\n }\n\n /**\n * Poll for rating change notifications.\n *\n * Returns a list of {@link RatingUpdate} objects for every player\n * whose rating has changed since the last poll. Members are\n * considered subscribed when they have an active OAuth connection\n * with the `user:webhook:subscribe` scope.\n */\n async ratingUpdates(): Promise<readonly RatingUpdate[]> {\n const data = await this.#http.request<{ updates?: PartnerRatingUpdateWire[] } | unknown>({\n method: 'GET',\n path: '/partner/rating-updates',\n });\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n const updates = (data as { updates?: PartnerRatingUpdateWire[] }).updates ?? [];\n return updates.map((wire) => new RatingUpdate(wire));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction buildSearchQuery(filters: SearchFilters, pageSize: number): QueryParams {\n const query: Record<string, string | number | boolean> = {};\n\n if (filters.sport !== undefined) {\n query.sport = Array.isArray(filters.sport)\n ? (filters.sport as readonly string[]).join(',')\n : (filters.sport as string);\n }\n\n if (filters.memberId !== undefined) {\n query.member = String(filters.memberId);\n } else if (filters.name !== undefined) {\n query.member = filters.name;\n }\n\n if (filters.city !== undefined) query.city = filters.city;\n if (filters.state !== undefined) query.state = filters.state;\n if (filters.country !== undefined) query.country = filters.country;\n if (filters.zip !== undefined) query.zip = filters.zip;\n if (filters.location !== undefined) query.location = filters.location;\n\n if (filters.gender !== undefined) {\n query.gender = filters.gender.toUpperCase();\n }\n if (filters.vairifiedOnly !== undefined) query.vairified = filters.vairifiedOnly;\n if (filters.wheelchair !== undefined) query.wheelchair = filters.wheelchair;\n\n if (filters.ratingMin !== undefined) query.rating1 = filters.ratingMin;\n if (filters.ratingMax !== undefined) query.rating2 = filters.ratingMax;\n\n const { ageFilterType, age1, age2 } = resolveAgeFilter(filters);\n if (ageFilterType !== undefined) query.ageFilterType = ageFilterType;\n if (age1 !== undefined) query.age1 = age1;\n if (age2 !== undefined) query.age2 = age2;\n\n if (filters.sortBy !== undefined) query.sortField = filters.sortBy;\n if (filters.sortOrder !== undefined) query.sortDirection = filters.sortOrder;\n\n query.limit = pageSize;\n\n return query;\n}\n\nfunction resolveAgeFilter(filters: SearchFilters): {\n ageFilterType?: string;\n age1?: number;\n age2?: number;\n} {\n if (filters.age !== undefined) {\n return { ageFilterType: 'exact', age1: filters.age };\n }\n if (filters.ageMin !== undefined && filters.ageMax !== undefined) {\n return { ageFilterType: 'range', age1: filters.ageMin, age2: filters.ageMax };\n }\n if (filters.ageMin !== undefined) {\n return { ageFilterType: 'above', age1: filters.ageMin };\n }\n if (filters.ageMax !== undefined) {\n return { ageFilterType: 'below', age1: filters.ageMax };\n }\n return {};\n}\n","/**\n * Vairified OAuth helpers and type definitions.\n *\n * Use the {@link OAuthResource} on `client.oauth` for the full flow —\n * these helpers exist for partners who need to build authorization URLs\n * or validate scopes outside the client (e.g. in a frontend that only\n * handles the redirect step).\n *\n * @module\n */\n\n// ---------------------------------------------------------------------------\n// Scope definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Every OAuth scope the Vairified authorization server accepts.\n *\n * Declaring this as a string union (rather than a free-form `string`)\n * lets TypeScript catch typos at authoring time:\n *\n * ```ts\n * const scopes: OAuthScope[] = ['user:profile:read', 'user:rating:read']; // ok\n * const bad: OAuthScope[] = ['user:profile:read', 'rating']; // type error\n * ```\n *\n * @category OAuth\n */\nexport type OAuthScope =\n | 'user:profile:read'\n | 'user:profile:email'\n | 'user:rating:read'\n | 'user:rating:history'\n | 'user:match:submit'\n | 'user:webhook:subscribe';\n\n/**\n * Human-readable description for every OAuth scope.\n *\n * @category OAuth\n */\nexport const SCOPES: Readonly<Record<OAuthScope, string>> = Object.freeze({\n 'user:profile:read': 'Access your name, location, and verification status',\n 'user:profile:email': 'Access your email address',\n 'user:rating:read': 'View your current rating and rating splits',\n 'user:rating:history': 'View your complete rating history',\n 'user:match:submit': 'Submit match results on your behalf',\n 'user:webhook:subscribe': 'Receive notifications when your rating changes',\n});\n\n/**\n * The scopes automatically requested when none are specified.\n *\n * @category OAuth\n */\nexport const DEFAULT_SCOPES: readonly OAuthScope[] = Object.freeze([\n 'user:profile:read',\n 'user:rating:read',\n]);\n\n// ---------------------------------------------------------------------------\n// OAuth data types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for building an authorization URL manually.\n *\n * @category OAuth\n */\nexport interface OAuthConfig {\n readonly apiKey: string;\n readonly redirectUri: string;\n readonly baseUrl?: string;\n /**\n * Your app's `client_id` — the `PartnerApp.slug` Vairified assigned you\n * (e.g. `dinkr`). Required by the browser `GET /partner/oauth/authorize`\n * endpoint to identify your app; without it the authorization page rejects\n * the request with `invalid_request: client_id is required`.\n *\n * Only needed for this pure-frontend URL helper. The recommended flow —\n * {@link OAuthResource.authorize} on a client — identifies your app by the\n * API key instead, so it needs no `client_id`.\n */\n readonly clientId?: string;\n}\n\n/**\n * Response from starting an OAuth authorization flow.\n *\n * @category OAuth\n */\nexport interface AuthorizationResponse {\n readonly authorizationUrl: string;\n readonly code: string;\n readonly state?: string;\n}\n\n/**\n * Response from exchanging an authorization code for tokens.\n *\n * @category OAuth\n */\nexport interface TokenResponse {\n readonly accessToken: string;\n readonly refreshToken: string | null;\n readonly expiresIn: number;\n readonly scope: readonly string[];\n readonly playerId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helper functions\n// ---------------------------------------------------------------------------\n\n/**\n * Build the URL to redirect users to for OAuth authorization.\n *\n * Prefer {@link OAuthResource.authorize} on a Vairified client — this\n * helper is only useful when you need to construct the URL without\n * making an HTTP call first (for example, in a pure-frontend handoff).\n *\n * @category OAuth\n */\nexport function getAuthorizationUrl(\n config: OAuthConfig,\n options: { scopes?: readonly OAuthScope[]; state?: string } = {},\n): string {\n const baseUrl = (config.baseUrl ?? 'https://api-next.vairified.com/api/v1').replace(/\\/+$/, '');\n const scopeList = ensureProfileRead(options.scopes ?? DEFAULT_SCOPES);\n\n // Scope is space-delimited per RFC 6749 §3.3 — the deployed authorization\n // server splits on whitespace, not commas.\n const params = new URLSearchParams({\n redirect_uri: config.redirectUri,\n scope: scopeList.join(' '),\n response_type: 'code',\n });\n // `client_id` (the PartnerApp slug) identifies the app to the browser\n // authorize endpoint; the URL is rejected without it.\n if (config.clientId) {\n params.set('client_id', config.clientId);\n }\n if (options.state) {\n params.set('state', options.state);\n }\n\n return `${baseUrl}/partner/oauth/authorize?${params.toString()}`;\n}\n\n/**\n * Check whether a scope string is one the Vairified server accepts.\n *\n * @category OAuth\n */\nexport function validateScope(scope: string): scope is OAuthScope {\n return scope in SCOPES;\n}\n\n/**\n * Get a human-readable description of an OAuth scope.\n *\n * Returns `\"Unknown scope: {scope}\"` for unrecognized scopes so this\n * function is safe to call on user-supplied input.\n *\n * @category OAuth\n */\nexport function describeScope(scope: string): string {\n if (validateScope(scope)) {\n return SCOPES[scope];\n }\n return `Unknown scope: ${scope}`;\n}\n\n/**\n * Describe multiple scopes at once.\n *\n * @category OAuth\n */\nexport function describeScopes(\n scopes: readonly string[],\n): readonly { scope: string; description: string }[] {\n return scopes.map((scope) => ({\n scope,\n description: describeScope(scope),\n }));\n}\n\n/**\n * Generate a cryptographically-random CSRF state token suitable for\n * use with {@link OAuthResource.authorize}.\n *\n * Uses the Web Crypto API `crypto.getRandomValues` (built into Node 19+\n * and all modern browsers). The returned string is URL-safe, unpadded\n * base64 of 32 random bytes.\n *\n * On runtimes without Web Crypto — notably **React Native / Hermes** —\n * this throws a descriptive `Error` (rather than a bare `ReferenceError`)\n * telling you to install and import `react-native-get-random-values` at\n * your app entry, or to pass your own high-entropy `state` string. Base64\n * encoding is done in pure JS, so no `btoa` polyfill is required.\n *\n * @throws {Error} when `crypto.getRandomValues` is unavailable.\n * @category OAuth\n */\nexport function generateState(): string {\n const webCrypto = globalThis.crypto;\n if (typeof webCrypto?.getRandomValues !== 'function') {\n throw new Error(\n 'generateState() needs Web Crypto (crypto.getRandomValues), which is unavailable in ' +\n 'this runtime. Node 19+ and browsers have it built in; on React Native / Hermes, ' +\n \"install 'react-native-get-random-values' and import it once at your app entry \" +\n 'before using the SDK — or pass your own high-entropy `state` string to ' +\n 'oauth.authorize().',\n );\n }\n const bytes = new Uint8Array(32);\n webCrypto.getRandomValues(bytes);\n return base64UrlNoPad(bytes);\n}\n\n/** URL-safe base64 alphabet (RFC 4648 §5), used by {@link base64UrlNoPad}. */\nconst B64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\n/**\n * URL-safe, unpadded base64 of a byte array — implemented in pure JS so it\n * works on runtimes without `btoa` (e.g. React Native / Hermes).\n *\n * @internal\n */\nfunction base64UrlNoPad(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const hasB1 = i + 1 < bytes.length;\n const hasB2 = i + 2 < bytes.length;\n out += B64URL_ALPHABET[b0 >> 2] ?? '';\n out += B64URL_ALPHABET[((b0 & 0x03) << 4) | (b1 >> 4)] ?? '';\n if (hasB1) out += B64URL_ALPHABET[((b1 & 0x0f) << 2) | (b2 >> 6)] ?? '';\n if (hasB2) out += B64URL_ALPHABET[b2 & 0x3f] ?? '';\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers (used by resources/oauth.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure `user:profile:read` is in the scope list, prepending if necessary.\n *\n * @internal\n */\nexport function ensureProfileRead(scopes: readonly OAuthScope[]): readonly OAuthScope[] {\n if (scopes.includes('user:profile:read')) {\n return scopes;\n }\n return ['user:profile:read', ...scopes];\n}\n","/**\n * {@link OAuthResource} — OAuth 2.0 flow for player consent.\n *\n * @module\n */\n\nimport { OAuthError } from '../errors.js';\nimport type { HttpTransport } from '../http.js';\nimport {\n type AuthorizationResponse,\n DEFAULT_SCOPES,\n ensureProfileRead,\n type OAuthScope,\n SCOPES,\n type TokenResponse,\n} from '../oauth.js';\n\n/**\n * OAuth 2.0 flow for obtaining player consent.\n *\n * Typical flow:\n *\n * 1. {@link authorize} — start an authorization, get a URL to redirect\n * the player to.\n * 2. Player approves on the Vairified site and is redirected back to\n * your `redirectUri` with a `code` query parameter.\n * 3. {@link exchangeToken} — swap the code for access and refresh\n * tokens plus the player's external ID.\n * 4. Store the refresh token and call {@link refresh} when the access\n * token expires.\n * 5. {@link revoke} — disconnect a player from your app.\n *\n * @category Resources\n */\nexport class OAuthResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Start an OAuth authorization flow.\n *\n * @throws {@link OAuthError} with `errorCode: 'invalid_scope'` if a\n * requested scope is not in the accepted list.\n */\n async authorize(options: {\n redirectUri: string;\n scopes?: readonly OAuthScope[];\n state?: string;\n }): Promise<AuthorizationResponse> {\n const scopeList = ensureProfileRead(options.scopes ?? DEFAULT_SCOPES);\n\n for (const scope of scopeList) {\n if (!(scope in SCOPES)) {\n throw new OAuthError(`Invalid scope: ${scope}`, 'invalid_scope');\n }\n }\n\n const data = await this.#http.request<{\n authorization_url?: string;\n code?: string;\n }>({\n method: 'POST',\n path: '/partner/oauth/authorize',\n body: {\n redirect_uri: options.redirectUri,\n scope: scopeList.join(' '),\n state: options.state,\n },\n });\n\n return {\n authorizationUrl: data?.authorization_url ?? '',\n code: data?.code ?? '',\n state: options.state,\n };\n }\n\n /** Exchange an authorization code for access and refresh tokens. */\n async exchangeToken(options: { code: string; redirectUri: string }): Promise<TokenResponse> {\n const data = await this.#http.request<Record<string, unknown>>({\n method: 'POST',\n path: '/partner/oauth/token',\n // RFC 6749 §4.1.3 — the endpoint requires `grant_type` and the\n // snake_case `redirect_uri` (which must match the authorize request).\n body: {\n grant_type: 'authorization_code',\n code: options.code,\n redirect_uri: options.redirectUri,\n },\n });\n return tokenResponseFromWire(data);\n }\n\n /** Refresh an expired access token using a refresh token. */\n async refresh(refreshToken: string): Promise<TokenResponse> {\n const data = await this.#http.request<Record<string, unknown>>({\n method: 'POST',\n path: '/partner/oauth/refresh',\n // RFC 6749 §6 — refresh grant, snake_case body.\n body: { grant_type: 'refresh_token', refresh_token: refreshToken },\n });\n return tokenResponseFromWire(data);\n }\n\n /** Revoke a player's OAuth connection to your app. */\n async revoke(playerId: string): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/partner/oauth/revoke',\n body: { player_id: playerId },\n });\n return data ?? {};\n }\n\n /** Return the list of OAuth scopes the server currently supports. */\n async availableScopes(): Promise<readonly { name: string; description: string }[]> {\n const data = await this.#http.request<\n { scopes?: { name: string; description: string }[] } | unknown\n >({\n method: 'GET',\n path: '/partner/oauth/scopes',\n });\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n return (data as { scopes?: { name: string; description: string }[] }).scopes ?? [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction tokenResponseFromWire(data: Record<string, unknown> | undefined): TokenResponse {\n // The API returns `scope` as a space-delimited string (RFC 6749 §3.3).\n // Fall back to the deprecated `scopes` array for older API builds.\n const scopeRaw = (data?.scope ?? '') as string;\n const scopeList =\n scopeRaw.length > 0\n ? scopeRaw.split(/\\s+/).filter(Boolean)\n : Array.isArray(data?.scopes)\n ? (data.scopes as string[])\n : [];\n return {\n accessToken: (data?.access_token ?? '') as string,\n refreshToken: (data?.refresh_token ?? null) as string | null,\n expiresIn: (data?.expires_in ?? 3600) as number,\n scope: Object.freeze(scopeList),\n playerId: (data?.player_id ?? '') as string,\n };\n}\n","/**\n * {@link MemberAttribution}, {@link MembersAttributionResult} and\n * {@link AttributionResult} — ambassador referral credit.\n *\n * @module\n */\n\nimport type {\n AttributionOutcome,\n AttributionResultWire,\n MemberAttributionWire,\n MembersAttributionResultWire,\n} from '../types.js';\n\n/**\n * Who currently earns referral credit for one member.\n *\n * @category Referrals\n */\nexport class MemberAttribution {\n readonly memberId: number;\n /** True when some ambassador already holds credit for this member. */\n readonly attributed: boolean;\n /**\n * The member id of the ambassador holding the credit.\n *\n * Compare it against your own event host's member id to tell \"already credited\n * to my host — nothing to do\" from \"credited to somebody else — a person needs\n * to look, because claiming it takes credit from them\".\n *\n * `null` both when nobody holds credit and when credit is held by a record that\n * has no member id of its own, so check {@link attributed} to tell those apart.\n */\n readonly ambassadorMemberId: number | null;\n /** The date the credit was established (`YYYY-MM-DD`), or `null`. */\n readonly attributedAt: string | null;\n\n /** @internal */\n constructor(wire: MemberAttributionWire) {\n this.memberId = wire.memberId;\n this.attributed = wire.attributed;\n this.ambassadorMemberId = wire.ambassadorMemberId ?? null;\n this.attributedAt = wire.attributedAt ?? null;\n Object.freeze(this);\n }\n\n /** True when nobody holds credit yet, so this member can be claimed. */\n get isClaimable(): boolean {\n return !this.attributed;\n }\n\n /** True when credit is held by an ambassador OTHER than the one given. */\n heldBySomeoneOtherThan(ambassadorMemberId: number): boolean {\n return this.attributed && this.ambassadorMemberId !== ambassadorMemberId;\n }\n}\n\n/**\n * Attribution for a batch of members, plus the ids that matched nothing.\n *\n * @category Referrals\n */\nexport class MembersAttributionResult {\n readonly attributions: readonly MemberAttribution[];\n /**\n * Member ids that matched no member. Read this rather than diffing your input\n * against the results — every id you sent lands in one bucket or the other.\n */\n readonly notFound: readonly number[];\n\n /** @internal */\n constructor(wire: MembersAttributionResultWire) {\n this.attributions = Object.freeze(\n (wire.attributions ?? []).map((a) => new MemberAttribution(a)),\n );\n this.notFound = Object.freeze([...(wire.notFound ?? [])]);\n Object.freeze(this);\n }\n\n /** Attribution for one member id, or `undefined` if it was not returned. */\n get(memberId: number): MemberAttribution | undefined {\n return this.attributions.find((a) => a.memberId === memberId);\n }\n\n /** Members nobody holds credit for yet. */\n get claimable(): readonly MemberAttribution[] {\n return this.attributions.filter((a) => a.isClaimable);\n }\n}\n\n/**\n * Outcome of submitting attribution for a batch of members.\n *\n * @category Referrals\n */\nexport class AttributionResult {\n /** How many members were newly attributed by this request. */\n readonly attributed: number;\n /** One entry per member id supplied, in the order supplied. */\n readonly results: readonly Readonly<{ memberId: number; outcome: AttributionOutcome }>[];\n\n /** @internal */\n constructor(wire: AttributionResultWire) {\n this.attributed = wire.attributed;\n this.results = Object.freeze(\n (wire.results ?? []).map((r) => Object.freeze({ memberId: r.memberId, outcome: r.outcome })),\n );\n Object.freeze(this);\n }\n\n /** Member ids with the given outcome. */\n withOutcome(outcome: AttributionOutcome): readonly number[] {\n return this.results.filter((r) => r.outcome === outcome).map((r) => r.memberId);\n }\n\n /**\n * Members already credited to somebody. These are the ones worth a human\n * look — it may be your own host, or it may be another ambassador.\n */\n get alreadyAttributed(): readonly number[] {\n return this.withOutcome('already_attributed');\n }\n\n /**\n * Members rejected because their account pre-dates the event's registration\n * page. The event did not recruit them, so no credit is due.\n */\n get predatedEvent(): readonly number[] {\n return this.withOutcome('account_predates_event');\n }\n}\n","/**\n * {@link ReferralsResource} — read and record ambassador referral credit.\n *\n * @module\n */\n\nimport { ValidationError } from '../errors.js';\nimport type { HttpTransport } from '../http.js';\nimport { AttributionResult, MembersAttributionResult } from '../models/attribution.js';\nimport type { AttributionResultWire, MembersAttributionResultWire } from '../types.js';\n\nconst MAX_IDS_PER_READ = 100;\nconst MAX_IDS_PER_WRITE = 500;\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Read and record which ambassador earns referral credit for a player.\n *\n * Each method needs its own API-key permission, granted per partner:\n * `key:referral:read` for {@link get} and `key:referral:write` for\n * {@link attribute}. Neither is implied by a general read, write or admin key —\n * reading attribution exposes who recruited whom, and writing it decides who\n * earns commission.\n *\n * @category Resources\n */\nexport class ReferralsResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Who currently earns referral credit for these members.\n *\n * Use this before {@link attribute} to tell the two cases apart that matter:\n * a member already credited to your own event host (nothing to do) and one\n * credited to a different ambassador (a person should look, because claiming\n * it takes credit from them).\n *\n * @example\n * ```ts\n * const result = await client.referrals.get([4873327, 4873328]);\n *\n * for (const a of result.claimable) {\n * console.log(a.memberId, 'has no credit yet');\n * }\n * console.log('no such member:', result.notFound);\n * ```\n *\n * @throws {@link ValidationError} If the list is empty or exceeds 100 ids.\n */\n async get(memberIds: readonly number[]): Promise<MembersAttributionResult> {\n if (memberIds.length === 0) {\n throw new ValidationError('At least one member id is required');\n }\n if (memberIds.length > MAX_IDS_PER_READ) {\n throw new ValidationError(`Maximum ${MAX_IDS_PER_READ} member ids per request`);\n }\n\n const wire = await this.#http.request<MembersAttributionResultWire>({\n method: 'GET',\n path: '/partner/members/attribution',\n query: { memberIds: memberIds.join(',') },\n });\n return new MembersAttributionResult({\n attributions: wire?.attributions ?? [],\n notFound: wire?.notFound ?? [],\n });\n }\n\n /**\n * Credit an ambassador for players their event recruited.\n *\n * `registrationPublishedAt` is the date the event's registration page was\n * **first published**. Accounts created before it did not come from the event\n * and are rejected with `account_predates_event`. VAIR applies that rule\n * itself, so every partner is held to the same one.\n *\n * VAIR cannot verify the date — it holds no record of your registration pages —\n * so the value you send is recorded for audit. Send the real one.\n *\n * Safe to retry: attribution is one-per-player forever, enforced by the\n * database, so a resubmitted player returns `already_attributed` and nothing\n * changes.\n *\n * @example\n * ```ts\n * const result = await client.referrals.attribute({\n * referralCode: 'hillhurst-open',\n * registrationPublishedAt: '2026-08-01',\n * memberIds: [4873327, 4873328],\n * });\n *\n * console.log(result.attributed, 'newly credited');\n * console.log('need a human:', result.alreadyAttributed);\n * console.log('too old to credit:', result.predatedEvent);\n * ```\n *\n * @throws {@link ValidationError} If the list is empty or exceeds 500 ids, or\n * the date is not `YYYY-MM-DD`.\n */\n async attribute(input: {\n referralCode: string;\n registrationPublishedAt: string;\n memberIds: readonly number[];\n }): Promise<AttributionResult> {\n if (!input.referralCode.trim()) {\n throw new ValidationError('A referral code is required');\n }\n // Checked here so a typo fails immediately rather than as a 400 the caller\n // has to interpret — and because a wrong date silently changes who is\n // creditable, which is worse than an outright rejection.\n if (!ISO_DATE.test(input.registrationPublishedAt)) {\n throw new ValidationError(\n 'registrationPublishedAt must be a calendar date formatted YYYY-MM-DD',\n );\n }\n if (input.memberIds.length === 0) {\n throw new ValidationError('At least one member id is required');\n }\n if (input.memberIds.length > MAX_IDS_PER_WRITE) {\n throw new ValidationError(`Maximum ${MAX_IDS_PER_WRITE} member ids per request`);\n }\n\n const wire = await this.#http.request<AttributionResultWire>({\n method: 'POST',\n path: '/partner/ambassador/attribution',\n body: {\n referralCode: input.referralCode.trim(),\n registrationPublishedAt: input.registrationPublishedAt,\n memberIds: [...input.memberIds],\n },\n });\n return new AttributionResult({\n attributed: wire?.attributed ?? 0,\n results: wire?.results ?? [],\n });\n }\n}\n","/**\n * {@link WebhookDelivery} and {@link WebhookDeliveriesResult} — webhook\n * delivery inspection models.\n *\n * @module\n */\n\nimport type { WebhookDeliveriesResultWire, WebhookDeliveryWire } from '../types.js';\n\n/**\n * A single webhook delivery attempt.\n *\n * @category Webhooks\n */\nexport class WebhookDelivery {\n readonly id: string;\n readonly event: string;\n readonly url: string;\n readonly statusCode: number | null;\n readonly responseBody: string | null;\n readonly errorMessage: string | null;\n readonly attempts: number;\n readonly maxAttempts: number;\n readonly lastAttemptAt: string;\n readonly nextRetryAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly payload: Readonly<Record<string, unknown>>;\n\n /** @internal */\n constructor(wire: WebhookDeliveryWire) {\n this.id = wire.id;\n this.event = wire.event;\n this.url = wire.url;\n this.statusCode = wire.statusCode;\n this.responseBody = wire.responseBody;\n this.errorMessage = wire.errorMessage;\n this.attempts = wire.attempts;\n this.maxAttempts = wire.maxAttempts;\n this.lastAttemptAt = wire.lastAttemptAt;\n this.nextRetryAt = wire.nextRetryAt;\n this.completedAt = wire.completedAt;\n this.createdAt = wire.createdAt;\n this.payload = Object.freeze({ ...wire.payload });\n Object.freeze(this);\n }\n\n /** Whether delivery completed successfully (2xx status). */\n get succeeded(): boolean {\n return (\n this.completedAt != null &&\n this.statusCode != null &&\n this.statusCode >= 200 &&\n this.statusCode < 300\n );\n }\n\n /** Whether delivery failed definitively (completed with non-2xx). */\n get failed(): boolean {\n return this.completedAt != null && !this.succeeded;\n }\n}\n\n/**\n * Paginated list of webhook delivery attempts.\n *\n * @category Webhooks\n */\nexport class WebhookDeliveriesResult {\n readonly deliveries: readonly WebhookDelivery[];\n readonly total: number;\n\n /** @internal */\n constructor(wire: WebhookDeliveriesResultWire) {\n this.deliveries = Object.freeze(wire.deliveries.map((d) => new WebhookDelivery(d)));\n this.total = wire.total;\n Object.freeze(this);\n }\n}\n","/**\n * {@link WebhooksResource} — webhook delivery inspection.\n *\n * @module\n */\n\nimport type { HttpTransport } from '../http.js';\nimport { WebhookDeliveriesResult } from '../models/webhook-delivery.js';\nimport type { WebhookDeliveriesResultWire } from '../types.js';\n\n/**\n * Webhook delivery inspection.\n *\n * @category Resources\n */\nexport class WebhooksResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * List recent webhook delivery attempts.\n *\n * @param options - Optional filters and pagination.\n * @param options.event - Filter by event type (e.g. `'rating.updated'`).\n * @param options.status - Filter: `'all'`, `'pending'`, `'success'`, or `'failed'`.\n * @param options.limit - Results per page (1-100, default 20).\n * @param options.offset - Pagination offset.\n * @returns {@link WebhookDeliveriesResult} with entries and total.\n * @category Webhooks\n *\n * @example\n * ```ts\n * const result = await client.webhooks.deliveries({ status: 'failed' });\n * for (const d of result.deliveries) {\n * console.log(d.event, d.statusCode, d.errorMessage);\n * }\n * ```\n */\n async deliveries(options?: {\n event?: string;\n status?: 'all' | 'pending' | 'success' | 'failed';\n limit?: number;\n offset?: number;\n }): Promise<WebhookDeliveriesResult> {\n const query: Record<string, string | number> = {};\n if (options?.event) query.event = options.event;\n if (options?.status) query.status = options.status;\n if (options?.limit != null) query.limit = options.limit;\n if (options?.offset != null) query.offset = options.offset;\n const data = await this.#http.request<WebhookDeliveriesResultWire>({\n method: 'GET',\n path: '/partner/webhook-deliveries',\n query,\n });\n return new WebhookDeliveriesResult(data);\n }\n}\n","/**\n * {@link Vairified} — the main entry point of the SDK.\n *\n * @module\n */\n\nimport { HttpTransport } from './http.js';\nimport {\n LeaderboardResource,\n MatchesResource,\n MembersResource,\n OAuthResource,\n ReferralsResource,\n WebhooksResource,\n} from './resources/index.js';\nimport type { VairifiedEnvironment, VairifiedOptions } from './types.js';\n\n/**\n * Environment preset → base URL mapping.\n *\n * Partners can switch between production, staging, and local development\n * without memorizing hostnames.\n *\n * @category Client\n */\nexport const ENVIRONMENTS: Readonly<Record<VairifiedEnvironment, string>> = Object.freeze({\n production: 'https://api-next.vairified.com/api/v1',\n staging: 'https://api-staging.vairified.com/api/v1',\n local: 'http://localhost:3001/api/v1',\n});\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Read an environment variable without assuming `process` exists.\n *\n * Node and most bundlers expose `process.env`, but React Native / Hermes\n * (and some edge runtimes) do not — touching `process.env` there can throw\n * a `ReferenceError`. This resolves to `undefined` instead of crashing, so\n * those callers simply pass `apiKey`/`env` explicitly (see the README's\n * \"React Native\" section).\n *\n * @internal\n */\nfunction readEnv(name: string): string | undefined {\n try {\n if (typeof process === 'undefined') return undefined;\n return process.env?.[name];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async client for the Vairified Partner API.\n *\n * The client is organized around sub-resources that mirror the REST\n * structure — {@link members}, {@link matches}, {@link oauth},\n * {@link leaderboard}. Each sub-resource is a thin wrapper around the\n * HTTP transport on this object.\n *\n * ## Lifecycle\n *\n * The client holds no persistent connections itself — it's safe to\n * create one per request if you want. But for typical usage, wrap it\n * in `await using` so resources are cleaned up deterministically:\n *\n * ```ts\n * await using client = new Vairified({ apiKey: 'vair_pk_xxx' });\n *\n * const member = await client.members.get('vair_mem_xxx');\n * console.log(member.name, member.ratingFor('pickleball'));\n *\n * for await (const m of client.members.search({ city: 'Austin' })) {\n * console.log(m.name);\n * }\n * ```\n *\n * `await using` requires TypeScript 5.2+ and Node 20+. If you can't\n * use it, just call `await client.close()` manually when you're done.\n *\n * @category Client\n */\nexport class Vairified {\n /** The resolved API key this client is using. */\n readonly apiKey: string;\n /** The resolved base URL (production, staging, local, or custom). */\n readonly baseUrl: string;\n /** The resolved environment name. */\n readonly env: VairifiedEnvironment;\n /** Request timeout in milliseconds. */\n readonly timeoutMs: number;\n\n /** Member operations — get, search, find, ratingUpdates. */\n readonly members: MembersResource;\n /** Match submission — submit, testWebhook. */\n readonly matches: MatchesResource;\n /** OAuth flow — authorize, exchangeToken, refresh, revoke. */\n readonly oauth: OAuthResource;\n /** Leaderboard queries — list, rank, categories. */\n readonly leaderboard: LeaderboardResource;\n /** Webhook delivery inspection — deliveries. */\n readonly webhooks: WebhooksResource;\n /**\n * Read and record ambassador referral credit. Each method needs its own\n * per-partner permission — see {@link ReferralsResource}.\n */\n readonly referrals: ReferralsResource;\n\n readonly #transport: HttpTransport;\n\n constructor(options: VairifiedOptions = {}) {\n const apiKey = options.apiKey ?? readEnv('VAIRIFIED_API_KEY') ?? '';\n if (apiKey.length === 0) {\n throw new Error('API key required. Pass { apiKey } or set VAIRIFIED_API_KEY.');\n }\n this.apiKey = apiKey;\n\n if (options.baseUrl) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.env = options.env ?? 'production';\n } else {\n const envName = (options.env ??\n (readEnv('VAIRIFIED_ENV') as VairifiedEnvironment | undefined) ??\n 'production') as VairifiedEnvironment;\n if (options.env && !(envName in ENVIRONMENTS)) {\n throw new Error(\n `Unknown environment: ${envName}. Use one of: ${Object.keys(ENVIRONMENTS).join(', ')}`,\n );\n }\n this.env = envName;\n this.baseUrl = ENVIRONMENTS[envName] ?? ENVIRONMENTS.production;\n }\n\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n this.#transport = new HttpTransport({\n baseUrl: this.baseUrl,\n apiKey: this.apiKey,\n timeoutMs: this.timeoutMs,\n fetch: options.fetch ?? fetch,\n });\n\n this.members = new MembersResource(this.#transport);\n this.matches = new MatchesResource(this.#transport);\n this.oauth = new OAuthResource(this.#transport);\n this.leaderboard = new LeaderboardResource(this.#transport);\n this.webhooks = new WebhooksResource(this.#transport);\n this.referrals = new ReferralsResource(this.#transport);\n }\n\n /**\n * API usage statistics for the current API key.\n *\n * Returns rate-limit status, request counts, and quota usage.\n */\n async usage(): Promise<Record<string, unknown>> {\n const data = await this.#transport.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/partner/usage',\n });\n return data ?? {};\n }\n\n /**\n * Release any resources held by the client.\n *\n * The current transport is stateless, so this is a no-op today, but\n * partners should still call it (or use `await using`) so the SDK\n * can add connection pooling later without breaking them.\n */\n async close(): Promise<void> {\n // Nothing to release today. Kept as an explicit hook so future\n // transports (keep-alive, HTTP/2) can release their pools.\n }\n\n /**\n * Explicit resource management hook — enables\n * `await using client = new Vairified({ ... })` (TypeScript 5.2+).\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n /** Compact summary for console output. */\n toString(): string {\n return `Vairified { env: '${this.env}', baseUrl: '${this.baseUrl}' }`;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/leaderboard.ts","../src/models/match-batch-result.ts","../src/models/tournament-import-result.ts","../src/resources/matches.ts","../src/models/sport-rating.ts","../src/models/member.ts","../src/models/members-by-email-result.ts","../src/models/rating-update.ts","../src/resources/members.ts","../src/oauth.ts","../src/resources/oauth.ts","../src/models/attribution.ts","../src/resources/referrals.ts","../src/models/webhook-delivery.ts","../src/resources/webhooks.ts","../src/client.ts","../src/webhooks/verify.ts"],"sourcesContent":["/**\n * Vairified SDK — error hierarchy.\n *\n * All SDK errors inherit from {@link VairifiedError}, so a single\n * `catch (err: unknown) { if (err instanceof VairifiedError) ... }`\n * covers everything. HTTP-status-specific subclasses (auth, not found,\n * rate limit, validation) are thrown automatically by the HTTP layer.\n *\n * @module\n */\n\n/**\n * Base class for every error thrown by the Vairified SDK.\n *\n * @category Errors\n */\nexport class VairifiedError extends Error {\n /** HTTP status code (if the error came from an API response). */\n readonly statusCode?: number;\n /** Raw response body parsed as JSON when available. */\n readonly response?: unknown;\n\n constructor(message: string, statusCode?: number, response?: unknown) {\n super(message);\n this.name = 'VairifiedError';\n this.statusCode = statusCode;\n this.response = response;\n }\n}\n\n/**\n * Thrown on HTTP 429 responses. Carries the `Retry-After` header value\n * when the server provides one.\n *\n * @category Errors\n */\nexport class RateLimitError extends VairifiedError {\n /** Seconds to wait before retrying, or `undefined` if the server didn't say. */\n readonly retryAfter?: number;\n\n constructor(message = 'Rate limit exceeded', retryAfter?: number, response?: unknown) {\n super(message, 429, response);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Thrown on HTTP 401 — invalid or missing API key.\n *\n * @category Errors\n */\nexport class AuthenticationError extends VairifiedError {\n constructor(message = 'Invalid API key', response?: unknown) {\n super(message, 401, response);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown on HTTP 404 — the resource doesn't exist.\n *\n * @category Errors\n */\nexport class NotFoundError extends VairifiedError {\n constructor(message = 'Resource not found', response?: unknown) {\n super(message, 404, response);\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Thrown on HTTP 400 — the server rejected the request payload.\n *\n * Inspect {@link VairifiedError.response} for field-level details.\n *\n * @category Errors\n */\nexport class ValidationError extends VairifiedError {\n constructor(message = 'Validation error', response?: unknown) {\n super(message, 400, response);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Thrown by {@link OAuthResource} methods when authorization, token\n * exchange, refresh, or revocation fails.\n *\n * @category Errors\n */\nexport class OAuthError extends VairifiedError {\n /**\n * OAuth error code such as `'invalid_grant'`, `'invalid_scope'`,\n * or `'expired_token'`. Check this to branch on the specific failure.\n */\n readonly errorCode?: string;\n\n constructor(message = 'OAuth error', errorCode?: string, response?: unknown) {\n super(message, undefined, response);\n this.name = 'OAuthError';\n this.errorCode = errorCode;\n }\n}\n\n/**\n * Why a webhook was refused. Lets a handler distinguish \"someone is forging\n * requests\" from \"our clock drifted\" without parsing an error message.\n *\n * @category Errors\n */\nexport type WebhookRejectionReason =\n /**\n * No usable signing secret was supplied to {@link verifyWebhook}.\n *\n * This is **your** configuration, not an attack — almost always an unset\n * environment variable. It is separate from {@link signature_mismatch} on\n * purpose: reporting a missing secret as a mismatch sends people hunting an\n * attacker when the fix is one env var.\n */\n | 'no_secret_configured'\n /**\n * A caller-supplied option was not usable — a non-finite tolerance, a\n * negative window, a non-finite clock.\n *\n * Separate from {@link timestamp_out_of_tolerance} deliberately: that one\n * means \"this delivery's clock disagrees with yours\", and reporting a typo in\n * your own configuration under it sends people hunting clock skew on a\n * healthy box. Same reasoning as {@link no_secret_configured}.\n */\n | 'invalid_option'\n /** No `X-Vairified-Signature` header at all. */\n | 'missing_signature'\n /** Present but unparseable, or missing its `t` / `v1` parts. */\n | 'malformed_signature'\n /** Outside the tolerance window, in either direction. */\n | 'timestamp_out_of_tolerance'\n /** Parsed fine; no supplied secret produces this digest. */\n | 'signature_mismatch'\n /** Verified, but the body is not JSON, or is not a webhook envelope. */\n | 'malformed_body';\n\n/**\n * Thrown when a webhook delivery cannot be trusted.\n *\n * :rotating_light: **The message never contains the signing secret, the\n * received digest, or the computed one.** Writing either digest into an error\n * puts a valid HMAC of the partner's own payload into their application logs,\n * where it is an oracle for anyone who can read them. Use {@link reason} to\n * branch; there is deliberately nothing finer-grained to log.\n *\n * @category Errors\n */\nexport class WebhookSignatureError extends VairifiedError {\n readonly reason: WebhookRejectionReason;\n\n constructor(reason: WebhookRejectionReason, message: string) {\n super(message);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n","/**\n * HTTP transport — internal. Not part of the public API.\n *\n * The transport is a thin wrapper around native `fetch` that:\n *\n * - Injects the API key header on every request.\n * - Serializes query params (dropping `undefined`) and JSON bodies.\n * - Applies a request timeout via `AbortController`.\n * - Maps non-2xx responses to the right typed exception.\n *\n * @internal\n * @module\n */\n\nimport {\n AuthenticationError,\n NotFoundError,\n RateLimitError,\n VairifiedError,\n ValidationError,\n} from './errors.js';\nimport type { ApiErrorResponse } from './types.js';\n\n/**\n * Values accepted as query parameters.\n *\n * `null` and `undefined` are silently dropped. Arrays are joined with\n * `','`. Everything else is stringified.\n *\n * @internal\n */\nexport type QueryParams = Readonly<\n Record<string, string | number | boolean | readonly (string | number)[] | null | undefined>\n>;\n\n/**\n * Options passed to the internal HTTP layer.\n *\n * @internal\n */\nexport interface RequestOptions {\n readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n readonly path: string;\n readonly query?: QueryParams;\n readonly body?: unknown;\n}\n\n/**\n * Transport configuration.\n *\n * @internal\n */\nexport interface TransportConfig {\n readonly baseUrl: string;\n readonly apiKey: string;\n readonly timeoutMs: number;\n readonly fetch: typeof fetch;\n}\n\n/**\n * Internal HTTP client. Holds no state between requests — each call\n * builds a fresh `Request`, runs it, and returns the parsed body.\n *\n * @internal\n */\nexport class HttpTransport {\n readonly #config: TransportConfig;\n\n constructor(config: TransportConfig) {\n this.#config = config;\n }\n\n async request<T>(options: RequestOptions): Promise<T> {\n const url = buildUrl(this.#config.baseUrl, options.path, options.query);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error(`Request timed out after ${this.#config.timeoutMs}ms`)),\n this.#config.timeoutMs,\n );\n\n const headers: Record<string, string> = {\n 'X-API-Key': this.#config.apiKey,\n Accept: 'application/json',\n };\n\n const init: RequestInit = {\n method: options.method,\n headers,\n signal: controller.signal,\n };\n\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await this.#config.fetch(url, init);\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n await throwFromResponse(response);\n }\n\n if (response.status === 204 || response.headers.get('content-length') === '0') {\n return undefined as T;\n }\n\n const text = await response.text();\n if (text.length === 0) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VairifiedError(`Unable to parse response as JSON: ${text}`, response.status);\n }\n }\n}\n\n/**\n * Build a full URL from a base and relative path, appending query params.\n *\n * @internal\n */\nexport function buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const cleanBase = baseUrl.replace(/\\/+$/, '');\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n const url = new URL(cleanBase + cleanPath);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === null || value === undefined) continue;\n if (Array.isArray(value)) {\n if (value.length === 0) continue;\n url.searchParams.set(key, value.join(','));\n } else {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n}\n\n/**\n * Parse an error response and throw the right typed exception.\n *\n * @internal\n */\nasync function throwFromResponse(response: Response): Promise<never> {\n const status = response.status;\n const text = await response.text().catch(() => '');\n\n let body: ApiErrorResponse | unknown = null;\n if (text.length > 0) {\n try {\n body = JSON.parse(text);\n } catch {\n body = null;\n }\n }\n\n let message: string;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n const apiBody = body as ApiErrorResponse;\n message = apiBody.message || apiBody.error || text || `HTTP ${status}`;\n } else {\n message = text || `HTTP ${status}`;\n }\n\n switch (status) {\n case 400:\n throw new ValidationError(message, body);\n case 401:\n throw new AuthenticationError(message, body);\n case 404:\n throw new NotFoundError(message, body);\n case 429: {\n const retryAfterHeader = response.headers.get('Retry-After');\n const retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : undefined;\n throw new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : undefined, body);\n }\n default:\n throw new VairifiedError(message, status, body);\n }\n}\n","/**\n * {@link LeaderboardResource} — read-only leaderboard queries.\n *\n * @module\n */\n\nimport type { HttpTransport, QueryParams } from '../http.js';\nimport type { LeaderboardOptions, PlayerRankOptions } from '../types.js';\n\n/**\n * Read-only leaderboard queries.\n *\n * @category Resources\n */\nexport class LeaderboardResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /** Fetch a leaderboard page with optional filters. */\n async list(options: LeaderboardOptions = {}): Promise<Record<string, unknown>> {\n const query: QueryParams = {\n limit: options.limit ?? 50,\n offset: options.offset ?? 0,\n category: options.category,\n ageBracket: options.ageBracket,\n scope: options.scope,\n state: options.state,\n city: options.city,\n clubId: options.clubId,\n gender: options.gender?.toUpperCase(),\n minGames: options.minGames,\n search: options.search,\n verifiedOnly: options.verifiedOnly === true ? true : undefined,\n };\n\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/leaderboard',\n query,\n });\n return data ?? {};\n }\n\n /** Fetch a specific player's rank plus nearby players. */\n async rank(playerId: string, options: PlayerRankOptions = {}): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {\n playerId,\n category: options.category ?? 'doubles',\n ageBracket: options.ageBracket ?? 'open',\n scope: options.scope ?? 'global',\n contextSize: options.contextSize ?? 5,\n };\n if (options.state !== undefined) body.state = options.state;\n if (options.city !== undefined) body.city = options.city;\n if (options.clubId !== undefined) body.clubId = options.clubId;\n\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/leaderboard/rank',\n body,\n });\n return data ?? {};\n }\n\n /** List available leaderboard categories, brackets, and scopes. */\n async categories(): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/leaderboard/categories',\n });\n return data ?? {};\n }\n}\n","/**\n * {@link MatchBatchResult} — result of a batch match submission.\n *\n * @module\n */\n\nimport type { MatchBatchResultWire } from '../types.js';\n\n/**\n * Result of a {@link MatchesResource.submit} call.\n *\n * {@link success} is `true` only when every match in the batch was\n * accepted. Check {@link errors} for per-match validation failures.\n *\n * @category Matches\n */\nexport class MatchBatchResult {\n readonly success: boolean;\n readonly numMatches: number;\n readonly numGames: number;\n readonly dryRun: boolean | null;\n readonly message: string | null;\n readonly errors: readonly string[] | null;\n\n constructor(wire: MatchBatchResultWire) {\n this.success = wire.success;\n this.numMatches = wire.numMatches;\n this.numGames = wire.numGames;\n this.dryRun = wire.dryRun ?? null;\n this.message = wire.message ?? null;\n this.errors = wire.errors ? Object.freeze([...wire.errors]) : null;\n Object.freeze(this);\n }\n\n /** Shorthand: successful submission with zero errors. */\n get ok(): boolean {\n return this.success && (this.errors === null || this.errors.length === 0);\n }\n\n /** Whether this was a dry-run (validation only, nothing persisted). */\n get isDryRun(): boolean {\n return this.dryRun === true;\n }\n\n toString(): string {\n const mode = this.isDryRun ? ' [dry-run]' : '';\n const errs = this.errors && this.errors.length > 0 ? ` errors=${this.errors.length}` : '';\n const status = this.ok ? 'ok' : 'FAILED';\n return `MatchBatchResult ${status}${mode} matches=${this.numMatches} games=${this.numGames}${errs}`;\n }\n}\n","/**\n * {@link TournamentImportResult} — result of a tournament import submission.\n *\n * @module\n */\n\nimport type { TournamentImportResultWire } from '../types.js';\n\n/**\n * One ghost player created by a tournament import.\n *\n * @category Matches\n */\nexport interface TournamentImportCreatedGhost {\n /** The email or phone you supplied for this person in `ghostMembers[]`. */\n readonly ref: string;\n /** The public member id allocated to them, usable in `matches.submit()`. */\n readonly memberId: number;\n}\n\n/**\n * Result of a tournament import submission.\n *\n * @category Matches\n */\nexport class TournamentImportResult {\n readonly success: boolean;\n readonly matchesImported: number;\n readonly gamesRecorded: number;\n readonly ghostPlayersCreated: number;\n readonly existingPlayersMatched: number;\n readonly dryRun: boolean;\n readonly message: string | undefined;\n readonly errors: readonly string[];\n /**\n * Public member ids for the ghost players THIS import created, keyed by the\n * `ref` supplied in `ghostMembers[]`.\n *\n * Empty on a dry-run, and empty for entries the import matched to a player who\n * already existed — resolving an existing email to a member requires the\n * `key:player:lookup` scope and `members.getByEmail()`.\n *\n * Use these ids directly in a follow-up `matches.submit()`; without them an\n * import reports only how many accounts it caused and you cannot address any\n * of them.\n */\n readonly createdGhostMembers: readonly TournamentImportCreatedGhost[];\n\n /** @internal */\n constructor(wire: TournamentImportResultWire) {\n this.success = wire.success;\n this.matchesImported = wire.matchesImported;\n this.gamesRecorded = wire.gamesRecorded;\n this.ghostPlayersCreated = wire.ghostPlayersCreated;\n this.existingPlayersMatched = wire.existingPlayersMatched;\n this.dryRun = wire.dryRun ?? false;\n this.message = wire.message;\n this.errors = Object.freeze(wire.errors ?? []);\n this.createdGhostMembers = Object.freeze(\n (wire.createdGhostMembers ?? []).map((entry) =>\n Object.freeze({ ref: entry.ref, memberId: entry.memberId }),\n ),\n );\n Object.freeze(this);\n }\n\n /** True when the import succeeded without errors. */\n get ok(): boolean {\n return this.success && this.errors.length === 0;\n }\n}\n","/**\n * {@link MatchesResource} — bulk match submission.\n *\n * @module\n */\n\nimport type { HttpTransport } from '../http.js';\nimport { MatchBatchResult } from '../models/match-batch-result.js';\nimport { TournamentImportResult } from '../models/tournament-import-result.js';\nimport type { MatchBatch, MatchBatchResultWire, TournamentImportResultWire } from '../types.js';\n\n/**\n * Match submission — one call submits a full batch.\n *\n * @category Resources\n */\nexport class MatchesResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Submit a {@link MatchBatch} for rating calculation.\n *\n * All players in every match must have granted the `user:match:submit`\n * scope via OAuth (unless your API key has the\n * `user:match:submit:trusted` scope, which skips per-player consent).\n *\n * Set `batch.dryRun = true` to validate without persisting.\n *\n * ```ts\n * const result = await client.matches.submit({\n * sport: 'pickleball',\n * winScore: 11,\n * winBy: 2,\n * bracket: '4.0 Doubles',\n * event: 'Weekly League',\n * matchDate: '2026-04-11T14:00:00Z',\n * matches: [\n * {\n * identifier: 'm1',\n * teams: [['vair_mem_aaa', 'vair_mem_bbb'],\n * ['vair_mem_ccc', 'vair_mem_ddd']],\n * games: [{ scores: [11, 8] }, { scores: [11, 5] }],\n * },\n * ],\n * });\n * if (result.ok) {\n * console.log(`Submitted ${result.numGames} games`);\n * }\n * ```\n */\n async submit(batch: MatchBatch): Promise<MatchBatchResult> {\n const wire = await this.#http.request<MatchBatchResultWire>({\n method: 'POST',\n path: '/partner/matches',\n body: batch,\n });\n return new MatchBatchResult(wire);\n }\n\n /**\n * Import tournament results.\n *\n * The request body is a free-form JSON object whose structure is\n * defined by the Vairified tournament import schema. Set\n * `body.dryRun = true` to validate without persisting.\n *\n * @param body - Tournament import payload.\n * @returns {@link TournamentImportResult} with match/game counts.\n * @category Matches\n *\n * @example\n * ```ts\n * const result = await client.matches.tournamentImport({\n * sport: 'pickleball',\n * tournamentName: 'Spring Classic',\n * matches: [...],\n * });\n * if (result.ok) {\n * console.log(`Imported ${result.matchesImported} matches`);\n * }\n * ```\n */\n async tournamentImport(body: Record<string, unknown>): Promise<TournamentImportResult> {\n const wire = await this.#http.request<TournamentImportResultWire>({\n method: 'POST',\n path: '/partner/tournament-import',\n body,\n });\n return new TournamentImportResult(wire);\n }\n\n /** Send a test payload to a webhook URL. */\n async testWebhook(webhookUrl: string): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/partner/webhook-test',\n body: { webhookUrl },\n });\n return data ?? {};\n }\n}\n","/**\n * {@link SportRating} — a player's ratings for one sport.\n *\n * @module\n */\n\nimport type { RatingSplitWire, SportRatingWire, VairProStatus } from '../types.js';\n\n/**\n * A player's ratings for a single sport.\n *\n * The top-level `rating` / `abbr` is the primary rating for that sport\n * (conventionally the overall-open bracket). Every category × age\n * bracket the player has played is also available via {@link get},\n * {@link has}, subscript-style access, and iteration.\n *\n * ```ts\n * const pb = member.sport.get('pickleball');\n * if (pb) {\n * console.log(pb.rating, pb.abbr); // 3.915 VO\n * console.log(pb.get('overall-open')?.rating); // 3.915\n * console.log(pb.has('singles-40+')); // false\n * console.log(pb.size); // 3\n * for (const [key, split] of pb) {\n * console.log(key, split.rating);\n * }\n * }\n * ```\n *\n * @category Members\n */\nexport class SportRating {\n /** Primary rating for this sport. */\n readonly rating: number;\n /** Category abbreviation for the primary rating (e.g. `'VO'`). */\n readonly abbr: string;\n\n /** Player is VAIRified in this sport (has a verified, non-recreational rating). */\n readonly isVairified: boolean;\n /** Player is an active VAIR Pro (can rate) in this sport. Alias of {@link isVairPro}. */\n readonly isRater: boolean;\n /** Player is an active VAIR Pro (can rate) in this sport. */\n readonly isVairPro: boolean;\n /** VAIR-Pro lifecycle status in this sport (`'ACTIVE'` / `'PENDING'` / `null`). */\n readonly isVairProStatus: VairProStatus;\n\n readonly #splits: ReadonlyMap<string, RatingSplitWire>;\n\n constructor(wire: SportRatingWire) {\n this.rating = wire.rating;\n this.abbr = wire.abbr;\n this.isVairified = wire.isVairified ?? false;\n this.isRater = wire.isRater ?? false;\n this.isVairPro = wire.isVairPro ?? false;\n this.isVairProStatus = wire.isVairProStatus ?? null;\n this.#splits = new Map(Object.entries(wire.ratingSplits ?? {}));\n Object.freeze(this);\n }\n\n /**\n * Look up a rating split by key (e.g. `'overall-open'`,\n * `'singles-12-13'`, `'gender-40+'`). Returns `undefined` if the\n * player has no rating for that bracket.\n */\n get(key: string): RatingSplitWire | undefined {\n return this.#splits.get(key);\n }\n\n /** Whether the player has a rating for the given split key. */\n has(key: string): boolean {\n return this.#splits.has(key);\n }\n\n /** Number of rating splits. */\n get size(): number {\n return this.#splits.size;\n }\n\n /** All split keys the player has ratings for. */\n keys(): IterableIterator<string> {\n return this.#splits.keys();\n }\n\n /** All rating splits the player has. */\n values(): IterableIterator<RatingSplitWire> {\n return this.#splits.values();\n }\n\n /** `[key, split]` pairs for every rating split. */\n entries(): IterableIterator<[string, RatingSplitWire]> {\n return this.#splits.entries();\n }\n\n /**\n * `for (const [key, split] of sportRating) { ... }` — iterate every\n * rating split the player has in this sport.\n */\n [Symbol.iterator](): IterableIterator<[string, RatingSplitWire]> {\n return this.#splits.entries();\n }\n}\n","/**\n * {@link Member} — partner-facing player record.\n *\n * @module\n */\n\nimport type {\n Gender,\n MemberStatusWire,\n PartnerMemberWire,\n RatingSplitWire,\n SportRatingWire,\n} from '../types.js';\nimport { SportRating } from './sport-rating.js';\n\n/**\n * Map-like wrapper around a player's sport → {@link SportRating}.\n *\n * Supports `.get(code)`, `.has(code)`, `.size`, and iteration so the\n * shape feels native:\n *\n * ```ts\n * member.sport.get('pickleball')?.rating\n * for (const [code, rating] of member.sport) { ... }\n * ```\n *\n * @category Members\n */\nexport class MemberSportMap {\n readonly #sports: ReadonlyMap<string, SportRating>;\n\n constructor(wire: Readonly<Record<string, SportRatingWire>> | undefined) {\n const entries = Object.entries(wire ?? {}).map(\n ([code, w]) => [code, new SportRating(w)] as const,\n );\n this.#sports = new Map(entries);\n Object.freeze(this);\n }\n\n get(sport: string): SportRating | undefined {\n return this.#sports.get(sport);\n }\n\n has(sport: string): boolean {\n return this.#sports.has(sport);\n }\n\n get size(): number {\n return this.#sports.size;\n }\n\n keys(): IterableIterator<string> {\n return this.#sports.keys();\n }\n\n values(): IterableIterator<SportRating> {\n return this.#sports.values();\n }\n\n entries(): IterableIterator<[string, SportRating]> {\n return this.#sports.entries();\n }\n\n [Symbol.iterator](): IterableIterator<[string, SportRating]> {\n return this.#sports.entries();\n }\n}\n\n/**\n * A partner-facing player record.\n *\n * Returned by {@link MembersResource.get} (full detail, requires an\n * active OAuth connection) and {@link MembersResource.search} (limited\n * detail for public search).\n *\n * Rating data lives under {@link sport} — keyed by sport code. The\n * backend returns only the sports the player has ratings in, or only\n * the sports requested via the `sport=` query filter. Use\n * {@link ratingFor} to fetch the primary rating for a specific sport\n * with a sensible default.\n *\n * @category Members\n */\nexport class Member {\n readonly memberId: number;\n readonly id: string | null;\n readonly firstName: string;\n readonly lastName: string;\n readonly fullName: string;\n readonly displayName: string;\n readonly age: number | null;\n readonly city: string | null;\n readonly state: string | null;\n readonly zip: string | null;\n readonly country: string | null;\n /**\n * The DATE this member's VAIR account was created (`YYYY-MM-DD`, UTC), or\n * `null` when the endpoint does not supply it.\n *\n * Present on `members.get()`, `members.getBulk()` and `members.getByEmail()` —\n * the calls where you already know which member you asked about. **Never on\n * `members.search()`**, which is discovery: account age is not something you\n * can browse strangers by.\n *\n * Deliberately a date, not a timestamp. It exists so you can apply a\n * new-accounts-only referral rule — crediting an ambassador only for accounts\n * created because of their event.\n */\n readonly memberSince: string | null;\n readonly gender: Gender | null;\n readonly status: MemberStatusWire;\n readonly sport: MemberSportMap;\n readonly activeLeagues: readonly string[] | null;\n readonly email: string | null;\n readonly grantedScopes: readonly string[] | null;\n\n constructor(wire: PartnerMemberWire) {\n this.memberId = wire.memberId;\n this.id = wire.id ?? null;\n this.firstName = wire.firstName;\n this.lastName = wire.lastName;\n this.fullName = wire.fullName;\n this.displayName = wire.displayName;\n this.age = wire.age ?? null;\n this.city = wire.city ?? null;\n this.state = wire.state ?? null;\n this.zip = wire.zip ?? null;\n this.country = wire.country ?? null;\n this.memberSince = wire.memberSince ?? null;\n this.gender = wire.gender ?? null;\n this.status = Object.freeze({ ...wire.status });\n this.sport = new MemberSportMap(wire.sport);\n this.activeLeagues = wire.activeLeagues ? Object.freeze([...wire.activeLeagues]) : null;\n this.email = wire.email ?? null;\n this.grantedScopes = wire.grantedScopes ? Object.freeze([...wire.grantedScopes]) : null;\n Object.freeze(this);\n }\n\n /** Full name — alias for {@link fullName}, matching common usage. */\n get name(): string {\n return this.fullName;\n }\n\n /** The list of sport codes this player has ratings in. */\n get sports(): readonly string[] {\n return [...this.sport.keys()];\n }\n\n /**\n * Primary rating for a given sport.\n *\n * @param sport Sport code — defaults to `'pickleball'`.\n * @returns The primary rating value, or `null` if the player has no\n * ratings for that sport.\n */\n ratingFor(sport = 'pickleball'): number | null {\n return this.sport.get(sport)?.rating ?? null;\n }\n\n /**\n * Get a specific rating split for a sport.\n *\n * @param key Split key (e.g. `'overall-open'`).\n * @param sport Sport code — defaults to `'pickleball'`.\n */\n split(key: string, sport = 'pickleball'): RatingSplitWire | null {\n return this.sport.get(sport)?.get(key) ?? null;\n }\n\n /** Compact summary for console output. */\n toString(): string {\n const primary = this.sport.values().next().value;\n if (primary) {\n return `Member #${this.memberId} '${this.displayName}' rating=${primary.rating.toFixed(\n 3,\n )} ${primary.abbr}`;\n }\n return `Member #${this.memberId} '${this.displayName}'`;\n }\n}\n","/**\n * {@link MembersByEmailResult} — result of an email → member lookup.\n *\n * @module\n */\n\nimport type { MembersByEmailResultWire, PartnerMemberEmailMatchWire } from '../types.js';\nimport { Member } from './member.js';\n\n/**\n * One requested address that resolved to at least one member.\n *\n * {@link members} is always an array. An email address is not a unique\n * key in VAIR — an unclaimed imported record can share an address with a\n * claimed account — so never assume a single element without checking.\n *\n * @category Members\n */\nexport class MemberEmailMatch {\n /** The address exactly as you supplied it, not as stored. */\n readonly email: string;\n /** Every member holding this address. Never empty. */\n readonly members: readonly Member[];\n\n /** @internal */\n constructor(wire: PartnerMemberEmailMatchWire) {\n this.email = wire.email;\n this.members = Object.freeze(wire.members.map((m) => new Member(m)));\n Object.freeze(this);\n }\n\n /**\n * The single member for this address, or `null` when the address is\n * ambiguous (more than one match).\n *\n * Use this rather than `members[0]` when a wrong link is worse than no\n * link — it refuses to guess instead of silently picking one.\n */\n get sole(): Member | null {\n return this.members.length === 1 ? (this.members[0] ?? null) : null;\n }\n\n /** Whether this address resolved to more than one member. */\n get isAmbiguous(): boolean {\n return this.members.length > 1;\n }\n\n toString(): string {\n return `MemberEmailMatch ${this.email} -> ${this.members.length} member(s)`;\n }\n}\n\n/**\n * Result of a {@link MembersResource.getByEmail} call.\n *\n * Every address you supplied appears in exactly one of {@link matched} or\n * {@link notFound} — the endpoint never silently drops one, so consume\n * {@link notFound} directly rather than diffing your input against the\n * results.\n *\n * A `notFound` address is not proof the person has no VAIR account:\n * unclaimed imported records are deliberately excluded from this lookup.\n *\n * @category Members\n */\nexport class MembersByEmailResult {\n readonly matched: readonly MemberEmailMatch[];\n /** Addresses that resolved to nothing, echoed as you supplied them. */\n readonly notFound: readonly string[];\n\n /** @internal */\n constructor(wire: MembersByEmailResultWire) {\n this.matched = Object.freeze(wire.matched.map((m) => new MemberEmailMatch(m)));\n this.notFound = Object.freeze([...wire.notFound]);\n Object.freeze(this);\n }\n\n /**\n * Look up one address's match, case-insensitively.\n *\n * Saves callers a linear scan and, more importantly, saves them from\n * matching case-sensitively against an address the server echoed back\n * in whatever case they originally sent.\n */\n get(email: string): MemberEmailMatch | null {\n const needle = email.trim().toLowerCase();\n return this.matched.find((m) => m.email.toLowerCase() === needle) ?? null;\n }\n\n /** Whether every requested address resolved to at least one member. */\n get allResolved(): boolean {\n return this.notFound.length === 0;\n }\n\n /** Total number of members across every matched address. */\n get memberCount(): number {\n return this.matched.reduce((n, m) => n + m.members.length, 0);\n }\n\n toString(): string {\n return `MembersByEmailResult matched=${this.matched.length} notFound=${this.notFound.length}`;\n }\n}\n","/**\n * {@link RatingUpdate} — a single rating change notification.\n *\n * @module\n */\n\nimport type { PartnerRatingUpdateWire, RatingSplitWire } from '../types.js';\n\n/**\n * A single rating change notification.\n *\n * Returned by {@link MembersResource.ratingUpdates} — the **polling** endpoint,\n * and only that.\n *\n * :rotating_light: **This is NOT the `rating.updated` webhook payload.** It was,\n * once; the webhook has sent a full multi-sport snapshot since Vairified#899\n * and the two shapes have not matched since. The webhook's is\n * {@link RatingUpdatedEventDataWire}, reachable through `verifyWebhook`. This\n * sentence used to claim otherwise, and a later change believed it.\n *\n * @category Members\n */\nexport class RatingUpdate {\n readonly memberId: number;\n readonly id: string | null;\n readonly displayName: string | null;\n readonly sport: string | null;\n readonly previousRating: number | null;\n readonly newRating: number | null;\n readonly changedAt: string | null;\n readonly ratingSplits: Readonly<Record<string, RatingSplitWire>> | null;\n\n constructor(wire: PartnerRatingUpdateWire) {\n this.memberId = wire.memberId;\n this.id = wire.id ?? null;\n this.displayName = wire.displayName ?? null;\n this.sport = wire.sport ?? null;\n this.previousRating = wire.previousRating ?? null;\n this.newRating = wire.newRating ?? null;\n this.changedAt = wire.changedAt ?? null;\n this.ratingSplits = wire.ratingSplits ? Object.freeze({ ...wire.ratingSplits }) : null;\n Object.freeze(this);\n }\n\n /**\n * Rating change amount — `newRating - previousRating`. Returns `null`\n * if either rating is missing from the update payload.\n */\n get delta(): number | null {\n if (this.previousRating === null || this.newRating === null) {\n return null;\n }\n return this.newRating - this.previousRating;\n }\n\n /** `true` when the new rating is strictly higher than the previous. */\n get improved(): boolean {\n const delta = this.delta;\n return delta !== null && delta > 0;\n }\n\n toString(): string {\n const arrow = this.improved ? '↑' : '↓';\n const prev = this.previousRating !== null ? this.previousRating.toFixed(3) : '?';\n const next = this.newRating !== null ? this.newRating.toFixed(3) : '?';\n const name = this.displayName ? ` '${this.displayName}'` : '';\n return `RatingUpdate #${this.memberId}${name} ${prev} ${arrow} ${next}`;\n }\n}\n","/**\n * {@link MembersResource} — member lookups, search, and rating updates.\n *\n * @module\n */\n\nimport { ValidationError } from '../errors.js';\nimport type { HttpTransport, QueryParams } from '../http.js';\nimport { Member } from '../models/member.js';\nimport { MembersByEmailResult } from '../models/members-by-email-result.js';\nimport { RatingUpdate } from '../models/rating-update.js';\nimport type {\n MembersByEmailResultWire,\n PartnerMemberWire,\n PartnerRatingUpdateWire,\n SearchFilters,\n} from '../types.js';\n\nconst DEFAULT_PAGE_SIZE = 20;\nconst MAX_PAGE_SIZE = 100;\nconst MAX_EMAILS_PER_LOOKUP = 100;\n\n/**\n * Member operations — get a single member, auto-paginating search,\n * find by name, and polling for rating change notifications.\n *\n * @category Resources\n */\nexport class MembersResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Get a connected member by external ID.\n *\n * **Requires an active OAuth connection** between your partner app\n * and the player. Use the OAuth flow on `client.oauth` first.\n *\n * @param playerId External player ID in `vair_mem_xxx` format.\n * @param options.sport Optional sport filter — single code or list.\n * When omitted, the response contains every sport the player has\n * ratings in.\n * @throws {@link NotFoundError} if the external ID is unknown.\n * @throws {@link VairifiedError} if the player has not connected to\n * your app (403) or the request otherwise fails.\n *\n * @example\n * ```ts\n * const member = await client.members.get('vair_mem_xxx');\n * console.log(member.name, member.ratingFor('pickleball'));\n *\n * // Just pickleball\n * const member2 = await client.members.get('vair_mem_xxx', { sport: 'pickleball' });\n *\n * // Multiple sports\n * const member3 = await client.members.get('vair_mem_xxx', {\n * sport: ['pickleball', 'padel'],\n * });\n * ```\n */\n async get(\n playerId: string,\n options: { sport?: string | readonly string[] } = {},\n ): Promise<Member> {\n const query: Record<string, string> = { memberId: playerId };\n if (options.sport !== undefined) {\n query.sport = Array.isArray(options.sport)\n ? (options.sport as readonly string[]).join(',')\n : (options.sport as string);\n }\n const wire = await this.#http.request<PartnerMemberWire>({\n method: 'GET',\n path: '/partner/member',\n query,\n });\n return new Member(wire);\n }\n\n /**\n * Search for members, yielding each match as a {@link Member}.\n *\n * This is an **auto-paginating async iterator** — it fetches pages\n * from the server lazily as you iterate, so you can stream through\n * thousands of results without holding them all in memory:\n *\n * ```ts\n * for await (const m of client.members.search({ city: 'Austin' })) {\n * console.log(m.name, m.ratingFor('pickleball'));\n * }\n * ```\n *\n * Stop early by `break`-ing out of the loop, or cap the total with\n * `maxResults`.\n */\n async *search(filters: SearchFilters = {}): AsyncGenerator<Member, void, void> {\n const pageSize = Math.min(filters.pageSize ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);\n const maxResults = filters.maxResults;\n\n const baseQuery = buildSearchQuery(filters, pageSize);\n\n let offset = 0;\n let yielded = 0;\n\n while (true) {\n const query: QueryParams = { ...baseQuery, offset };\n const data = await this.#http.request<\n PartnerMemberWire[] | { players?: PartnerMemberWire[] }\n >({ method: 'GET', path: '/partner/search', query });\n\n const batch: PartnerMemberWire[] = Array.isArray(data) ? data : (data?.players ?? []);\n\n if (batch.length === 0) {\n return;\n }\n\n for (const wire of batch) {\n yield new Member(wire);\n yielded += 1;\n if (maxResults !== undefined && yielded >= maxResults) {\n return;\n }\n }\n\n if (batch.length < pageSize) {\n return;\n }\n offset += pageSize;\n }\n }\n\n /**\n * Return the first search hit for a name, or `null`.\n *\n * Convenience for the common \"look up by name\" case:\n *\n * ```ts\n * const mike = await client.members.find('Mike Barker');\n * if (mike) {\n * console.log(mike.ratingFor('pickleball'));\n * }\n * ```\n */\n async find(name: string): Promise<Member | null> {\n for await (const member of this.search({ name, pageSize: 1, maxResults: 1 })) {\n return member;\n }\n return null;\n }\n\n /**\n * Fetch up to 100 members by their member IDs in one call.\n *\n * Unknown IDs are silently omitted — the returned array may be\n * shorter than the input. Results are returned in the same order\n * as the input IDs.\n *\n * @param ids - Array of integer member IDs (max 100).\n * @param options - Optional filters.\n * @param options.sport - Sport code to scope ratings (e.g. `'pickleball'`).\n * @returns Array of {@link Member} instances.\n * @throws {@link ValidationError} If more than 100 IDs are provided.\n * @category Members\n *\n * @example\n * ```ts\n * const members = await client.members.getBulk([4873327, 4873328]);\n * for (const m of members) {\n * console.log(m.name, m.ratingFor('pickleball'));\n * }\n * ```\n */\n async getBulk(ids: number[], options?: { sport?: string }): Promise<Member[]> {\n if (ids.length > 100) {\n throw new ValidationError('Maximum 100 member IDs per request');\n }\n const query: Record<string, string> = { ids: ids.join(',') };\n if (options?.sport) query.sport = options.sport;\n const rows = await this.#http.request<PartnerMemberWire[]>({\n method: 'GET',\n path: '/partner/members',\n query,\n });\n return rows.map((row) => new Member(row));\n }\n\n /**\n * Resolve up to 100 members by their **exact** email address.\n *\n * Use this to link your users to their VAIR identity when you hold\n * their email but not their member ID — e.g. resolving a tournament\n * roster at registration instead of waiting for each player to\n * complete SSO.\n *\n * **Requires the `key:player:lookup` scope**, which is granted per\n * partner on approval. Holding `key:player:search` does not imply it.\n *\n * Matching is exact and case-insensitive; there is deliberately no\n * partial, prefix or fuzzy matching. Every address you supply comes\n * back in either `matched` or `notFound`, so read `notFound` directly\n * instead of diffing your input against the results.\n *\n * A `notFound` address is **not** proof the person has no VAIR\n * account — unclaimed imported records are excluded from this lookup.\n *\n * @param emails - Email addresses to resolve (max 100).\n * @param options - Optional filters.\n * @param options.sport - Sport code to scope ratings (e.g. `'pickleball'`).\n * @throws {@link ValidationError} If more than 100 addresses are\n * provided, or the list is empty.\n * @category Members\n *\n * @example\n * ```ts\n * const result = await client.members.getByEmail([\n * 'ada@example.com',\n * 'nobody@example.com',\n * ]);\n *\n * for (const match of result.matched) {\n * const member = match.sole; // null when the address is ambiguous\n * if (member) console.log(match.email, '->', member.memberId);\n * }\n *\n * console.log('no VAIR account found for:', result.notFound);\n * ```\n */\n async getByEmail(\n emails: readonly string[],\n options?: { sport?: string },\n ): Promise<MembersByEmailResult> {\n // Validate before the round trip so the caller gets a typed error\n // rather than a 400 they have to interpret.\n if (emails.length === 0) {\n throw new ValidationError('At least one email address is required');\n }\n if (emails.length > MAX_EMAILS_PER_LOOKUP) {\n throw new ValidationError(`Maximum ${MAX_EMAILS_PER_LOOKUP} email addresses per request`);\n }\n // A comma inside an entry would split into two addresses server-side\n // and silently shift every result — reject it rather than send it.\n const offender = emails.find((e) => e.includes(','));\n if (offender !== undefined) {\n throw new ValidationError(`Email address must not contain a comma: ${offender}`);\n }\n\n const query: Record<string, string> = { emails: emails.join(',') };\n if (options?.sport) query.sport = options.sport;\n const wire = await this.#http.request<MembersByEmailResultWire>({\n method: 'GET',\n path: '/partner/members/by-email',\n query,\n });\n return new MembersByEmailResult({\n matched: wire?.matched ?? [],\n notFound: wire?.notFound ?? [],\n });\n }\n\n /**\n * Poll for rating change notifications.\n *\n * Returns a list of {@link RatingUpdate} objects for every player\n * whose rating has changed since the last poll. Members are\n * considered subscribed when they have an active OAuth connection\n * with the `user:webhook:subscribe` scope.\n */\n async ratingUpdates(): Promise<readonly RatingUpdate[]> {\n const data = await this.#http.request<{ updates?: PartnerRatingUpdateWire[] } | unknown>({\n method: 'GET',\n path: '/partner/rating-updates',\n });\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n const updates = (data as { updates?: PartnerRatingUpdateWire[] }).updates ?? [];\n return updates.map((wire) => new RatingUpdate(wire));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction buildSearchQuery(filters: SearchFilters, pageSize: number): QueryParams {\n const query: Record<string, string | number | boolean> = {};\n\n if (filters.sport !== undefined) {\n query.sport = Array.isArray(filters.sport)\n ? (filters.sport as readonly string[]).join(',')\n : (filters.sport as string);\n }\n\n if (filters.memberId !== undefined) {\n query.member = String(filters.memberId);\n } else if (filters.name !== undefined) {\n query.member = filters.name;\n }\n\n if (filters.city !== undefined) query.city = filters.city;\n if (filters.state !== undefined) query.state = filters.state;\n if (filters.country !== undefined) query.country = filters.country;\n if (filters.zip !== undefined) query.zip = filters.zip;\n if (filters.location !== undefined) query.location = filters.location;\n\n if (filters.gender !== undefined) {\n query.gender = filters.gender.toUpperCase();\n }\n if (filters.vairifiedOnly !== undefined) query.vairified = filters.vairifiedOnly;\n if (filters.wheelchair !== undefined) query.wheelchair = filters.wheelchair;\n\n if (filters.ratingMin !== undefined) query.rating1 = filters.ratingMin;\n if (filters.ratingMax !== undefined) query.rating2 = filters.ratingMax;\n\n const { ageFilterType, age1, age2 } = resolveAgeFilter(filters);\n if (ageFilterType !== undefined) query.ageFilterType = ageFilterType;\n if (age1 !== undefined) query.age1 = age1;\n if (age2 !== undefined) query.age2 = age2;\n\n if (filters.sortBy !== undefined) query.sortField = filters.sortBy;\n if (filters.sortOrder !== undefined) query.sortDirection = filters.sortOrder;\n\n query.limit = pageSize;\n\n return query;\n}\n\nfunction resolveAgeFilter(filters: SearchFilters): {\n ageFilterType?: string;\n age1?: number;\n age2?: number;\n} {\n if (filters.age !== undefined) {\n return { ageFilterType: 'exact', age1: filters.age };\n }\n if (filters.ageMin !== undefined && filters.ageMax !== undefined) {\n return { ageFilterType: 'range', age1: filters.ageMin, age2: filters.ageMax };\n }\n if (filters.ageMin !== undefined) {\n return { ageFilterType: 'above', age1: filters.ageMin };\n }\n if (filters.ageMax !== undefined) {\n return { ageFilterType: 'below', age1: filters.ageMax };\n }\n return {};\n}\n","/**\n * Vairified OAuth helpers and type definitions.\n *\n * Use the {@link OAuthResource} on `client.oauth` for the full flow —\n * these helpers exist for partners who need to build authorization URLs\n * or validate scopes outside the client (e.g. in a frontend that only\n * handles the redirect step).\n *\n * @module\n */\n\n// ---------------------------------------------------------------------------\n// Scope definitions\n// ---------------------------------------------------------------------------\n\n/**\n * Every OAuth scope the Vairified authorization server accepts.\n *\n * Declaring this as a string union (rather than a free-form `string`)\n * lets TypeScript catch typos at authoring time:\n *\n * ```ts\n * const scopes: OAuthScope[] = ['user:profile:read', 'user:rating:read']; // ok\n * const bad: OAuthScope[] = ['user:profile:read', 'rating']; // type error\n * ```\n *\n * @category OAuth\n */\nexport type OAuthScope =\n | 'user:profile:read'\n | 'user:profile:email'\n | 'user:rating:read'\n | 'user:rating:history'\n | 'user:match:submit'\n | 'user:webhook:subscribe';\n\n/**\n * Human-readable description for every OAuth scope.\n *\n * @category OAuth\n */\nexport const SCOPES: Readonly<Record<OAuthScope, string>> = Object.freeze({\n 'user:profile:read': 'Access your name, location, and verification status',\n 'user:profile:email': 'Access your email address',\n 'user:rating:read': 'View your current rating and rating splits',\n 'user:rating:history': 'View your complete rating history',\n 'user:match:submit': 'Submit match results on your behalf',\n 'user:webhook:subscribe': 'Receive notifications when your rating changes',\n});\n\n/**\n * The scopes automatically requested when none are specified.\n *\n * @category OAuth\n */\nexport const DEFAULT_SCOPES: readonly OAuthScope[] = Object.freeze([\n 'user:profile:read',\n 'user:rating:read',\n]);\n\n// ---------------------------------------------------------------------------\n// OAuth data types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for building an authorization URL manually.\n *\n * @category OAuth\n */\nexport interface OAuthConfig {\n readonly apiKey: string;\n readonly redirectUri: string;\n readonly baseUrl?: string;\n /**\n * Your app's `client_id` — the `PartnerApp.slug` Vairified assigned you\n * (e.g. `dinkr`). Required by the browser `GET /partner/oauth/authorize`\n * endpoint to identify your app; without it the authorization page rejects\n * the request with `invalid_request: client_id is required`.\n *\n * Only needed for this pure-frontend URL helper. The recommended flow —\n * {@link OAuthResource.authorize} on a client — identifies your app by the\n * API key instead, so it needs no `client_id`.\n */\n readonly clientId?: string;\n}\n\n/**\n * Response from starting an OAuth authorization flow.\n *\n * @category OAuth\n */\nexport interface AuthorizationResponse {\n readonly authorizationUrl: string;\n readonly code: string;\n readonly state?: string;\n}\n\n/**\n * Response from exchanging an authorization code for tokens.\n *\n * @category OAuth\n */\nexport interface TokenResponse {\n readonly accessToken: string;\n readonly refreshToken: string | null;\n readonly expiresIn: number;\n readonly scope: readonly string[];\n readonly playerId: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helper functions\n// ---------------------------------------------------------------------------\n\n/**\n * Build the URL to redirect users to for OAuth authorization.\n *\n * Prefer {@link OAuthResource.authorize} on a Vairified client — this\n * helper is only useful when you need to construct the URL without\n * making an HTTP call first (for example, in a pure-frontend handoff).\n *\n * @category OAuth\n */\nexport function getAuthorizationUrl(\n config: OAuthConfig,\n options: { scopes?: readonly OAuthScope[]; state?: string } = {},\n): string {\n const baseUrl = (config.baseUrl ?? 'https://api-next.vairified.com/api/v1').replace(/\\/+$/, '');\n const scopeList = ensureProfileRead(options.scopes ?? DEFAULT_SCOPES);\n\n // Scope is space-delimited per RFC 6749 §3.3 — the deployed authorization\n // server splits on whitespace, not commas.\n const params = new URLSearchParams({\n redirect_uri: config.redirectUri,\n scope: scopeList.join(' '),\n response_type: 'code',\n });\n // `client_id` (the PartnerApp slug) identifies the app to the browser\n // authorize endpoint; the URL is rejected without it.\n if (config.clientId) {\n params.set('client_id', config.clientId);\n }\n if (options.state) {\n params.set('state', options.state);\n }\n\n return `${baseUrl}/partner/oauth/authorize?${params.toString()}`;\n}\n\n/**\n * Check whether a scope string is one the Vairified server accepts.\n *\n * @category OAuth\n */\nexport function validateScope(scope: string): scope is OAuthScope {\n return scope in SCOPES;\n}\n\n/**\n * Get a human-readable description of an OAuth scope.\n *\n * Returns `\"Unknown scope: {scope}\"` for unrecognized scopes so this\n * function is safe to call on user-supplied input.\n *\n * @category OAuth\n */\nexport function describeScope(scope: string): string {\n if (validateScope(scope)) {\n return SCOPES[scope];\n }\n return `Unknown scope: ${scope}`;\n}\n\n/**\n * Describe multiple scopes at once.\n *\n * @category OAuth\n */\nexport function describeScopes(\n scopes: readonly string[],\n): readonly { scope: string; description: string }[] {\n return scopes.map((scope) => ({\n scope,\n description: describeScope(scope),\n }));\n}\n\n/**\n * Generate a cryptographically-random CSRF state token suitable for\n * use with {@link OAuthResource.authorize}.\n *\n * Uses the Web Crypto API `crypto.getRandomValues` (built into Node 19+\n * and all modern browsers). The returned string is URL-safe, unpadded\n * base64 of 32 random bytes.\n *\n * On runtimes without Web Crypto — notably **React Native / Hermes** —\n * this throws a descriptive `Error` (rather than a bare `ReferenceError`)\n * telling you to install and import `react-native-get-random-values` at\n * your app entry, or to pass your own high-entropy `state` string. Base64\n * encoding is done in pure JS, so no `btoa` polyfill is required.\n *\n * @throws {Error} when `crypto.getRandomValues` is unavailable.\n * @category OAuth\n */\nexport function generateState(): string {\n const webCrypto = globalThis.crypto;\n if (typeof webCrypto?.getRandomValues !== 'function') {\n throw new Error(\n 'generateState() needs Web Crypto (crypto.getRandomValues), which is unavailable in ' +\n 'this runtime. Node 19+ and browsers have it built in; on React Native / Hermes, ' +\n \"install 'react-native-get-random-values' and import it once at your app entry \" +\n 'before using the SDK — or pass your own high-entropy `state` string to ' +\n 'oauth.authorize().',\n );\n }\n const bytes = new Uint8Array(32);\n webCrypto.getRandomValues(bytes);\n return base64UrlNoPad(bytes);\n}\n\n/** URL-safe base64 alphabet (RFC 4648 §5), used by {@link base64UrlNoPad}. */\nconst B64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\n/**\n * URL-safe, unpadded base64 of a byte array — implemented in pure JS so it\n * works on runtimes without `btoa` (e.g. React Native / Hermes).\n *\n * @internal\n */\nfunction base64UrlNoPad(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const hasB1 = i + 1 < bytes.length;\n const hasB2 = i + 2 < bytes.length;\n out += B64URL_ALPHABET[b0 >> 2] ?? '';\n out += B64URL_ALPHABET[((b0 & 0x03) << 4) | (b1 >> 4)] ?? '';\n if (hasB1) out += B64URL_ALPHABET[((b1 & 0x0f) << 2) | (b2 >> 6)] ?? '';\n if (hasB2) out += B64URL_ALPHABET[b2 & 0x3f] ?? '';\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers (used by resources/oauth.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure `user:profile:read` is in the scope list, prepending if necessary.\n *\n * @internal\n */\nexport function ensureProfileRead(scopes: readonly OAuthScope[]): readonly OAuthScope[] {\n if (scopes.includes('user:profile:read')) {\n return scopes;\n }\n return ['user:profile:read', ...scopes];\n}\n","/**\n * {@link OAuthResource} — OAuth 2.0 flow for player consent.\n *\n * @module\n */\n\nimport { OAuthError } from '../errors.js';\nimport type { HttpTransport } from '../http.js';\nimport {\n type AuthorizationResponse,\n DEFAULT_SCOPES,\n ensureProfileRead,\n type OAuthScope,\n SCOPES,\n type TokenResponse,\n} from '../oauth.js';\n\n/**\n * OAuth 2.0 flow for obtaining player consent.\n *\n * Typical flow:\n *\n * 1. {@link authorize} — start an authorization, get a URL to redirect\n * the player to.\n * 2. Player approves on the Vairified site and is redirected back to\n * your `redirectUri` with a `code` query parameter.\n * 3. {@link exchangeToken} — swap the code for access and refresh\n * tokens plus the player's external ID.\n * 4. Store the refresh token and call {@link refresh} when the access\n * token expires.\n * 5. {@link revoke} — disconnect a player from your app.\n *\n * @category Resources\n */\nexport class OAuthResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Start an OAuth authorization flow.\n *\n * @throws {@link OAuthError} with `errorCode: 'invalid_scope'` if a\n * requested scope is not in the accepted list.\n */\n async authorize(options: {\n redirectUri: string;\n scopes?: readonly OAuthScope[];\n state?: string;\n }): Promise<AuthorizationResponse> {\n const scopeList = ensureProfileRead(options.scopes ?? DEFAULT_SCOPES);\n\n for (const scope of scopeList) {\n if (!(scope in SCOPES)) {\n throw new OAuthError(`Invalid scope: ${scope}`, 'invalid_scope');\n }\n }\n\n const data = await this.#http.request<{\n authorization_url?: string;\n code?: string;\n }>({\n method: 'POST',\n path: '/partner/oauth/authorize',\n body: {\n redirect_uri: options.redirectUri,\n scope: scopeList.join(' '),\n state: options.state,\n },\n });\n\n return {\n authorizationUrl: data?.authorization_url ?? '',\n code: data?.code ?? '',\n state: options.state,\n };\n }\n\n /** Exchange an authorization code for access and refresh tokens. */\n async exchangeToken(options: { code: string; redirectUri: string }): Promise<TokenResponse> {\n const data = await this.#http.request<Record<string, unknown>>({\n method: 'POST',\n path: '/partner/oauth/token',\n // RFC 6749 §4.1.3 — the endpoint requires `grant_type` and the\n // snake_case `redirect_uri` (which must match the authorize request).\n body: {\n grant_type: 'authorization_code',\n code: options.code,\n redirect_uri: options.redirectUri,\n },\n });\n return tokenResponseFromWire(data);\n }\n\n /** Refresh an expired access token using a refresh token. */\n async refresh(refreshToken: string): Promise<TokenResponse> {\n const data = await this.#http.request<Record<string, unknown>>({\n method: 'POST',\n path: '/partner/oauth/refresh',\n // RFC 6749 §6 — refresh grant, snake_case body.\n body: { grant_type: 'refresh_token', refresh_token: refreshToken },\n });\n return tokenResponseFromWire(data);\n }\n\n /** Revoke a player's OAuth connection to your app. */\n async revoke(playerId: string): Promise<Record<string, unknown>> {\n const data = await this.#http.request<Record<string, unknown> | undefined>({\n method: 'POST',\n path: '/partner/oauth/revoke',\n body: { player_id: playerId },\n });\n return data ?? {};\n }\n\n /** Return the list of OAuth scopes the server currently supports. */\n async availableScopes(): Promise<readonly { name: string; description: string }[]> {\n const data = await this.#http.request<\n { scopes?: { name: string; description: string }[] } | unknown\n >({\n method: 'GET',\n path: '/partner/oauth/scopes',\n });\n if (!data || typeof data !== 'object' || Array.isArray(data)) {\n return [];\n }\n return (data as { scopes?: { name: string; description: string }[] }).scopes ?? [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction tokenResponseFromWire(data: Record<string, unknown> | undefined): TokenResponse {\n // The API returns `scope` as a space-delimited string (RFC 6749 §3.3).\n // Fall back to the deprecated `scopes` array for older API builds.\n const scopeRaw = (data?.scope ?? '') as string;\n const scopeList =\n scopeRaw.length > 0\n ? scopeRaw.split(/\\s+/).filter(Boolean)\n : Array.isArray(data?.scopes)\n ? (data.scopes as string[])\n : [];\n return {\n accessToken: (data?.access_token ?? '') as string,\n refreshToken: (data?.refresh_token ?? null) as string | null,\n expiresIn: (data?.expires_in ?? 3600) as number,\n scope: Object.freeze(scopeList),\n playerId: (data?.player_id ?? '') as string,\n };\n}\n","/**\n * {@link MemberAttribution}, {@link MembersAttributionResult} and\n * {@link AttributionResult} — ambassador referral credit.\n *\n * @module\n */\n\nimport type {\n AttributionOutcome,\n AttributionResultWire,\n MemberAttributionWire,\n MembersAttributionResultWire,\n} from '../types.js';\n\n/**\n * Who currently earns referral credit for one member.\n *\n * @category Referrals\n */\nexport class MemberAttribution {\n readonly memberId: number;\n /** True when some ambassador already holds credit for this member. */\n readonly attributed: boolean;\n /**\n * The member id of the ambassador holding the credit.\n *\n * Compare it against your own event host's member id to tell \"already credited\n * to my host — nothing to do\" from \"credited to somebody else — a person needs\n * to look, because claiming it takes credit from them\".\n *\n * `null` both when nobody holds credit and when credit is held by a record that\n * has no member id of its own, so check {@link attributed} to tell those apart.\n */\n readonly ambassadorMemberId: number | null;\n /** The date the credit was established (`YYYY-MM-DD`), or `null`. */\n readonly attributedAt: string | null;\n\n /** @internal */\n constructor(wire: MemberAttributionWire) {\n this.memberId = wire.memberId;\n this.attributed = wire.attributed;\n this.ambassadorMemberId = wire.ambassadorMemberId ?? null;\n this.attributedAt = wire.attributedAt ?? null;\n Object.freeze(this);\n }\n\n /** True when nobody holds credit yet, so this member can be claimed. */\n get isClaimable(): boolean {\n return !this.attributed;\n }\n\n /** True when credit is held by an ambassador OTHER than the one given. */\n heldBySomeoneOtherThan(ambassadorMemberId: number): boolean {\n return this.attributed && this.ambassadorMemberId !== ambassadorMemberId;\n }\n}\n\n/**\n * Attribution for a batch of members, plus the ids that matched nothing.\n *\n * @category Referrals\n */\nexport class MembersAttributionResult {\n readonly attributions: readonly MemberAttribution[];\n /**\n * Member ids that matched no member. Read this rather than diffing your input\n * against the results — every id you sent lands in one bucket or the other.\n */\n readonly notFound: readonly number[];\n\n /** @internal */\n constructor(wire: MembersAttributionResultWire) {\n this.attributions = Object.freeze(\n (wire.attributions ?? []).map((a) => new MemberAttribution(a)),\n );\n this.notFound = Object.freeze([...(wire.notFound ?? [])]);\n Object.freeze(this);\n }\n\n /** Attribution for one member id, or `undefined` if it was not returned. */\n get(memberId: number): MemberAttribution | undefined {\n return this.attributions.find((a) => a.memberId === memberId);\n }\n\n /** Members nobody holds credit for yet. */\n get claimable(): readonly MemberAttribution[] {\n return this.attributions.filter((a) => a.isClaimable);\n }\n}\n\n/**\n * Outcome of submitting attribution for a batch of members.\n *\n * @category Referrals\n */\nexport class AttributionResult {\n /** How many members were newly attributed by this request. */\n readonly attributed: number;\n /** One entry per member id supplied, in the order supplied. */\n readonly results: readonly Readonly<{ memberId: number; outcome: AttributionOutcome }>[];\n\n /** @internal */\n constructor(wire: AttributionResultWire) {\n this.attributed = wire.attributed;\n this.results = Object.freeze(\n (wire.results ?? []).map((r) => Object.freeze({ memberId: r.memberId, outcome: r.outcome })),\n );\n Object.freeze(this);\n }\n\n /** Member ids with the given outcome. */\n withOutcome(outcome: AttributionOutcome): readonly number[] {\n return this.results.filter((r) => r.outcome === outcome).map((r) => r.memberId);\n }\n\n /**\n * Members already credited to somebody. These are the ones worth a human\n * look — it may be your own host, or it may be another ambassador.\n */\n get alreadyAttributed(): readonly number[] {\n return this.withOutcome('already_attributed');\n }\n\n /**\n * Members rejected because their account pre-dates the event's registration\n * page. The event did not recruit them, so no credit is due.\n */\n get predatedEvent(): readonly number[] {\n return this.withOutcome('account_predates_event');\n }\n}\n","/**\n * {@link ReferralsResource} — read and record ambassador referral credit.\n *\n * @module\n */\n\nimport { ValidationError } from '../errors.js';\nimport type { HttpTransport } from '../http.js';\nimport { AttributionResult, MembersAttributionResult } from '../models/attribution.js';\nimport type { AttributionResultWire, MembersAttributionResultWire } from '../types.js';\n\nconst MAX_IDS_PER_READ = 100;\nconst MAX_IDS_PER_WRITE = 500;\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Read and record which ambassador earns referral credit for a player.\n *\n * Each method needs its own API-key permission, granted per partner:\n * `key:referral:read` for {@link get} and `key:referral:write` for\n * {@link attribute}. Neither is implied by a general read, write or admin key —\n * reading attribution exposes who recruited whom, and writing it decides who\n * earns commission.\n *\n * @category Resources\n */\nexport class ReferralsResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * Who currently earns referral credit for these members.\n *\n * Use this before {@link attribute} to tell the two cases apart that matter:\n * a member already credited to your own event host (nothing to do) and one\n * credited to a different ambassador (a person should look, because claiming\n * it takes credit from them).\n *\n * @example\n * ```ts\n * const result = await client.referrals.get([4873327, 4873328]);\n *\n * for (const a of result.claimable) {\n * console.log(a.memberId, 'has no credit yet');\n * }\n * console.log('no such member:', result.notFound);\n * ```\n *\n * @throws {@link ValidationError} If the list is empty or exceeds 100 ids.\n */\n async get(memberIds: readonly number[]): Promise<MembersAttributionResult> {\n if (memberIds.length === 0) {\n throw new ValidationError('At least one member id is required');\n }\n if (memberIds.length > MAX_IDS_PER_READ) {\n throw new ValidationError(`Maximum ${MAX_IDS_PER_READ} member ids per request`);\n }\n\n const wire = await this.#http.request<MembersAttributionResultWire>({\n method: 'GET',\n path: '/partner/members/attribution',\n query: { memberIds: memberIds.join(',') },\n });\n return new MembersAttributionResult({\n attributions: wire?.attributions ?? [],\n notFound: wire?.notFound ?? [],\n });\n }\n\n /**\n * Credit an ambassador for players their event recruited.\n *\n * `registrationPublishedAt` is the date the event's registration page was\n * **first published**. Accounts created before it did not come from the event\n * and are rejected with `account_predates_event`. VAIR applies that rule\n * itself, so every partner is held to the same one.\n *\n * VAIR cannot verify the date — it holds no record of your registration pages —\n * so the value you send is recorded for audit. Send the real one.\n *\n * Safe to retry: attribution is one-per-player forever, enforced by the\n * database, so a resubmitted player returns `already_attributed` and nothing\n * changes.\n *\n * @example\n * ```ts\n * const result = await client.referrals.attribute({\n * referralCode: 'hillhurst-open',\n * registrationPublishedAt: '2026-08-01',\n * memberIds: [4873327, 4873328],\n * });\n *\n * console.log(result.attributed, 'newly credited');\n * console.log('need a human:', result.alreadyAttributed);\n * console.log('too old to credit:', result.predatedEvent);\n * ```\n *\n * @throws {@link ValidationError} If the list is empty or exceeds 500 ids, or\n * the date is not `YYYY-MM-DD`.\n */\n async attribute(input: {\n referralCode: string;\n registrationPublishedAt: string;\n memberIds: readonly number[];\n }): Promise<AttributionResult> {\n if (!input.referralCode.trim()) {\n throw new ValidationError('A referral code is required');\n }\n // Checked here so a typo fails immediately rather than as a 400 the caller\n // has to interpret — and because a wrong date silently changes who is\n // creditable, which is worse than an outright rejection.\n if (!ISO_DATE.test(input.registrationPublishedAt)) {\n throw new ValidationError(\n 'registrationPublishedAt must be a calendar date formatted YYYY-MM-DD',\n );\n }\n if (input.memberIds.length === 0) {\n throw new ValidationError('At least one member id is required');\n }\n if (input.memberIds.length > MAX_IDS_PER_WRITE) {\n throw new ValidationError(`Maximum ${MAX_IDS_PER_WRITE} member ids per request`);\n }\n\n const wire = await this.#http.request<AttributionResultWire>({\n method: 'POST',\n path: '/partner/ambassador/attribution',\n body: {\n referralCode: input.referralCode.trim(),\n registrationPublishedAt: input.registrationPublishedAt,\n memberIds: [...input.memberIds],\n },\n });\n return new AttributionResult({\n attributed: wire?.attributed ?? 0,\n results: wire?.results ?? [],\n });\n }\n}\n","/**\n * {@link WebhookDelivery} and {@link WebhookDeliveriesResult} — webhook\n * delivery inspection models.\n *\n * @module\n */\n\nimport type { WebhookDeliveriesResultWire, WebhookDeliveryWire } from '../types.js';\n\n/**\n * A single webhook delivery attempt.\n *\n * @category Webhooks\n */\nexport class WebhookDelivery {\n readonly id: string;\n readonly event: string;\n readonly url: string;\n readonly statusCode: number | null;\n readonly responseBody: string | null;\n readonly errorMessage: string | null;\n readonly attempts: number;\n readonly maxAttempts: number;\n readonly lastAttemptAt: string;\n readonly nextRetryAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly payload: Readonly<Record<string, unknown>>;\n\n /** @internal */\n constructor(wire: WebhookDeliveryWire) {\n this.id = wire.id;\n this.event = wire.event;\n this.url = wire.url;\n this.statusCode = wire.statusCode;\n this.responseBody = wire.responseBody;\n this.errorMessage = wire.errorMessage;\n this.attempts = wire.attempts;\n this.maxAttempts = wire.maxAttempts;\n this.lastAttemptAt = wire.lastAttemptAt;\n this.nextRetryAt = wire.nextRetryAt;\n this.completedAt = wire.completedAt;\n this.createdAt = wire.createdAt;\n this.payload = Object.freeze({ ...wire.payload });\n Object.freeze(this);\n }\n\n /** Whether delivery completed successfully (2xx status). */\n get succeeded(): boolean {\n return (\n this.completedAt != null &&\n this.statusCode != null &&\n this.statusCode >= 200 &&\n this.statusCode < 300\n );\n }\n\n /** Whether delivery failed definitively (completed with non-2xx). */\n get failed(): boolean {\n return this.completedAt != null && !this.succeeded;\n }\n}\n\n/**\n * Paginated list of webhook delivery attempts.\n *\n * @category Webhooks\n */\nexport class WebhookDeliveriesResult {\n readonly deliveries: readonly WebhookDelivery[];\n readonly total: number;\n\n /** @internal */\n constructor(wire: WebhookDeliveriesResultWire) {\n this.deliveries = Object.freeze(wire.deliveries.map((d) => new WebhookDelivery(d)));\n this.total = wire.total;\n Object.freeze(this);\n }\n}\n","/**\n * {@link WebhooksResource} — webhook delivery inspection.\n *\n * @module\n */\n\nimport type { HttpTransport } from '../http.js';\nimport { WebhookDeliveriesResult } from '../models/webhook-delivery.js';\nimport type { WebhookDeliveriesResultWire } from '../types.js';\n\n/**\n * Webhook delivery inspection.\n *\n * @category Resources\n */\nexport class WebhooksResource {\n readonly #http: HttpTransport;\n\n /** @internal */\n constructor(http: HttpTransport) {\n this.#http = http;\n }\n\n /**\n * List recent webhook delivery attempts.\n *\n * @param options - Optional filters and pagination.\n * @param options.event - Filter by event type (e.g. `'rating.updated'`).\n * @param options.status - Filter: `'all'`, `'pending'`, `'success'`, or `'failed'`.\n * @param options.limit - Results per page (1-100, default 20).\n * @param options.offset - Pagination offset.\n * @returns {@link WebhookDeliveriesResult} with entries and total.\n * @category Webhooks\n *\n * @example\n * ```ts\n * const result = await client.webhooks.deliveries({ status: 'failed' });\n * for (const d of result.deliveries) {\n * console.log(d.event, d.statusCode, d.errorMessage);\n * }\n * ```\n */\n async deliveries(options?: {\n event?: string;\n status?: 'all' | 'pending' | 'success' | 'failed';\n limit?: number;\n offset?: number;\n }): Promise<WebhookDeliveriesResult> {\n const query: Record<string, string | number> = {};\n if (options?.event) query.event = options.event;\n if (options?.status) query.status = options.status;\n if (options?.limit != null) query.limit = options.limit;\n if (options?.offset != null) query.offset = options.offset;\n const data = await this.#http.request<WebhookDeliveriesResultWire>({\n method: 'GET',\n path: '/partner/webhook-deliveries',\n query,\n });\n return new WebhookDeliveriesResult(data);\n }\n}\n","/**\n * {@link Vairified} — the main entry point of the SDK.\n *\n * @module\n */\n\nimport { HttpTransport } from './http.js';\nimport {\n LeaderboardResource,\n MatchesResource,\n MembersResource,\n OAuthResource,\n ReferralsResource,\n WebhooksResource,\n} from './resources/index.js';\nimport type { VairifiedEnvironment, VairifiedOptions } from './types.js';\n\n/**\n * Environment preset → base URL mapping.\n *\n * Partners can switch between production, staging, and local development\n * without memorizing hostnames.\n *\n * @category Client\n */\nexport const ENVIRONMENTS: Readonly<Record<VairifiedEnvironment, string>> = Object.freeze({\n production: 'https://api-next.vairified.com/api/v1',\n staging: 'https://api-staging.vairified.com/api/v1',\n local: 'http://localhost:3001/api/v1',\n});\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Read an environment variable without assuming `process` exists.\n *\n * Node and most bundlers expose `process.env`, but React Native / Hermes\n * (and some edge runtimes) do not — touching `process.env` there can throw\n * a `ReferenceError`. This resolves to `undefined` instead of crashing, so\n * those callers simply pass `apiKey`/`env` explicitly (see the README's\n * \"React Native\" section).\n *\n * @internal\n */\nfunction readEnv(name: string): string | undefined {\n try {\n if (typeof process === 'undefined') return undefined;\n return process.env?.[name];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Async client for the Vairified Partner API.\n *\n * The client is organized around sub-resources that mirror the REST\n * structure — {@link members}, {@link matches}, {@link oauth},\n * {@link leaderboard}. Each sub-resource is a thin wrapper around the\n * HTTP transport on this object.\n *\n * ## Lifecycle\n *\n * The client holds no persistent connections itself — it's safe to\n * create one per request if you want. But for typical usage, wrap it\n * in `await using` so resources are cleaned up deterministically:\n *\n * ```ts\n * await using client = new Vairified({ apiKey: 'vair_pk_xxx' });\n *\n * const member = await client.members.get('vair_mem_xxx');\n * console.log(member.name, member.ratingFor('pickleball'));\n *\n * for await (const m of client.members.search({ city: 'Austin' })) {\n * console.log(m.name);\n * }\n * ```\n *\n * `await using` requires TypeScript 5.2+ and Node 20+. If you can't\n * use it, just call `await client.close()` manually when you're done.\n *\n * @category Client\n */\nexport class Vairified {\n /** The resolved API key this client is using. */\n readonly apiKey: string;\n /** The resolved base URL (production, staging, local, or custom). */\n readonly baseUrl: string;\n /** The resolved environment name. */\n readonly env: VairifiedEnvironment;\n /** Request timeout in milliseconds. */\n readonly timeoutMs: number;\n\n /** Member operations — get, search, find, ratingUpdates. */\n readonly members: MembersResource;\n /** Match submission — submit, testWebhook. */\n readonly matches: MatchesResource;\n /** OAuth flow — authorize, exchangeToken, refresh, revoke. */\n readonly oauth: OAuthResource;\n /** Leaderboard queries — list, rank, categories. */\n readonly leaderboard: LeaderboardResource;\n /** Webhook delivery inspection — deliveries. */\n readonly webhooks: WebhooksResource;\n /**\n * Read and record ambassador referral credit. Each method needs its own\n * per-partner permission — see {@link ReferralsResource}.\n */\n readonly referrals: ReferralsResource;\n\n readonly #transport: HttpTransport;\n\n constructor(options: VairifiedOptions = {}) {\n const apiKey = options.apiKey ?? readEnv('VAIRIFIED_API_KEY') ?? '';\n if (apiKey.length === 0) {\n throw new Error('API key required. Pass { apiKey } or set VAIRIFIED_API_KEY.');\n }\n this.apiKey = apiKey;\n\n if (options.baseUrl) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.env = options.env ?? 'production';\n } else {\n const envName = (options.env ??\n (readEnv('VAIRIFIED_ENV') as VairifiedEnvironment | undefined) ??\n 'production') as VairifiedEnvironment;\n if (options.env && !(envName in ENVIRONMENTS)) {\n throw new Error(\n `Unknown environment: ${envName}. Use one of: ${Object.keys(ENVIRONMENTS).join(', ')}`,\n );\n }\n this.env = envName;\n this.baseUrl = ENVIRONMENTS[envName] ?? ENVIRONMENTS.production;\n }\n\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n this.#transport = new HttpTransport({\n baseUrl: this.baseUrl,\n apiKey: this.apiKey,\n timeoutMs: this.timeoutMs,\n fetch: options.fetch ?? fetch,\n });\n\n this.members = new MembersResource(this.#transport);\n this.matches = new MatchesResource(this.#transport);\n this.oauth = new OAuthResource(this.#transport);\n this.leaderboard = new LeaderboardResource(this.#transport);\n this.webhooks = new WebhooksResource(this.#transport);\n this.referrals = new ReferralsResource(this.#transport);\n }\n\n /**\n * API usage statistics for the current API key.\n *\n * Returns rate-limit status, request counts, and quota usage.\n */\n async usage(): Promise<Record<string, unknown>> {\n const data = await this.#transport.request<Record<string, unknown> | undefined>({\n method: 'GET',\n path: '/partner/usage',\n });\n return data ?? {};\n }\n\n /**\n * Release any resources held by the client.\n *\n * The current transport is stateless, so this is a no-op today, but\n * partners should still call it (or use `await using`) so the SDK\n * can add connection pooling later without breaking them.\n */\n async close(): Promise<void> {\n // Nothing to release today. Kept as an explicit hook so future\n // transports (keep-alive, HTTP/2) can release their pools.\n }\n\n /**\n * Explicit resource management hook — enables\n * `await using client = new Vairified({ ... })` (TypeScript 5.2+).\n */\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n /** Compact summary for console output. */\n toString(): string {\n return `Vairified { env: '${this.env}', baseUrl: '${this.baseUrl}' }`;\n }\n}\n","/**\n * Webhook signature verification.\n *\n * Standalone on purpose — a webhook receiver is an inbound HTTP handler. It\n * usually holds no API key, may never call the Partner API, and should not\n * have to construct a client just to check a signature.\n *\n * @module\n */\n\nimport { WebhookSignatureError } from '../errors.js';\nimport type {\n ConnectionRevokedEventWire,\n EventCreatedEventWire,\n MemberStatusEventWire,\n RatingUpdatedEventWire,\n VerifiedWebhookEvent,\n} from '../types.js';\n\n/**\n * How far apart the delivery's timestamp and your clock may be, in seconds.\n *\n * Five minutes, matching the scheme this signature format follows. The window\n * is what stops a captured delivery being replayed indefinitely — without it a\n * valid signature stays valid forever.\n */\nexport const DEFAULT_TOLERANCE_SECONDS = 300;\n\nconst SIGNATURE_HEADER = 'X-Vairified-Signature';\n\n/** Options for {@link verifyWebhook}. */\nexport interface VerifyWebhookOptions {\n /**\n * Clock-skew allowance in seconds. Applied in **both** directions: a\n * delivery dated too far in the future is refused exactly as a stale one is.\n * A one-sided check would accept a forged future timestamp forever, which is\n * the replay hole the window exists to close.\n *\n * @defaultValue {@link DEFAULT_TOLERANCE_SECONDS}\n */\n toleranceSeconds?: number;\n\n /**\n * Current time in **seconds** since the epoch. Injectable for tests; you\n * should not need it in production.\n */\n nowSeconds?: number;\n}\n\nconst encoder = new TextEncoder();\n\nfunction toBytes(body: string | Uint8Array): Uint8Array {\n return typeof body === 'string' ? encoder.encode(body) : body;\n}\n\n/**\n * Parse `t=…,v1=…` into its parts.\n *\n * Deliberately tolerant of shape and strict about content: pairs may arrive in\n * any order and unknown keys are ignored, so adding a `v2=` later cannot break\n * a receiver built today. What is *not* tolerated is a missing `t` or `v1` —\n * those are the signature.\n */\nfunction parseSignatureHeader(header: string): {\n timestamp: number;\n /**\n * The `t` value exactly as transmitted. The HMAC is built from THIS, not\n * from the re-stringified number: `t=01758700000` and `t=1758700000` are\n * different bytes, and signing the normalised form would let both verify\n * against one digest. No exploit is known today — the window check and the\n * HMAC read the same value either way — but it is a split between \"what was\n * checked\" and \"what was signed\", and the moment anything starts reading the\n * raw `t` it becomes live.\n */\n rawTimestamp: string;\n signature: string;\n} {\n let timestamp: number | null = null;\n let rawTimestamp: string | null = null;\n let signature: string | null = null;\n\n for (const part of header.split(',')) {\n const eq = part.indexOf('=');\n if (eq === -1) continue;\n const key = part.slice(0, eq).trim();\n const value = part.slice(eq + 1).trim();\n if (key === 't' && timestamp === null) {\n // Reject anything that is not a plain integer. `Number()` would happily\n // accept '1e9', ' 12 ' and '0x10'.\n if (!/^\\d+$/.test(value)) {\n throw new WebhookSignatureError(\n 'malformed_signature',\n `${SIGNATURE_HEADER} carried a timestamp that is not an integer`,\n );\n }\n timestamp = Number(value);\n rawTimestamp = value;\n } else if (key === 'v1' && signature === null) {\n signature = value;\n }\n }\n\n if (timestamp === null || rawTimestamp === null || signature === null) {\n throw new WebhookSignatureError(\n 'malformed_signature',\n `${SIGNATURE_HEADER} must carry both a 't' and a 'v1' part`,\n );\n }\n return { timestamp, rawTimestamp, signature };\n}\n\n/**\n * Decode a hex digest to bytes.\n *\n * Case-insensitive by construction. The API emits lowercase today, and a\n * case-sensitive string comparison would be correct right up until it wasn't.\n */\n// The `<ArrayBuffer>` argument is load-bearing, not decoration: a bare\n// `Uint8Array` widens to `Uint8Array<ArrayBufferLike>`, which WebCrypto's\n// `BufferSource` rejects because a SharedArrayBuffer-backed view could be\n// mutated by another thread mid-verify.\n/** A SHA-256 digest is 32 bytes; nothing longer can ever match one. */\nconst MAX_SIGNATURE_HEX = 64;\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> {\n // Length-capped before the regex and the allocation. Without it an attacker\n // who knows the webhook URL can make us decode a multi-megabyte `v1` on\n // every request — bounded in practice only by the HTTP server's header\n // limit, which a partner is free to raise.\n if (hex.length > MAX_SIGNATURE_HEX) {\n throw new WebhookSignatureError(\n 'malformed_signature',\n `${SIGNATURE_HEADER} carried a 'v1' value longer than a SHA-256 digest`,\n );\n }\n if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) {\n throw new WebhookSignatureError(\n 'malformed_signature',\n `${SIGNATURE_HEADER} carried a 'v1' value that is not a hex digest`,\n );\n }\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\nfunction assertEnvelope(value: unknown): asserts value is VerifiedWebhookEvent {\n if (typeof value !== 'object' || value === null) {\n throw new WebhookSignatureError('malformed_body', 'The webhook body is not a JSON object');\n }\n const o = value as Record<string, unknown>;\n for (const field of ['event', 'eventId', 'timestamp'] as const) {\n if (typeof o[field] !== 'string') {\n throw new WebhookSignatureError(\n 'malformed_body',\n `The webhook body is missing a string '${field}'`,\n );\n }\n }\n}\n\n/**\n * Validate the fields a partner gates access on. Nothing else.\n *\n * :rotating_light: **This list is the WHOLE validated surface, and its shortness\n * is the design**:\n *\n * - every event: `event`, `eventId`, `timestamp` (checked by the envelope guard)\n * - `member.status`: `memberId`, `isVairPlus`, `isAmbassador`\n * - `rating.updated`: `memberId`, `sequence`\n * - `connection.revoked`, `event.created`: nothing beyond the envelope\n *\n * Everything else is handed over exactly as it arrived — including anything\n * nested, at any depth.\n *\n * **Why so little.** Three review rounds found divergences between this SDK and\n * the Python one, and **every single one was in validation; none was in\n * signature verification.** Refusing a whole delivery because an informational\n * field was the wrong type protects nobody and costs everybody: a rejection is\n * retried ~13 times over ~3.4 h and then **dropped**, so the member's status\n * silently stops updating at that partner. Handing over an odd field costs one\n * bad value. The asymmetry decides it.\n *\n * **Why these four.** `isVairPlus` and `isAmbassador` are what a partner gates\n * entry on, and a missing one read as a quiet `false` denies someone who paid.\n * `memberId` says who the event is about. `sequence` is how a stale rating is\n * discarded, and getting it wrong is silent and permanent.\n *\n * Still never a VALUE — only presence and type. Enum-shaped fields grow on the\n * API's schedule.\n *\n * :rotating_light: **Checks presence and type. Never a VALUE.** An unfamiliar\n * `vairProStatus`, `reason` or any other enum-shaped string must pass — those\n * sets grow on the API's schedule, not this package's, and rejecting a new one\n * would break a live handler for a change that is not a break. What is checked\n * is that a field the caller will read is actually there and is the type they\n * will read it as.\n *\n * The alternative — filling a missing field with a default — is worse than\n * throwing here. These fields gate entitlement: a missing `isVairPlus` quietly\n * read as `false` denies entry to a member who paid, and nobody ever traces it.\n */\nfunction assertKnownEventShape(event: VerifiedWebhookEvent): void {\n const bad = (field: string): never => {\n throw new WebhookSignatureError(\n 'malformed_body',\n `A '${event.event}' event is missing a valid '${field}'`,\n );\n };\n\n const data = event.data as Record<string, unknown> | undefined;\n if (typeof data !== 'object' || data === null) {\n if (event.event === 'member.status' || event.event === 'rating.updated') bad('data');\n return;\n }\n\n if (event.event === 'member.status') {\n if (typeof data.memberId !== 'number') bad('data.memberId');\n if (typeof data.isVairPlus !== 'boolean') bad('data.isVairPlus');\n if (typeof data.isAmbassador !== 'boolean') bad('data.isAmbassador');\n } else if (event.event === 'rating.updated') {\n if (typeof data.memberId !== 'number') bad('data.memberId');\n // Without it a partner cannot discard a stale snapshot, and applying an\n // older one last leaves them holding a rating the member no longer has.\n if (typeof data.sequence !== 'string') bad('data.sequence');\n }\n}\n\n/**\n * Verify a webhook delivery and return it typed.\n *\n * @param rawBody - The **exact bytes** of the request body. This is the part\n * people get wrong: the signature covers what was sent, so a body that has\n * been parsed and re-serialised (`express.json()`, FastAPI's parsed body)\n * produces different bytes and every signature fails. Capture the raw body.\n * @param signatureHeader - The `X-Vairified-Signature` header value.\n * @param secret - Your webhook signing secret, or **several**. Pass both the\n * old and the new around a rotation: deliveries already queued were signed\n * with the old secret and keep arriving for hours afterwards, so a verifier\n * that knows only the new one discards them.\n * @param options - See {@link VerifyWebhookOptions}.\n * @returns The verified event. Narrow on `event` to reach the typed members.\n * @throws {@link WebhookSignatureError} for every refusal; read `reason`.\n *\n * @remarks\n * :rotating_light: **Deduplicate on the BODY's `eventId`, never on the\n * `X-Vairified-Event-Id` header.** Delivery is at-least-once, so a retry can\n * present the same event twice — but that header is **outside the signature**.\n * An attacker who captures one delivery can replay it inside the tolerance\n * window with a fresh header value, and header-based deduplication will let it\n * through every time. The body's `eventId` is covered by the HMAC; the header\n * is a convenience for routing, not an identity you can trust.\n *\n * Replay inside the window is otherwise unprevented by design: the timestamp\n * tolerance is the only replay control this function applies, so your own\n * deduplication is what stops a captured delivery being applied twice.\n *\n * :rotating_light: **`await` it.** It is async because it uses Web Crypto, which\n * has no synchronous HMAC. Forgetting the `await` on a *valid* signature hands\n * your code a Promise whose `.data` is `undefined` — an entitlement check then\n * reads falsy and denies a member who paid, silently. On an *invalid* one it is\n * an unhandled rejection, which on Node >=24 exits the process. This function\n * never returns a boolean, precisely so that mistake cannot be quiet.\n *\n * @example\n * ```ts\n * import { verifyWebhook, isMemberStatusEvent, WebhookSignatureError } from 'vairified';\n *\n * app.post('/hooks/vair', express.raw({ type: 'application/json' }), async (req, res) => {\n * try {\n * const event = await verifyWebhook(\n * req.body, // a Buffer — the raw bytes\n * req.header('X-Vairified-Signature') ?? '',\n * [process.env.VAIR_WEBHOOK_SECRET, process.env.VAIR_WEBHOOK_SECRET_PREVIOUS],\n * );\n * // Narrow with the exported guards — `event.event === '…'` cannot narrow\n * // while the unknown-event fallback is part of the union.\n * if (isMemberStatusEvent(event)) {\n * console.log(event.data.memberId, event.data.isVairPlus);\n * }\n * res.sendStatus(204);\n * } catch (err) {\n * if (err instanceof WebhookSignatureError) return res.sendStatus(400);\n * throw err;\n * }\n * });\n * ```\n *\n * @category Webhooks\n */\nexport async function verifyWebhook(\n rawBody: string | Uint8Array,\n signatureHeader: string | null | undefined,\n // Deliberately tolerant of `undefined` entries rather than demanding\n // `string`: in practice this is fed straight from `process.env`, and forcing\n // a `!` at the call site is what turned an unset variable into a crash\n // instead of a refusal. Absent values are filtered and reported as\n // `no_secret_configured`.\n secret: string | undefined | null | readonly (string | undefined | null)[],\n options: VerifyWebhookOptions = {},\n): Promise<VerifiedWebhookEvent> {\n // Validated BEFORE anything else, and never allowed to escape as a\n // `TypeError`. The common single-secret call is\n // `verifyWebhook(body, sig, process.env.VAIR_WEBHOOK_SECRET!)`; with that\n // variable unset the `!` is a lie and `undefined` arrives here. Reaching\n // `.filter` on it threw a raw TypeError, which this function's own\n // documented handler rethrows — an unhandled rejection, and on Node >=24 a\n // process exit, reachable by anyone who can POST to the endpoint.\n const candidates = typeof secret === 'string' ? [secret] : Array.isArray(secret) ? secret : [];\n // `trim()` first: a whitespace-only secret passes a bare length check and is\n // then reported as a signature mismatch, i.e. as an attack, when it is a\n // misconfiguration.\n const secrets = candidates.filter(\n (s): s is string => typeof s === 'string' && s.trim().length > 0,\n );\n if (secrets.length === 0) {\n throw new WebhookSignatureError(\n 'no_secret_configured',\n 'No usable signing secret was supplied',\n );\n }\n\n if (!signatureHeader) {\n throw new WebhookSignatureError(\n 'missing_signature',\n `No ${SIGNATURE_HEADER} header was supplied`,\n );\n }\n\n // A non-finite window disables the check entirely, because every comparison\n // against NaN is false. `Number(process.env.SOMETHING_UNSET)` is NaN, so the\n // obvious way to make the tolerance configurable silently removes replay\n // protection and nothing warns. Refuse instead of falling back to a default:\n // a caller who passed a bad value should find out, not be quietly corrected.\n const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n if (!Number.isFinite(tolerance) || tolerance < 0) {\n throw new WebhookSignatureError(\n 'invalid_option',\n 'toleranceSeconds must be a finite, non-negative number',\n );\n }\n if (!Number.isFinite(now)) {\n throw new WebhookSignatureError('invalid_option', 'nowSeconds must be a finite number');\n }\n\n const { timestamp, rawTimestamp, signature } = parseSignatureHeader(signatureHeader);\n\n // Window check BEFORE the hex decode: it is the cheap test, and doing it\n // first means a garbage `v1` never reaches an allocation.\n // Two-sided — a future-dated delivery is as refused as a stale one.\n if (Math.abs(now - timestamp) > tolerance) {\n throw new WebhookSignatureError(\n 'timestamp_out_of_tolerance',\n `The delivery timestamp is outside the ${tolerance}s tolerance`,\n );\n }\n\n const signatureBytes = hexToBytes(signature);\n\n const bodyBytes = toBytes(rawBody);\n const prefix = encoder.encode(`${rawTimestamp}.`);\n const signed = new Uint8Array(prefix.length + bodyBytes.length);\n signed.set(prefix, 0);\n signed.set(bodyBytes, prefix.length);\n\n // `crypto.subtle.verify` does the comparison inside the primitive, so there\n // is no digest equality check to get wrong. WebCrypto rather than\n // `node:crypto` because this package imports no Node builtins and runs on\n // every runtime with a Web Crypto implementation.\n let matched = false;\n for (const candidate of secrets) {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(candidate),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['verify'],\n );\n // Deliberately no early exit: every supplied secret is tried.\n matched = (await crypto.subtle.verify('HMAC', key, signatureBytes, signed)) || matched;\n }\n\n if (!matched) {\n throw new WebhookSignatureError(\n 'signature_mismatch',\n 'The webhook signature did not match any supplied secret',\n );\n }\n\n // :rotating_light: Parse the bytes that were SIGNED, not the caller's buffer.\n //\n // `signed` is a private copy taken before any `await`; `bodyBytes` may be a\n // live view into the caller's memory. Two awaits happen above, so a server\n // that recycles its read buffer — uWebSockets.js documents that its\n // ArrayBuffer is only valid for the duration of the callback — can have the\n // arena reused while we are still verifying. Decoding `bodyBytes` here meant\n // verifying one payload and handing back another: measured, a signature\n // verified over `isVairPlus:true` returned `isVairPlus:false` and a\n // different member. A verifier that returns unsigned data is not a verifier.\n let parsed: unknown;\n try {\n // `fatal` and `ignoreBOM` are both load-bearing. The default decoder\n // strips a UTF-8 BOM and substitutes U+FFFD for invalid bytes, so a\n // verifier would hand back text that is NOT what the signed bytes said —\n // characters quietly replaced inside verified data. Python's decode raises\n // on both, so without this the two SDKs disagree on what is even a valid\n // body.\n parsed = JSON.parse(\n new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(\n signed.subarray(prefix.length),\n ),\n );\n } catch {\n throw new WebhookSignatureError('malformed_body', 'The webhook body is not valid JSON');\n }\n\n assertEnvelope(parsed);\n assertKnownEventShape(parsed);\n // `readonly` is erased at runtime, so without this the event only *looks*\n // immutable — while every other model in this SDK is genuinely frozen and the\n // Python models raise on assignment. A partner who mutates the event in one\n // handler and reads it in another should get the same guarantee in both\n // languages, not one that disappears when the types do.\n return deepFreeze(parsed);\n}\n\n/** Freeze an object and everything reachable from it. */\nfunction deepFreeze<T>(value: T): T {\n if (value === null || typeof value !== 'object' || Object.isFrozen(value)) return value;\n Object.freeze(value);\n for (const key of Object.getOwnPropertyNames(value)) {\n deepFreeze((value as Record<string, unknown>)[key]);\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n//\n// `VerifiedWebhookEvent` cannot discriminate on `event` alone, and that is not\n// an oversight in the union — it is unavoidable while a catch-all is a member.\n// `UnknownWebhookEventWire.event` is `string`, which is comparable to every\n// literal, so it survives `e.event === 'member.status'` and collapses `data`\n// to `unknown`. A branded discriminant was measured and does not help either.\n//\n// So narrowing is done with guards. The alternative — telling partners to cast\n// — would publish an unchecked cast on webhook data as the documented pattern,\n// which is exactly what the malformed-event guard exists to make unnecessary.\n\n/**\n * Narrow a verified event to `member.status`.\n *\n * @example\n * ```ts\n * const event = await verifyWebhook(rawBody, header, secret);\n * if (isMemberStatusEvent(event)) {\n * // `event.data` is fully typed here.\n * if (!event.data.isVairPlus) denyEntry(event.data.memberId);\n * }\n * ```\n *\n * @category Webhooks\n */\nexport function isMemberStatusEvent(event: VerifiedWebhookEvent): event is MemberStatusEventWire {\n return event.event === 'member.status';\n}\n\n/**\n * Narrow a verified event to `connection.revoked`.\n *\n * :warning: `data.reason` is an open set — `player_deleted` and\n * `player_disconnected` today, more later. Never branch on it exhaustively.\n *\n * @category Webhooks\n */\nexport function isConnectionRevokedEvent(\n event: VerifiedWebhookEvent,\n): event is ConnectionRevokedEventWire {\n return event.event === 'connection.revoked';\n}\n\n/**\n * Narrow a verified event to `rating.updated` — the event that makes up\n * almost all real traffic.\n *\n * :rotating_light: **`data` is NOT the shape {@link MembersResource.ratingUpdates}\n * returns when polling.** The webhook has carried a full multi-sport snapshot\n * since Vairified#899 and the two have not matched since — there is no\n * `previousRating`, `newRating` or `ratingSplits` on it. Reusing a polling\n * handler here reads fields that will never be present. See\n * {@link RatingUpdatedEventDataWire}.\n *\n * @category Webhooks\n */\nexport function isRatingUpdatedEvent(event: VerifiedWebhookEvent): event is RatingUpdatedEventWire {\n return event.event === 'rating.updated';\n}\n\n/**\n * Narrow a verified event to `event.created`.\n *\n * :warning: `data.eventId` is a **number** and is not the envelope's string\n * `eventId`. Deduplicate on the envelope's; refer to the event by `data`'s.\n *\n * @category Webhooks\n */\nexport function isEventCreatedEvent(event: VerifiedWebhookEvent): event is EventCreatedEventWire {\n return event.event === 'event.created';\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — shipped instead of documented\n// ---------------------------------------------------------------------------\n//\n// Everything below replaces an instruction this SDK used to give partners.\n// The reason is one defect: the docs said \"keep the highest `sequence` and\n// discard anything lower\", and `sequence` is an unpadded decimal string, so the\n// obvious implementation in either language is a string comparison —\n// `'10000000' > '9999999'` is `false`. At every power-of-ten crossing a partner\n// following our own documentation would discard every later delivery for that\n// member, permanently, with no error anywhere. The code was right; the\n// instruction was wrong, and no test of ours could have caught it.\n//\n// So: wherever we would tell a partner to implement something, we provide it.\n\n/**\n * Compare two `sequence` values as integers.\n *\n * @returns negative if `a` is older, `0` if equal, positive if `a` is newer.\n * @category Webhooks\n */\nexport function compareSequence(a: string, b: string): number {\n const x = BigInt(a);\n const y = BigInt(b);\n return x < y ? -1 : x > y ? 1 : 0;\n}\n\n/**\n * Whether an incoming `sequence` is newer than the last one you applied **for\n * that same member**.\n *\n * Use it to discard stale deliveries. `rating.updated` carries a full snapshot\n * rather than a diff, so applying an older one last leaves you holding a rating\n * the member no longer has.\n *\n * ```ts\n * if (isRatingUpdatedEvent(event)) {\n * const last = await store.get(event.data.memberId);\n * if (last && !isNewerSequence(event.data.sequence, last)) return; // stale\n * await store.put(event.data.memberId, event.data.sequence);\n * }\n * ```\n *\n * @param incoming - the delivery's `sequence`.\n * @param lastApplied - the highest you have applied for that member, or\n * `null`/`undefined` if you have applied none — in which case this is `true`.\n * @category Webhooks\n */\nexport function isNewerSequence(incoming: string, lastApplied?: string | null): boolean {\n if (lastApplied === undefined || lastApplied === null || lastApplied === '') return true;\n return compareSequence(incoming, lastApplied) > 0;\n}\n\n/**\n * The key to deduplicate a delivery on.\n *\n * Delivery is at-least-once, so a retry can present the same event twice. This\n * returns the id **from the signed body**.\n *\n * :rotating_light: Calling this is the point. The `X-Vairified-Event-Id` header\n * carries the same value and is **not covered by the signature**, so an attacker\n * replaying a captured delivery inside the tolerance window can change it freely\n * and header-based deduplication lets it through every time.\n *\n * @category Webhooks\n */\nexport function dedupeKey(event: VerifiedWebhookEvent): string {\n return event.eventId;\n}\n"],"mappings":"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","names":[]}
|