propline 0.17.0 → 0.19.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 +2 -0
- package/dist/index.cjs +21 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -2
- package/dist/index.d.ts +68 -2
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -395,6 +395,8 @@ for (const e of hist.entries) {
|
|
|
395
395
|
```ts
|
|
396
396
|
// Rolling over/under hit-rates per market: last 5/10/20/50 graded games,
|
|
397
397
|
// current streak, and the most recent game. Omit `market` for all markets.
|
|
398
|
+
// Pass `dfsOddsType: "standard" | "goblin" | "demon"` to compute the trend
|
|
399
|
+
// against that PrizePicks flavor's line only.
|
|
398
400
|
const trends = await client.getPlayerTrends("baseball_mlb", "Aaron Judge", {
|
|
399
401
|
market: "batter_total_bases",
|
|
400
402
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -223,6 +223,23 @@ var PropLine = class {
|
|
|
223
223
|
{ params: { days_from: options.daysFrom ?? 3 } }
|
|
224
224
|
);
|
|
225
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg
|
|
228
|
+
* breakeven win probability for each play. Pass `legWinProb` to also get
|
|
229
|
+
* `expected_return` (per $1) and `is_plus_ev` per play — turning a slip
|
|
230
|
+
* into the hit rate it actually needs to clear.
|
|
231
|
+
*
|
|
232
|
+
* These are PrizePicks's *standard* published payouts; demon/goblin per-pick
|
|
233
|
+
* modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven
|
|
234
|
+
* assumes independent legs. See the `disclaimer` field on the response.
|
|
235
|
+
*/
|
|
236
|
+
getDfsPayouts(options = {}) {
|
|
237
|
+
const params = {
|
|
238
|
+
platform: options.platform ?? "prizepicks"
|
|
239
|
+
};
|
|
240
|
+
if (options.legWinProb !== void 0) params.leg_win_prob = options.legWinProb;
|
|
241
|
+
return this._request("GET", "/dfs/payouts", { params });
|
|
242
|
+
}
|
|
226
243
|
/**
|
|
227
244
|
* Synthetic MLB Grand Salami for a given UTC date — total runs scored
|
|
228
245
|
* across every MLB game on the slate, plus each book's implied Grand
|
|
@@ -380,6 +397,9 @@ var PropLine = class {
|
|
|
380
397
|
if (options.market) {
|
|
381
398
|
params.market = options.market;
|
|
382
399
|
}
|
|
400
|
+
if (options.dfsOddsType) {
|
|
401
|
+
params.dfs_odds_type = options.dfsOddsType;
|
|
402
|
+
}
|
|
383
403
|
return this._request(
|
|
384
404
|
"GET",
|
|
385
405
|
`/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,
|
|
@@ -714,7 +734,7 @@ var Bookmakers = {
|
|
|
714
734
|
POLYMARKET: "polymarket",
|
|
715
735
|
PRIZEPICKS: "prizepicks"
|
|
716
736
|
};
|
|
717
|
-
var VERSION = "0.
|
|
737
|
+
var VERSION = "0.19.0";
|
|
718
738
|
// Annotate the CommonJS export names for ESM import in node:
|
|
719
739
|
0 && (module.exports = {
|
|
720
740
|
AuthError,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["/**\n * PropLine — Node/TypeScript SDK for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const events = await client.getEvents(\"basketball_nba\");\n * const odds = await client.getOdds(\"basketball_nba\", {\n * eventId: events[0].id,\n * markets: [\"player_points\", \"player_rebounds\"],\n * });\n * ```\n */\n\nexport { PropLine } from \"./client.js\";\nexport {\n PropLineError,\n AuthError,\n RateLimitError,\n} from \"./client.js\";\nexport type {\n PropLineOptions,\n GetOddsOptions,\n GetOddsHistoryOptions,\n GetOddsClosingOptions,\n PeriodFilter,\n GetScoresOptions,\n GetMlbGrandSalamiOptions,\n GetNhlDailyGoalsTotalOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetPlayerTrendsOptions,\n GetEventEvOptions,\n GetEventBestLineOptions,\n CalcEventEvOptions,\n ExportResolvedPropsOptions,\n ExportOddsHistoryOptions,\n WebhookEventType,\n CreateWebhookOptions,\n UpdateWebhookOptions,\n ListWebhookDeliveriesOptions,\n VerifySignatureOptions,\n} from \"./client.js\";\n\nexport type {\n Sport,\n Event,\n Outcome,\n ResolvedOutcome,\n Market,\n Bookmaker,\n OddsResponse,\n MarketSummary,\n OutcomeSnapshot,\n OddsHistoryOutcome,\n OddsHistoryMarket,\n OddsHistoryBookmaker,\n OddsHistoryResponse,\n ClosingOutcome,\n ClosingMarket,\n ClosingBookmaker,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiBook,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalBook,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n ResolutionSummarySport,\n ResolutionSummaryMarket,\n PlayerStat,\n StatsResponse,\n WeatherInfo,\n ContextResponse,\n MovementOutcome,\n MovementMarket,\n MovementBookmaker,\n SteamMove,\n MovementResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n HitRateSplit,\n TrendStreak,\n TrendLastGame,\n PlayerMarketTrend,\n PlayerTrends,\n EvOutcome,\n EvLine,\n EventEvResponse,\n EventEvCalcResponse,\n BestPrice,\n BestLineSide,\n BestLine,\n EventBestLineResponse,\n FuturesOutcome,\n FuturesMarket,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n} from \"./types.js\";\n\n/** String constants for bookmaker keys in odds responses. */\nexport const Bookmakers = {\n BOVADA: \"bovada\",\n DRAFTKINGS: \"draftkings\",\n FANDUEL: \"fanduel\",\n PINNACLE: \"pinnacle\",\n UNIBET: \"unibet\",\n UNDERDOG: \"underdog\",\n KALSHI: \"kalshi\",\n POLYMARKET: \"polymarket\",\n PRIZEPICKS: \"prizepicks\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.17.0\";\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport type {\n Sport,\n Event as PropLineEvent,\n OddsResponse,\n MarketSummary,\n OddsHistoryResponse,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n StatsResponse,\n ContextResponse,\n MovementResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n PlayerTrends,\n EventEvResponse,\n EventEvCalcResponse,\n EventBestLineResponse,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n} from \"./types.js\";\n\n/** Base error for all PropLine API failures. */\nexport class PropLineError extends Error {\n readonly statusCode: number;\n readonly detail: string;\n\n constructor(statusCode: number, detail: string) {\n super(`[${statusCode}] ${detail}`);\n this.name = \"PropLineError\";\n this.statusCode = statusCode;\n this.detail = detail;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the API key is missing or invalid (HTTP 401). */\nexport class AuthError extends PropLineError {\n constructor(detail = \"Invalid API key\") {\n super(401, detail);\n this.name = \"AuthError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the daily request limit is exceeded (HTTP 429). */\nexport class RateLimitError extends PropLineError {\n constructor(detail = \"Rate limit exceeded\") {\n super(429, detail);\n this.name = \"RateLimitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport interface PropLineOptions {\n /** API base URL. Default: `https://api.prop-line.com/v1`. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Default: 15000. */\n timeoutMs?: number;\n /** Custom fetch implementation. Defaults to global `fetch` (Node 18+). */\n fetch?: typeof fetch;\n}\n\n/**\n * Game-period filter. String of canonical codes, optionally\n * comma-separated, or the sentinel `\"all\"`. Omitted = full-game\n * markets only (backwards-compatible default).\n *\n * \"q1\" — 1st quarter\n * \"q1,q2\" — 1st and 2nd quarters\n * [\"q1\",\"q2\"] — same, as an array\n * \"h1\" — 1st half\n * \"p1\"|\"p2\"|\"p3\" — hockey periods\n * \"i6\" — 6th inning\n * \"f3\"|\"f5\"|\"f7\" — first N innings\n * \"all\" — every period including full game\n */\nexport type PeriodFilter = string | string[];\n\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /**\n * Market keys to filter by. If omitted, the bulk `/odds` endpoint\n * defaults to `h2h` and the per-event `/odds` endpoint defaults to\n * `h2h,spreads,totals` — game-line markets every book carries across\n * every sport. Pass an explicit list to fetch player props (e.g.\n * `[\"pitcher_strikeouts\", \"batter_home_runs\"]` for MLB,\n * `[\"player_points\", \"player_rebounds\"]` for NBA).\n */\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n /** ISO timestamp; only include snapshots at or after this time. Mutually exclusive with `relativeFrom`. */\n from?: string;\n /** ISO timestamp; only include snapshots at or before this time. Mutually exclusive with `relativeTo`. */\n to?: string;\n /** Offset relative to commence_time, e.g. \"-3h\", \"-30m\", \"-90s\". Mutually exclusive with `from`. */\n relativeFrom?: string;\n /** Offset relative to commence_time, e.g. \"-1m\" or \"0\" for commence_time itself. Mutually exclusive with `to`. */\n relativeTo?: string;\n /** Downsample to one snapshot per bucket. Latest snapshot in each bucket wins. */\n interval?: \"30s\" | \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\";\n /** When true, drop snapshots whose (price, point) match the previous one. Opening line is always kept. */\n changesOnly?: boolean;\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsClosingOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetMovementOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nfunction _periodParam(p: PeriodFilter | undefined): string | undefined {\n if (p === undefined) return undefined;\n return typeof p === \"string\" ? p : p.join(\",\");\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\n}\n\nexport interface GetMlbGrandSalamiOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetNhlDailyGoalsTotalOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetStatsOptions {\n /** Stat types to filter by (e.g. `[\"strikeouts\", \"hits\"]`). */\n statType?: string[];\n}\n\nexport interface GetResultsOptions {\n markets?: string[];\n}\n\nexport interface GetPlayerHistoryOptions {\n /** Market key (e.g. `\"pitcher_strikeouts\"`). Required. */\n market: string;\n /** Restrict to a single bookmaker (e.g. `\"draftkings\"`). */\n bookmaker?: string;\n /** Max entries (1-100). Default 20. */\n limit?: number;\n}\n\nexport interface GetPlayerTrendsOptions {\n /** Market key (e.g. `\"batter_total_bases\"`). Omit for all markets. */\n market?: string;\n}\n\nexport interface GetEventEvOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"batter_hits\"]`).\n * Omit to evaluate every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface GetEventBestLineOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"h2h\"]`). Omit\n * to include every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface ExportOddsHistoryOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `recorded_at`. */\n since?: string;\n /** ISO datetime upper bound on `recorded_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\n/** Webhook event types. `steam` = cross-book sharp-money alert. */\nexport type WebhookEventType = \"line_movement\" | \"resolution\" | \"steam\";\n\nexport interface CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n /** Minimum % change in American odds to fire a line_movement. Point-only shifts always pass. */\n minPriceChangePct?: number;\n /** Minimum 0-100 steam score to fire a `steam` event. Null = detector's global floor. */\n minSteamScore?: number;\n}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: number;\n minSteamScore?: number;\n active?: boolean;\n}\n\nexport interface ListWebhookDeliveriesOptions {\n /** Max deliveries to return. Default 50. */\n limit?: number;\n}\n\nexport interface VerifySignatureOptions {\n /** Webhook signing secret (returned once from `createWebhook`). */\n secret: string;\n /** Value of the `X-PropLine-Timestamp` header. */\n timestamp: string;\n /** Raw request body. */\n body: Uint8Array | Buffer | string;\n /** Value of the `X-PropLine-Signature` header. */\n signature: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com/v1\";\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Client for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const sports = await client.getSports();\n * ```\n */\nexport class PropLine {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n private readonly _fetch: typeof fetch;\n\n constructor(apiKey: string, options: PropLineOptions = {}) {\n if (!apiKey) {\n throw new Error(\"PropLine: apiKey is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this._fetch = options.fetch ?? globalThis.fetch;\n if (!this._fetch) {\n throw new Error(\n \"PropLine: global fetch is unavailable. Use Node 18+ or pass options.fetch.\"\n );\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private _buildUrl(path: string, params?: Record<string, string | number | undefined>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) {\n url.searchParams.set(k, String(v));\n }\n }\n }\n return url.toString();\n }\n\n private async _request<T>(\n method: string,\n path: string,\n init: { params?: Record<string, string | number | undefined>; body?: unknown } = {}\n ): Promise<T> {\n const url = this._buildUrl(path, init.params);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method,\n headers: {\n \"X-API-Key\": this.apiKey,\n ...(init.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: init.body !== undefined ? JSON.stringify(init.body) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError(await readDetail(resp, \"Invalid API key\"));\n }\n if (resp.status === 429) {\n throw new RateLimitError(await readDetail(resp, \"Rate limit exceeded\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n if (resp.status === 204) {\n return undefined as T;\n }\n return (await resp.json()) as T;\n }\n\n // ------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------\n\n /** List all available sports. */\n getSports(): Promise<Sport[]> {\n return this._request<Sport[]>(\"GET\", \"/sports\");\n }\n\n /** List upcoming events for a sport. */\n getEvents(sport: string): Promise<PropLineEvent[]> {\n return this._request<PropLineEvent[]>(\"GET\", `/sports/${encodeURIComponent(sport)}/events`);\n }\n\n /**\n * Get current odds. With `eventId`, returns single-event odds (including\n * player props). Without, returns bulk odds for all upcoming events.\n *\n * Each response carries a `bookmakers` array — iterate it to compare\n * lines across Bovada, DraftKings, FanDuel, Pinnacle, Unibet, and\n * PrizePicks (coverage varies by sport).\n */\n getOdds(\n sport: string,\n options: GetOddsOptions & { eventId: number | string }\n ): Promise<OddsResponse>;\n getOdds(\n sport: string,\n options?: Omit<GetOddsOptions, \"eventId\"> & { eventId?: undefined }\n ): Promise<OddsResponse[]>;\n getOdds(\n sport: string,\n options: GetOddsOptions = {}\n ): Promise<OddsResponse | OddsResponse[]> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam = _periodParam(options.period);\n if (periodParam !== undefined) params.period = periodParam;\n const sp = encodeURIComponent(sport);\n if (options.eventId !== undefined) {\n return this._request<OddsResponse>(\n \"GET\",\n `/sports/${sp}/events/${encodeURIComponent(String(options.eventId))}/odds`,\n { params }\n );\n }\n return this._request<OddsResponse[]>(\"GET\", `/sports/${sp}/odds`, { params });\n }\n\n /** List the available market types for an event. */\n getMarkets(sport: string, eventId: number | string): Promise<MarketSummary[]> {\n return this._request<MarketSummary[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/markets`\n );\n }\n\n /**\n * Get historical odds movement for an event.\n *\n * Hobby+: full snapshots. Free tier: redacted (snapshot counts only).\n *\n * Supports period-historical filters:\n * - `from` / `to` — absolute ISO timestamps\n * - `relativeFrom` / `relativeTo` — offsets to commence_time (\"-3h\", \"-30m\", \"0\")\n * - `interval` — downsample to a fixed bucket size\n * - `changesOnly` — drop unchanged adjacent snapshots\n */\n getOddsHistory(\n sport: string,\n eventId: number | string,\n options: GetOddsHistoryOptions = {}\n ): Promise<OddsHistoryResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n if (options.from !== undefined) params.from = options.from;\n if (options.to !== undefined) params.to = options.to;\n if (options.relativeFrom !== undefined) params.relative_from = options.relativeFrom;\n if (options.relativeTo !== undefined) params.relative_to = options.relativeTo;\n if (options.interval !== undefined) params.interval = options.interval;\n if (options.changesOnly) params.changes_only = \"true\";\n const periodParam2 = _periodParam(options.period);\n if (periodParam2 !== undefined) params.period = periodParam2;\n return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\n { params }\n );\n }\n\n /**\n * Get the closing line per `(book, market, outcome)` for an event —\n * the last snapshot at or before commence_time. Canonical CLV helper:\n * replaces \"fetch full history → find latest pre-game row\" with one\n * call. Each outcome carries a `closingAt` field with the snapshot's\n * recorded_at timestamp.\n *\n * Hobby+: full data. Free tier: redacted.\n */\n getOddsClosing(\n sport: string,\n eventId: number | string,\n options: GetOddsClosingOptions = {}\n ): Promise<OddsClosingResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam3 = _periodParam(options.period);\n if (periodParam3 !== undefined) params.period = periodParam3;\n return this._request<OddsClosingResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/closing`,\n { params }\n );\n }\n\n /** Get game scores and status (free tier). */\n getScores(sport: string, options: GetScoresOptions = {}): Promise<ScoreEvent[]> {\n return this._request<ScoreEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/scores`,\n { params: { days_from: options.daysFrom ?? 3 } }\n );\n }\n\n /**\n * Synthetic MLB Grand Salami for a given UTC date — total runs scored\n * across every MLB game on the slate, plus each book's implied Grand\n * Salami line (median of per-game primary totals across our MLB books).\n *\n * No retail sportsbook quotes this as a single market, so historical\n * cross-book Grand Salami data isn't available elsewhere. Free tier;\n * defaults to today (UTC).\n */\n getMlbGrandSalami(\n options: GetMlbGrandSalamiOptions = {}\n ): Promise<MlbGrandSalamiResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<MlbGrandSalamiResponse>(\n \"GET\",\n \"/sports/baseball_mlb/grand-salami\",\n { params }\n );\n }\n\n /**\n * Synthetic NHL Daily Goals Total for a given UTC date — total goals\n * scored (incl. OT/SO) across every NHL game on the slate, plus each\n * book's implied Daily Goals Total line (median of per-game primary\n * totals across our NHL books).\n *\n * Hockey's equivalent of the MLB Grand Salami. No retail sportsbook\n * quotes this as a single market. Free tier; defaults to today (UTC).\n */\n getNhlDailyGoalsTotal(\n options: GetNhlDailyGoalsTotalOptions = {}\n ): Promise<NhlDailyGoalsTotalResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<NhlDailyGoalsTotalResponse>(\n \"GET\",\n \"/sports/hockey_nhl/daily-goals-total\",\n { params }\n );\n }\n\n /**\n * Factual volume of graded player props over the last N days (free tier).\n *\n * Aggregated counts only — a coverage proof (every outcome counted was\n * graded against the real box score), never a profitability claim.\n *\n * @param days Look-back window, 1-90 (default 30).\n */\n getResolutionSummary(days = 30): Promise<ResolutionSummary> {\n return this._request<ResolutionSummary>(\n \"GET\",\n \"/markets/resolution-summary\",\n { params: { days: String(days) } }\n );\n }\n\n /**\n * Get raw player/team box-score stats (book-agnostic, free tier).\n *\n * Returns actual stat values decoupled from any bookmaker's lines.\n */\n getStats(\n sport: string,\n eventId: number | string,\n options: GetStatsOptions = {}\n ): Promise<StatsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.statType?.length) {\n params.stat_type = options.statType.join(\",\");\n }\n return this._request<StatsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/stats`,\n { params }\n );\n }\n\n /**\n * Get game context — the conditions a prop settles under.\n *\n * For MLB: probable starting pitchers and their throwing hand\n * (`home_probable_pitcher_hand` / `away_probable_pitcher_hand`, \"L\"/\"R\"/\"S\"\n * — platoon-split context for every batter prop), a confirmed-lineup flag,\n * the home-plate umpire, and first-pitch weather (outdoor / open-roof\n * venues; indoor venues return `weather: null` with `is_indoor: true`).\n * For NFL & NCAAF: the venue and kickoff weather (pitcher/umpire/lineup\n * fields are null for football). The same block is embedded in\n * {@link getResults}, so every graded prop carries its conditions — unique\n * to PropLine. Free tier. Rejects with a 404 when no context is on file\n * for the event yet.\n */\n getContext(\n sport: string,\n eventId: number | string\n ): Promise<ContextResponse> {\n return this._request<ContextResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/context`\n );\n }\n\n /**\n * Get line movement + steam detection from the snapshot tick history.\n *\n * Per (book, market, outcome): opening line, latest line, signed\n * implied-probability shift, point shift, direction. The `steam` array\n * flags outcomes multiple books moved the same direction — the\n * sharp-money signal across every book PropLine polls. When a book moves\n * the line itself, that outcome's `prob_shift` is null and `direction` is\n * `\"line_moved\"` (excluded from the steam signal). Unique to PropLine.\n * Hobby+ full; free tier redacted.\n */\n getMovement(\n sport: string,\n eventId: number | string,\n options: GetMovementOptions = {}\n ): Promise<MovementResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n params.period = _periodParam(options.period);\n return this._request<MovementResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/movement`,\n { params }\n );\n }\n\n /**\n * Get resolved prop outcomes with actual player stats.\n *\n * Pro tier: full data. Free tier: redacted (resolution + actual nulled).\n */\n getResults(\n sport: string,\n eventId: number | string,\n options: GetResultsOptions = {}\n ): Promise<ResultsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n return this._request<ResultsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/results`,\n { params }\n );\n }\n\n /**\n * Recent resolved prop history for a player on a market.\n *\n * One entry per (event, bookmaker) pair. Pro: full. Free: redacted.\n */\n getPlayerHistory(\n sport: string,\n playerName: string,\n options: GetPlayerHistoryOptions\n ): Promise<PlayerHistoryResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n limit: options.limit ?? 20,\n };\n if (options.bookmaker) {\n params.bookmaker = options.bookmaker;\n }\n return this._request<PlayerHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/players/${encodeURIComponent(playerName)}/history`,\n { params }\n );\n }\n\n /**\n * Rolling hit-rate trends for a player across one or all markets.\n *\n * Returns over/under/push splits over the last 5/10/20/50 graded games,\n * the current streak, and the most recent game per market. Pro: full.\n * Free: redacted.\n */\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n options: GetPlayerTrendsOptions = {}\n ): Promise<PlayerTrends> {\n const params: Record<string, string | undefined> = {};\n if (options.market) {\n params.market = options.market;\n }\n return this._request<PlayerTrends>(\n \"GET\",\n `/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,\n { params }\n );\n }\n\n /**\n * Cross-book +EV analysis for a single event (Pro+ tier).\n *\n * Groups every outcome by (market, player, line) across the books we\n * carry, derives a no-vig fair line from a sharp anchor (Pinnacle\n * preferred, Bovada fallback), and returns EV% per book at the same\n * line. Outcomes are sorted with +EV plays floated to the top.\n *\n * PrizePicks is excluded — its synthetic +100/+100 prices aren't\n * payout odds. Lines without sharp-anchor coverage are dropped.\n *\n * @example\n * ```ts\n * const ev = await client.getEventEv(\"baseball_mlb\", 12345);\n * for (const line of ev.lines) {\n * const plus = line.outcomes.filter(o => o.is_plus_ev);\n * if (plus.length) console.log(line.market_key, line.description, plus);\n * }\n * ```\n */\n /**\n * List futures markets for a sport — championship winner, MVP,\n * division winner, season win totals, etc. Each row is one (futures\n * event, book, market) with every team or player priced. Free tier;\n * aggregated across each book's futures feed (Bovada, FanDuel,\n * DraftKings, and Pinnacle).\n *\n * @example\n * ```ts\n * const futures = await client.getFutures(\"baseball_mlb\");\n * for (const event of futures) {\n * console.log(`${event.title} @ ${event.commence_time}`);\n * for (const m of event.markets) {\n * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);\n * for (const o of top3) console.log(` ${o.name}: ${o.price}`);\n * }\n * }\n * ```\n */\n getFutures(sport: string): Promise<FuturesEvent[]> {\n return this._request<FuturesEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/futures`\n );\n }\n\n getEventEv(\n sport: string,\n eventId: number | string,\n options: GetEventEvOptions = {}\n ): Promise<EventEvResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventEvResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev`,\n { params }\n );\n }\n\n /**\n * Cross-book best-line lookup for a single event.\n *\n * For each (market, player, line) tuple, returns the single best\n * American price across every book we carry, with the book name\n * attached. Companion to `getEventEv`: best-line tells you which\n * book has the highest payout right now; +EV tells you whether\n * that price beats a sharp no-vig fair line. Most line shoppers\n * want both.\n *\n * PrizePicks is excluded from the comparison — its DFS payout\n * structure (synthetic +100/+100 quotes) isn't directly comparable\n * to traditional sportsbook odds.\n *\n * Hobby tier or higher required (returns 403 on free).\n *\n * @example\n * ```ts\n * const bl = await client.getEventBestLine(\"baseball_mlb\", 12345);\n * for (const line of bl.lines) {\n * for (const [side, info] of Object.entries(line.sides)) {\n * console.log(\n * `${line.description} ${side} ${line.point}: ` +\n * `${info.best.price} @ ${info.best.book_title}`\n * );\n * }\n * }\n * ```\n */\n getEventBestLine(\n sport: string,\n eventId: number | string,\n options: GetEventBestLineOptions = {}\n ): Promise<EventBestLineResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventBestLineResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/best-line`,\n { params }\n );\n }\n\n /**\n * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n /**\n * Bulk CSV export of the full line-movement time-series.\n *\n * One row per (outcome, snapshot): every recorded odds snapshot (price +\n * line, per book, including period markets), not just the closing line.\n * This is the raw tick history no subscription tier can pull in bulk —\n * Pro/Streaming get per-event {@link getOddsHistory} only; this bulk\n * firehose is exclusive to the one-time Historical Backfill pass and\n * Enterprise.\n *\n * A full archive runs to gigabytes per sport — page month by month with\n * `since`/`until`. If `outPath` is provided, streams to disk and resolves\n * to the path; otherwise resolves to the CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportOddsHistory({\n * sport: \"baseball_mlb\",\n * since: \"2026-04-01T00:00:00Z\",\n * until: \"2026-05-01T00:00:00Z\",\n * outPath: \"./mlb-line-history-apr.csv\",\n * });\n * ```\n */\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath: string }\n ): Promise<string>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/odds-history\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(\n 403,\n await readDetail(resp, \"Historical Backfill pass or Enterprise required\")\n );\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // Webhooks (Streaming tier)\n // ------------------------------------------------------------------\n\n /**\n * Register a webhook subscription. Streaming tier only.\n *\n * The returned object includes the full signing `secret` — this is the\n * ONLY time it's revealed. Store it securely.\n */\n createWebhook(options: CreateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"POST\", \"/webhooks\", {\n body: webhookBody(options),\n });\n }\n\n /** List your webhook subscriptions. Secrets are masked. */\n listWebhooks(): Promise<Webhook[]> {\n return this._request<Webhook[]>(\"GET\", \"/webhooks\");\n }\n\n /** Get a single webhook subscription. Secret is masked. */\n getWebhook(webhookId: number): Promise<Webhook> {\n return this._request<Webhook>(\"GET\", `/webhooks/${webhookId}`);\n }\n\n /** Update fields on a webhook. Only supplied fields are changed. */\n updateWebhook(webhookId: number, options: UpdateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"PATCH\", `/webhooks/${webhookId}`, {\n body: webhookBody(options),\n });\n }\n\n /** Delete a webhook (cascades its delivery history). */\n deleteWebhook(webhookId: number): Promise<{ ok: boolean } | unknown> {\n return this._request(\"DELETE\", `/webhooks/${webhookId}`);\n }\n\n /** Queue a sample `test` payload to the webhook's URL. */\n testWebhook(webhookId: number): Promise<unknown> {\n return this._request(\"POST\", `/webhooks/${webhookId}/test`);\n }\n\n /** Last 50 (default) delivery attempts for a webhook. */\n listWebhookDeliveries(\n webhookId: number,\n options: ListWebhookDeliveriesOptions = {}\n ): Promise<WebhookDelivery[]> {\n return this._request<WebhookDelivery[]>(\n \"GET\",\n `/webhooks/${webhookId}/deliveries`,\n { params: { limit: options.limit ?? 50 } }\n );\n }\n\n /**\n * Verify that an inbound webhook delivery was signed by PropLine.\n *\n * Compares HMAC-SHA256(secret, `${timestamp}.` + body) against the\n * `X-PropLine-Signature` header in constant time.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * app.post(\"/hooks/propline\", express.raw({ type: \"*\\/*\" }), (req, res) => {\n * const ok = PropLine.verifySignature({\n * secret: process.env.WEBHOOK_SECRET!,\n * timestamp: req.header(\"X-PropLine-Timestamp\")!,\n * body: req.body, // raw Buffer\n * signature: req.header(\"X-PropLine-Signature\")!,\n * });\n * if (!ok) return res.status(401).end();\n * // ...\n * });\n * ```\n */\n static verifySignature(options: VerifySignatureOptions): boolean {\n const { secret, timestamp, body, signature } = options;\n const bodyBuf =\n typeof body === \"string\"\n ? Buffer.from(body, \"utf8\")\n : body instanceof Buffer\n ? body\n : Buffer.from(body);\n const message = Buffer.concat([Buffer.from(`${timestamp}.`, \"utf8\"), bodyBuf]);\n const expected = createHmac(\"sha256\", secret).update(message).digest(\"hex\");\n if (expected.length !== signature.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected, \"hex\"), Buffer.from(signature, \"hex\"));\n } catch {\n return false;\n }\n }\n}\n\nfunction webhookBody(options: CreateWebhookOptions | UpdateWebhookOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n const map: Array<[keyof (CreateWebhookOptions & UpdateWebhookOptions), string]> = [\n [\"url\", \"url\"],\n [\"events\", \"events\"],\n [\"filterSportKey\", \"filter_sport_key\"],\n [\"filterEventId\", \"filter_event_id\"],\n [\"filterMarketKey\", \"filter_market_key\"],\n [\"filterPlayerName\", \"filter_player_name\"],\n [\"minPriceChangePct\", \"min_price_change_pct\"],\n [\"minSteamScore\", \"min_steam_score\"],\n [\"active\", \"active\"],\n ];\n for (const [src, dst] of map) {\n const v = (options as Record<string, unknown>)[src as string];\n if (v !== undefined) body[dst] = v;\n }\n return body;\n}\n\nasync function readDetail(resp: Response, fallback: string): Promise<string> {\n try {\n const text = await resp.text();\n if (!text) return fallback;\n try {\n const json = JSON.parse(text) as { detail?: unknown };\n if (typeof json.detail === \"string\") return json.detail;\n } catch {\n // not JSON\n }\n return text || fallback;\n } catch {\n return fallback;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["/**\n * PropLine — Node/TypeScript SDK for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const events = await client.getEvents(\"basketball_nba\");\n * const odds = await client.getOdds(\"basketball_nba\", {\n * eventId: events[0].id,\n * markets: [\"player_points\", \"player_rebounds\"],\n * });\n * ```\n */\n\nexport { PropLine } from \"./client.js\";\nexport {\n PropLineError,\n AuthError,\n RateLimitError,\n} from \"./client.js\";\nexport type {\n PropLineOptions,\n GetOddsOptions,\n GetOddsHistoryOptions,\n GetOddsClosingOptions,\n PeriodFilter,\n GetScoresOptions,\n GetDfsPayoutsOptions,\n GetMlbGrandSalamiOptions,\n GetNhlDailyGoalsTotalOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetPlayerTrendsOptions,\n GetEventEvOptions,\n GetEventBestLineOptions,\n CalcEventEvOptions,\n ExportResolvedPropsOptions,\n ExportOddsHistoryOptions,\n WebhookEventType,\n CreateWebhookOptions,\n UpdateWebhookOptions,\n ListWebhookDeliveriesOptions,\n VerifySignatureOptions,\n} from \"./client.js\";\n\nexport type {\n Sport,\n Event,\n Outcome,\n ResolvedOutcome,\n Market,\n Bookmaker,\n OddsResponse,\n MarketSummary,\n OutcomeSnapshot,\n OddsHistoryOutcome,\n OddsHistoryMarket,\n OddsHistoryBookmaker,\n OddsHistoryResponse,\n ClosingOutcome,\n ClosingMarket,\n ClosingBookmaker,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiBook,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalBook,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n ResolutionSummarySport,\n ResolutionSummaryMarket,\n PlayerStat,\n StatsResponse,\n WeatherInfo,\n ContextResponse,\n MovementOutcome,\n MovementMarket,\n MovementBookmaker,\n SteamMove,\n MovementResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n HitRateSplit,\n TrendStreak,\n TrendLastGame,\n PlayerMarketTrend,\n PlayerTrends,\n EvOutcome,\n EvLine,\n EventEvResponse,\n EventEvCalcResponse,\n BestPrice,\n BestLineSide,\n BestLine,\n EventBestLineResponse,\n FuturesOutcome,\n FuturesMarket,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n DfsPayoutTier,\n DfsPlayPayout,\n DfsPayoutsResponse,\n} from \"./types.js\";\n\n/** String constants for bookmaker keys in odds responses. */\nexport const Bookmakers = {\n BOVADA: \"bovada\",\n DRAFTKINGS: \"draftkings\",\n FANDUEL: \"fanduel\",\n PINNACLE: \"pinnacle\",\n UNIBET: \"unibet\",\n UNDERDOG: \"underdog\",\n KALSHI: \"kalshi\",\n POLYMARKET: \"polymarket\",\n PRIZEPICKS: \"prizepicks\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.19.0\";\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport type {\n Sport,\n Event as PropLineEvent,\n OddsResponse,\n MarketSummary,\n OddsHistoryResponse,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n StatsResponse,\n ContextResponse,\n MovementResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n PlayerTrends,\n EventEvResponse,\n EventEvCalcResponse,\n EventBestLineResponse,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n DfsPayoutsResponse,\n} from \"./types.js\";\n\n/** Options for {@link PropLineClient.getDfsPayouts}. */\nexport interface GetDfsPayoutsOptions {\n /** DFS platform. Only \"prizepicks\" today. */\n platform?: string;\n /**\n * Assumed per-leg win probability in [0, 1]. When supplied, each play\n * also carries `expected_return` (per $1) and `is_plus_ev` at that rate.\n */\n legWinProb?: number;\n}\n\n/** Base error for all PropLine API failures. */\nexport class PropLineError extends Error {\n readonly statusCode: number;\n readonly detail: string;\n\n constructor(statusCode: number, detail: string) {\n super(`[${statusCode}] ${detail}`);\n this.name = \"PropLineError\";\n this.statusCode = statusCode;\n this.detail = detail;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the API key is missing or invalid (HTTP 401). */\nexport class AuthError extends PropLineError {\n constructor(detail = \"Invalid API key\") {\n super(401, detail);\n this.name = \"AuthError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the daily request limit is exceeded (HTTP 429). */\nexport class RateLimitError extends PropLineError {\n constructor(detail = \"Rate limit exceeded\") {\n super(429, detail);\n this.name = \"RateLimitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport interface PropLineOptions {\n /** API base URL. Default: `https://api.prop-line.com/v1`. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Default: 15000. */\n timeoutMs?: number;\n /** Custom fetch implementation. Defaults to global `fetch` (Node 18+). */\n fetch?: typeof fetch;\n}\n\n/**\n * Game-period filter. String of canonical codes, optionally\n * comma-separated, or the sentinel `\"all\"`. Omitted = full-game\n * markets only (backwards-compatible default).\n *\n * \"q1\" — 1st quarter\n * \"q1,q2\" — 1st and 2nd quarters\n * [\"q1\",\"q2\"] — same, as an array\n * \"h1\" — 1st half\n * \"p1\"|\"p2\"|\"p3\" — hockey periods\n * \"i6\" — 6th inning\n * \"f3\"|\"f5\"|\"f7\" — first N innings\n * \"all\" — every period including full game\n */\nexport type PeriodFilter = string | string[];\n\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /**\n * Market keys to filter by. If omitted, the bulk `/odds` endpoint\n * defaults to `h2h` and the per-event `/odds` endpoint defaults to\n * `h2h,spreads,totals` — game-line markets every book carries across\n * every sport. Pass an explicit list to fetch player props (e.g.\n * `[\"pitcher_strikeouts\", \"batter_home_runs\"]` for MLB,\n * `[\"player_points\", \"player_rebounds\"]` for NBA).\n */\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n /** ISO timestamp; only include snapshots at or after this time. Mutually exclusive with `relativeFrom`. */\n from?: string;\n /** ISO timestamp; only include snapshots at or before this time. Mutually exclusive with `relativeTo`. */\n to?: string;\n /** Offset relative to commence_time, e.g. \"-3h\", \"-30m\", \"-90s\". Mutually exclusive with `from`. */\n relativeFrom?: string;\n /** Offset relative to commence_time, e.g. \"-1m\" or \"0\" for commence_time itself. Mutually exclusive with `to`. */\n relativeTo?: string;\n /** Downsample to one snapshot per bucket. Latest snapshot in each bucket wins. */\n interval?: \"30s\" | \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\";\n /** When true, drop snapshots whose (price, point) match the previous one. Opening line is always kept. */\n changesOnly?: boolean;\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsClosingOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetMovementOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nfunction _periodParam(p: PeriodFilter | undefined): string | undefined {\n if (p === undefined) return undefined;\n return typeof p === \"string\" ? p : p.join(\",\");\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\n}\n\nexport interface GetMlbGrandSalamiOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetNhlDailyGoalsTotalOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetStatsOptions {\n /** Stat types to filter by (e.g. `[\"strikeouts\", \"hits\"]`). */\n statType?: string[];\n}\n\nexport interface GetResultsOptions {\n markets?: string[];\n}\n\nexport interface GetPlayerHistoryOptions {\n /** Market key (e.g. `\"pitcher_strikeouts\"`). Required. */\n market: string;\n /** Restrict to a single bookmaker (e.g. `\"draftkings\"`). */\n bookmaker?: string;\n /** Max entries (1-100). Default 20. */\n limit?: number;\n}\n\nexport interface GetPlayerTrendsOptions {\n /** Market key (e.g. `\"batter_total_bases\"`). Omit for all markets. */\n market?: string;\n /**\n * PrizePicks pick-em flavor to compute trends against: `\"standard\"`\n * (default market line), `\"goblin\"`, or `\"demon\"`. When set, the trend is\n * computed against that flavor's PrizePicks line only. Omit for the default\n * cross-book behavior. Flavor tagging began 2026-06-16.\n */\n dfsOddsType?: \"standard\" | \"goblin\" | \"demon\";\n}\n\nexport interface GetEventEvOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"batter_hits\"]`).\n * Omit to evaluate every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface GetEventBestLineOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"h2h\"]`). Omit\n * to include every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface ExportOddsHistoryOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `recorded_at`. */\n since?: string;\n /** ISO datetime upper bound on `recorded_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\n/** Webhook event types. `steam` = cross-book sharp-money alert. */\nexport type WebhookEventType = \"line_movement\" | \"resolution\" | \"steam\";\n\nexport interface CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n /** Minimum % change in American odds to fire a line_movement. Point-only shifts always pass. */\n minPriceChangePct?: number;\n /** Minimum 0-100 steam score to fire a `steam` event. Null = detector's global floor. */\n minSteamScore?: number;\n}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: number;\n minSteamScore?: number;\n active?: boolean;\n}\n\nexport interface ListWebhookDeliveriesOptions {\n /** Max deliveries to return. Default 50. */\n limit?: number;\n}\n\nexport interface VerifySignatureOptions {\n /** Webhook signing secret (returned once from `createWebhook`). */\n secret: string;\n /** Value of the `X-PropLine-Timestamp` header. */\n timestamp: string;\n /** Raw request body. */\n body: Uint8Array | Buffer | string;\n /** Value of the `X-PropLine-Signature` header. */\n signature: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com/v1\";\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Client for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const sports = await client.getSports();\n * ```\n */\nexport class PropLine {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n private readonly _fetch: typeof fetch;\n\n constructor(apiKey: string, options: PropLineOptions = {}) {\n if (!apiKey) {\n throw new Error(\"PropLine: apiKey is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this._fetch = options.fetch ?? globalThis.fetch;\n if (!this._fetch) {\n throw new Error(\n \"PropLine: global fetch is unavailable. Use Node 18+ or pass options.fetch.\"\n );\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private _buildUrl(path: string, params?: Record<string, string | number | undefined>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) {\n url.searchParams.set(k, String(v));\n }\n }\n }\n return url.toString();\n }\n\n private async _request<T>(\n method: string,\n path: string,\n init: { params?: Record<string, string | number | undefined>; body?: unknown } = {}\n ): Promise<T> {\n const url = this._buildUrl(path, init.params);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method,\n headers: {\n \"X-API-Key\": this.apiKey,\n ...(init.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: init.body !== undefined ? JSON.stringify(init.body) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError(await readDetail(resp, \"Invalid API key\"));\n }\n if (resp.status === 429) {\n throw new RateLimitError(await readDetail(resp, \"Rate limit exceeded\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n if (resp.status === 204) {\n return undefined as T;\n }\n return (await resp.json()) as T;\n }\n\n // ------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------\n\n /** List all available sports. */\n getSports(): Promise<Sport[]> {\n return this._request<Sport[]>(\"GET\", \"/sports\");\n }\n\n /** List upcoming events for a sport. */\n getEvents(sport: string): Promise<PropLineEvent[]> {\n return this._request<PropLineEvent[]>(\"GET\", `/sports/${encodeURIComponent(sport)}/events`);\n }\n\n /**\n * Get current odds. With `eventId`, returns single-event odds (including\n * player props). Without, returns bulk odds for all upcoming events.\n *\n * Each response carries a `bookmakers` array — iterate it to compare\n * lines across Bovada, DraftKings, FanDuel, Pinnacle, Unibet, and\n * PrizePicks (coverage varies by sport).\n */\n getOdds(\n sport: string,\n options: GetOddsOptions & { eventId: number | string }\n ): Promise<OddsResponse>;\n getOdds(\n sport: string,\n options?: Omit<GetOddsOptions, \"eventId\"> & { eventId?: undefined }\n ): Promise<OddsResponse[]>;\n getOdds(\n sport: string,\n options: GetOddsOptions = {}\n ): Promise<OddsResponse | OddsResponse[]> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam = _periodParam(options.period);\n if (periodParam !== undefined) params.period = periodParam;\n const sp = encodeURIComponent(sport);\n if (options.eventId !== undefined) {\n return this._request<OddsResponse>(\n \"GET\",\n `/sports/${sp}/events/${encodeURIComponent(String(options.eventId))}/odds`,\n { params }\n );\n }\n return this._request<OddsResponse[]>(\"GET\", `/sports/${sp}/odds`, { params });\n }\n\n /** List the available market types for an event. */\n getMarkets(sport: string, eventId: number | string): Promise<MarketSummary[]> {\n return this._request<MarketSummary[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/markets`\n );\n }\n\n /**\n * Get historical odds movement for an event.\n *\n * Hobby+: full snapshots. Free tier: redacted (snapshot counts only).\n *\n * Supports period-historical filters:\n * - `from` / `to` — absolute ISO timestamps\n * - `relativeFrom` / `relativeTo` — offsets to commence_time (\"-3h\", \"-30m\", \"0\")\n * - `interval` — downsample to a fixed bucket size\n * - `changesOnly` — drop unchanged adjacent snapshots\n */\n getOddsHistory(\n sport: string,\n eventId: number | string,\n options: GetOddsHistoryOptions = {}\n ): Promise<OddsHistoryResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n if (options.from !== undefined) params.from = options.from;\n if (options.to !== undefined) params.to = options.to;\n if (options.relativeFrom !== undefined) params.relative_from = options.relativeFrom;\n if (options.relativeTo !== undefined) params.relative_to = options.relativeTo;\n if (options.interval !== undefined) params.interval = options.interval;\n if (options.changesOnly) params.changes_only = \"true\";\n const periodParam2 = _periodParam(options.period);\n if (periodParam2 !== undefined) params.period = periodParam2;\n return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\n { params }\n );\n }\n\n /**\n * Get the closing line per `(book, market, outcome)` for an event —\n * the last snapshot at or before commence_time. Canonical CLV helper:\n * replaces \"fetch full history → find latest pre-game row\" with one\n * call. Each outcome carries a `closingAt` field with the snapshot's\n * recorded_at timestamp.\n *\n * Hobby+: full data. Free tier: redacted.\n */\n getOddsClosing(\n sport: string,\n eventId: number | string,\n options: GetOddsClosingOptions = {}\n ): Promise<OddsClosingResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam3 = _periodParam(options.period);\n if (periodParam3 !== undefined) params.period = periodParam3;\n return this._request<OddsClosingResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/closing`,\n { params }\n );\n }\n\n /** Get game scores and status (free tier). */\n getScores(sport: string, options: GetScoresOptions = {}): Promise<ScoreEvent[]> {\n return this._request<ScoreEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/scores`,\n { params: { days_from: options.daysFrom ?? 3 } }\n );\n }\n\n /**\n * PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg\n * breakeven win probability for each play. Pass `legWinProb` to also get\n * `expected_return` (per $1) and `is_plus_ev` per play — turning a slip\n * into the hit rate it actually needs to clear.\n *\n * These are PrizePicks's *standard* published payouts; demon/goblin per-pick\n * modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven\n * assumes independent legs. See the `disclaimer` field on the response.\n */\n getDfsPayouts(\n options: GetDfsPayoutsOptions = {}\n ): Promise<DfsPayoutsResponse> {\n const params: Record<string, string | number> = {\n platform: options.platform ?? \"prizepicks\",\n };\n if (options.legWinProb !== undefined) params.leg_win_prob = options.legWinProb;\n return this._request<DfsPayoutsResponse>(\"GET\", \"/dfs/payouts\", { params });\n }\n\n /**\n * Synthetic MLB Grand Salami for a given UTC date — total runs scored\n * across every MLB game on the slate, plus each book's implied Grand\n * Salami line (median of per-game primary totals across our MLB books).\n *\n * No retail sportsbook quotes this as a single market, so historical\n * cross-book Grand Salami data isn't available elsewhere. Free tier;\n * defaults to today (UTC).\n */\n getMlbGrandSalami(\n options: GetMlbGrandSalamiOptions = {}\n ): Promise<MlbGrandSalamiResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<MlbGrandSalamiResponse>(\n \"GET\",\n \"/sports/baseball_mlb/grand-salami\",\n { params }\n );\n }\n\n /**\n * Synthetic NHL Daily Goals Total for a given UTC date — total goals\n * scored (incl. OT/SO) across every NHL game on the slate, plus each\n * book's implied Daily Goals Total line (median of per-game primary\n * totals across our NHL books).\n *\n * Hockey's equivalent of the MLB Grand Salami. No retail sportsbook\n * quotes this as a single market. Free tier; defaults to today (UTC).\n */\n getNhlDailyGoalsTotal(\n options: GetNhlDailyGoalsTotalOptions = {}\n ): Promise<NhlDailyGoalsTotalResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<NhlDailyGoalsTotalResponse>(\n \"GET\",\n \"/sports/hockey_nhl/daily-goals-total\",\n { params }\n );\n }\n\n /**\n * Factual volume of graded player props over the last N days (free tier).\n *\n * Aggregated counts only — a coverage proof (every outcome counted was\n * graded against the real box score), never a profitability claim.\n *\n * @param days Look-back window, 1-90 (default 30).\n */\n getResolutionSummary(days = 30): Promise<ResolutionSummary> {\n return this._request<ResolutionSummary>(\n \"GET\",\n \"/markets/resolution-summary\",\n { params: { days: String(days) } }\n );\n }\n\n /**\n * Get raw player/team box-score stats (book-agnostic, free tier).\n *\n * Returns actual stat values decoupled from any bookmaker's lines.\n */\n getStats(\n sport: string,\n eventId: number | string,\n options: GetStatsOptions = {}\n ): Promise<StatsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.statType?.length) {\n params.stat_type = options.statType.join(\",\");\n }\n return this._request<StatsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/stats`,\n { params }\n );\n }\n\n /**\n * Get game context — the conditions a prop settles under.\n *\n * For MLB: probable starting pitchers and their throwing hand\n * (`home_probable_pitcher_hand` / `away_probable_pitcher_hand`, \"L\"/\"R\"/\"S\"\n * — platoon-split context for every batter prop), a confirmed-lineup flag,\n * the home-plate umpire, and first-pitch weather (outdoor / open-roof\n * venues; indoor venues return `weather: null` with `is_indoor: true`).\n * For NFL & NCAAF: the venue and kickoff weather (pitcher/umpire/lineup\n * fields are null for football). The same block is embedded in\n * {@link getResults}, so every graded prop carries its conditions — unique\n * to PropLine. Free tier. Rejects with a 404 when no context is on file\n * for the event yet.\n */\n getContext(\n sport: string,\n eventId: number | string\n ): Promise<ContextResponse> {\n return this._request<ContextResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/context`\n );\n }\n\n /**\n * Get line movement + steam detection from the snapshot tick history.\n *\n * Per (book, market, outcome): opening line, latest line, signed\n * implied-probability shift, point shift, direction. The `steam` array\n * flags outcomes multiple books moved the same direction — the\n * sharp-money signal across every book PropLine polls. When a book moves\n * the line itself, that outcome's `prob_shift` is null and `direction` is\n * `\"line_moved\"` (excluded from the steam signal). Unique to PropLine.\n * Hobby+ full; free tier redacted.\n */\n getMovement(\n sport: string,\n eventId: number | string,\n options: GetMovementOptions = {}\n ): Promise<MovementResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n params.period = _periodParam(options.period);\n return this._request<MovementResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/movement`,\n { params }\n );\n }\n\n /**\n * Get resolved prop outcomes with actual player stats.\n *\n * Pro tier: full data. Free tier: redacted (resolution + actual nulled).\n */\n getResults(\n sport: string,\n eventId: number | string,\n options: GetResultsOptions = {}\n ): Promise<ResultsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n return this._request<ResultsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/results`,\n { params }\n );\n }\n\n /**\n * Recent resolved prop history for a player on a market.\n *\n * One entry per (event, bookmaker) pair. Pro: full. Free: redacted.\n */\n getPlayerHistory(\n sport: string,\n playerName: string,\n options: GetPlayerHistoryOptions\n ): Promise<PlayerHistoryResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n limit: options.limit ?? 20,\n };\n if (options.bookmaker) {\n params.bookmaker = options.bookmaker;\n }\n return this._request<PlayerHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/players/${encodeURIComponent(playerName)}/history`,\n { params }\n );\n }\n\n /**\n * Rolling hit-rate trends for a player across one or all markets.\n *\n * Returns over/under/push splits over the last 5/10/20/50 graded games,\n * the current streak, and the most recent game per market. Pro: full.\n * Free: redacted.\n */\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n options: GetPlayerTrendsOptions = {}\n ): Promise<PlayerTrends> {\n const params: Record<string, string | undefined> = {};\n if (options.market) {\n params.market = options.market;\n }\n if (options.dfsOddsType) {\n params.dfs_odds_type = options.dfsOddsType;\n }\n return this._request<PlayerTrends>(\n \"GET\",\n `/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,\n { params }\n );\n }\n\n /**\n * Cross-book +EV analysis for a single event (Pro+ tier).\n *\n * Groups every outcome by (market, player, line) across the books we\n * carry, derives a no-vig fair line from a sharp anchor (Pinnacle\n * preferred, Bovada fallback), and returns EV% per book at the same\n * line. Outcomes are sorted with +EV plays floated to the top.\n *\n * PrizePicks is excluded — its synthetic +100/+100 prices aren't\n * payout odds. Lines without sharp-anchor coverage are dropped.\n *\n * @example\n * ```ts\n * const ev = await client.getEventEv(\"baseball_mlb\", 12345);\n * for (const line of ev.lines) {\n * const plus = line.outcomes.filter(o => o.is_plus_ev);\n * if (plus.length) console.log(line.market_key, line.description, plus);\n * }\n * ```\n */\n /**\n * List futures markets for a sport — championship winner, MVP,\n * division winner, season win totals, etc. Each row is one (futures\n * event, book, market) with every team or player priced. Free tier;\n * aggregated across each book's futures feed (Bovada, FanDuel,\n * DraftKings, and Pinnacle).\n *\n * @example\n * ```ts\n * const futures = await client.getFutures(\"baseball_mlb\");\n * for (const event of futures) {\n * console.log(`${event.title} @ ${event.commence_time}`);\n * for (const m of event.markets) {\n * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);\n * for (const o of top3) console.log(` ${o.name}: ${o.price}`);\n * }\n * }\n * ```\n */\n getFutures(sport: string): Promise<FuturesEvent[]> {\n return this._request<FuturesEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/futures`\n );\n }\n\n getEventEv(\n sport: string,\n eventId: number | string,\n options: GetEventEvOptions = {}\n ): Promise<EventEvResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventEvResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev`,\n { params }\n );\n }\n\n /**\n * Cross-book best-line lookup for a single event.\n *\n * For each (market, player, line) tuple, returns the single best\n * American price across every book we carry, with the book name\n * attached. Companion to `getEventEv`: best-line tells you which\n * book has the highest payout right now; +EV tells you whether\n * that price beats a sharp no-vig fair line. Most line shoppers\n * want both.\n *\n * PrizePicks is excluded from the comparison — its DFS payout\n * structure (synthetic +100/+100 quotes) isn't directly comparable\n * to traditional sportsbook odds.\n *\n * Hobby tier or higher required (returns 403 on free).\n *\n * @example\n * ```ts\n * const bl = await client.getEventBestLine(\"baseball_mlb\", 12345);\n * for (const line of bl.lines) {\n * for (const [side, info] of Object.entries(line.sides)) {\n * console.log(\n * `${line.description} ${side} ${line.point}: ` +\n * `${info.best.price} @ ${info.best.book_title}`\n * );\n * }\n * }\n * ```\n */\n getEventBestLine(\n sport: string,\n eventId: number | string,\n options: GetEventBestLineOptions = {}\n ): Promise<EventBestLineResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventBestLineResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/best-line`,\n { params }\n );\n }\n\n /**\n * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n /**\n * Bulk CSV export of the full line-movement time-series.\n *\n * One row per (outcome, snapshot): every recorded odds snapshot (price +\n * line, per book, including period markets), not just the closing line.\n * This is the raw tick history no subscription tier can pull in bulk —\n * Pro/Streaming get per-event {@link getOddsHistory} only; this bulk\n * firehose is exclusive to the one-time Historical Backfill pass and\n * Enterprise.\n *\n * A full archive runs to gigabytes per sport — page month by month with\n * `since`/`until`. If `outPath` is provided, streams to disk and resolves\n * to the path; otherwise resolves to the CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportOddsHistory({\n * sport: \"baseball_mlb\",\n * since: \"2026-04-01T00:00:00Z\",\n * until: \"2026-05-01T00:00:00Z\",\n * outPath: \"./mlb-line-history-apr.csv\",\n * });\n * ```\n */\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath: string }\n ): Promise<string>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/odds-history\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(\n 403,\n await readDetail(resp, \"Historical Backfill pass or Enterprise required\")\n );\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // Webhooks (Streaming tier)\n // ------------------------------------------------------------------\n\n /**\n * Register a webhook subscription. Streaming tier only.\n *\n * The returned object includes the full signing `secret` — this is the\n * ONLY time it's revealed. Store it securely.\n */\n createWebhook(options: CreateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"POST\", \"/webhooks\", {\n body: webhookBody(options),\n });\n }\n\n /** List your webhook subscriptions. Secrets are masked. */\n listWebhooks(): Promise<Webhook[]> {\n return this._request<Webhook[]>(\"GET\", \"/webhooks\");\n }\n\n /** Get a single webhook subscription. Secret is masked. */\n getWebhook(webhookId: number): Promise<Webhook> {\n return this._request<Webhook>(\"GET\", `/webhooks/${webhookId}`);\n }\n\n /** Update fields on a webhook. Only supplied fields are changed. */\n updateWebhook(webhookId: number, options: UpdateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"PATCH\", `/webhooks/${webhookId}`, {\n body: webhookBody(options),\n });\n }\n\n /** Delete a webhook (cascades its delivery history). */\n deleteWebhook(webhookId: number): Promise<{ ok: boolean } | unknown> {\n return this._request(\"DELETE\", `/webhooks/${webhookId}`);\n }\n\n /** Queue a sample `test` payload to the webhook's URL. */\n testWebhook(webhookId: number): Promise<unknown> {\n return this._request(\"POST\", `/webhooks/${webhookId}/test`);\n }\n\n /** Last 50 (default) delivery attempts for a webhook. */\n listWebhookDeliveries(\n webhookId: number,\n options: ListWebhookDeliveriesOptions = {}\n ): Promise<WebhookDelivery[]> {\n return this._request<WebhookDelivery[]>(\n \"GET\",\n `/webhooks/${webhookId}/deliveries`,\n { params: { limit: options.limit ?? 50 } }\n );\n }\n\n /**\n * Verify that an inbound webhook delivery was signed by PropLine.\n *\n * Compares HMAC-SHA256(secret, `${timestamp}.` + body) against the\n * `X-PropLine-Signature` header in constant time.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * app.post(\"/hooks/propline\", express.raw({ type: \"*\\/*\" }), (req, res) => {\n * const ok = PropLine.verifySignature({\n * secret: process.env.WEBHOOK_SECRET!,\n * timestamp: req.header(\"X-PropLine-Timestamp\")!,\n * body: req.body, // raw Buffer\n * signature: req.header(\"X-PropLine-Signature\")!,\n * });\n * if (!ok) return res.status(401).end();\n * // ...\n * });\n * ```\n */\n static verifySignature(options: VerifySignatureOptions): boolean {\n const { secret, timestamp, body, signature } = options;\n const bodyBuf =\n typeof body === \"string\"\n ? Buffer.from(body, \"utf8\")\n : body instanceof Buffer\n ? body\n : Buffer.from(body);\n const message = Buffer.concat([Buffer.from(`${timestamp}.`, \"utf8\"), bodyBuf]);\n const expected = createHmac(\"sha256\", secret).update(message).digest(\"hex\");\n if (expected.length !== signature.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected, \"hex\"), Buffer.from(signature, \"hex\"));\n } catch {\n return false;\n }\n }\n}\n\nfunction webhookBody(options: CreateWebhookOptions | UpdateWebhookOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n const map: Array<[keyof (CreateWebhookOptions & UpdateWebhookOptions), string]> = [\n [\"url\", \"url\"],\n [\"events\", \"events\"],\n [\"filterSportKey\", \"filter_sport_key\"],\n [\"filterEventId\", \"filter_event_id\"],\n [\"filterMarketKey\", \"filter_market_key\"],\n [\"filterPlayerName\", \"filter_player_name\"],\n [\"minPriceChangePct\", \"min_price_change_pct\"],\n [\"minSteamScore\", \"min_steam_score\"],\n [\"active\", \"active\"],\n ];\n for (const [src, dst] of map) {\n const v = (options as Record<string, unknown>)[src as string];\n if (v !== undefined) body[dst] = v;\n }\n return body;\n}\n\nasync function readDetail(resp: Response, fallback: string): Promise<string> {\n try {\n const text = await resp.text();\n if (!text) return fallback;\n try {\n const json = JSON.parse(text) as { detail?: unknown };\n if (typeof json.detail === \"string\") return json.detail;\n } catch {\n // not JSON\n }\n return text || fallback;\n } catch {\n return fallback;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -41,6 +41,15 @@ interface Outcome {
|
|
|
41
41
|
* overwrite it. PrizePicks publishes no numeric multiplier for these.
|
|
42
42
|
*/
|
|
43
43
|
dfs_odds_type?: "standard" | "goblin" | "demon" | null;
|
|
44
|
+
/**
|
|
45
|
+
* PropLine's observed timestamp (ISO datetime) of the last time this
|
|
46
|
+
* outcome's price actually changed. Distinct from `book_updated_at` (the
|
|
47
|
+
* book's own publish-time, which only Bovada exposes): `last_change_at` is
|
|
48
|
+
* derived by PropLine and is populated for every book, including Pinnacle
|
|
49
|
+
* and PrizePicks. Compare it across books in a single `getOdds` call to
|
|
50
|
+
* detect repricing lag without a separate `getOddsHistory` call per event.
|
|
51
|
+
*/
|
|
52
|
+
last_change_at?: string | null;
|
|
44
53
|
[k: string]: unknown;
|
|
45
54
|
}
|
|
46
55
|
interface ResolvedOutcome extends Outcome {
|
|
@@ -419,6 +428,12 @@ interface PlayerMarketTrend {
|
|
|
419
428
|
interface PlayerTrends {
|
|
420
429
|
player_name: string;
|
|
421
430
|
sport_key: string;
|
|
431
|
+
/**
|
|
432
|
+
* Echo of the `dfs_odds_type` filter that scoped these trends (PrizePicks
|
|
433
|
+
* flavor: `"standard"`/`"goblin"`/`"demon"`). `null` = cross-book,
|
|
434
|
+
* flavor-agnostic.
|
|
435
|
+
*/
|
|
436
|
+
dfs_odds_type?: string | null;
|
|
422
437
|
markets: PlayerMarketTrend[];
|
|
423
438
|
upgrade_url?: string | null;
|
|
424
439
|
[k: string]: unknown;
|
|
@@ -574,7 +589,40 @@ interface WebhookDelivery {
|
|
|
574
589
|
payload: Record<string, unknown>;
|
|
575
590
|
[k: string]: unknown;
|
|
576
591
|
}
|
|
592
|
+
interface DfsPayoutTier {
|
|
593
|
+
correct: number;
|
|
594
|
+
multiplier: number;
|
|
595
|
+
}
|
|
596
|
+
interface DfsPlayPayout {
|
|
597
|
+
play_type: "power" | "flex" | string;
|
|
598
|
+
legs: number;
|
|
599
|
+
all_correct_multiplier: number;
|
|
600
|
+
payouts: DfsPayoutTier[];
|
|
601
|
+
/** Per-leg win probability needed to break even (independent legs). */
|
|
602
|
+
breakeven_leg_win_prob: number;
|
|
603
|
+
/** Only present when leg_win_prob was supplied in the request. */
|
|
604
|
+
expected_return?: number | null;
|
|
605
|
+
is_plus_ev?: boolean | null;
|
|
606
|
+
[k: string]: unknown;
|
|
607
|
+
}
|
|
608
|
+
interface DfsPayoutsResponse {
|
|
609
|
+
platform: string;
|
|
610
|
+
leg_win_prob: number | null;
|
|
611
|
+
disclaimer: string;
|
|
612
|
+
plays: DfsPlayPayout[];
|
|
613
|
+
[k: string]: unknown;
|
|
614
|
+
}
|
|
577
615
|
|
|
616
|
+
/** Options for {@link PropLineClient.getDfsPayouts}. */
|
|
617
|
+
interface GetDfsPayoutsOptions {
|
|
618
|
+
/** DFS platform. Only "prizepicks" today. */
|
|
619
|
+
platform?: string;
|
|
620
|
+
/**
|
|
621
|
+
* Assumed per-leg win probability in [0, 1]. When supplied, each play
|
|
622
|
+
* also carries `expected_return` (per $1) and `is_plus_ev` at that rate.
|
|
623
|
+
*/
|
|
624
|
+
legWinProb?: number;
|
|
625
|
+
}
|
|
578
626
|
/** Base error for all PropLine API failures. */
|
|
579
627
|
declare class PropLineError extends Error {
|
|
580
628
|
readonly statusCode: number;
|
|
@@ -684,6 +732,13 @@ interface GetPlayerHistoryOptions {
|
|
|
684
732
|
interface GetPlayerTrendsOptions {
|
|
685
733
|
/** Market key (e.g. `"batter_total_bases"`). Omit for all markets. */
|
|
686
734
|
market?: string;
|
|
735
|
+
/**
|
|
736
|
+
* PrizePicks pick-em flavor to compute trends against: `"standard"`
|
|
737
|
+
* (default market line), `"goblin"`, or `"demon"`. When set, the trend is
|
|
738
|
+
* computed against that flavor's PrizePicks line only. Omit for the default
|
|
739
|
+
* cross-book behavior. Flavor tagging began 2026-06-16.
|
|
740
|
+
*/
|
|
741
|
+
dfsOddsType?: "standard" | "goblin" | "demon";
|
|
687
742
|
}
|
|
688
743
|
interface GetEventEvOptions {
|
|
689
744
|
/**
|
|
@@ -845,6 +900,17 @@ declare class PropLine {
|
|
|
845
900
|
getOddsClosing(sport: string, eventId: number | string, options?: GetOddsClosingOptions): Promise<OddsClosingResponse>;
|
|
846
901
|
/** Get game scores and status (free tier). */
|
|
847
902
|
getScores(sport: string, options?: GetScoresOptions): Promise<ScoreEvent[]>;
|
|
903
|
+
/**
|
|
904
|
+
* PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg
|
|
905
|
+
* breakeven win probability for each play. Pass `legWinProb` to also get
|
|
906
|
+
* `expected_return` (per $1) and `is_plus_ev` per play — turning a slip
|
|
907
|
+
* into the hit rate it actually needs to clear.
|
|
908
|
+
*
|
|
909
|
+
* These are PrizePicks's *standard* published payouts; demon/goblin per-pick
|
|
910
|
+
* modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven
|
|
911
|
+
* assumes independent legs. See the `disclaimer` field on the response.
|
|
912
|
+
*/
|
|
913
|
+
getDfsPayouts(options?: GetDfsPayoutsOptions): Promise<DfsPayoutsResponse>;
|
|
848
914
|
/**
|
|
849
915
|
* Synthetic MLB Grand Salami for a given UTC date — total runs scored
|
|
850
916
|
* across every MLB game on the slate, plus each book's implied Grand
|
|
@@ -1146,6 +1212,6 @@ declare const Bookmakers: {
|
|
|
1146
1212
|
readonly PRIZEPICKS: "prizepicks";
|
|
1147
1213
|
};
|
|
1148
1214
|
type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
|
|
1149
|
-
declare const VERSION = "0.
|
|
1215
|
+
declare const VERSION = "0.19.0";
|
|
1150
1216
|
|
|
1151
|
-
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
|
|
1217
|
+
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,15 @@ interface Outcome {
|
|
|
41
41
|
* overwrite it. PrizePicks publishes no numeric multiplier for these.
|
|
42
42
|
*/
|
|
43
43
|
dfs_odds_type?: "standard" | "goblin" | "demon" | null;
|
|
44
|
+
/**
|
|
45
|
+
* PropLine's observed timestamp (ISO datetime) of the last time this
|
|
46
|
+
* outcome's price actually changed. Distinct from `book_updated_at` (the
|
|
47
|
+
* book's own publish-time, which only Bovada exposes): `last_change_at` is
|
|
48
|
+
* derived by PropLine and is populated for every book, including Pinnacle
|
|
49
|
+
* and PrizePicks. Compare it across books in a single `getOdds` call to
|
|
50
|
+
* detect repricing lag without a separate `getOddsHistory` call per event.
|
|
51
|
+
*/
|
|
52
|
+
last_change_at?: string | null;
|
|
44
53
|
[k: string]: unknown;
|
|
45
54
|
}
|
|
46
55
|
interface ResolvedOutcome extends Outcome {
|
|
@@ -419,6 +428,12 @@ interface PlayerMarketTrend {
|
|
|
419
428
|
interface PlayerTrends {
|
|
420
429
|
player_name: string;
|
|
421
430
|
sport_key: string;
|
|
431
|
+
/**
|
|
432
|
+
* Echo of the `dfs_odds_type` filter that scoped these trends (PrizePicks
|
|
433
|
+
* flavor: `"standard"`/`"goblin"`/`"demon"`). `null` = cross-book,
|
|
434
|
+
* flavor-agnostic.
|
|
435
|
+
*/
|
|
436
|
+
dfs_odds_type?: string | null;
|
|
422
437
|
markets: PlayerMarketTrend[];
|
|
423
438
|
upgrade_url?: string | null;
|
|
424
439
|
[k: string]: unknown;
|
|
@@ -574,7 +589,40 @@ interface WebhookDelivery {
|
|
|
574
589
|
payload: Record<string, unknown>;
|
|
575
590
|
[k: string]: unknown;
|
|
576
591
|
}
|
|
592
|
+
interface DfsPayoutTier {
|
|
593
|
+
correct: number;
|
|
594
|
+
multiplier: number;
|
|
595
|
+
}
|
|
596
|
+
interface DfsPlayPayout {
|
|
597
|
+
play_type: "power" | "flex" | string;
|
|
598
|
+
legs: number;
|
|
599
|
+
all_correct_multiplier: number;
|
|
600
|
+
payouts: DfsPayoutTier[];
|
|
601
|
+
/** Per-leg win probability needed to break even (independent legs). */
|
|
602
|
+
breakeven_leg_win_prob: number;
|
|
603
|
+
/** Only present when leg_win_prob was supplied in the request. */
|
|
604
|
+
expected_return?: number | null;
|
|
605
|
+
is_plus_ev?: boolean | null;
|
|
606
|
+
[k: string]: unknown;
|
|
607
|
+
}
|
|
608
|
+
interface DfsPayoutsResponse {
|
|
609
|
+
platform: string;
|
|
610
|
+
leg_win_prob: number | null;
|
|
611
|
+
disclaimer: string;
|
|
612
|
+
plays: DfsPlayPayout[];
|
|
613
|
+
[k: string]: unknown;
|
|
614
|
+
}
|
|
577
615
|
|
|
616
|
+
/** Options for {@link PropLineClient.getDfsPayouts}. */
|
|
617
|
+
interface GetDfsPayoutsOptions {
|
|
618
|
+
/** DFS platform. Only "prizepicks" today. */
|
|
619
|
+
platform?: string;
|
|
620
|
+
/**
|
|
621
|
+
* Assumed per-leg win probability in [0, 1]. When supplied, each play
|
|
622
|
+
* also carries `expected_return` (per $1) and `is_plus_ev` at that rate.
|
|
623
|
+
*/
|
|
624
|
+
legWinProb?: number;
|
|
625
|
+
}
|
|
578
626
|
/** Base error for all PropLine API failures. */
|
|
579
627
|
declare class PropLineError extends Error {
|
|
580
628
|
readonly statusCode: number;
|
|
@@ -684,6 +732,13 @@ interface GetPlayerHistoryOptions {
|
|
|
684
732
|
interface GetPlayerTrendsOptions {
|
|
685
733
|
/** Market key (e.g. `"batter_total_bases"`). Omit for all markets. */
|
|
686
734
|
market?: string;
|
|
735
|
+
/**
|
|
736
|
+
* PrizePicks pick-em flavor to compute trends against: `"standard"`
|
|
737
|
+
* (default market line), `"goblin"`, or `"demon"`. When set, the trend is
|
|
738
|
+
* computed against that flavor's PrizePicks line only. Omit for the default
|
|
739
|
+
* cross-book behavior. Flavor tagging began 2026-06-16.
|
|
740
|
+
*/
|
|
741
|
+
dfsOddsType?: "standard" | "goblin" | "demon";
|
|
687
742
|
}
|
|
688
743
|
interface GetEventEvOptions {
|
|
689
744
|
/**
|
|
@@ -845,6 +900,17 @@ declare class PropLine {
|
|
|
845
900
|
getOddsClosing(sport: string, eventId: number | string, options?: GetOddsClosingOptions): Promise<OddsClosingResponse>;
|
|
846
901
|
/** Get game scores and status (free tier). */
|
|
847
902
|
getScores(sport: string, options?: GetScoresOptions): Promise<ScoreEvent[]>;
|
|
903
|
+
/**
|
|
904
|
+
* PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg
|
|
905
|
+
* breakeven win probability for each play. Pass `legWinProb` to also get
|
|
906
|
+
* `expected_return` (per $1) and `is_plus_ev` per play — turning a slip
|
|
907
|
+
* into the hit rate it actually needs to clear.
|
|
908
|
+
*
|
|
909
|
+
* These are PrizePicks's *standard* published payouts; demon/goblin per-pick
|
|
910
|
+
* modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven
|
|
911
|
+
* assumes independent legs. See the `disclaimer` field on the response.
|
|
912
|
+
*/
|
|
913
|
+
getDfsPayouts(options?: GetDfsPayoutsOptions): Promise<DfsPayoutsResponse>;
|
|
848
914
|
/**
|
|
849
915
|
* Synthetic MLB Grand Salami for a given UTC date — total runs scored
|
|
850
916
|
* across every MLB game on the slate, plus each book's implied Grand
|
|
@@ -1146,6 +1212,6 @@ declare const Bookmakers: {
|
|
|
1146
1212
|
readonly PRIZEPICKS: "prizepicks";
|
|
1147
1213
|
};
|
|
1148
1214
|
type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
|
|
1149
|
-
declare const VERSION = "0.
|
|
1215
|
+
declare const VERSION = "0.19.0";
|
|
1150
1216
|
|
|
1151
|
-
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
|
|
1217
|
+
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
|
package/dist/index.js
CHANGED
|
@@ -192,6 +192,23 @@ var PropLine = class {
|
|
|
192
192
|
{ params: { days_from: options.daysFrom ?? 3 } }
|
|
193
193
|
);
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg
|
|
197
|
+
* breakeven win probability for each play. Pass `legWinProb` to also get
|
|
198
|
+
* `expected_return` (per $1) and `is_plus_ev` per play — turning a slip
|
|
199
|
+
* into the hit rate it actually needs to clear.
|
|
200
|
+
*
|
|
201
|
+
* These are PrizePicks's *standard* published payouts; demon/goblin per-pick
|
|
202
|
+
* modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven
|
|
203
|
+
* assumes independent legs. See the `disclaimer` field on the response.
|
|
204
|
+
*/
|
|
205
|
+
getDfsPayouts(options = {}) {
|
|
206
|
+
const params = {
|
|
207
|
+
platform: options.platform ?? "prizepicks"
|
|
208
|
+
};
|
|
209
|
+
if (options.legWinProb !== void 0) params.leg_win_prob = options.legWinProb;
|
|
210
|
+
return this._request("GET", "/dfs/payouts", { params });
|
|
211
|
+
}
|
|
195
212
|
/**
|
|
196
213
|
* Synthetic MLB Grand Salami for a given UTC date — total runs scored
|
|
197
214
|
* across every MLB game on the slate, plus each book's implied Grand
|
|
@@ -349,6 +366,9 @@ var PropLine = class {
|
|
|
349
366
|
if (options.market) {
|
|
350
367
|
params.market = options.market;
|
|
351
368
|
}
|
|
369
|
+
if (options.dfsOddsType) {
|
|
370
|
+
params.dfs_odds_type = options.dfsOddsType;
|
|
371
|
+
}
|
|
352
372
|
return this._request(
|
|
353
373
|
"GET",
|
|
354
374
|
`/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,
|
|
@@ -683,7 +703,7 @@ var Bookmakers = {
|
|
|
683
703
|
POLYMARKET: "polymarket",
|
|
684
704
|
PRIZEPICKS: "prizepicks"
|
|
685
705
|
};
|
|
686
|
-
var VERSION = "0.
|
|
706
|
+
var VERSION = "0.19.0";
|
|
687
707
|
export {
|
|
688
708
|
AuthError,
|
|
689
709
|
Bookmakers,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport type {\n Sport,\n Event as PropLineEvent,\n OddsResponse,\n MarketSummary,\n OddsHistoryResponse,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n StatsResponse,\n ContextResponse,\n MovementResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n PlayerTrends,\n EventEvResponse,\n EventEvCalcResponse,\n EventBestLineResponse,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n} from \"./types.js\";\n\n/** Base error for all PropLine API failures. */\nexport class PropLineError extends Error {\n readonly statusCode: number;\n readonly detail: string;\n\n constructor(statusCode: number, detail: string) {\n super(`[${statusCode}] ${detail}`);\n this.name = \"PropLineError\";\n this.statusCode = statusCode;\n this.detail = detail;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the API key is missing or invalid (HTTP 401). */\nexport class AuthError extends PropLineError {\n constructor(detail = \"Invalid API key\") {\n super(401, detail);\n this.name = \"AuthError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the daily request limit is exceeded (HTTP 429). */\nexport class RateLimitError extends PropLineError {\n constructor(detail = \"Rate limit exceeded\") {\n super(429, detail);\n this.name = \"RateLimitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport interface PropLineOptions {\n /** API base URL. Default: `https://api.prop-line.com/v1`. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Default: 15000. */\n timeoutMs?: number;\n /** Custom fetch implementation. Defaults to global `fetch` (Node 18+). */\n fetch?: typeof fetch;\n}\n\n/**\n * Game-period filter. String of canonical codes, optionally\n * comma-separated, or the sentinel `\"all\"`. Omitted = full-game\n * markets only (backwards-compatible default).\n *\n * \"q1\" — 1st quarter\n * \"q1,q2\" — 1st and 2nd quarters\n * [\"q1\",\"q2\"] — same, as an array\n * \"h1\" — 1st half\n * \"p1\"|\"p2\"|\"p3\" — hockey periods\n * \"i6\" — 6th inning\n * \"f3\"|\"f5\"|\"f7\" — first N innings\n * \"all\" — every period including full game\n */\nexport type PeriodFilter = string | string[];\n\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /**\n * Market keys to filter by. If omitted, the bulk `/odds` endpoint\n * defaults to `h2h` and the per-event `/odds` endpoint defaults to\n * `h2h,spreads,totals` — game-line markets every book carries across\n * every sport. Pass an explicit list to fetch player props (e.g.\n * `[\"pitcher_strikeouts\", \"batter_home_runs\"]` for MLB,\n * `[\"player_points\", \"player_rebounds\"]` for NBA).\n */\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n /** ISO timestamp; only include snapshots at or after this time. Mutually exclusive with `relativeFrom`. */\n from?: string;\n /** ISO timestamp; only include snapshots at or before this time. Mutually exclusive with `relativeTo`. */\n to?: string;\n /** Offset relative to commence_time, e.g. \"-3h\", \"-30m\", \"-90s\". Mutually exclusive with `from`. */\n relativeFrom?: string;\n /** Offset relative to commence_time, e.g. \"-1m\" or \"0\" for commence_time itself. Mutually exclusive with `to`. */\n relativeTo?: string;\n /** Downsample to one snapshot per bucket. Latest snapshot in each bucket wins. */\n interval?: \"30s\" | \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\";\n /** When true, drop snapshots whose (price, point) match the previous one. Opening line is always kept. */\n changesOnly?: boolean;\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsClosingOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetMovementOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nfunction _periodParam(p: PeriodFilter | undefined): string | undefined {\n if (p === undefined) return undefined;\n return typeof p === \"string\" ? p : p.join(\",\");\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\n}\n\nexport interface GetMlbGrandSalamiOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetNhlDailyGoalsTotalOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetStatsOptions {\n /** Stat types to filter by (e.g. `[\"strikeouts\", \"hits\"]`). */\n statType?: string[];\n}\n\nexport interface GetResultsOptions {\n markets?: string[];\n}\n\nexport interface GetPlayerHistoryOptions {\n /** Market key (e.g. `\"pitcher_strikeouts\"`). Required. */\n market: string;\n /** Restrict to a single bookmaker (e.g. `\"draftkings\"`). */\n bookmaker?: string;\n /** Max entries (1-100). Default 20. */\n limit?: number;\n}\n\nexport interface GetPlayerTrendsOptions {\n /** Market key (e.g. `\"batter_total_bases\"`). Omit for all markets. */\n market?: string;\n}\n\nexport interface GetEventEvOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"batter_hits\"]`).\n * Omit to evaluate every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface GetEventBestLineOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"h2h\"]`). Omit\n * to include every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface ExportOddsHistoryOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `recorded_at`. */\n since?: string;\n /** ISO datetime upper bound on `recorded_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\n/** Webhook event types. `steam` = cross-book sharp-money alert. */\nexport type WebhookEventType = \"line_movement\" | \"resolution\" | \"steam\";\n\nexport interface CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n /** Minimum % change in American odds to fire a line_movement. Point-only shifts always pass. */\n minPriceChangePct?: number;\n /** Minimum 0-100 steam score to fire a `steam` event. Null = detector's global floor. */\n minSteamScore?: number;\n}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: number;\n minSteamScore?: number;\n active?: boolean;\n}\n\nexport interface ListWebhookDeliveriesOptions {\n /** Max deliveries to return. Default 50. */\n limit?: number;\n}\n\nexport interface VerifySignatureOptions {\n /** Webhook signing secret (returned once from `createWebhook`). */\n secret: string;\n /** Value of the `X-PropLine-Timestamp` header. */\n timestamp: string;\n /** Raw request body. */\n body: Uint8Array | Buffer | string;\n /** Value of the `X-PropLine-Signature` header. */\n signature: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com/v1\";\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Client for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const sports = await client.getSports();\n * ```\n */\nexport class PropLine {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n private readonly _fetch: typeof fetch;\n\n constructor(apiKey: string, options: PropLineOptions = {}) {\n if (!apiKey) {\n throw new Error(\"PropLine: apiKey is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this._fetch = options.fetch ?? globalThis.fetch;\n if (!this._fetch) {\n throw new Error(\n \"PropLine: global fetch is unavailable. Use Node 18+ or pass options.fetch.\"\n );\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private _buildUrl(path: string, params?: Record<string, string | number | undefined>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) {\n url.searchParams.set(k, String(v));\n }\n }\n }\n return url.toString();\n }\n\n private async _request<T>(\n method: string,\n path: string,\n init: { params?: Record<string, string | number | undefined>; body?: unknown } = {}\n ): Promise<T> {\n const url = this._buildUrl(path, init.params);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method,\n headers: {\n \"X-API-Key\": this.apiKey,\n ...(init.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: init.body !== undefined ? JSON.stringify(init.body) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError(await readDetail(resp, \"Invalid API key\"));\n }\n if (resp.status === 429) {\n throw new RateLimitError(await readDetail(resp, \"Rate limit exceeded\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n if (resp.status === 204) {\n return undefined as T;\n }\n return (await resp.json()) as T;\n }\n\n // ------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------\n\n /** List all available sports. */\n getSports(): Promise<Sport[]> {\n return this._request<Sport[]>(\"GET\", \"/sports\");\n }\n\n /** List upcoming events for a sport. */\n getEvents(sport: string): Promise<PropLineEvent[]> {\n return this._request<PropLineEvent[]>(\"GET\", `/sports/${encodeURIComponent(sport)}/events`);\n }\n\n /**\n * Get current odds. With `eventId`, returns single-event odds (including\n * player props). Without, returns bulk odds for all upcoming events.\n *\n * Each response carries a `bookmakers` array — iterate it to compare\n * lines across Bovada, DraftKings, FanDuel, Pinnacle, Unibet, and\n * PrizePicks (coverage varies by sport).\n */\n getOdds(\n sport: string,\n options: GetOddsOptions & { eventId: number | string }\n ): Promise<OddsResponse>;\n getOdds(\n sport: string,\n options?: Omit<GetOddsOptions, \"eventId\"> & { eventId?: undefined }\n ): Promise<OddsResponse[]>;\n getOdds(\n sport: string,\n options: GetOddsOptions = {}\n ): Promise<OddsResponse | OddsResponse[]> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam = _periodParam(options.period);\n if (periodParam !== undefined) params.period = periodParam;\n const sp = encodeURIComponent(sport);\n if (options.eventId !== undefined) {\n return this._request<OddsResponse>(\n \"GET\",\n `/sports/${sp}/events/${encodeURIComponent(String(options.eventId))}/odds`,\n { params }\n );\n }\n return this._request<OddsResponse[]>(\"GET\", `/sports/${sp}/odds`, { params });\n }\n\n /** List the available market types for an event. */\n getMarkets(sport: string, eventId: number | string): Promise<MarketSummary[]> {\n return this._request<MarketSummary[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/markets`\n );\n }\n\n /**\n * Get historical odds movement for an event.\n *\n * Hobby+: full snapshots. Free tier: redacted (snapshot counts only).\n *\n * Supports period-historical filters:\n * - `from` / `to` — absolute ISO timestamps\n * - `relativeFrom` / `relativeTo` — offsets to commence_time (\"-3h\", \"-30m\", \"0\")\n * - `interval` — downsample to a fixed bucket size\n * - `changesOnly` — drop unchanged adjacent snapshots\n */\n getOddsHistory(\n sport: string,\n eventId: number | string,\n options: GetOddsHistoryOptions = {}\n ): Promise<OddsHistoryResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n if (options.from !== undefined) params.from = options.from;\n if (options.to !== undefined) params.to = options.to;\n if (options.relativeFrom !== undefined) params.relative_from = options.relativeFrom;\n if (options.relativeTo !== undefined) params.relative_to = options.relativeTo;\n if (options.interval !== undefined) params.interval = options.interval;\n if (options.changesOnly) params.changes_only = \"true\";\n const periodParam2 = _periodParam(options.period);\n if (periodParam2 !== undefined) params.period = periodParam2;\n return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\n { params }\n );\n }\n\n /**\n * Get the closing line per `(book, market, outcome)` for an event —\n * the last snapshot at or before commence_time. Canonical CLV helper:\n * replaces \"fetch full history → find latest pre-game row\" with one\n * call. Each outcome carries a `closingAt` field with the snapshot's\n * recorded_at timestamp.\n *\n * Hobby+: full data. Free tier: redacted.\n */\n getOddsClosing(\n sport: string,\n eventId: number | string,\n options: GetOddsClosingOptions = {}\n ): Promise<OddsClosingResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam3 = _periodParam(options.period);\n if (periodParam3 !== undefined) params.period = periodParam3;\n return this._request<OddsClosingResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/closing`,\n { params }\n );\n }\n\n /** Get game scores and status (free tier). */\n getScores(sport: string, options: GetScoresOptions = {}): Promise<ScoreEvent[]> {\n return this._request<ScoreEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/scores`,\n { params: { days_from: options.daysFrom ?? 3 } }\n );\n }\n\n /**\n * Synthetic MLB Grand Salami for a given UTC date — total runs scored\n * across every MLB game on the slate, plus each book's implied Grand\n * Salami line (median of per-game primary totals across our MLB books).\n *\n * No retail sportsbook quotes this as a single market, so historical\n * cross-book Grand Salami data isn't available elsewhere. Free tier;\n * defaults to today (UTC).\n */\n getMlbGrandSalami(\n options: GetMlbGrandSalamiOptions = {}\n ): Promise<MlbGrandSalamiResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<MlbGrandSalamiResponse>(\n \"GET\",\n \"/sports/baseball_mlb/grand-salami\",\n { params }\n );\n }\n\n /**\n * Synthetic NHL Daily Goals Total for a given UTC date — total goals\n * scored (incl. OT/SO) across every NHL game on the slate, plus each\n * book's implied Daily Goals Total line (median of per-game primary\n * totals across our NHL books).\n *\n * Hockey's equivalent of the MLB Grand Salami. No retail sportsbook\n * quotes this as a single market. Free tier; defaults to today (UTC).\n */\n getNhlDailyGoalsTotal(\n options: GetNhlDailyGoalsTotalOptions = {}\n ): Promise<NhlDailyGoalsTotalResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<NhlDailyGoalsTotalResponse>(\n \"GET\",\n \"/sports/hockey_nhl/daily-goals-total\",\n { params }\n );\n }\n\n /**\n * Factual volume of graded player props over the last N days (free tier).\n *\n * Aggregated counts only — a coverage proof (every outcome counted was\n * graded against the real box score), never a profitability claim.\n *\n * @param days Look-back window, 1-90 (default 30).\n */\n getResolutionSummary(days = 30): Promise<ResolutionSummary> {\n return this._request<ResolutionSummary>(\n \"GET\",\n \"/markets/resolution-summary\",\n { params: { days: String(days) } }\n );\n }\n\n /**\n * Get raw player/team box-score stats (book-agnostic, free tier).\n *\n * Returns actual stat values decoupled from any bookmaker's lines.\n */\n getStats(\n sport: string,\n eventId: number | string,\n options: GetStatsOptions = {}\n ): Promise<StatsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.statType?.length) {\n params.stat_type = options.statType.join(\",\");\n }\n return this._request<StatsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/stats`,\n { params }\n );\n }\n\n /**\n * Get game context — the conditions a prop settles under.\n *\n * For MLB: probable starting pitchers and their throwing hand\n * (`home_probable_pitcher_hand` / `away_probable_pitcher_hand`, \"L\"/\"R\"/\"S\"\n * — platoon-split context for every batter prop), a confirmed-lineup flag,\n * the home-plate umpire, and first-pitch weather (outdoor / open-roof\n * venues; indoor venues return `weather: null` with `is_indoor: true`).\n * For NFL & NCAAF: the venue and kickoff weather (pitcher/umpire/lineup\n * fields are null for football). The same block is embedded in\n * {@link getResults}, so every graded prop carries its conditions — unique\n * to PropLine. Free tier. Rejects with a 404 when no context is on file\n * for the event yet.\n */\n getContext(\n sport: string,\n eventId: number | string\n ): Promise<ContextResponse> {\n return this._request<ContextResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/context`\n );\n }\n\n /**\n * Get line movement + steam detection from the snapshot tick history.\n *\n * Per (book, market, outcome): opening line, latest line, signed\n * implied-probability shift, point shift, direction. The `steam` array\n * flags outcomes multiple books moved the same direction — the\n * sharp-money signal across every book PropLine polls. When a book moves\n * the line itself, that outcome's `prob_shift` is null and `direction` is\n * `\"line_moved\"` (excluded from the steam signal). Unique to PropLine.\n * Hobby+ full; free tier redacted.\n */\n getMovement(\n sport: string,\n eventId: number | string,\n options: GetMovementOptions = {}\n ): Promise<MovementResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n params.period = _periodParam(options.period);\n return this._request<MovementResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/movement`,\n { params }\n );\n }\n\n /**\n * Get resolved prop outcomes with actual player stats.\n *\n * Pro tier: full data. Free tier: redacted (resolution + actual nulled).\n */\n getResults(\n sport: string,\n eventId: number | string,\n options: GetResultsOptions = {}\n ): Promise<ResultsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n return this._request<ResultsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/results`,\n { params }\n );\n }\n\n /**\n * Recent resolved prop history for a player on a market.\n *\n * One entry per (event, bookmaker) pair. Pro: full. Free: redacted.\n */\n getPlayerHistory(\n sport: string,\n playerName: string,\n options: GetPlayerHistoryOptions\n ): Promise<PlayerHistoryResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n limit: options.limit ?? 20,\n };\n if (options.bookmaker) {\n params.bookmaker = options.bookmaker;\n }\n return this._request<PlayerHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/players/${encodeURIComponent(playerName)}/history`,\n { params }\n );\n }\n\n /**\n * Rolling hit-rate trends for a player across one or all markets.\n *\n * Returns over/under/push splits over the last 5/10/20/50 graded games,\n * the current streak, and the most recent game per market. Pro: full.\n * Free: redacted.\n */\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n options: GetPlayerTrendsOptions = {}\n ): Promise<PlayerTrends> {\n const params: Record<string, string | undefined> = {};\n if (options.market) {\n params.market = options.market;\n }\n return this._request<PlayerTrends>(\n \"GET\",\n `/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,\n { params }\n );\n }\n\n /**\n * Cross-book +EV analysis for a single event (Pro+ tier).\n *\n * Groups every outcome by (market, player, line) across the books we\n * carry, derives a no-vig fair line from a sharp anchor (Pinnacle\n * preferred, Bovada fallback), and returns EV% per book at the same\n * line. Outcomes are sorted with +EV plays floated to the top.\n *\n * PrizePicks is excluded — its synthetic +100/+100 prices aren't\n * payout odds. Lines without sharp-anchor coverage are dropped.\n *\n * @example\n * ```ts\n * const ev = await client.getEventEv(\"baseball_mlb\", 12345);\n * for (const line of ev.lines) {\n * const plus = line.outcomes.filter(o => o.is_plus_ev);\n * if (plus.length) console.log(line.market_key, line.description, plus);\n * }\n * ```\n */\n /**\n * List futures markets for a sport — championship winner, MVP,\n * division winner, season win totals, etc. Each row is one (futures\n * event, book, market) with every team or player priced. Free tier;\n * aggregated across each book's futures feed (Bovada, FanDuel,\n * DraftKings, and Pinnacle).\n *\n * @example\n * ```ts\n * const futures = await client.getFutures(\"baseball_mlb\");\n * for (const event of futures) {\n * console.log(`${event.title} @ ${event.commence_time}`);\n * for (const m of event.markets) {\n * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);\n * for (const o of top3) console.log(` ${o.name}: ${o.price}`);\n * }\n * }\n * ```\n */\n getFutures(sport: string): Promise<FuturesEvent[]> {\n return this._request<FuturesEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/futures`\n );\n }\n\n getEventEv(\n sport: string,\n eventId: number | string,\n options: GetEventEvOptions = {}\n ): Promise<EventEvResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventEvResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev`,\n { params }\n );\n }\n\n /**\n * Cross-book best-line lookup for a single event.\n *\n * For each (market, player, line) tuple, returns the single best\n * American price across every book we carry, with the book name\n * attached. Companion to `getEventEv`: best-line tells you which\n * book has the highest payout right now; +EV tells you whether\n * that price beats a sharp no-vig fair line. Most line shoppers\n * want both.\n *\n * PrizePicks is excluded from the comparison — its DFS payout\n * structure (synthetic +100/+100 quotes) isn't directly comparable\n * to traditional sportsbook odds.\n *\n * Hobby tier or higher required (returns 403 on free).\n *\n * @example\n * ```ts\n * const bl = await client.getEventBestLine(\"baseball_mlb\", 12345);\n * for (const line of bl.lines) {\n * for (const [side, info] of Object.entries(line.sides)) {\n * console.log(\n * `${line.description} ${side} ${line.point}: ` +\n * `${info.best.price} @ ${info.best.book_title}`\n * );\n * }\n * }\n * ```\n */\n getEventBestLine(\n sport: string,\n eventId: number | string,\n options: GetEventBestLineOptions = {}\n ): Promise<EventBestLineResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventBestLineResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/best-line`,\n { params }\n );\n }\n\n /**\n * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n /**\n * Bulk CSV export of the full line-movement time-series.\n *\n * One row per (outcome, snapshot): every recorded odds snapshot (price +\n * line, per book, including period markets), not just the closing line.\n * This is the raw tick history no subscription tier can pull in bulk —\n * Pro/Streaming get per-event {@link getOddsHistory} only; this bulk\n * firehose is exclusive to the one-time Historical Backfill pass and\n * Enterprise.\n *\n * A full archive runs to gigabytes per sport — page month by month with\n * `since`/`until`. If `outPath` is provided, streams to disk and resolves\n * to the path; otherwise resolves to the CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportOddsHistory({\n * sport: \"baseball_mlb\",\n * since: \"2026-04-01T00:00:00Z\",\n * until: \"2026-05-01T00:00:00Z\",\n * outPath: \"./mlb-line-history-apr.csv\",\n * });\n * ```\n */\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath: string }\n ): Promise<string>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/odds-history\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(\n 403,\n await readDetail(resp, \"Historical Backfill pass or Enterprise required\")\n );\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // Webhooks (Streaming tier)\n // ------------------------------------------------------------------\n\n /**\n * Register a webhook subscription. Streaming tier only.\n *\n * The returned object includes the full signing `secret` — this is the\n * ONLY time it's revealed. Store it securely.\n */\n createWebhook(options: CreateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"POST\", \"/webhooks\", {\n body: webhookBody(options),\n });\n }\n\n /** List your webhook subscriptions. Secrets are masked. */\n listWebhooks(): Promise<Webhook[]> {\n return this._request<Webhook[]>(\"GET\", \"/webhooks\");\n }\n\n /** Get a single webhook subscription. Secret is masked. */\n getWebhook(webhookId: number): Promise<Webhook> {\n return this._request<Webhook>(\"GET\", `/webhooks/${webhookId}`);\n }\n\n /** Update fields on a webhook. Only supplied fields are changed. */\n updateWebhook(webhookId: number, options: UpdateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"PATCH\", `/webhooks/${webhookId}`, {\n body: webhookBody(options),\n });\n }\n\n /** Delete a webhook (cascades its delivery history). */\n deleteWebhook(webhookId: number): Promise<{ ok: boolean } | unknown> {\n return this._request(\"DELETE\", `/webhooks/${webhookId}`);\n }\n\n /** Queue a sample `test` payload to the webhook's URL. */\n testWebhook(webhookId: number): Promise<unknown> {\n return this._request(\"POST\", `/webhooks/${webhookId}/test`);\n }\n\n /** Last 50 (default) delivery attempts for a webhook. */\n listWebhookDeliveries(\n webhookId: number,\n options: ListWebhookDeliveriesOptions = {}\n ): Promise<WebhookDelivery[]> {\n return this._request<WebhookDelivery[]>(\n \"GET\",\n `/webhooks/${webhookId}/deliveries`,\n { params: { limit: options.limit ?? 50 } }\n );\n }\n\n /**\n * Verify that an inbound webhook delivery was signed by PropLine.\n *\n * Compares HMAC-SHA256(secret, `${timestamp}.` + body) against the\n * `X-PropLine-Signature` header in constant time.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * app.post(\"/hooks/propline\", express.raw({ type: \"*\\/*\" }), (req, res) => {\n * const ok = PropLine.verifySignature({\n * secret: process.env.WEBHOOK_SECRET!,\n * timestamp: req.header(\"X-PropLine-Timestamp\")!,\n * body: req.body, // raw Buffer\n * signature: req.header(\"X-PropLine-Signature\")!,\n * });\n * if (!ok) return res.status(401).end();\n * // ...\n * });\n * ```\n */\n static verifySignature(options: VerifySignatureOptions): boolean {\n const { secret, timestamp, body, signature } = options;\n const bodyBuf =\n typeof body === \"string\"\n ? Buffer.from(body, \"utf8\")\n : body instanceof Buffer\n ? body\n : Buffer.from(body);\n const message = Buffer.concat([Buffer.from(`${timestamp}.`, \"utf8\"), bodyBuf]);\n const expected = createHmac(\"sha256\", secret).update(message).digest(\"hex\");\n if (expected.length !== signature.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected, \"hex\"), Buffer.from(signature, \"hex\"));\n } catch {\n return false;\n }\n }\n}\n\nfunction webhookBody(options: CreateWebhookOptions | UpdateWebhookOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n const map: Array<[keyof (CreateWebhookOptions & UpdateWebhookOptions), string]> = [\n [\"url\", \"url\"],\n [\"events\", \"events\"],\n [\"filterSportKey\", \"filter_sport_key\"],\n [\"filterEventId\", \"filter_event_id\"],\n [\"filterMarketKey\", \"filter_market_key\"],\n [\"filterPlayerName\", \"filter_player_name\"],\n [\"minPriceChangePct\", \"min_price_change_pct\"],\n [\"minSteamScore\", \"min_steam_score\"],\n [\"active\", \"active\"],\n ];\n for (const [src, dst] of map) {\n const v = (options as Record<string, unknown>)[src as string];\n if (v !== undefined) body[dst] = v;\n }\n return body;\n}\n\nasync function readDetail(resp: Response, fallback: string): Promise<string> {\n try {\n const text = await resp.text();\n if (!text) return fallback;\n try {\n const json = JSON.parse(text) as { detail?: unknown };\n if (typeof json.detail === \"string\") return json.detail;\n } catch {\n // not JSON\n }\n return text || fallback;\n } catch {\n return fallback;\n }\n}\n","/**\n * PropLine — Node/TypeScript SDK for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const events = await client.getEvents(\"basketball_nba\");\n * const odds = await client.getOdds(\"basketball_nba\", {\n * eventId: events[0].id,\n * markets: [\"player_points\", \"player_rebounds\"],\n * });\n * ```\n */\n\nexport { PropLine } from \"./client.js\";\nexport {\n PropLineError,\n AuthError,\n RateLimitError,\n} from \"./client.js\";\nexport type {\n PropLineOptions,\n GetOddsOptions,\n GetOddsHistoryOptions,\n GetOddsClosingOptions,\n PeriodFilter,\n GetScoresOptions,\n GetMlbGrandSalamiOptions,\n GetNhlDailyGoalsTotalOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetPlayerTrendsOptions,\n GetEventEvOptions,\n GetEventBestLineOptions,\n CalcEventEvOptions,\n ExportResolvedPropsOptions,\n ExportOddsHistoryOptions,\n WebhookEventType,\n CreateWebhookOptions,\n UpdateWebhookOptions,\n ListWebhookDeliveriesOptions,\n VerifySignatureOptions,\n} from \"./client.js\";\n\nexport type {\n Sport,\n Event,\n Outcome,\n ResolvedOutcome,\n Market,\n Bookmaker,\n OddsResponse,\n MarketSummary,\n OutcomeSnapshot,\n OddsHistoryOutcome,\n OddsHistoryMarket,\n OddsHistoryBookmaker,\n OddsHistoryResponse,\n ClosingOutcome,\n ClosingMarket,\n ClosingBookmaker,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiBook,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalBook,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n ResolutionSummarySport,\n ResolutionSummaryMarket,\n PlayerStat,\n StatsResponse,\n WeatherInfo,\n ContextResponse,\n MovementOutcome,\n MovementMarket,\n MovementBookmaker,\n SteamMove,\n MovementResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n HitRateSplit,\n TrendStreak,\n TrendLastGame,\n PlayerMarketTrend,\n PlayerTrends,\n EvOutcome,\n EvLine,\n EventEvResponse,\n EventEvCalcResponse,\n BestPrice,\n BestLineSide,\n BestLine,\n EventBestLineResponse,\n FuturesOutcome,\n FuturesMarket,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n} from \"./types.js\";\n\n/** String constants for bookmaker keys in odds responses. */\nexport const Bookmakers = {\n BOVADA: \"bovada\",\n DRAFTKINGS: \"draftkings\",\n FANDUEL: \"fanduel\",\n PINNACLE: \"pinnacle\",\n UNIBET: \"unibet\",\n UNDERDOG: \"underdog\",\n KALSHI: \"kalshi\",\n POLYMARKET: \"polymarket\",\n PRIZEPICKS: \"prizepicks\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.17.0\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/index.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport type {\n Sport,\n Event as PropLineEvent,\n OddsResponse,\n MarketSummary,\n OddsHistoryResponse,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n StatsResponse,\n ContextResponse,\n MovementResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n PlayerTrends,\n EventEvResponse,\n EventEvCalcResponse,\n EventBestLineResponse,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n DfsPayoutsResponse,\n} from \"./types.js\";\n\n/** Options for {@link PropLineClient.getDfsPayouts}. */\nexport interface GetDfsPayoutsOptions {\n /** DFS platform. Only \"prizepicks\" today. */\n platform?: string;\n /**\n * Assumed per-leg win probability in [0, 1]. When supplied, each play\n * also carries `expected_return` (per $1) and `is_plus_ev` at that rate.\n */\n legWinProb?: number;\n}\n\n/** Base error for all PropLine API failures. */\nexport class PropLineError extends Error {\n readonly statusCode: number;\n readonly detail: string;\n\n constructor(statusCode: number, detail: string) {\n super(`[${statusCode}] ${detail}`);\n this.name = \"PropLineError\";\n this.statusCode = statusCode;\n this.detail = detail;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the API key is missing or invalid (HTTP 401). */\nexport class AuthError extends PropLineError {\n constructor(detail = \"Invalid API key\") {\n super(401, detail);\n this.name = \"AuthError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when the daily request limit is exceeded (HTTP 429). */\nexport class RateLimitError extends PropLineError {\n constructor(detail = \"Rate limit exceeded\") {\n super(429, detail);\n this.name = \"RateLimitError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport interface PropLineOptions {\n /** API base URL. Default: `https://api.prop-line.com/v1`. */\n baseUrl?: string;\n /** Request timeout in milliseconds. Default: 15000. */\n timeoutMs?: number;\n /** Custom fetch implementation. Defaults to global `fetch` (Node 18+). */\n fetch?: typeof fetch;\n}\n\n/**\n * Game-period filter. String of canonical codes, optionally\n * comma-separated, or the sentinel `\"all\"`. Omitted = full-game\n * markets only (backwards-compatible default).\n *\n * \"q1\" — 1st quarter\n * \"q1,q2\" — 1st and 2nd quarters\n * [\"q1\",\"q2\"] — same, as an array\n * \"h1\" — 1st half\n * \"p1\"|\"p2\"|\"p3\" — hockey periods\n * \"i6\" — 6th inning\n * \"f3\"|\"f5\"|\"f7\" — first N innings\n * \"all\" — every period including full game\n */\nexport type PeriodFilter = string | string[];\n\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /**\n * Market keys to filter by. If omitted, the bulk `/odds` endpoint\n * defaults to `h2h` and the per-event `/odds` endpoint defaults to\n * `h2h,spreads,totals` — game-line markets every book carries across\n * every sport. Pass an explicit list to fetch player props (e.g.\n * `[\"pitcher_strikeouts\", \"batter_home_runs\"]` for MLB,\n * `[\"player_points\", \"player_rebounds\"]` for NBA).\n */\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n /** ISO timestamp; only include snapshots at or after this time. Mutually exclusive with `relativeFrom`. */\n from?: string;\n /** ISO timestamp; only include snapshots at or before this time. Mutually exclusive with `relativeTo`. */\n to?: string;\n /** Offset relative to commence_time, e.g. \"-3h\", \"-30m\", \"-90s\". Mutually exclusive with `from`. */\n relativeFrom?: string;\n /** Offset relative to commence_time, e.g. \"-1m\" or \"0\" for commence_time itself. Mutually exclusive with `to`. */\n relativeTo?: string;\n /** Downsample to one snapshot per bucket. Latest snapshot in each bucket wins. */\n interval?: \"30s\" | \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\";\n /** When true, drop snapshots whose (price, point) match the previous one. Opening line is always kept. */\n changesOnly?: boolean;\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetOddsClosingOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nexport interface GetMovementOptions {\n markets?: string[];\n /** Game-period filter — see `PeriodFilter`. */\n period?: PeriodFilter;\n}\n\nfunction _periodParam(p: PeriodFilter | undefined): string | undefined {\n if (p === undefined) return undefined;\n return typeof p === \"string\" ? p : p.join(\",\");\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\n}\n\nexport interface GetMlbGrandSalamiOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetNhlDailyGoalsTotalOptions {\n /** YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted. */\n date?: string;\n}\n\nexport interface GetStatsOptions {\n /** Stat types to filter by (e.g. `[\"strikeouts\", \"hits\"]`). */\n statType?: string[];\n}\n\nexport interface GetResultsOptions {\n markets?: string[];\n}\n\nexport interface GetPlayerHistoryOptions {\n /** Market key (e.g. `\"pitcher_strikeouts\"`). Required. */\n market: string;\n /** Restrict to a single bookmaker (e.g. `\"draftkings\"`). */\n bookmaker?: string;\n /** Max entries (1-100). Default 20. */\n limit?: number;\n}\n\nexport interface GetPlayerTrendsOptions {\n /** Market key (e.g. `\"batter_total_bases\"`). Omit for all markets. */\n market?: string;\n /**\n * PrizePicks pick-em flavor to compute trends against: `\"standard\"`\n * (default market line), `\"goblin\"`, or `\"demon\"`. When set, the trend is\n * computed against that flavor's PrizePicks line only. Omit for the default\n * cross-book behavior. Flavor tagging began 2026-06-16.\n */\n dfsOddsType?: \"standard\" | \"goblin\" | \"demon\";\n}\n\nexport interface GetEventEvOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"batter_hits\"]`).\n * Omit to evaluate every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface GetEventBestLineOptions {\n /**\n * Optional market filter. Pass a single comma-separated string or an\n * array of market keys (e.g. `[\"pitcher_strikeouts\", \"h2h\"]`). Omit\n * to include every market on the event.\n */\n markets?: string | string[];\n}\n\nexport interface CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface ExportOddsHistoryOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `recorded_at`. */\n since?: string;\n /** ISO datetime upper bound on `recorded_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\n/** Webhook event types. `steam` = cross-book sharp-money alert. */\nexport type WebhookEventType = \"line_movement\" | \"resolution\" | \"steam\";\n\nexport interface CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n /** Minimum % change in American odds to fire a line_movement. Point-only shifts always pass. */\n minPriceChangePct?: number;\n /** Minimum 0-100 steam score to fire a `steam` event. Null = detector's global floor. */\n minSteamScore?: number;\n}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: WebhookEventType[];\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: number;\n minSteamScore?: number;\n active?: boolean;\n}\n\nexport interface ListWebhookDeliveriesOptions {\n /** Max deliveries to return. Default 50. */\n limit?: number;\n}\n\nexport interface VerifySignatureOptions {\n /** Webhook signing secret (returned once from `createWebhook`). */\n secret: string;\n /** Value of the `X-PropLine-Timestamp` header. */\n timestamp: string;\n /** Raw request body. */\n body: Uint8Array | Buffer | string;\n /** Value of the `X-PropLine-Signature` header. */\n signature: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com/v1\";\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Client for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const sports = await client.getSports();\n * ```\n */\nexport class PropLine {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n private readonly _fetch: typeof fetch;\n\n constructor(apiKey: string, options: PropLineOptions = {}) {\n if (!apiKey) {\n throw new Error(\"PropLine: apiKey is required\");\n }\n this.apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this._fetch = options.fetch ?? globalThis.fetch;\n if (!this._fetch) {\n throw new Error(\n \"PropLine: global fetch is unavailable. Use Node 18+ or pass options.fetch.\"\n );\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private _buildUrl(path: string, params?: Record<string, string | number | undefined>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) {\n url.searchParams.set(k, String(v));\n }\n }\n }\n return url.toString();\n }\n\n private async _request<T>(\n method: string,\n path: string,\n init: { params?: Record<string, string | number | undefined>; body?: unknown } = {}\n ): Promise<T> {\n const url = this._buildUrl(path, init.params);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method,\n headers: {\n \"X-API-Key\": this.apiKey,\n ...(init.body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: init.body !== undefined ? JSON.stringify(init.body) : undefined,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError(await readDetail(resp, \"Invalid API key\"));\n }\n if (resp.status === 429) {\n throw new RateLimitError(await readDetail(resp, \"Rate limit exceeded\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n if (resp.status === 204) {\n return undefined as T;\n }\n return (await resp.json()) as T;\n }\n\n // ------------------------------------------------------------------\n // Public API\n // ------------------------------------------------------------------\n\n /** List all available sports. */\n getSports(): Promise<Sport[]> {\n return this._request<Sport[]>(\"GET\", \"/sports\");\n }\n\n /** List upcoming events for a sport. */\n getEvents(sport: string): Promise<PropLineEvent[]> {\n return this._request<PropLineEvent[]>(\"GET\", `/sports/${encodeURIComponent(sport)}/events`);\n }\n\n /**\n * Get current odds. With `eventId`, returns single-event odds (including\n * player props). Without, returns bulk odds for all upcoming events.\n *\n * Each response carries a `bookmakers` array — iterate it to compare\n * lines across Bovada, DraftKings, FanDuel, Pinnacle, Unibet, and\n * PrizePicks (coverage varies by sport).\n */\n getOdds(\n sport: string,\n options: GetOddsOptions & { eventId: number | string }\n ): Promise<OddsResponse>;\n getOdds(\n sport: string,\n options?: Omit<GetOddsOptions, \"eventId\"> & { eventId?: undefined }\n ): Promise<OddsResponse[]>;\n getOdds(\n sport: string,\n options: GetOddsOptions = {}\n ): Promise<OddsResponse | OddsResponse[]> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam = _periodParam(options.period);\n if (periodParam !== undefined) params.period = periodParam;\n const sp = encodeURIComponent(sport);\n if (options.eventId !== undefined) {\n return this._request<OddsResponse>(\n \"GET\",\n `/sports/${sp}/events/${encodeURIComponent(String(options.eventId))}/odds`,\n { params }\n );\n }\n return this._request<OddsResponse[]>(\"GET\", `/sports/${sp}/odds`, { params });\n }\n\n /** List the available market types for an event. */\n getMarkets(sport: string, eventId: number | string): Promise<MarketSummary[]> {\n return this._request<MarketSummary[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/markets`\n );\n }\n\n /**\n * Get historical odds movement for an event.\n *\n * Hobby+: full snapshots. Free tier: redacted (snapshot counts only).\n *\n * Supports period-historical filters:\n * - `from` / `to` — absolute ISO timestamps\n * - `relativeFrom` / `relativeTo` — offsets to commence_time (\"-3h\", \"-30m\", \"0\")\n * - `interval` — downsample to a fixed bucket size\n * - `changesOnly` — drop unchanged adjacent snapshots\n */\n getOddsHistory(\n sport: string,\n eventId: number | string,\n options: GetOddsHistoryOptions = {}\n ): Promise<OddsHistoryResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n if (options.from !== undefined) params.from = options.from;\n if (options.to !== undefined) params.to = options.to;\n if (options.relativeFrom !== undefined) params.relative_from = options.relativeFrom;\n if (options.relativeTo !== undefined) params.relative_to = options.relativeTo;\n if (options.interval !== undefined) params.interval = options.interval;\n if (options.changesOnly) params.changes_only = \"true\";\n const periodParam2 = _periodParam(options.period);\n if (periodParam2 !== undefined) params.period = periodParam2;\n return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\n { params }\n );\n }\n\n /**\n * Get the closing line per `(book, market, outcome)` for an event —\n * the last snapshot at or before commence_time. Canonical CLV helper:\n * replaces \"fetch full history → find latest pre-game row\" with one\n * call. Each outcome carries a `closingAt` field with the snapshot's\n * recorded_at timestamp.\n *\n * Hobby+: full data. Free tier: redacted.\n */\n getOddsClosing(\n sport: string,\n eventId: number | string,\n options: GetOddsClosingOptions = {}\n ): Promise<OddsClosingResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n const periodParam3 = _periodParam(options.period);\n if (periodParam3 !== undefined) params.period = periodParam3;\n return this._request<OddsClosingResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/closing`,\n { params }\n );\n }\n\n /** Get game scores and status (free tier). */\n getScores(sport: string, options: GetScoresOptions = {}): Promise<ScoreEvent[]> {\n return this._request<ScoreEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/scores`,\n { params: { days_from: options.daysFrom ?? 3 } }\n );\n }\n\n /**\n * PrizePicks Power/Flex entry payout schedule (2-6 legs) plus the per-leg\n * breakeven win probability for each play. Pass `legWinProb` to also get\n * `expected_return` (per $1) and `is_plus_ev` per play — turning a slip\n * into the hit rate it actually needs to clear.\n *\n * These are PrizePicks's *standard* published payouts; demon/goblin per-pick\n * modifiers aren't in PrizePicks's feed, so they're not reflected. Breakeven\n * assumes independent legs. See the `disclaimer` field on the response.\n */\n getDfsPayouts(\n options: GetDfsPayoutsOptions = {}\n ): Promise<DfsPayoutsResponse> {\n const params: Record<string, string | number> = {\n platform: options.platform ?? \"prizepicks\",\n };\n if (options.legWinProb !== undefined) params.leg_win_prob = options.legWinProb;\n return this._request<DfsPayoutsResponse>(\"GET\", \"/dfs/payouts\", { params });\n }\n\n /**\n * Synthetic MLB Grand Salami for a given UTC date — total runs scored\n * across every MLB game on the slate, plus each book's implied Grand\n * Salami line (median of per-game primary totals across our MLB books).\n *\n * No retail sportsbook quotes this as a single market, so historical\n * cross-book Grand Salami data isn't available elsewhere. Free tier;\n * defaults to today (UTC).\n */\n getMlbGrandSalami(\n options: GetMlbGrandSalamiOptions = {}\n ): Promise<MlbGrandSalamiResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<MlbGrandSalamiResponse>(\n \"GET\",\n \"/sports/baseball_mlb/grand-salami\",\n { params }\n );\n }\n\n /**\n * Synthetic NHL Daily Goals Total for a given UTC date — total goals\n * scored (incl. OT/SO) across every NHL game on the slate, plus each\n * book's implied Daily Goals Total line (median of per-game primary\n * totals across our NHL books).\n *\n * Hockey's equivalent of the MLB Grand Salami. No retail sportsbook\n * quotes this as a single market. Free tier; defaults to today (UTC).\n */\n getNhlDailyGoalsTotal(\n options: GetNhlDailyGoalsTotalOptions = {}\n ): Promise<NhlDailyGoalsTotalResponse> {\n const params: Record<string, string> = {};\n if (options.date) params.date = options.date;\n return this._request<NhlDailyGoalsTotalResponse>(\n \"GET\",\n \"/sports/hockey_nhl/daily-goals-total\",\n { params }\n );\n }\n\n /**\n * Factual volume of graded player props over the last N days (free tier).\n *\n * Aggregated counts only — a coverage proof (every outcome counted was\n * graded against the real box score), never a profitability claim.\n *\n * @param days Look-back window, 1-90 (default 30).\n */\n getResolutionSummary(days = 30): Promise<ResolutionSummary> {\n return this._request<ResolutionSummary>(\n \"GET\",\n \"/markets/resolution-summary\",\n { params: { days: String(days) } }\n );\n }\n\n /**\n * Get raw player/team box-score stats (book-agnostic, free tier).\n *\n * Returns actual stat values decoupled from any bookmaker's lines.\n */\n getStats(\n sport: string,\n eventId: number | string,\n options: GetStatsOptions = {}\n ): Promise<StatsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.statType?.length) {\n params.stat_type = options.statType.join(\",\");\n }\n return this._request<StatsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/stats`,\n { params }\n );\n }\n\n /**\n * Get game context — the conditions a prop settles under.\n *\n * For MLB: probable starting pitchers and their throwing hand\n * (`home_probable_pitcher_hand` / `away_probable_pitcher_hand`, \"L\"/\"R\"/\"S\"\n * — platoon-split context for every batter prop), a confirmed-lineup flag,\n * the home-plate umpire, and first-pitch weather (outdoor / open-roof\n * venues; indoor venues return `weather: null` with `is_indoor: true`).\n * For NFL & NCAAF: the venue and kickoff weather (pitcher/umpire/lineup\n * fields are null for football). The same block is embedded in\n * {@link getResults}, so every graded prop carries its conditions — unique\n * to PropLine. Free tier. Rejects with a 404 when no context is on file\n * for the event yet.\n */\n getContext(\n sport: string,\n eventId: number | string\n ): Promise<ContextResponse> {\n return this._request<ContextResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/context`\n );\n }\n\n /**\n * Get line movement + steam detection from the snapshot tick history.\n *\n * Per (book, market, outcome): opening line, latest line, signed\n * implied-probability shift, point shift, direction. The `steam` array\n * flags outcomes multiple books moved the same direction — the\n * sharp-money signal across every book PropLine polls. When a book moves\n * the line itself, that outcome's `prob_shift` is null and `direction` is\n * `\"line_moved\"` (excluded from the steam signal). Unique to PropLine.\n * Hobby+ full; free tier redacted.\n */\n getMovement(\n sport: string,\n eventId: number | string,\n options: GetMovementOptions = {}\n ): Promise<MovementResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n params.period = _periodParam(options.period);\n return this._request<MovementResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/movement`,\n { params }\n );\n }\n\n /**\n * Get resolved prop outcomes with actual player stats.\n *\n * Pro tier: full data. Free tier: redacted (resolution + actual nulled).\n */\n getResults(\n sport: string,\n eventId: number | string,\n options: GetResultsOptions = {}\n ): Promise<ResultsResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets?.length) {\n params.markets = options.markets.join(\",\");\n }\n return this._request<ResultsResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/results`,\n { params }\n );\n }\n\n /**\n * Recent resolved prop history for a player on a market.\n *\n * One entry per (event, bookmaker) pair. Pro: full. Free: redacted.\n */\n getPlayerHistory(\n sport: string,\n playerName: string,\n options: GetPlayerHistoryOptions\n ): Promise<PlayerHistoryResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n limit: options.limit ?? 20,\n };\n if (options.bookmaker) {\n params.bookmaker = options.bookmaker;\n }\n return this._request<PlayerHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/players/${encodeURIComponent(playerName)}/history`,\n { params }\n );\n }\n\n /**\n * Rolling hit-rate trends for a player across one or all markets.\n *\n * Returns over/under/push splits over the last 5/10/20/50 graded games,\n * the current streak, and the most recent game per market. Pro: full.\n * Free: redacted.\n */\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n options: GetPlayerTrendsOptions = {}\n ): Promise<PlayerTrends> {\n const params: Record<string, string | undefined> = {};\n if (options.market) {\n params.market = options.market;\n }\n if (options.dfsOddsType) {\n params.dfs_odds_type = options.dfsOddsType;\n }\n return this._request<PlayerTrends>(\n \"GET\",\n `/sports/${encodeURIComponent(sportKey)}/players/${encodeURIComponent(playerName)}/trends`,\n { params }\n );\n }\n\n /**\n * Cross-book +EV analysis for a single event (Pro+ tier).\n *\n * Groups every outcome by (market, player, line) across the books we\n * carry, derives a no-vig fair line from a sharp anchor (Pinnacle\n * preferred, Bovada fallback), and returns EV% per book at the same\n * line. Outcomes are sorted with +EV plays floated to the top.\n *\n * PrizePicks is excluded — its synthetic +100/+100 prices aren't\n * payout odds. Lines without sharp-anchor coverage are dropped.\n *\n * @example\n * ```ts\n * const ev = await client.getEventEv(\"baseball_mlb\", 12345);\n * for (const line of ev.lines) {\n * const plus = line.outcomes.filter(o => o.is_plus_ev);\n * if (plus.length) console.log(line.market_key, line.description, plus);\n * }\n * ```\n */\n /**\n * List futures markets for a sport — championship winner, MVP,\n * division winner, season win totals, etc. Each row is one (futures\n * event, book, market) with every team or player priced. Free tier;\n * aggregated across each book's futures feed (Bovada, FanDuel,\n * DraftKings, and Pinnacle).\n *\n * @example\n * ```ts\n * const futures = await client.getFutures(\"baseball_mlb\");\n * for (const event of futures) {\n * console.log(`${event.title} @ ${event.commence_time}`);\n * for (const m of event.markets) {\n * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);\n * for (const o of top3) console.log(` ${o.name}: ${o.price}`);\n * }\n * }\n * ```\n */\n getFutures(sport: string): Promise<FuturesEvent[]> {\n return this._request<FuturesEvent[]>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/futures`\n );\n }\n\n getEventEv(\n sport: string,\n eventId: number | string,\n options: GetEventEvOptions = {}\n ): Promise<EventEvResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventEvResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev`,\n { params }\n );\n }\n\n /**\n * Cross-book best-line lookup for a single event.\n *\n * For each (market, player, line) tuple, returns the single best\n * American price across every book we carry, with the book name\n * attached. Companion to `getEventEv`: best-line tells you which\n * book has the highest payout right now; +EV tells you whether\n * that price beats a sharp no-vig fair line. Most line shoppers\n * want both.\n *\n * PrizePicks is excluded from the comparison — its DFS payout\n * structure (synthetic +100/+100 quotes) isn't directly comparable\n * to traditional sportsbook odds.\n *\n * Hobby tier or higher required (returns 403 on free).\n *\n * @example\n * ```ts\n * const bl = await client.getEventBestLine(\"baseball_mlb\", 12345);\n * for (const line of bl.lines) {\n * for (const [side, info] of Object.entries(line.sides)) {\n * console.log(\n * `${line.description} ${side} ${line.point}: ` +\n * `${info.best.price} @ ${info.best.book_title}`\n * );\n * }\n * }\n * ```\n */\n getEventBestLine(\n sport: string,\n eventId: number | string,\n options: GetEventBestLineOptions = {}\n ): Promise<EventBestLineResponse> {\n const params: Record<string, string | undefined> = {};\n if (options.markets) {\n params.markets = Array.isArray(options.markets)\n ? options.markets.join(\",\")\n : options.markets;\n }\n return this._request<EventBestLineResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/best-line`,\n { params }\n );\n }\n\n /**\n * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n /**\n * Bulk CSV export of the full line-movement time-series.\n *\n * One row per (outcome, snapshot): every recorded odds snapshot (price +\n * line, per book, including period markets), not just the closing line.\n * This is the raw tick history no subscription tier can pull in bulk —\n * Pro/Streaming get per-event {@link getOddsHistory} only; this bulk\n * firehose is exclusive to the one-time Historical Backfill pass and\n * Enterprise.\n *\n * A full archive runs to gigabytes per sport — page month by month with\n * `since`/`until`. If `outPath` is provided, streams to disk and resolves\n * to the path; otherwise resolves to the CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportOddsHistory({\n * sport: \"baseball_mlb\",\n * since: \"2026-04-01T00:00:00Z\",\n * until: \"2026-05-01T00:00:00Z\",\n * outPath: \"./mlb-line-history-apr.csv\",\n * });\n * ```\n */\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath: string }\n ): Promise<string>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportOddsHistory(\n options: ExportOddsHistoryOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/odds-history\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(\n 403,\n await readDetail(resp, \"Historical Backfill pass or Enterprise required\")\n );\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // Webhooks (Streaming tier)\n // ------------------------------------------------------------------\n\n /**\n * Register a webhook subscription. Streaming tier only.\n *\n * The returned object includes the full signing `secret` — this is the\n * ONLY time it's revealed. Store it securely.\n */\n createWebhook(options: CreateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"POST\", \"/webhooks\", {\n body: webhookBody(options),\n });\n }\n\n /** List your webhook subscriptions. Secrets are masked. */\n listWebhooks(): Promise<Webhook[]> {\n return this._request<Webhook[]>(\"GET\", \"/webhooks\");\n }\n\n /** Get a single webhook subscription. Secret is masked. */\n getWebhook(webhookId: number): Promise<Webhook> {\n return this._request<Webhook>(\"GET\", `/webhooks/${webhookId}`);\n }\n\n /** Update fields on a webhook. Only supplied fields are changed. */\n updateWebhook(webhookId: number, options: UpdateWebhookOptions): Promise<Webhook> {\n return this._request<Webhook>(\"PATCH\", `/webhooks/${webhookId}`, {\n body: webhookBody(options),\n });\n }\n\n /** Delete a webhook (cascades its delivery history). */\n deleteWebhook(webhookId: number): Promise<{ ok: boolean } | unknown> {\n return this._request(\"DELETE\", `/webhooks/${webhookId}`);\n }\n\n /** Queue a sample `test` payload to the webhook's URL. */\n testWebhook(webhookId: number): Promise<unknown> {\n return this._request(\"POST\", `/webhooks/${webhookId}/test`);\n }\n\n /** Last 50 (default) delivery attempts for a webhook. */\n listWebhookDeliveries(\n webhookId: number,\n options: ListWebhookDeliveriesOptions = {}\n ): Promise<WebhookDelivery[]> {\n return this._request<WebhookDelivery[]>(\n \"GET\",\n `/webhooks/${webhookId}/deliveries`,\n { params: { limit: options.limit ?? 50 } }\n );\n }\n\n /**\n * Verify that an inbound webhook delivery was signed by PropLine.\n *\n * Compares HMAC-SHA256(secret, `${timestamp}.` + body) against the\n * `X-PropLine-Signature` header in constant time.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * app.post(\"/hooks/propline\", express.raw({ type: \"*\\/*\" }), (req, res) => {\n * const ok = PropLine.verifySignature({\n * secret: process.env.WEBHOOK_SECRET!,\n * timestamp: req.header(\"X-PropLine-Timestamp\")!,\n * body: req.body, // raw Buffer\n * signature: req.header(\"X-PropLine-Signature\")!,\n * });\n * if (!ok) return res.status(401).end();\n * // ...\n * });\n * ```\n */\n static verifySignature(options: VerifySignatureOptions): boolean {\n const { secret, timestamp, body, signature } = options;\n const bodyBuf =\n typeof body === \"string\"\n ? Buffer.from(body, \"utf8\")\n : body instanceof Buffer\n ? body\n : Buffer.from(body);\n const message = Buffer.concat([Buffer.from(`${timestamp}.`, \"utf8\"), bodyBuf]);\n const expected = createHmac(\"sha256\", secret).update(message).digest(\"hex\");\n if (expected.length !== signature.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected, \"hex\"), Buffer.from(signature, \"hex\"));\n } catch {\n return false;\n }\n }\n}\n\nfunction webhookBody(options: CreateWebhookOptions | UpdateWebhookOptions): Record<string, unknown> {\n const body: Record<string, unknown> = {};\n const map: Array<[keyof (CreateWebhookOptions & UpdateWebhookOptions), string]> = [\n [\"url\", \"url\"],\n [\"events\", \"events\"],\n [\"filterSportKey\", \"filter_sport_key\"],\n [\"filterEventId\", \"filter_event_id\"],\n [\"filterMarketKey\", \"filter_market_key\"],\n [\"filterPlayerName\", \"filter_player_name\"],\n [\"minPriceChangePct\", \"min_price_change_pct\"],\n [\"minSteamScore\", \"min_steam_score\"],\n [\"active\", \"active\"],\n ];\n for (const [src, dst] of map) {\n const v = (options as Record<string, unknown>)[src as string];\n if (v !== undefined) body[dst] = v;\n }\n return body;\n}\n\nasync function readDetail(resp: Response, fallback: string): Promise<string> {\n try {\n const text = await resp.text();\n if (!text) return fallback;\n try {\n const json = JSON.parse(text) as { detail?: unknown };\n if (typeof json.detail === \"string\") return json.detail;\n } catch {\n // not JSON\n }\n return text || fallback;\n } catch {\n return fallback;\n }\n}\n","/**\n * PropLine — Node/TypeScript SDK for the PropLine player props API.\n *\n * @example\n * ```ts\n * import { PropLine } from \"propline\";\n *\n * const client = new PropLine(\"your_api_key\");\n * const events = await client.getEvents(\"basketball_nba\");\n * const odds = await client.getOdds(\"basketball_nba\", {\n * eventId: events[0].id,\n * markets: [\"player_points\", \"player_rebounds\"],\n * });\n * ```\n */\n\nexport { PropLine } from \"./client.js\";\nexport {\n PropLineError,\n AuthError,\n RateLimitError,\n} from \"./client.js\";\nexport type {\n PropLineOptions,\n GetOddsOptions,\n GetOddsHistoryOptions,\n GetOddsClosingOptions,\n PeriodFilter,\n GetScoresOptions,\n GetDfsPayoutsOptions,\n GetMlbGrandSalamiOptions,\n GetNhlDailyGoalsTotalOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetPlayerTrendsOptions,\n GetEventEvOptions,\n GetEventBestLineOptions,\n CalcEventEvOptions,\n ExportResolvedPropsOptions,\n ExportOddsHistoryOptions,\n WebhookEventType,\n CreateWebhookOptions,\n UpdateWebhookOptions,\n ListWebhookDeliveriesOptions,\n VerifySignatureOptions,\n} from \"./client.js\";\n\nexport type {\n Sport,\n Event,\n Outcome,\n ResolvedOutcome,\n Market,\n Bookmaker,\n OddsResponse,\n MarketSummary,\n OutcomeSnapshot,\n OddsHistoryOutcome,\n OddsHistoryMarket,\n OddsHistoryBookmaker,\n OddsHistoryResponse,\n ClosingOutcome,\n ClosingMarket,\n ClosingBookmaker,\n OddsClosingResponse,\n ScoreEvent,\n MlbGrandSalamiBook,\n MlbGrandSalamiResponse,\n NhlDailyGoalsTotalBook,\n NhlDailyGoalsTotalResponse,\n ResolutionSummary,\n ResolutionSummarySport,\n ResolutionSummaryMarket,\n PlayerStat,\n StatsResponse,\n WeatherInfo,\n ContextResponse,\n MovementOutcome,\n MovementMarket,\n MovementBookmaker,\n SteamMove,\n MovementResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n HitRateSplit,\n TrendStreak,\n TrendLastGame,\n PlayerMarketTrend,\n PlayerTrends,\n EvOutcome,\n EvLine,\n EventEvResponse,\n EventEvCalcResponse,\n BestPrice,\n BestLineSide,\n BestLine,\n EventBestLineResponse,\n FuturesOutcome,\n FuturesMarket,\n FuturesEvent,\n Webhook,\n WebhookDelivery,\n DfsPayoutTier,\n DfsPlayPayout,\n DfsPayoutsResponse,\n} from \"./types.js\";\n\n/** String constants for bookmaker keys in odds responses. */\nexport const Bookmakers = {\n BOVADA: \"bovada\",\n DRAFTKINGS: \"draftkings\",\n FANDUEL: \"fanduel\",\n PINNACLE: \"pinnacle\",\n UNIBET: \"unibet\",\n UNDERDOG: \"underdog\",\n KALSHI: \"kalshi\",\n POLYMARKET: \"polymarket\",\n PRIZEPICKS: \"prizepicks\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.19.0\";\n"],"mappings":";AAAA,SAAS,YAAY,uBAAuB;AAC5C,SAAS,iBAAiB;AAwCnB,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EAET,YAAY,YAAoB,QAAgB;AAC9C,UAAM,IAAI,UAAU,KAAK,MAAM,EAAE;AACjC,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,YAAN,cAAwB,cAAc;AAAA,EAC3C,YAAY,SAAS,mBAAmB;AACtC,UAAM,KAAK,MAAM;AACjB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,iBAAN,cAA6B,cAAc;AAAA,EAChD,YAAY,SAAS,uBAAuB;AAC1C,UAAM,KAAK,MAAM;AACjB,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAyEA,SAAS,aAAa,GAAiD;AACrE,MAAI,MAAM,OAAW,QAAO;AAC5B,SAAO,OAAO,MAAM,WAAW,IAAI,EAAE,KAAK,GAAG;AAC/C;AA0JA,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAapB,IAAM,WAAN,MAAe;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EAEjB,YAAY,QAAgB,UAA2B,CAAC,GAAG;AACzD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,SAAK,SAAS;AACd,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AACtE,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,SAAS,QAAQ,SAAS,WAAW;AAC1C,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAU,MAAc,QAA8D;AAC5F,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAG,IAAI,EAAE;AAC5C,QAAI,QAAQ;AACV,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,YAAI,MAAM,UAAa,MAAM,MAAM;AACjC,cAAI,aAAa,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI,SAAS;AAAA,EACtB;AAAA,EAEA,MAAc,SACZ,QACA,MACA,OAAiF,CAAC,GACtE;AACZ,UAAM,MAAM,KAAK,UAAU,MAAM,KAAK,MAAM;AAE5C,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEjE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK;AAAA,QAC5B;AAAA,QACA,SAAS;AAAA,UACP,aAAa,KAAK;AAAA,UAClB,GAAI,KAAK,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,QAC1E;AAAA,QACA,MAAM,KAAK,SAAS,SAAY,KAAK,UAAU,KAAK,IAAI,IAAI;AAAA,QAC5D,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI,UAAU,MAAM,WAAW,MAAM,iBAAiB,CAAC;AAAA,IAC/D;AACA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI,eAAe,MAAM,WAAW,MAAM,qBAAqB,CAAC;AAAA,IACxE;AACA,QAAI,KAAK,UAAU,KAAK;AACtB,YAAM,IAAI,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,KAAK,UAAU,CAAC;AAAA,IAC9E;AAEA,QAAI,KAAK,WAAW,KAAK;AACvB,aAAO;AAAA,IACT;AACA,WAAQ,MAAM,KAAK,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAA8B;AAC5B,WAAO,KAAK,SAAkB,OAAO,SAAS;AAAA,EAChD;AAAA;AAAA,EAGA,UAAU,OAAyC;AACjD,WAAO,KAAK,SAA0B,OAAO,WAAW,mBAAmB,KAAK,CAAC,SAAS;AAAA,EAC5F;AAAA,EAkBA,QACE,OACA,UAA0B,CAAC,GACa;AACxC,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO,UAAU,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAC3C;AACA,UAAM,cAAc,aAAa,QAAQ,MAAM;AAC/C,QAAI,gBAAgB,OAAW,QAAO,SAAS;AAC/C,UAAM,KAAK,mBAAmB,KAAK;AACnC,QAAI,QAAQ,YAAY,QAAW;AACjC,aAAO,KAAK;AAAA,QACV;AAAA,QACA,WAAW,EAAE,WAAW,mBAAmB,OAAO,QAAQ,OAAO,CAAC,CAAC;AAAA,QACnE,EAAE,OAAO;AAAA,MACX;AAAA,IACF;AACA,WAAO,KAAK,SAAyB,OAAO,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC;AAAA,EAC9E;AAAA;AAAA,EAGA,WAAW,OAAe,SAAoD;AAC5E,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,IACpF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,eACE,OACA,SACA,UAAiC,CAAC,GACJ;AAC9B,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO,UAAU,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAC3C;AACA,QAAI,QAAQ,SAAS,OAAW,QAAO,OAAO,QAAQ;AACtD,QAAI,QAAQ,OAAO,OAAW,QAAO,KAAK,QAAQ;AAClD,QAAI,QAAQ,iBAAiB,OAAW,QAAO,gBAAgB,QAAQ;AACvE,QAAI,QAAQ,eAAe,OAAW,QAAO,cAAc,QAAQ;AACnE,QAAI,QAAQ,aAAa,OAAW,QAAO,WAAW,QAAQ;AAC9D,QAAI,QAAQ,YAAa,QAAO,eAAe;AAC/C,UAAM,eAAe,aAAa,QAAQ,MAAM;AAChD,QAAI,iBAAiB,OAAW,QAAO,SAAS;AAChD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,eACE,OACA,SACA,UAAiC,CAAC,GACJ;AAC9B,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO,UAAU,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAC3C;AACA,UAAM,eAAe,aAAa,QAAQ,MAAM;AAChD,QAAI,iBAAiB,OAAW,QAAO,SAAS;AAChD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA,EAGA,UAAU,OAAe,UAA4B,CAAC,GAA0B;AAC9E,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC;AAAA,MACpC,EAAE,QAAQ,EAAE,WAAW,QAAQ,YAAY,EAAE,EAAE;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,cACE,UAAgC,CAAC,GACJ;AAC7B,UAAM,SAA0C;AAAA,MAC9C,UAAU,QAAQ,YAAY;AAAA,IAChC;AACA,QAAI,QAAQ,eAAe,OAAW,QAAO,eAAe,QAAQ;AACpE,WAAO,KAAK,SAA6B,OAAO,gBAAgB,EAAE,OAAO,CAAC;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,kBACE,UAAoC,CAAC,GACJ;AACjC,UAAM,SAAiC,CAAC;AACxC,QAAI,QAAQ,KAAM,QAAO,OAAO,QAAQ;AACxC,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,sBACE,UAAwC,CAAC,GACJ;AACrC,UAAM,SAAiC,CAAC;AACxC,QAAI,QAAQ,KAAM,QAAO,OAAO,QAAQ;AACxC,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,qBAAqB,OAAO,IAAgC;AAC1D,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,EAAE,MAAM,OAAO,IAAI,EAAE,EAAE;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SACE,OACA,SACA,UAA2B,CAAC,GACJ;AACxB,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,UAAU,QAAQ;AAC5B,aAAO,YAAY,QAAQ,SAAS,KAAK,GAAG;AAAA,IAC9C;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,WACE,OACA,SAC0B;AAC1B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,IACpF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,YACE,OACA,SACA,UAA8B,CAAC,GACJ;AAC3B,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO,UAAU,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAC3C;AACA,WAAO,SAAS,aAAa,QAAQ,MAAM;AAC3C,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WACE,OACA,SACA,UAA6B,CAAC,GACJ;AAC1B,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO,UAAU,QAAQ,QAAQ,KAAK,GAAG;AAAA,IAC3C;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBACE,OACA,YACA,SACgC;AAChC,UAAM,SAAsD;AAAA,MAC1D,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ,SAAS;AAAA,IAC1B;AACA,QAAI,QAAQ,WAAW;AACrB,aAAO,YAAY,QAAQ;AAAA,IAC7B;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,YAAY,mBAAmB,UAAU,CAAC;AAAA,MAC9E,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,gBACE,UACA,YACA,UAAkC,CAAC,GACZ;AACvB,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,QAAQ;AAClB,aAAO,SAAS,QAAQ;AAAA,IAC1B;AACA,QAAI,QAAQ,aAAa;AACvB,aAAO,gBAAgB,QAAQ;AAAA,IACjC;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,QAAQ,CAAC,YAAY,mBAAmB,UAAU,CAAC;AAAA,MACjF,EAAE,OAAO;AAAA,IACX;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyCA,WAAW,OAAwC;AACjD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,WACE,OACA,SACA,UAA6B,CAAC,GACJ;AAC1B,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS;AACnB,aAAO,UAAU,MAAM,QAAQ,QAAQ,OAAO,IAC1C,QAAQ,QAAQ,KAAK,GAAG,IACxB,QAAQ;AAAA,IACd;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;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;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,iBACE,OACA,SACA,UAAmC,CAAC,GACJ;AAChC,UAAM,SAA6C,CAAC;AACpD,QAAI,QAAQ,SAAS;AACnB,aAAO,UAAU,MAAM,QAAQ,QAAQ,OAAO,IAC1C,QAAQ,QAAQ,KAAK,GAAG,IACxB,QAAQ;AAAA,IACd;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,YACE,OACA,SACA,SAC8B;AAC9B,UAAM,SAAsD;AAAA,MAC1D,QAAQ,QAAQ;AAAA,MAChB,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ;AAAA,IACjB;AACA,QAAI,QAAQ,UAAU,OAAW,QAAO,QAAQ,QAAQ;AACxD,QAAI,QAAQ,YAAa,QAAO,cAAc,QAAQ;AACtD,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,mBAAmB,KAAK,CAAC,WAAW,mBAAmB,OAAO,OAAO,CAAC,CAAC;AAAA,MAClF,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAAA,EAwBA,MAAM,oBACJ,SAC8B;AAC9B,UAAM,SAA6C,EAAE,OAAO,QAAQ,MAAM;AAC1E,QAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAC5C,QAAI,QAAQ,UAAW,QAAO,YAAY,QAAQ;AAClD,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAE1C,UAAM,MAAM,KAAK,UAAU,2BAA2B,MAAM;AAC5D,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEjE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS,EAAE,aAAa,KAAK,OAAO;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI,UAAU;AAAA,IACtB;AACA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI,cAAc,KAAK,MAAM,WAAW,MAAM,mBAAmB,CAAC;AAAA,IAC1E;AACA,QAAI,KAAK,UAAU,KAAK;AACtB,YAAM,IAAI,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,KAAK,UAAU,CAAC;AAAA,IAC9E;AAEA,UAAM,MAAM,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;AACnD,QAAI,QAAQ,SAAS;AACnB,YAAM,UAAU,QAAQ,SAAS,GAAG;AACpC,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAAA,EAgCA,MAAM,kBACJ,SAC8B;AAC9B,UAAM,SAA6C,EAAE,OAAO,QAAQ,MAAM;AAC1E,QAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAC5C,QAAI,QAAQ,UAAW,QAAO,YAAY,QAAQ;AAClD,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAE1C,UAAM,MAAM,KAAK,UAAU,yBAAyB,MAAM;AAC1D,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEjE,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,KAAK;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS,EAAE,aAAa,KAAK,OAAO;AAAA,QACpC,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI,UAAU;AAAA,IACtB;AACA,QAAI,KAAK,WAAW,KAAK;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,MAAM,WAAW,MAAM,iDAAiD;AAAA,MAC1E;AAAA,IACF;AACA,QAAI,KAAK,UAAU,KAAK;AACtB,YAAM,IAAI,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,KAAK,UAAU,CAAC;AAAA,IAC9E;AAEA,UAAM,MAAM,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;AACnD,QAAI,QAAQ,SAAS;AACnB,YAAM,UAAU,QAAQ,SAAS,GAAG;AACpC,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,cAAc,SAAiD;AAC7D,WAAO,KAAK,SAAkB,QAAQ,aAAa;AAAA,MACjD,MAAM,YAAY,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,eAAmC;AACjC,WAAO,KAAK,SAAoB,OAAO,WAAW;AAAA,EACpD;AAAA;AAAA,EAGA,WAAW,WAAqC;AAC9C,WAAO,KAAK,SAAkB,OAAO,aAAa,SAAS,EAAE;AAAA,EAC/D;AAAA;AAAA,EAGA,cAAc,WAAmB,SAAiD;AAChF,WAAO,KAAK,SAAkB,SAAS,aAAa,SAAS,IAAI;AAAA,MAC/D,MAAM,YAAY,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,cAAc,WAAuD;AACnE,WAAO,KAAK,SAAS,UAAU,aAAa,SAAS,EAAE;AAAA,EACzD;AAAA;AAAA,EAGA,YAAY,WAAqC;AAC/C,WAAO,KAAK,SAAS,QAAQ,aAAa,SAAS,OAAO;AAAA,EAC5D;AAAA;AAAA,EAGA,sBACE,WACA,UAAwC,CAAC,GACb;AAC5B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,aAAa,SAAS;AAAA,MACtB,EAAE,QAAQ,EAAE,OAAO,QAAQ,SAAS,GAAG,EAAE;AAAA,IAC3C;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,EAwBA,OAAO,gBAAgB,SAA0C;AAC/D,UAAM,EAAE,QAAQ,WAAW,MAAM,UAAU,IAAI;AAC/C,UAAM,UACJ,OAAO,SAAS,WACZ,OAAO,KAAK,MAAM,MAAM,IACxB,gBAAgB,SACd,OACA,OAAO,KAAK,IAAI;AACxB,UAAM,UAAU,OAAO,OAAO,CAAC,OAAO,KAAK,GAAG,SAAS,KAAK,MAAM,GAAG,OAAO,CAAC;AAC7E,UAAM,WAAW,WAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1E,QAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,QAAI;AACF,aAAO,gBAAgB,OAAO,KAAK,UAAU,KAAK,GAAG,OAAO,KAAK,WAAW,KAAK,CAAC;AAAA,IACpF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,YAAY,SAA+E;AAClG,QAAM,OAAgC,CAAC;AACvC,QAAM,MAA4E;AAAA,IAChF,CAAC,OAAO,KAAK;AAAA,IACb,CAAC,UAAU,QAAQ;AAAA,IACnB,CAAC,kBAAkB,kBAAkB;AAAA,IACrC,CAAC,iBAAiB,iBAAiB;AAAA,IACnC,CAAC,mBAAmB,mBAAmB;AAAA,IACvC,CAAC,oBAAoB,oBAAoB;AAAA,IACzC,CAAC,qBAAqB,sBAAsB;AAAA,IAC5C,CAAC,iBAAiB,iBAAiB;AAAA,IACnC,CAAC,UAAU,QAAQ;AAAA,EACrB;AACA,aAAW,CAAC,KAAK,GAAG,KAAK,KAAK;AAC5B,UAAM,IAAK,QAAoC,GAAa;AAC5D,QAAI,MAAM,OAAW,MAAK,GAAG,IAAI;AAAA,EACnC;AACA,SAAO;AACT;AAEA,eAAe,WAAW,MAAgB,UAAmC;AAC3E,MAAI;AACF,UAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,UAAI,OAAO,KAAK,WAAW,SAAU,QAAO,KAAK;AAAA,IACnD,QAAQ;AAAA,IAER;AACA,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC5hCO,IAAM,aAAa;AAAA,EACxB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,YAAY;AACd;AAIO,IAAM,UAAU;","names":[]}
|