vairified 0.1.0 → 0.2.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 +250 -256
- package/dist/index.cjs +766 -819
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +821 -780
- package/dist/index.d.ts +821 -780
- package/dist/index.js +758 -813
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/models.ts","../src/oauth.ts","../src/client.ts"],"sourcesContent":["/**\n * Vairified JavaScript SDK\n *\n * Official TypeScript/JavaScript SDK for the Vairified Partner API.\n *\n * Features:\n * - Opaque external IDs (vair_mem_xxx format) for privacy\n * - OAuth-based player consent for data access\n * - Tiered access: public search vs connected member data\n *\n * @packageDocumentation\n * @module vairified\n */\n\nexport { Vairified, type VairifiedEnvironment } from './client.js';\nexport {\n AuthenticationError,\n NotFoundError,\n OAuthError,\n RateLimitError,\n VairifiedError,\n ValidationError,\n} from './errors.js';\nexport {\n Match,\n MatchResult,\n Member,\n Player,\n RatingSplit,\n RatingSplits,\n RatingUpdate,\n SearchResults,\n} from './models.js';\nexport {\n type AuthorizationResponse,\n DEFAULT_SCOPES,\n describeScope,\n describeScopes,\n generateState,\n getAuthorizationUrl,\n type OAuthConfig,\n type OAuthScope,\n SCOPES,\n type TokenResponse,\n validateScope,\n} from './oauth.js';\nexport type {\n MatchApiData,\n MatchInput,\n MatchResultData,\n MemberData,\n PlayerSearchData,\n RatingSplitData,\n RatingSplitsData,\n RatingUpdateData,\n SearchFilters,\n SearchResultsData,\n VairifiedOptions,\n} from './types.js';\n","/**\n * Vairified SDK Errors\n *\n * @module\n */\n\n/**\n * Base error class for Vairified SDK errors.\n *\n * @category Errors\n */\nexport class VairifiedError extends Error {\n /** HTTP status code */\n statusCode?: number;\n /** Response body */\n 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 * Error thrown when API rate limit is exceeded.\n *\n * @category Errors\n */\nexport class RateLimitError extends VairifiedError {\n /** Seconds to wait before retrying */\n 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 * Error thrown when API key is invalid or missing.\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 * Error thrown when a requested resource is not found.\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 * Error thrown when request validation fails.\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 * Error thrown when an OAuth operation fails.\n *\n * This can occur during authorization, token exchange, refresh, or revocation.\n *\n * @category Errors\n */\nexport class OAuthError extends VairifiedError {\n /** OAuth error code (e.g., 'invalid_grant', 'expired_token') */\n 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 * Vairified SDK Models\n *\n * Rich model classes with methods for easy API interaction.\n *\n * @module\n */\n\nimport type { Vairified } from './client.js';\nimport type {\n MatchApiData,\n MatchInput,\n MatchResultData,\n MemberData,\n PlayerSearchData,\n RatingSplitData,\n RatingSplitsData,\n RatingUpdateData,\n SearchFilters,\n SearchResultsData,\n} from './types.js';\n\n/** Union type for player data from different endpoints */\ntype PlayerData = MemberData | PlayerSearchData;\n\n/**\n * A single rating split with metadata.\n *\n * @category Models\n */\nexport class RatingSplit {\n /** The rating value */\n readonly rating: number;\n /** Abbreviation (e.g., \"VG\", \"50+\") */\n readonly abbr: string;\n /** Date of last match in this category */\n readonly datePlayed?: string;\n\n constructor(data: RatingSplitData | number) {\n if (typeof data === 'number') {\n this.rating = data;\n this.abbr = '';\n } else {\n const ratingVal = data.rating;\n this.rating = typeof ratingVal === 'string' ? Number.parseFloat(ratingVal) || 0 : ratingVal;\n this.abbr = data.abbr;\n this.datePlayed = data.date_played;\n }\n }\n}\n\n/**\n * Rating breakdown by category.\n *\n * Access ratings by category name or use convenience properties.\n *\n * @category Models\n */\nexport class RatingSplits {\n /** Map of category names to rating splits */\n readonly splits: Map<string, RatingSplit>;\n\n constructor(data?: RatingSplitsData) {\n this.splits = new Map();\n if (data) {\n for (const [key, value] of Object.entries(data)) {\n this.splits.set(key, new RatingSplit(value));\n }\n }\n }\n\n /** Get rating for a category */\n get(category: string): number | undefined {\n return this.splits.get(category)?.rating;\n }\n\n /** Open division rating */\n get open(): number | undefined {\n return this.get('open') ?? this.get('VO');\n }\n\n /** Gender-specific rating (same gender doubles) */\n get gender(): number | undefined {\n return this.get('gender') ?? this.get('VG');\n }\n\n /** Mixed doubles rating */\n get mixed(): number | undefined {\n return this.get('mixed') ?? this.get('VM');\n }\n\n /** Recreational rating */\n get recreational(): number | undefined {\n return this.get('recreational') ?? this.get('R');\n }\n\n /** Singles rating */\n get singles(): number | undefined {\n return this.get('singles') ?? this.get('S');\n }\n\n /** Best available verified rating */\n get best(): number | undefined {\n const ratings = Array.from(this.splits.values())\n .map((s) => s.rating)\n .filter((r) => r > 0);\n return ratings.length > 0 ? Math.max(...ratings) : undefined;\n }\n\n /** Convert to plain object */\n toJSON(): Record<string, { rating: number; abbr: string }> {\n const result: Record<string, { rating: number; abbr: string }> = {};\n for (const [key, split] of this.splits) {\n result[key] = { rating: split.rating, abbr: split.abbr };\n }\n return result;\n }\n}\n\n/**\n * Check if data is from search endpoint (has displayName)\n */\nfunction isSearchData(data: PlayerData): data is PlayerSearchData {\n return 'displayName' in data;\n}\n\n/**\n * A player in the Vairified system.\n *\n * From public search, only limited data is available (display name, location, rating).\n * For full profile data, use getMember() with OAuth consent.\n *\n * @category Models\n */\nexport class Player {\n /** External player ID (vair_mem_xxx format) */\n readonly id: string;\n /** Display name (First Name + Last Initial from search) */\n readonly displayName?: string;\n /** First name (only from connected member) */\n readonly firstName?: string;\n /** Last name (only from connected member) */\n readonly lastName?: string;\n /** Primary/overall rating (2.0-8.0) */\n readonly rating: number;\n /** Whether player is verified */\n readonly isVairified: boolean;\n /** Whether player has connected to your app */\n readonly isConnected: boolean;\n /** Ratings by category (only from connected member) */\n readonly ratingSplits: RatingSplits;\n /** City */\n readonly city?: string;\n /** State code */\n readonly state?: string;\n /** Country code */\n readonly country?: string;\n\n protected _client?: Vairified;\n\n constructor(data: PlayerData, client?: Vairified) {\n if (isSearchData(data)) {\n // Search format (limited data)\n this.id = data.id;\n this.displayName = data.displayName;\n this.rating = data.rating ?? 0;\n this.isVairified = data.isVairified ?? false;\n this.isConnected = data.isConnected ?? false;\n this.ratingSplits = new RatingSplits();\n } else {\n // Member format (full data)\n this.id = data.id;\n this.firstName = data.firstName ?? '';\n this.lastName = data.lastName ?? '';\n this.rating = data.rating ?? 0;\n this.isVairified = data.isVairified ?? false;\n this.isConnected = true; // If we have member data, they're connected\n this.ratingSplits = new RatingSplits(data.ratingSplits);\n }\n\n this.city = data.city;\n this.state = data.state;\n this.country = data.country;\n this._client = client;\n }\n\n /** Full name (or display name if full name not available) */\n get name(): string {\n if (this.firstName && this.lastName) {\n return `${this.firstName} ${this.lastName}`.trim();\n }\n return this.displayName ?? '';\n }\n\n /** Best verified rating */\n get verifiedRating(): number | undefined {\n return this.ratingSplits.best;\n }\n\n toString(): string {\n const verified = this.isVairified ? ' ✓' : '';\n return `${this.name} (${this.rating.toFixed(2)})${verified}`;\n }\n}\n\n/**\n * A member with full profile access (requires OAuth connection).\n *\n * Only accessible for players who have connected their account via OAuth.\n *\n * @category Models\n */\nexport class Member extends Player {\n /** Email address (only if profile:email scope granted) */\n readonly email?: string;\n /** Scopes the player granted to your app */\n readonly grantedScopes: string[];\n\n constructor(data: MemberData, client?: Vairified) {\n super(data, client);\n this.email = data.email;\n this.grantedScopes = data.grantedScopes ?? [];\n }\n\n /** Check if the player has granted a specific scope */\n hasScope(scope: string): boolean {\n return this.grantedScopes.includes(scope);\n }\n\n /** Refresh member data from API */\n async refresh(): Promise<Member> {\n if (!this._client) {\n throw new Error('Member not connected to client');\n }\n const updated = await this._client.getMember(this.id);\n Object.assign(this, updated);\n return this;\n }\n}\n\n/**\n * Generate a unique identifier for matches.\n */\nfunction generateId(): string {\n return `SDK-${Math.random().toString(36).substring(2, 14)}`;\n}\n\n/**\n * A match to submit to the Vairified Partner API.\n *\n * @category Models\n *\n * @example\n * ```ts\n * // Doubles match: 11-9, 11-7\n * const match = new Match({\n * event: 'Weekly League',\n * bracket: '4.0 Doubles',\n * date: new Date(),\n * team1: ['player1_id', 'player2_id'],\n * team2: ['player3_id', 'player4_id'],\n * scores: [[11, 9], [11, 7]],\n * });\n *\n * // Singles match: 11-8, 9-11, 11-6\n * const match = new Match({\n * event: 'Club Singles',\n * bracket: 'Open Singles',\n * date: new Date(),\n * team1: ['player1_id'],\n * team2: ['player2_id'],\n * scores: [[11, 8], [9, 11], [11, 6]],\n * });\n * ```\n */\nexport class Match {\n /** Event/tournament name */\n readonly event: string;\n /** Bracket/division name */\n readonly bracket: string;\n /** Match date */\n readonly date: Date;\n /** Team 1 player IDs */\n readonly team1: readonly string[];\n /** Team 2 player IDs */\n readonly team2: readonly string[];\n /** Game scores */\n readonly scores: readonly [number, number][];\n /** Match type */\n readonly matchType: string;\n /** Match source */\n readonly source: string;\n /** Location */\n readonly location?: string;\n /** Unique identifier */\n readonly identifier: string;\n /** Match ID (set after submission) */\n id?: string;\n\n constructor(data: MatchInput) {\n this.event = data.event;\n this.bracket = data.bracket;\n this.date = data.date instanceof Date ? data.date : new Date(data.date);\n this.team1 = data.team1;\n this.team2 = data.team2;\n this.scores = data.scores;\n this.matchType = data.matchType ?? 'SIDEOUT';\n this.source = data.source ?? 'PARTNER';\n this.location = data.location;\n this.identifier = data.identifier ?? generateId();\n }\n\n /** Match format: SINGLES or DOUBLES */\n get format(): 'SINGLES' | 'DOUBLES' {\n return this.team1.length === 1 ? 'SINGLES' : 'DOUBLES';\n }\n\n /** Team that won (1 or 2). Returns 0 if tie. */\n get winner(): 0 | 1 | 2 {\n let t1Wins = 0;\n let t2Wins = 0;\n for (const [s1, s2] of this.scores) {\n if (s1 > s2) t1Wins++;\n else if (s2 > s1) t2Wins++;\n }\n if (t1Wins > t2Wins) return 1;\n if (t2Wins > t1Wins) return 2;\n return 0;\n }\n\n /** Score summary like \"11-9, 11-7\" */\n get scoreSummary(): string {\n return this.scores.map(([s1, s2]) => `${s1}-${s2}`).join(', ');\n }\n\n /** Convert to API request format */\n toJSON(): MatchApiData {\n const player1A = this.team1[0];\n const player1B = this.team2[0];\n if (!player1A || !player1B) {\n throw new Error('Match must have at least one player per team');\n }\n\n const teamA: MatchApiData['teamA'] = { player1: player1A };\n const teamB: MatchApiData['teamB'] = { player1: player1B };\n\n if (this.team1[1]) teamA.player2 = this.team1[1];\n if (this.team2[1]) teamB.player2 = this.team2[1];\n\n // Add game scores to teams\n const gameKeys = ['game1', 'game2', 'game3', 'game4', 'game5'] as const;\n for (let i = 0; i < Math.min(this.scores.length, 5); i++) {\n const score = this.scores[i];\n const key = gameKeys[i];\n if (score && key) {\n teamA[key] = score[0];\n teamB[key] = score[1];\n }\n }\n\n return {\n identifier: this.identifier,\n bracket: this.bracket,\n event: this.event,\n format: this.format,\n matchDate: this.date.toISOString(),\n matchSource: this.source,\n matchType: this.matchType,\n location: this.location,\n teamA,\n teamB,\n };\n }\n}\n\n/**\n * Result of a match submission.\n *\n * @category Models\n */\nexport class MatchResult {\n /** Whether submission succeeded */\n readonly success: boolean;\n /** Number of matches processed */\n readonly numMatches: number;\n /** Number of games recorded */\n readonly numGames: number;\n /** Whether this was a dry-run (validation only) */\n readonly dryRun: boolean;\n /** Human-readable result message */\n readonly message?: string;\n /** List of validation/processing errors */\n readonly errors: string[];\n\n constructor(data: MatchResultData) {\n this.success = data.success;\n this.numMatches = data.numMatches;\n this.numGames = data.numGames;\n this.dryRun = data.dryRun ?? false;\n this.message = data.message;\n this.errors = data.errors ?? [];\n }\n\n /** Alias for dryRun */\n get isDryRun(): boolean {\n return this.dryRun;\n }\n\n /** Returns true if submission succeeded without errors */\n get ok(): boolean {\n return this.success && this.errors.length === 0;\n }\n}\n\n/**\n * A rating change notification.\n *\n * @category Models\n */\nexport class RatingUpdate {\n /** External player ID (vair_mem_xxx format) */\n readonly id: string;\n /** Member name */\n readonly memberName?: string;\n /** Previous rating */\n readonly previousRating: number;\n /** New rating */\n readonly newRating: number;\n /** When the change occurred */\n readonly changedAt: Date;\n /** Updated rating splits */\n readonly ratingSplits: RatingSplits;\n\n private _client?: Vairified;\n\n constructor(data: RatingUpdateData, client?: Vairified) {\n this.id = data.id;\n this.memberName = data.memberName;\n this.previousRating = data.previousRating ?? 0;\n this.newRating = data.newRating ?? 0;\n this.changedAt = data.changedAt ? new Date(data.changedAt) : new Date();\n this.ratingSplits = new RatingSplits(data.ratingSplits);\n this._client = client;\n }\n\n /** Amount of rating change */\n get change(): number {\n return this.newRating - this.previousRating;\n }\n\n /** Whether rating improved */\n get improved(): boolean {\n return this.change > 0;\n }\n\n /** Fetch the member associated with this update */\n async getMember(): Promise<Member> {\n if (!this._client) {\n throw new Error('Update not connected to client');\n }\n return this._client.getMember(this.id);\n }\n\n toString(): string {\n const direction = this.improved ? '↑' : '↓';\n const name = this.memberName ? ` (${this.memberName})` : '';\n return `${this.id}${name}: ${this.previousRating.toFixed(2)} ${direction} ${this.newRating.toFixed(2)}`;\n }\n}\n\n/**\n * Paginated search results.\n *\n * @category Models\n */\nexport class SearchResults implements Iterable<Player> {\n /** List of players */\n readonly players: Player[];\n /** Total matching players */\n readonly total: number;\n /** Current page */\n readonly page: number;\n /** Results per page */\n readonly limit: number;\n\n private _client?: Vairified;\n private _filters: SearchFilters;\n\n constructor(data: SearchResultsData, client?: Vairified, filters: SearchFilters = {}) {\n this.players = data.players.map((p) => new Player(p, client));\n this.total = data.total;\n this.page = data.page;\n this.limit = data.limit;\n this._client = client;\n this._filters = filters;\n }\n\n /** Whether more results are available */\n get hasMore(): boolean {\n return this.page * this.limit < this.total;\n }\n\n /** Total number of pages */\n get pages(): number {\n return this.limit > 0 ? Math.ceil(this.total / this.limit) : 0;\n }\n\n /** Number of players in current page */\n get length(): number {\n return this.players.length;\n }\n\n /** Get player by index */\n at(index: number): Player | undefined {\n return this.players[index];\n }\n\n /** Iterate over players */\n [Symbol.iterator](): Iterator<Player> {\n return this.players[Symbol.iterator]();\n }\n\n /** Fetch next page of results */\n async nextPage(): Promise<SearchResults> {\n if (!this._client) {\n throw new Error('Results not connected to client');\n }\n if (!this.hasMore) {\n throw new Error('No more pages');\n }\n\n return this._client.search({\n ...this._filters,\n page: this.page + 1,\n });\n }\n}\n","/**\n * Vairified OAuth Helpers\n *\n * Utilities for implementing the \"Connect with Vairified\" OAuth flow.\n *\n * @module\n */\n\n/**\n * Available OAuth scopes with descriptions.\n *\n * @category OAuth\n */\nexport const SCOPES = {\n 'profile:read': 'Access your name, location, and verification status',\n 'profile:email': 'Access your email address',\n 'rating:read': 'View your current rating and rating splits',\n 'rating:history': 'View your complete rating history',\n 'match:submit': 'Submit match results on your behalf',\n 'webhook:subscribe': 'Receive notifications when your rating changes',\n} as const;\n\n/**\n * Available OAuth scope keys.\n *\n * @category OAuth\n */\nexport type OAuthScope = keyof typeof SCOPES;\n\n/**\n * Default scopes requested for new connections.\n *\n * @category OAuth\n */\nexport const DEFAULT_SCOPES: OAuthScope[] = ['profile:read', 'rating:read'];\n\n/**\n * OAuth configuration for a partner application.\n *\n * @category OAuth\n */\nexport interface OAuthConfig {\n /** Partner API key */\n apiKey: string;\n /** Your application's callback URL */\n redirectUri: string;\n /** Vairified API base URL */\n baseUrl?: string;\n}\n\n/**\n * Response from starting an OAuth authorization.\n *\n * @category OAuth\n */\nexport interface AuthorizationResponse {\n /** Full URL to redirect the user to */\n authorizationUrl: string;\n /** Authorization code (for internal tracking) */\n code: string;\n /** CSRF state parameter */\n state?: string;\n}\n\n/**\n * Response from exchanging an authorization code for tokens.\n *\n * @category OAuth\n */\nexport interface TokenResponse {\n /** Access token for API requests */\n accessToken: string;\n /** Refresh token for obtaining new access tokens */\n refreshToken?: string;\n /** Token expiration in seconds */\n expiresIn: number;\n /** Granted scopes */\n scope: string[];\n /** Connected player's external ID */\n playerId: string;\n}\n\n/**\n * Build the URL to redirect users to for OAuth authorization.\n *\n * This is a helper for building the URL manually. In most cases,\n * you should use the Vairified client's OAuth methods instead.\n *\n * @param config - OAuth configuration\n * @param scopes - Permission scopes to request\n * @param state - CSRF protection state parameter\n * @returns URL to redirect the user to\n *\n * @example\n * ```ts\n * const url = getAuthorizationUrl(\n * {\n * apiKey: 'vair_pk_xxx',\n * redirectUri: 'https://myapp.com/oauth/callback',\n * },\n * ['profile:read', 'rating:read'],\n * );\n * // Redirect user to this URL\n * window.location.href = url;\n * ```\n *\n * @category OAuth\n */\nexport function getAuthorizationUrl(\n config: OAuthConfig,\n scopes: OAuthScope[] = DEFAULT_SCOPES,\n state?: string,\n): string {\n const baseUrl = config.baseUrl || 'https://api-next.vairified.com/api/v1';\n\n // Ensure profile:read is always included\n const scopeSet = new Set(scopes);\n scopeSet.add('profile:read');\n const scopeList = Array.from(scopeSet);\n\n const params = new URLSearchParams({\n redirect_uri: config.redirectUri,\n scope: scopeList.join(','),\n response_type: 'code',\n });\n\n if (state) {\n params.set('state', state);\n }\n\n // The actual authorization is done via API call, this builds the frontend URL\n // Partners should POST to /partner/oauth/authorize to get the actual auth URL\n return `${baseUrl}/partner/oauth/authorize?${params.toString()}`;\n}\n\n/**\n * Check if a scope is valid.\n *\n * @param scope - Scope string to validate\n * @returns True if scope is valid\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 a scope.\n *\n * @param scope - Scope string\n * @returns Description of what the scope grants access to\n *\n * @category OAuth\n */\nexport function describeScope(scope: OAuthScope): string {\n return SCOPES[scope] ?? `Unknown scope: ${scope}`;\n}\n\n/**\n * Get descriptions for multiple scopes.\n *\n * @param scopes - List of scope strings\n * @returns Array of objects with scope and description\n *\n * @category OAuth\n */\nexport function describeScopes(\n scopes: OAuthScope[],\n): Array<{ scope: OAuthScope; description: string }> {\n return scopes.map((scope) => ({\n scope,\n description: describeScope(scope),\n }));\n}\n\n/**\n * Generate a random state parameter for CSRF protection.\n *\n * @returns Random 32-character hexadecimal string\n *\n * @category OAuth\n */\nexport function generateState(): string {\n const array = new Uint8Array(16);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(array);\n } else {\n // Fallback for environments without crypto\n for (let i = 0; i < array.length; i++) {\n array[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(array)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/**\n * Vairified SDK Client\n *\n * Main client for the Vairified Partner API.\n *\n * @module\n */\n\nimport {\n AuthenticationError,\n NotFoundError,\n OAuthError,\n RateLimitError,\n VairifiedError,\n ValidationError,\n} from './errors.js';\nimport {\n type Match,\n MatchResult,\n Member,\n type Player,\n RatingUpdate,\n SearchResults,\n} from './models.js';\nimport type { AuthorizationResponse, OAuthScope, TokenResponse } from './oauth.js';\nimport { DEFAULT_SCOPES, SCOPES } from './oauth.js';\nimport type {\n ApiErrorResponse,\n MatchResultData,\n MemberData,\n PlayerSearchData,\n RatingUpdateData,\n SearchFilters,\n SearchResultsData,\n VairifiedOptions,\n} from './types.js';\n\n// Environment URLs\n// Note: \"production\" points to current active API\nconst ENVIRONMENTS = {\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} as const;\n\n/**\n * Available environment presets for the Vairified client.\n *\n * @category Client\n */\nexport type VairifiedEnvironment = keyof typeof ENVIRONMENTS;\n\nconst DEFAULT_BASE_URL = ENVIRONMENTS.production;\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * Client for the Vairified Partner API.\n *\n * @category Client\n *\n * @example\n * ```ts\n * const client = new Vairified({ apiKey: 'vair_pk_xxx' });\n *\n * // Get a member\n * const member = await client.getMember('user_123');\n * console.log(member.name, member.rating);\n *\n * // Search for players\n * const results = await client.search({ city: 'Austin', ratingMin: 4.0 });\n * for (const player of results) {\n * console.log(player.name, player.rating);\n * }\n *\n * // Submit a match (doubles: 11-9, 11-7)\n * const match = new Match({\n * event: 'Weekly League',\n * bracket: '4.0 Doubles',\n * date: new Date(),\n * team1: ['p1', 'p2'],\n * team2: ['p3', 'p4'],\n * scores: [[11, 9], [11, 7]],\n * });\n * const result = await client.submitMatch(match);\n * if (result.ok) {\n * console.log(`Submitted ${result.numGames} games`);\n * }\n * ```\n *\n * @remarks\n * If your API key has the \"dry-run\" scope, match submissions will be\n * validated but not persisted. This is useful for testing integrations.\n */\nexport class Vairified {\n /** API key */\n readonly apiKey: string;\n /** Base URL */\n readonly baseUrl: string;\n /** Environment name */\n readonly env: VairifiedEnvironment;\n /** Request timeout in ms */\n readonly timeout: number;\n\n constructor(options: VairifiedOptions = {}) {\n this.apiKey = options.apiKey || this.getEnvApiKey();\n if (!this.apiKey) {\n throw new Error('API key required. Pass apiKey option or set VAIRIFIED_API_KEY env var.');\n }\n\n // Resolve base URL from env or explicit baseUrl\n if (options.baseUrl) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.env = 'production';\n } else if (options.env) {\n this.baseUrl = ENVIRONMENTS[options.env];\n this.env = options.env;\n } else {\n // Default to VAIRIFIED_ENV or 'production'\n const envVar = this.getEnvVar('VAIRIFIED_ENV') as VairifiedEnvironment | undefined;\n const defaultEnv: VairifiedEnvironment =\n envVar && envVar in ENVIRONMENTS ? envVar : 'production';\n this.baseUrl = ENVIRONMENTS[defaultEnv] || DEFAULT_BASE_URL;\n this.env = defaultEnv;\n }\n\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n }\n\n private getEnvVar(name: string): string {\n if (typeof process !== 'undefined' && process.env?.[name]) {\n return process.env[name] as string;\n }\n return '';\n }\n\n private getEnvApiKey(): string {\n return this.getEnvVar('VAIRIFIED_API_KEY');\n }\n\n private getHeaders(): Record<string, string> {\n return {\n 'X-API-Key': this.apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n\n private async handleError(response: Response): Promise<never> {\n let body: ApiErrorResponse | undefined;\n let message: string;\n\n try {\n body = (await response.json()) as ApiErrorResponse;\n message = body.message || response.statusText;\n } catch {\n message = response.statusText;\n }\n\n const status = response.status;\n\n if (status === 401) throw new AuthenticationError(message, body);\n if (status === 404) throw new NotFoundError(message, body);\n if (status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n throw new RateLimitError(\n message,\n retryAfter ? Number.parseInt(retryAfter, 10) : undefined,\n body,\n );\n }\n if (status === 400) throw new ValidationError(message, body);\n\n throw new VairifiedError(message, status, body);\n }\n\n private async request<T>(\n method: string,\n path: string,\n options?: { params?: Record<string, string | number | boolean>; body?: unknown },\n ): Promise<T> {\n let url = `${this.baseUrl}${path}`;\n\n if (options?.params) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(options.params)) {\n if (value !== undefined && value !== null) {\n searchParams.append(key, String(value));\n }\n }\n const queryString = searchParams.toString();\n if (queryString) url += `?${queryString}`;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: this.getHeaders(),\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) await this.handleError(response);\n\n return (await response.json()) as T;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Member Operations\n // ---------------------------------------------------------------------------\n\n /**\n * Get a connected member by their external ID.\n *\n * **Requires OAuth Connection**: The player must have connected their\n * account to your application via OAuth before you can access their data.\n *\n * @param playerId - External player ID (vair_mem_xxx format)\n * @returns Member object with profile and rating data\n * @throws NotFoundError if member is not found or invalid ID format\n * @throws ForbiddenError if player has not connected to your app\n *\n * @example\n * ```ts\n * const member = await client.getMember('vair_mem_0ABC123def456GHI789jk');\n * console.log(member.name, member.rating);\n * console.log(member.ratingSplits.open); // Open division rating\n * console.log(member.grantedScopes); // ['profile:read', 'rating:read']\n * ```\n */\n async getMember(playerId: string): Promise<Member> {\n const data = await this.request<MemberData>('GET', '/partner/member', {\n params: { id: playerId },\n });\n return new Member(data, this);\n }\n\n // ---------------------------------------------------------------------------\n // Search Operations\n // ---------------------------------------------------------------------------\n\n /**\n * Search for players.\n *\n * @param filters - Search filters\n * @returns SearchResults with players and pagination\n *\n * @example\n * ```ts\n * const results = await client.search({\n * city: 'Austin',\n * ratingMin: 4.0,\n * vairifiedOnly: true,\n * });\n *\n * for (const player of results) {\n * console.log(player.name, player.rating);\n * }\n *\n * // Pagination\n * if (results.hasMore) {\n * const nextPage = await results.nextPage();\n * }\n * ```\n */\n async search(filters: SearchFilters = {}): Promise<SearchResults> {\n const params: Record<string, string | number | boolean> = {\n limit: filters.limit ?? 20,\n };\n\n if (filters.name) params.member = filters.name;\n if (filters.city) params.city = filters.city;\n if (filters.state) params.state = filters.state;\n if (filters.country) params.country = filters.country;\n if (filters.zipCode) params.zip = filters.zipCode;\n if (filters.ratingMin !== undefined) params.rating1 = filters.ratingMin;\n if (filters.ratingMax !== undefined) params.rating2 = filters.ratingMax;\n if (filters.gender) params.gender = filters.gender;\n if (filters.vairifiedOnly) params.vairified = true;\n if (filters.sortBy) {\n params.sortField = filters.sortBy;\n params.sortDirection = filters.sortOrder ?? 'desc';\n }\n\n // Age handling\n if (filters.age !== undefined) {\n params.ageFilterType = 'exact';\n params.age1 = filters.age;\n } else if (filters.ageMin !== undefined && filters.ageMax !== undefined) {\n params.ageFilterType = 'range';\n params.age1 = filters.ageMin;\n params.age2 = filters.ageMax;\n } else if (filters.ageMin !== undefined) {\n params.ageFilterType = 'above';\n params.age1 = filters.ageMin;\n } else if (filters.ageMax !== undefined) {\n params.ageFilterType = 'below';\n params.age1 = filters.ageMax;\n }\n\n // Pagination\n const page = filters.page ?? 1;\n if (page > 1) {\n params.offset = (page - 1) * (filters.limit ?? 20);\n }\n\n const data = await this.request<SearchResultsData | PlayerSearchData[]>(\n 'GET',\n '/partner/search',\n {\n params,\n },\n );\n\n // Handle both array and object responses\n const normalized: SearchResultsData = Array.isArray(data)\n ? { players: data, total: data.length, page, limit: filters.limit ?? 20 }\n : data;\n\n return new SearchResults(normalized, this, filters);\n }\n\n /**\n * Find a single player by name.\n *\n * @param name - Player name to search for\n * @returns Player if found, undefined otherwise\n *\n * @example\n * ```ts\n * const player = await client.findPlayer('John Smith');\n * if (player) {\n * console.log(player.rating);\n * }\n * ```\n */\n async findPlayer(name: string): Promise<Player | undefined> {\n const results = await this.search({ name, limit: 1 });\n return results.at(0);\n }\n\n // ---------------------------------------------------------------------------\n // Match Operations\n // ---------------------------------------------------------------------------\n\n /**\n * Submit a single match.\n *\n * @param match - Match object with teams and scores\n * @returns MatchResult with submission status\n *\n * @example\n * ```ts\n * const match = new Match({\n * event: 'Weekly League',\n * bracket: '4.0 Doubles',\n * date: new Date(),\n * team1: ['p1', 'p2'],\n * team2: ['p3', 'p4'],\n * scores: [[11, 9], [11, 7]],\n * });\n *\n * const result = await client.submitMatch(match);\n * if (result.ok) {\n * console.log(`Submitted ${result.numGames} games`);\n * }\n * ```\n */\n async submitMatch(match: Match): Promise<MatchResult> {\n return this.submitMatches([match]);\n }\n\n /**\n * Submit multiple matches in a batch.\n *\n * @param matches - List of Match objects\n * @returns MatchResult with submission status\n *\n * @example\n * ```ts\n * const result = await client.submitMatches([match1, match2, match3]);\n * console.log(`Submitted ${result.numGames} games from ${result.numMatches} matches`);\n *\n * if (result.dryRun) {\n * console.log('This was a dry run - no data persisted');\n * }\n * ```\n */\n async submitMatches(matches: Match[]): Promise<MatchResult> {\n const data = await this.request<MatchResultData>('POST', '/partner/matches', {\n body: { matches: matches.map((m) => m.toJSON()) },\n });\n return new MatchResult(data);\n }\n\n // ---------------------------------------------------------------------------\n // Rating Updates\n // ---------------------------------------------------------------------------\n\n /**\n * Get rating updates for subscribed members.\n *\n * Members are subscribed when you call getMember().\n *\n * @returns List of RatingUpdate objects\n *\n * @example\n * ```ts\n * const updates = await client.getRatingUpdates();\n * for (const update of updates) {\n * console.log(`${update.memberId}: ${update.previousRating} → ${update.newRating}`);\n * if (update.improved) {\n * const member = await update.getMember();\n * console.log(`${member.name} improved!`);\n * }\n * }\n * ```\n */\n async getRatingUpdates(): Promise<RatingUpdate[]> {\n const data = await this.request<{ updates: RatingUpdateData[] }>(\n 'GET',\n '/partner/rating-updates',\n );\n return (data.updates ?? []).map((u) => new RatingUpdate(u, this));\n }\n\n /**\n * Test webhook endpoint.\n *\n * @param webhookUrl - URL to send test webhook to\n * @returns Test result\n */\n async testWebhook(webhookUrl: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>('POST', '/partner/webhook-test', {\n body: { webhookUrl },\n });\n }\n\n // ---------------------------------------------------------------------------\n // OAuth Operations\n // ---------------------------------------------------------------------------\n\n /**\n * Start an OAuth authorization flow.\n *\n * This creates a pending authorization and returns the URL where\n * users should be redirected to approve access.\n *\n * @param redirectUri - Your application's callback URL\n * @param scopes - Permission scopes to request (defaults to profile:read, rating:read)\n * @param state - CSRF protection state parameter (recommended)\n * @returns AuthorizationResponse with the URL to redirect users to\n * @throws OAuthError if the authorization fails to start\n *\n * @example\n * ```ts\n * const auth = await client.startOAuth(\n * 'https://myapp.com/callback',\n * ['profile:read', 'rating:read', 'match:submit'],\n * 'random_csrf_token',\n * );\n * // Redirect user to auth.authorizationUrl\n * window.location.href = auth.authorizationUrl;\n * ```\n *\n * @category OAuth\n */\n async startOAuth(\n redirectUri: string,\n scopes: OAuthScope[] = [...DEFAULT_SCOPES],\n state?: string,\n ): Promise<AuthorizationResponse> {\n // Ensure profile:read is always included\n const scopeSet = new Set(scopes);\n scopeSet.add('profile:read');\n const scopeList = Array.from(scopeSet);\n\n // Validate scopes\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.request<{\n authorizationUrl: string;\n code: string;\n }>('POST', '/partner/oauth/authorize', {\n body: {\n redirectUri,\n scope: scopeList.join(','),\n state,\n },\n });\n\n return {\n authorizationUrl: data.authorizationUrl,\n code: data.code,\n state,\n };\n }\n\n /**\n * Exchange an authorization code for access and refresh tokens.\n *\n * Call this after the user approves access and is redirected back\n * to your application with a code parameter.\n *\n * @param code - Authorization code from the callback URL\n * @param redirectUri - Must match the redirectUri used in startOAuth\n * @returns TokenResponse with access_token, refresh_token, and player_id\n * @throws OAuthError if the code is invalid or expired\n *\n * @example\n * ```ts\n * // After user is redirected to: https://myapp.com/callback?code=xxx\n * const tokens = await client.exchangeToken(\n * new URL(window.location.href).searchParams.get('code')!,\n * 'https://myapp.com/callback',\n * );\n * // Store tokens.accessToken and tokens.refreshToken securely\n * // Use tokens.playerId to identify the connected player\n * ```\n *\n * @category OAuth\n */\n async exchangeToken(code: string, redirectUri: string): Promise<TokenResponse> {\n const data = await this.request<{\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n scope: string;\n playerId: string;\n }>('POST', '/partner/oauth/token', {\n body: { code, redirectUri },\n });\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n expiresIn: data.expiresIn,\n scope: data.scope ? data.scope.split(',') : [],\n playerId: data.playerId,\n };\n }\n\n /**\n * Refresh an expired access token.\n *\n * Use this when an access token expires to obtain a new one\n * without requiring the user to re-authorize.\n *\n * @param refreshToken - The refresh token from a previous token exchange\n * @returns TokenResponse with new access_token and optionally a new refresh_token\n * @throws OAuthError if the refresh token is invalid or revoked\n *\n * @example\n * ```ts\n * try {\n * const newTokens = await client.refreshAccessToken(storedRefreshToken);\n * // Update stored tokens\n * } catch (e) {\n * if (e instanceof OAuthError && e.errorCode === 'invalid_grant') {\n * // Refresh token revoked, user needs to re-authorize\n * }\n * }\n * ```\n *\n * @category OAuth\n */\n async refreshAccessToken(refreshToken: string): Promise<TokenResponse> {\n const data = await this.request<{\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n scope: string;\n playerId: string;\n }>('POST', '/partner/oauth/refresh', {\n body: { refreshToken },\n });\n\n return {\n accessToken: data.accessToken,\n refreshToken: data.refreshToken,\n expiresIn: data.expiresIn,\n scope: data.scope ? data.scope.split(',') : [],\n playerId: data.playerId,\n };\n }\n\n /**\n * Revoke a player's OAuth connection.\n *\n * This disconnects the player from your application. You will no\n * longer be able to access their data or submit matches on their behalf.\n *\n * @param playerId - The player's external ID (vair_mem_xxx format)\n * @throws OAuthError if the revocation fails\n *\n * @example\n * ```ts\n * await client.revokeConnection('vair_mem_0ABC123def456GHI789jk');\n * // Player is now disconnected\n * ```\n *\n * @category OAuth\n */\n async revokeConnection(playerId: string): Promise<void> {\n await this.request<{ success: boolean }>('POST', '/partner/oauth/revoke', {\n body: { playerId },\n });\n }\n\n /**\n * Get a list of available OAuth scopes.\n *\n * @returns List of scope objects with id, name, and description\n *\n * @example\n * ```ts\n * const scopes = await client.getAvailableScopes();\n * for (const scope of scopes) {\n * console.log(`${scope.id}: ${scope.description}`);\n * }\n * ```\n *\n * @category OAuth\n */\n async getAvailableScopes(): Promise<Array<{ id: string; name: string; description: string }>> {\n const data = await this.request<{\n scopes: Array<{ id: string; name: string; description: string }>;\n }>('GET', '/partner/oauth/scopes');\n return data.scopes ?? [];\n }\n\n /**\n * Get API usage statistics for your partner account.\n *\n * @returns Usage statistics (requests, limits, etc.)\n *\n * @example\n * ```ts\n * const usage = await client.getUsage();\n * console.log(`Requests today: ${usage.requestsToday}`);\n * console.log(`Rate limit: ${usage.rateLimit}/hour`);\n * ```\n *\n * @category Client\n */\n async getUsage(): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>('GET', '/partner/usage');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWO,IAAM,iBAAN,cAA6B,MAAM;AAAA;AAAA,EAExC;AAAA;AAAA,EAEA;AAAA,EAEA,YAAY,SAAiB,YAAqB,UAAoB;AACpE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAClB;AACF;AAOO,IAAM,iBAAN,cAA6B,eAAe;AAAA;AAAA,EAEjD;AAAA,EAEA,YAAY,UAAU,uBAAuB,YAAqB,UAAoB;AACpF,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAOO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EACtD,YAAY,UAAU,mBAAmB,UAAoB;AAC3D,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,OAAO;AAAA,EACd;AACF;AAOO,IAAM,gBAAN,cAA4B,eAAe;AAAA,EAChD,YAAY,UAAU,sBAAsB,UAAoB;AAC9D,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,OAAO;AAAA,EACd;AACF;AAOO,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAClD,YAAY,UAAU,oBAAoB,UAAoB;AAC5D,UAAM,SAAS,KAAK,QAAQ;AAC5B,SAAK,OAAO;AAAA,EACd;AACF;AASO,IAAM,aAAN,cAAyB,eAAe;AAAA;AAAA,EAE7C;AAAA,EAEA,YAAY,UAAU,eAAe,WAAoB,UAAoB;AAC3E,UAAM,SAAS,QAAW,QAAQ;AAClC,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AACF;;;AC/DO,IAAM,cAAN,MAAkB;AAAA;AAAA,EAEd;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAAgC;AAC1C,QAAI,OAAO,SAAS,UAAU;AAC5B,WAAK,SAAS;AACd,WAAK,OAAO;AAAA,IACd,OAAO;AACL,YAAM,YAAY,KAAK;AACvB,WAAK,SAAS,OAAO,cAAc,WAAW,OAAO,WAAW,SAAS,KAAK,IAAI;AAClF,WAAK,OAAO,KAAK;AACjB,WAAK,aAAa,KAAK;AAAA,IACzB;AAAA,EACF;AACF;AASO,IAAM,eAAN,MAAmB;AAAA;AAAA,EAEf;AAAA,EAET,YAAY,MAAyB;AACnC,SAAK,SAAS,oBAAI,IAAI;AACtB,QAAI,MAAM;AACR,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,aAAK,OAAO,IAAI,KAAK,IAAI,YAAY,KAAK,CAAC;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,UAAsC;AACxC,WAAO,KAAK,OAAO,IAAI,QAAQ,GAAG;AAAA,EACpC;AAAA;AAAA,EAGA,IAAI,OAA2B;AAC7B,WAAO,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI;AAAA,EAC1C;AAAA;AAAA,EAGA,IAAI,SAA6B;AAC/B,WAAO,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,IAAI;AAAA,EAC5C;AAAA;AAAA,EAGA,IAAI,QAA4B;AAC9B,WAAO,KAAK,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI;AAAA,EAC3C;AAAA;AAAA,EAGA,IAAI,eAAmC;AACrC,WAAO,KAAK,IAAI,cAAc,KAAK,KAAK,IAAI,GAAG;AAAA,EACjD;AAAA;AAAA,EAGA,IAAI,UAA8B;AAChC,WAAO,KAAK,IAAI,SAAS,KAAK,KAAK,IAAI,GAAG;AAAA,EAC5C;AAAA;AAAA,EAGA,IAAI,OAA2B;AAC7B,UAAM,UAAU,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,MAAM,EACnB,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,WAAO,QAAQ,SAAS,IAAI,KAAK,IAAI,GAAG,OAAO,IAAI;AAAA,EACrD;AAAA;AAAA,EAGA,SAA2D;AACzD,UAAM,SAA2D,CAAC;AAClE,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,QAAQ;AACtC,aAAO,GAAG,IAAI,EAAE,QAAQ,MAAM,QAAQ,MAAM,MAAM,KAAK;AAAA,IACzD;AACA,WAAO;AAAA,EACT;AACF;AAKA,SAAS,aAAa,MAA4C;AAChE,SAAO,iBAAiB;AAC1B;AAUO,IAAM,SAAN,MAAa;AAAA;AAAA,EAET;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAEC;AAAA,EAEV,YAAY,MAAkB,QAAoB;AAChD,QAAI,aAAa,IAAI,GAAG;AAEtB,WAAK,KAAK,KAAK;AACf,WAAK,cAAc,KAAK;AACxB,WAAK,SAAS,KAAK,UAAU;AAC7B,WAAK,cAAc,KAAK,eAAe;AACvC,WAAK,cAAc,KAAK,eAAe;AACvC,WAAK,eAAe,IAAI,aAAa;AAAA,IACvC,OAAO;AAEL,WAAK,KAAK,KAAK;AACf,WAAK,YAAY,KAAK,aAAa;AACnC,WAAK,WAAW,KAAK,YAAY;AACjC,WAAK,SAAS,KAAK,UAAU;AAC7B,WAAK,cAAc,KAAK,eAAe;AACvC,WAAK,cAAc;AACnB,WAAK,eAAe,IAAI,aAAa,KAAK,YAAY;AAAA,IACxD;AAEA,SAAK,OAAO,KAAK;AACjB,SAAK,QAAQ,KAAK;AAClB,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,QAAI,KAAK,aAAa,KAAK,UAAU;AACnC,aAAO,GAAG,KAAK,SAAS,IAAI,KAAK,QAAQ,GAAG,KAAK;AAAA,IACnD;AACA,WAAO,KAAK,eAAe;AAAA,EAC7B;AAAA;AAAA,EAGA,IAAI,iBAAqC;AACvC,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA,EAEA,WAAmB;AACjB,UAAM,WAAW,KAAK,cAAc,YAAO;AAC3C,WAAO,GAAG,KAAK,IAAI,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,IAAI,QAAQ;AAAA,EAC5D;AACF;AASO,IAAM,SAAN,cAAqB,OAAO;AAAA;AAAA,EAExB;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAAkB,QAAoB;AAChD,UAAM,MAAM,MAAM;AAClB,SAAK,QAAQ,KAAK;AAClB,SAAK,gBAAgB,KAAK,iBAAiB,CAAC;AAAA,EAC9C;AAAA;AAAA,EAGA,SAAS,OAAwB;AAC/B,WAAO,KAAK,cAAc,SAAS,KAAK;AAAA,EAC1C;AAAA;AAAA,EAGA,MAAM,UAA2B;AAC/B,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,UAAM,UAAU,MAAM,KAAK,QAAQ,UAAU,KAAK,EAAE;AACpD,WAAO,OAAO,MAAM,OAAO;AAC3B,WAAO;AAAA,EACT;AACF;AAKA,SAAS,aAAqB;AAC5B,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE,CAAC;AAC3D;AA8BO,IAAM,QAAN,MAAY;AAAA;AAAA,EAER;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAET;AAAA,EAEA,YAAY,MAAkB;AAC5B,SAAK,QAAQ,KAAK;AAClB,SAAK,UAAU,KAAK;AACpB,SAAK,OAAO,KAAK,gBAAgB,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK,IAAI;AACtE,SAAK,QAAQ,KAAK;AAClB,SAAK,QAAQ,KAAK;AAClB,SAAK,SAAS,KAAK;AACnB,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,SAAS,KAAK,UAAU;AAC7B,SAAK,WAAW,KAAK;AACrB,SAAK,aAAa,KAAK,cAAc,WAAW;AAAA,EAClD;AAAA;AAAA,EAGA,IAAI,SAAgC;AAClC,WAAO,KAAK,MAAM,WAAW,IAAI,YAAY;AAAA,EAC/C;AAAA;AAAA,EAGA,IAAI,SAAoB;AACtB,QAAI,SAAS;AACb,QAAI,SAAS;AACb,eAAW,CAAC,IAAI,EAAE,KAAK,KAAK,QAAQ;AAClC,UAAI,KAAK,GAAI;AAAA,eACJ,KAAK,GAAI;AAAA,IACpB;AACA,QAAI,SAAS,OAAQ,QAAO;AAC5B,QAAI,SAAS,OAAQ,QAAO;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,IAAI;AAAA,EAC/D;AAAA;AAAA,EAGA,SAAuB;AACrB,UAAM,WAAW,KAAK,MAAM,CAAC;AAC7B,UAAM,WAAW,KAAK,MAAM,CAAC;AAC7B,QAAI,CAAC,YAAY,CAAC,UAAU;AAC1B,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,UAAM,QAA+B,EAAE,SAAS,SAAS;AACzD,UAAM,QAA+B,EAAE,SAAS,SAAS;AAEzD,QAAI,KAAK,MAAM,CAAC,EAAG,OAAM,UAAU,KAAK,MAAM,CAAC;AAC/C,QAAI,KAAK,MAAM,CAAC,EAAG,OAAM,UAAU,KAAK,MAAM,CAAC;AAG/C,UAAM,WAAW,CAAC,SAAS,SAAS,SAAS,SAAS,OAAO;AAC7D,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK,OAAO,QAAQ,CAAC,GAAG,KAAK;AACxD,YAAM,QAAQ,KAAK,OAAO,CAAC;AAC3B,YAAM,MAAM,SAAS,CAAC;AACtB,UAAI,SAAS,KAAK;AAChB,cAAM,GAAG,IAAI,MAAM,CAAC;AACpB,cAAM,GAAG,IAAI,MAAM,CAAC;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK,KAAK,YAAY;AAAA,MACjC,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAOO,IAAM,cAAN,MAAkB;AAAA;AAAA,EAEd;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAAuB;AACjC,SAAK,UAAU,KAAK;AACpB,SAAK,aAAa,KAAK;AACvB,SAAK,WAAW,KAAK;AACrB,SAAK,SAAS,KAAK,UAAU;AAC7B,SAAK,UAAU,KAAK;AACpB,SAAK,SAAS,KAAK,UAAU,CAAC;AAAA,EAChC;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,KAAc;AAChB,WAAO,KAAK,WAAW,KAAK,OAAO,WAAW;AAAA,EAChD;AACF;AAOO,IAAM,eAAN,MAAmB;AAAA;AAAA,EAEf;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAED;AAAA,EAER,YAAY,MAAwB,QAAoB;AACtD,SAAK,KAAK,KAAK;AACf,SAAK,aAAa,KAAK;AACvB,SAAK,iBAAiB,KAAK,kBAAkB;AAC7C,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,YAAY,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI,oBAAI,KAAK;AACtE,SAAK,eAAe,IAAI,aAAa,KAAK,YAAY;AACtD,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,YAAY,KAAK;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA,EAGA,MAAM,YAA6B;AACjC,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,WAAO,KAAK,QAAQ,UAAU,KAAK,EAAE;AAAA,EACvC;AAAA,EAEA,WAAmB;AACjB,UAAM,YAAY,KAAK,WAAW,WAAM;AACxC,UAAM,OAAO,KAAK,aAAa,KAAK,KAAK,UAAU,MAAM;AACzD,WAAO,GAAG,KAAK,EAAE,GAAG,IAAI,KAAK,KAAK,eAAe,QAAQ,CAAC,CAAC,IAAI,SAAS,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,EACvG;AACF;AAOO,IAAM,gBAAN,MAAgD;AAAA;AAAA,EAE5C;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAED;AAAA,EACA;AAAA,EAER,YAAY,MAAyB,QAAoB,UAAyB,CAAC,GAAG;AACpF,SAAK,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,IAAI,OAAO,GAAG,MAAM,CAAC;AAC5D,SAAK,QAAQ,KAAK;AAClB,SAAK,OAAO,KAAK;AACjB,SAAK,QAAQ,KAAK;AAClB,SAAK,UAAU;AACf,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,KAAK,OAAO,KAAK,QAAQ,KAAK;AAAA,EACvC;AAAA;AAAA,EAGA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,IAAI;AAAA,EAC/D;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,GAAG,OAAmC;AACpC,WAAO,KAAK,QAAQ,KAAK;AAAA,EAC3B;AAAA;AAAA,EAGA,CAAC,OAAO,QAAQ,IAAsB;AACpC,WAAO,KAAK,QAAQ,OAAO,QAAQ,EAAE;AAAA,EACvC;AAAA;AAAA,EAGA,MAAM,WAAmC;AACvC,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AACA,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAEA,WAAO,KAAK,QAAQ,OAAO;AAAA,MACzB,GAAG,KAAK;AAAA,MACR,MAAM,KAAK,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AACF;;;AC3gBO,IAAM,SAAS;AAAA,EACpB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,qBAAqB;AACvB;AAcO,IAAM,iBAA+B,CAAC,gBAAgB,aAAa;AA0EnE,SAAS,oBACd,QACA,SAAuB,gBACvB,OACQ;AACR,QAAM,UAAU,OAAO,WAAW;AAGlC,QAAM,WAAW,IAAI,IAAI,MAAM;AAC/B,WAAS,IAAI,cAAc;AAC3B,QAAM,YAAY,MAAM,KAAK,QAAQ;AAErC,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,cAAc,OAAO;AAAA,IACrB,OAAO,UAAU,KAAK,GAAG;AAAA,IACzB,eAAe;AAAA,EACjB,CAAC;AAED,MAAI,OAAO;AACT,WAAO,IAAI,SAAS,KAAK;AAAA,EAC3B;AAIA,SAAO,GAAG,OAAO,4BAA4B,OAAO,SAAS,CAAC;AAChE;AAUO,SAAS,cAAc,OAAoC;AAChE,SAAO,SAAS;AAClB;AAUO,SAAS,cAAc,OAA2B;AACvD,SAAO,OAAO,KAAK,KAAK,kBAAkB,KAAK;AACjD;AAUO,SAAS,eACd,QACmD;AACnD,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B;AAAA,IACA,aAAa,cAAc,KAAK;AAAA,EAClC,EAAE;AACJ;AASO,SAAS,gBAAwB;AACtC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,MAAI,OAAO,WAAW,eAAe,OAAO,iBAAiB;AAC3D,WAAO,gBAAgB,KAAK;AAAA,EAC9B,OAAO;AAEL,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,CAAC,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AAAA,IAC3C;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,EACpB,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AACZ;;;AC7JA,IAAM,eAAe;AAAA,EACnB,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,OAAO;AACT;AASA,IAAM,mBAAmB,aAAa;AACtC,IAAM,kBAAkB;AAwCjB,IAAM,YAAN,MAAgB;AAAA;AAAA,EAEZ;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,UAA4B,CAAC,GAAG;AAC1C,SAAK,SAAS,QAAQ,UAAU,KAAK,aAAa;AAClD,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,wEAAwE;AAAA,IAC1F;AAGA,QAAI,QAAQ,SAAS;AACnB,WAAK,UAAU,QAAQ,QAAQ,QAAQ,OAAO,EAAE;AAChD,WAAK,MAAM;AAAA,IACb,WAAW,QAAQ,KAAK;AACtB,WAAK,UAAU,aAAa,QAAQ,GAAG;AACvC,WAAK,MAAM,QAAQ;AAAA,IACrB,OAAO;AAEL,YAAM,SAAS,KAAK,UAAU,eAAe;AAC7C,YAAM,aACJ,UAAU,UAAU,eAAe,SAAS;AAC9C,WAAK,UAAU,aAAa,UAAU,KAAK;AAC3C,WAAK,MAAM;AAAA,IACb;AAEA,SAAK,UAAU,QAAQ,WAAW;AAAA,EACpC;AAAA,EAEQ,UAAU,MAAsB;AACtC,QAAI,OAAO,YAAY,eAAe,QAAQ,MAAM,IAAI,GAAG;AACzD,aAAO,QAAQ,IAAI,IAAI;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAuB;AAC7B,WAAO,KAAK,UAAU,mBAAmB;AAAA,EAC3C;AAAA,EAEQ,aAAqC;AAC3C,WAAO;AAAA,MACL,aAAa,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,UAAoC;AAC5D,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,aAAQ,MAAM,SAAS,KAAK;AAC5B,gBAAU,KAAK,WAAW,SAAS;AAAA,IACrC,QAAQ;AACN,gBAAU,SAAS;AAAA,IACrB;AAEA,UAAM,SAAS,SAAS;AAExB,QAAI,WAAW,IAAK,OAAM,IAAI,oBAAoB,SAAS,IAAI;AAC/D,QAAI,WAAW,IAAK,OAAM,IAAI,cAAc,SAAS,IAAI;AACzD,QAAI,WAAW,KAAK;AAClB,YAAM,aAAa,SAAS,QAAQ,IAAI,aAAa;AACrD,YAAM,IAAI;AAAA,QACR;AAAA,QACA,aAAa,OAAO,SAAS,YAAY,EAAE,IAAI;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AACA,QAAI,WAAW,IAAK,OAAM,IAAI,gBAAgB,SAAS,IAAI;AAE3D,UAAM,IAAI,eAAe,SAAS,QAAQ,IAAI;AAAA,EAChD;AAAA,EAEA,MAAc,QACZ,QACA,MACA,SACY;AACZ,QAAI,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAEhC,QAAI,SAAS,QAAQ;AACnB,YAAM,eAAe,IAAI,gBAAgB;AACzC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACzD,YAAI,UAAU,UAAa,UAAU,MAAM;AACzC,uBAAa,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,QACxC;AAAA,MACF;AACA,YAAM,cAAc,aAAa,SAAS;AAC1C,UAAI,YAAa,QAAO,IAAI,WAAW;AAAA,IACzC;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAEnE,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC;AAAA,QACA,SAAS,KAAK,WAAW;AAAA,QACzB,MAAM,SAAS,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,QACrD,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,GAAI,OAAM,KAAK,YAAY,QAAQ;AAEjD,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,UAAU,UAAmC;AACjD,UAAM,OAAO,MAAM,KAAK,QAAoB,OAAO,mBAAmB;AAAA,MACpE,QAAQ,EAAE,IAAI,SAAS;AAAA,IACzB,CAAC;AACD,WAAO,IAAI,OAAO,MAAM,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,OAAO,UAAyB,CAAC,GAA2B;AAChE,UAAM,SAAoD;AAAA,MACxD,OAAO,QAAQ,SAAS;AAAA,IAC1B;AAEA,QAAI,QAAQ,KAAM,QAAO,SAAS,QAAQ;AAC1C,QAAI,QAAQ,KAAM,QAAO,OAAO,QAAQ;AACxC,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,QAAS,QAAO,MAAM,QAAQ;AAC1C,QAAI,QAAQ,cAAc,OAAW,QAAO,UAAU,QAAQ;AAC9D,QAAI,QAAQ,cAAc,OAAW,QAAO,UAAU,QAAQ;AAC9D,QAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAC5C,QAAI,QAAQ,cAAe,QAAO,YAAY;AAC9C,QAAI,QAAQ,QAAQ;AAClB,aAAO,YAAY,QAAQ;AAC3B,aAAO,gBAAgB,QAAQ,aAAa;AAAA,IAC9C;AAGA,QAAI,QAAQ,QAAQ,QAAW;AAC7B,aAAO,gBAAgB;AACvB,aAAO,OAAO,QAAQ;AAAA,IACxB,WAAW,QAAQ,WAAW,UAAa,QAAQ,WAAW,QAAW;AACvE,aAAO,gBAAgB;AACvB,aAAO,OAAO,QAAQ;AACtB,aAAO,OAAO,QAAQ;AAAA,IACxB,WAAW,QAAQ,WAAW,QAAW;AACvC,aAAO,gBAAgB;AACvB,aAAO,OAAO,QAAQ;AAAA,IACxB,WAAW,QAAQ,WAAW,QAAW;AACvC,aAAO,gBAAgB;AACvB,aAAO,OAAO,QAAQ;AAAA,IACxB;AAGA,UAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAI,OAAO,GAAG;AACZ,aAAO,UAAU,OAAO,MAAM,QAAQ,SAAS;AAAA,IACjD;AAEA,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAgC,MAAM,QAAQ,IAAI,IACpD,EAAE,SAAS,MAAM,OAAO,KAAK,QAAQ,MAAM,OAAO,QAAQ,SAAS,GAAG,IACtE;AAEJ,WAAO,IAAI,cAAc,YAAY,MAAM,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,WAAW,MAA2C;AAC1D,UAAM,UAAU,MAAM,KAAK,OAAO,EAAE,MAAM,OAAO,EAAE,CAAC;AACpD,WAAO,QAAQ,GAAG,CAAC;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,MAAM,YAAY,OAAoC;AACpD,WAAO,KAAK,cAAc,CAAC,KAAK,CAAC;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,cAAc,SAAwC;AAC1D,UAAM,OAAO,MAAM,KAAK,QAAyB,QAAQ,oBAAoB;AAAA,MAC3E,MAAM,EAAE,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;AAAA,IAClD,CAAC;AACD,WAAO,IAAI,YAAY,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,mBAA4C;AAChD,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA;AAAA,IACF;AACA,YAAQ,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,aAAa,GAAG,IAAI,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAY,YAAsD;AACtE,WAAO,KAAK,QAAiC,QAAQ,yBAAyB;AAAA,MAC5E,MAAM,EAAE,WAAW;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,MAAM,WACJ,aACA,SAAuB,CAAC,GAAG,cAAc,GACzC,OACgC;AAEhC,UAAM,WAAW,IAAI,IAAI,MAAM;AAC/B,aAAS,IAAI,cAAc;AAC3B,UAAM,YAAY,MAAM,KAAK,QAAQ;AAGrC,eAAW,SAAS,WAAW;AAC7B,UAAI,EAAE,SAAS,SAAS;AACtB,cAAM,IAAI,WAAW,kBAAkB,KAAK,IAAI,eAAe;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,KAAK,QAGrB,QAAQ,4BAA4B;AAAA,MACrC,MAAM;AAAA,QACJ;AAAA,QACA,OAAO,UAAU,KAAK,GAAG;AAAA,QACzB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,kBAAkB,KAAK;AAAA,MACvB,MAAM,KAAK;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,cAAc,MAAc,aAA6C;AAC7E,UAAM,OAAO,MAAM,KAAK,QAMrB,QAAQ,wBAAwB;AAAA,MACjC,MAAM,EAAE,MAAM,YAAY;AAAA,IAC5B,CAAC;AAED,WAAO;AAAA,MACL,aAAa,KAAK;AAAA,MAClB,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,IAAI,CAAC;AAAA,MAC7C,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,mBAAmB,cAA8C;AACrE,UAAM,OAAO,MAAM,KAAK,QAMrB,QAAQ,0BAA0B;AAAA,MACnC,MAAM,EAAE,aAAa;AAAA,IACvB,CAAC;AAED,WAAO;AAAA,MACL,aAAa,KAAK;AAAA,MAClB,cAAc,KAAK;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,IAAI,CAAC;AAAA,MAC7C,UAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,iBAAiB,UAAiC;AACtD,UAAM,KAAK,QAA8B,QAAQ,yBAAyB;AAAA,MACxE,MAAM,EAAE,SAAS;AAAA,IACnB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,qBAAwF;AAC5F,UAAM,OAAO,MAAM,KAAK,QAErB,OAAO,uBAAuB;AACjC,WAAO,KAAK,UAAU,CAAC;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,WAA6C;AACjD,WAAO,KAAK,QAAiC,OAAO,gBAAgB;AAAA,EACtE;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/leaderboard.ts","../src/models/match-batch-result.ts","../src/resources/matches.ts","../src/models/sport-rating.ts","../src/models/member.ts","../src/models/rating-update.ts","../src/resources/members.ts","../src/oauth.ts","../src/resources/oauth.ts","../src/client.ts"],"sourcesContent":["/**\n * Vairified SDK — Partner API v1.\n *\n * The Vairified Partner API lets you read player ratings, submit match\n * results, and subscribe to rating change notifications for\n * integrations in leagues, tournaments, and club-management software.\n *\n * ```ts\n * import { Vairified } from 'vairified';\n *\n * await using client = new Vairified({ apiKey: 'vair_pk_xxx' });\n *\n * // Look up a connected player\n * const member = await client.members.get('vair_mem_xxx');\n * console.log(member.name, member.ratingFor('pickleball'));\n *\n * // Auto-paginating search\n * for await (const player of client.members.search({ city: 'Austin' })) {\n * console.log(player.displayName);\n * }\n *\n * // Submit a bulk match batch\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 * console.log(`Submitted ${result.numGames} games`);\n * ```\n *\n * See https://vairified.github.io/vairified.js for full documentation.\n *\n * @packageDocumentation\n * @module vairified\n */\n\n// ---- Client ----\nexport { ENVIRONMENTS, Vairified } from './client.js';\n// ---- Error classes ----\nexport {\n AuthenticationError,\n NotFoundError,\n OAuthError,\n RateLimitError,\n VairifiedError,\n ValidationError,\n} from './errors.js';\n\n// ---- Response models ----\nexport {\n MatchBatchResult,\n Member,\n MemberSportMap,\n RatingUpdate,\n SportRating,\n} from './models/index.js';\nexport type {\n AuthorizationResponse,\n OAuthConfig,\n OAuthScope,\n TokenResponse,\n} from './oauth.js';\n// ---- OAuth helpers ----\nexport {\n DEFAULT_SCOPES,\n describeScope,\n describeScopes,\n generateState,\n getAuthorizationUrl,\n SCOPES,\n validateScope,\n} from './oauth.js';\n// ---- Resource classes (typically accessed via client.*) ----\nexport {\n LeaderboardResource,\n MatchesResource,\n MembersResource,\n OAuthResource,\n} from './resources/index.js';\n// ---- Types (request shapes + wire types) ----\nexport type {\n ApiErrorResponse,\n GameInput,\n Gender,\n LeaderboardOptions,\n MatchBatch,\n MatchBatchResultWire,\n MatchInput,\n MemberStatusWire,\n PartnerMemberWire,\n PartnerRatingUpdateWire,\n PlayerRankOptions,\n RatingSplitWire,\n SearchFilters,\n SportRatingWire,\n VairifiedEnvironment,\n VairifiedOptions,\n} from './types.js';\n","/**\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 MatchesResource} — bulk match submission.\n *\n * @module\n */\n\nimport type { HttpTransport } from '../http.js';\nimport { MatchBatchResult } from '../models/match-batch-result.js';\nimport type { MatchBatch, MatchBatchResultWire } 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 `match:submit`\n * scope via OAuth (unless your API key has the\n * `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 /** 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 } 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 readonly #splits: ReadonlyMap<string, RatingSplitWire>;\n\n constructor(wire: SportRatingWire) {\n this.rating = wire.rating;\n this.abbr = wire.abbr;\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 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.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 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 type { HttpTransport, QueryParams } from '../http.js';\nimport { Member } from '../models/member.js';\nimport { RatingUpdate } from '../models/rating-update.js';\nimport type { PartnerMemberWire, PartnerRatingUpdateWire, SearchFilters } from '../types.js';\n\nconst DEFAULT_PAGE_SIZE = 20;\nconst MAX_PAGE_SIZE = 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> = { id: 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 * 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 `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[] = ['profile:read', 'rating:read']; // ok\n * const bad: OAuthScope[] = ['profile:read', 'rating']; // type error\n * ```\n *\n * @category OAuth\n */\nexport type OAuthScope =\n | 'profile:read'\n | 'profile:email'\n | 'rating:read'\n | 'rating:history'\n | 'match:submit'\n | '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 'profile:read': 'Access your name, location, and verification status',\n 'profile:email': 'Access your email address',\n 'rating:read': 'View your current rating and rating splits',\n 'rating:history': 'View your complete rating history',\n 'match:submit': 'Submit match results on your behalf',\n '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(['profile:read', 'rating:read']);\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\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 const params = new URLSearchParams({\n redirect_uri: config.redirectUri,\n scope: scopeList.join(','),\n response_type: 'code',\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 (available in Node 19+ and all modern\n * browsers). The returned string is URL-safe base64 of 32 random bytes.\n *\n * @category OAuth\n */\nexport function generateState(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers (used by resources/oauth.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure `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('profile:read')) {\n return scopes;\n }\n return ['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 authorizationUrl?: string;\n code?: string;\n }>({\n method: 'POST',\n path: '/partner/oauth/authorize',\n body: {\n redirectUri: options.redirectUri,\n scope: scopeList.join(','),\n state: options.state,\n },\n });\n\n return {\n authorizationUrl: data?.authorizationUrl ?? '',\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 body: { code: options.code, redirectUri: options.redirectUri },\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 body: { 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: { 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 const scopeRaw = (data?.scope ?? '') as string;\n const scopeList = scopeRaw.length > 0 ? scopeRaw.split(',') : [];\n return {\n accessToken: (data?.accessToken ?? '') as string,\n refreshToken: (data?.refreshToken ?? null) as string | null,\n expiresIn: (data?.expiresIn ?? 3600) as number,\n scope: Object.freeze(scopeList),\n playerId: (data?.playerId ?? '') as string,\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} 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 * 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\n readonly #transport: HttpTransport;\n\n constructor(options: VairifiedOptions = {}) {\n const apiKey = options.apiKey ?? process.env.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 (process.env.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 }\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":[]}
|