propline 0.2.0 → 0.3.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 CHANGED
@@ -221,12 +221,22 @@ for (const market of history.markets) {
221
221
  for (const outcome of market.outcomes) {
222
222
  console.log(`\n${outcome.description}:`);
223
223
  for (const snap of outcome.snapshots) {
224
- console.log(` ${snap.recorded_at}: ${snap.price} @ ${snap.point}`);
224
+ console.log(
225
+ ` ${snap.recorded_at}: ${snap.price} @ ${snap.point}` +
226
+ ` (book reported: ${snap.book_updated_at ?? "n/a"})`,
227
+ );
225
228
  }
226
229
  }
227
230
  }
228
231
  ```
229
232
 
233
+ Each snapshot carries two timestamps: `recorded_at` (when our scraper
234
+ saw the odds) and `book_updated_at` (when the book itself reports the
235
+ price was last set). `book_updated_at` is populated for books that
236
+ expose a publish-time signal (Bovada today); other books leave it
237
+ `null`. The gap between the two is per-book scraper latency. See
238
+ <https://prop-line.com/docs#timestamps> for the full semantic.
239
+
230
240
  ### Get player prop history (Pro full, Free redacted)
231
241
 
232
242
  ```ts
@@ -290,7 +300,7 @@ console.log(`got ${csv.byteLength} bytes of CSV`);
290
300
 
291
301
  ## Webhooks (Streaming tier)
292
302
 
293
- The Streaming tier ($149/mo) pushes `line_movement` and `resolution` events to your URL in real time, with HMAC-SHA256 signing and automatic retries.
303
+ The Streaming tier ($79/mo) pushes `line_movement` and `resolution` events to your URL in real time, with HMAC-SHA256 signing and automatic retries.
294
304
 
295
305
  ### Register a subscription
296
306
 
package/dist/index.cjs CHANGED
@@ -252,6 +252,30 @@ var PropLine = class {
252
252
  * }
253
253
  * ```
254
254
  */
255
+ /**
256
+ * List futures markets for a sport — championship winner, MVP,
257
+ * division winner, etc. Each row is one (futures event, book,
258
+ * market) with every team or player priced. Free tier; pulled from
259
+ * each book's futures feed (Bovada today).
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * const futures = await client.getFutures("baseball_mlb");
264
+ * for (const event of futures) {
265
+ * console.log(`${event.title} @ ${event.commence_time}`);
266
+ * for (const m of event.markets) {
267
+ * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);
268
+ * for (const o of top3) console.log(` ${o.name}: ${o.price}`);
269
+ * }
270
+ * }
271
+ * ```
272
+ */
273
+ getFutures(sport) {
274
+ return this._request(
275
+ "GET",
276
+ `/sports/${encodeURIComponent(sport)}/futures`
277
+ );
278
+ }
255
279
  getEventEv(sport, eventId, options = {}) {
256
280
  const params = {};
257
281
  if (options.markets) {
@@ -419,7 +443,7 @@ var Bookmakers = {
419
443
  PRIZEPICKS: "prizepicks",
420
444
  UNIBET: "unibet"
421
445
  };
422
- var VERSION = "0.2.0";
446
+ var VERSION = "0.3.0";
423
447
  // Annotate the CommonJS export names for ESM import in node:
424
448
  0 && (module.exports = {
425
449
  AuthError,
@@ -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 GetScoresOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetEventEvOptions,\n ExportResolvedPropsOptions,\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 OddsHistoryResponse,\n ScoreEvent,\n PlayerStat,\n StatsResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n EvOutcome,\n EvLine,\n EventEvResponse,\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 PRIZEPICKS: \"prizepicks\",\n UNIBET: \"unibet\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.2.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 ScoreEvent,\n StatsResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n EventEvResponse,\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\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /** Market keys to filter by. */\n markets?: string[];\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\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 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 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 CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: Array<\"line_movement\" | \"resolution\">;\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}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: Array<\"line_movement\" | \"resolution\">;\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: 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 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 * Pro tier: full snapshots. Free tier: redacted (snapshot counts only).\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 return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\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 * 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 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 * 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 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 * 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 // 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 [\"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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,yBAA4C;AAC5C,sBAA0B;AAkBnB,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;AA6GA,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,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,EAOA,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,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,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,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,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,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,gBAAM,2BAAU,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,eAAW,+BAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1E,QAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,QAAI;AACF,iBAAO,oCAAgB,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,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;;;ADvjBO,IAAM,aAAa;AAAA,EACxB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AACV;AAIO,IAAM,UAAU;","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 GetScoresOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetEventEvOptions,\n ExportResolvedPropsOptions,\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 OddsHistoryResponse,\n ScoreEvent,\n PlayerStat,\n StatsResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n EvOutcome,\n EvLine,\n EventEvResponse,\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 PRIZEPICKS: \"prizepicks\",\n UNIBET: \"unibet\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.3.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 ScoreEvent,\n StatsResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n EventEvResponse,\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\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /** Market keys to filter by. */\n markets?: string[];\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\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 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 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 CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: Array<\"line_movement\" | \"resolution\">;\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}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: Array<\"line_movement\" | \"resolution\">;\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: 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 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 * Pro tier: full snapshots. Free tier: redacted (snapshot counts only).\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 return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\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 * 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 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 * 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, etc. Each row is one (futures event, book,\n * market) with every team or player priced. Free tier; pulled from\n * each book's futures feed (Bovada today).\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 * 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 // 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 [\"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
@@ -193,6 +193,37 @@ interface EventEvResponse {
193
193
  lines: EvLine[];
194
194
  [k: string]: unknown;
195
195
  }
196
+ interface FuturesOutcome {
197
+ /** Team or player name. */
198
+ name: string;
199
+ /** American odds. */
200
+ price: number | null;
201
+ price_decimal: number | null;
202
+ [k: string]: unknown;
203
+ }
204
+ interface FuturesMarket {
205
+ /** Slugified description, e.g. "world_series_winner". */
206
+ key: string;
207
+ /** Original book label, e.g. "World Series Winner". */
208
+ description: string;
209
+ bookmaker: string;
210
+ bookmaker_title: string;
211
+ last_update: string;
212
+ /** When the book itself reports this market was last updated; null when the book doesn't expose a publish-time signal. */
213
+ book_updated_at: string | null;
214
+ outcomes: FuturesOutcome[];
215
+ [k: string]: unknown;
216
+ }
217
+ interface FuturesEvent {
218
+ id: string;
219
+ sport_key: string;
220
+ /** The futures title from the book, e.g. "World Series 2026". */
221
+ title: string;
222
+ /** Season-end / target resolution time. */
223
+ commence_time: string;
224
+ markets: FuturesMarket[];
225
+ [k: string]: unknown;
226
+ }
196
227
  interface Webhook {
197
228
  id: number;
198
229
  url: string;
@@ -411,6 +442,25 @@ declare class PropLine {
411
442
  * }
412
443
  * ```
413
444
  */
445
+ /**
446
+ * List futures markets for a sport — championship winner, MVP,
447
+ * division winner, etc. Each row is one (futures event, book,
448
+ * market) with every team or player priced. Free tier; pulled from
449
+ * each book's futures feed (Bovada today).
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const futures = await client.getFutures("baseball_mlb");
454
+ * for (const event of futures) {
455
+ * console.log(`${event.title} @ ${event.commence_time}`);
456
+ * for (const m of event.markets) {
457
+ * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);
458
+ * for (const o of top3) console.log(` ${o.name}: ${o.price}`);
459
+ * }
460
+ * }
461
+ * ```
462
+ */
463
+ getFutures(sport: string): Promise<FuturesEvent[]>;
414
464
  getEventEv(sport: string, eventId: number | string, options?: GetEventEvOptions): Promise<EventEvResponse>;
415
465
  /**
416
466
  * Bulk CSV export of resolved prop outcomes (Pro+ tier).
@@ -506,6 +556,6 @@ declare const Bookmakers: {
506
556
  readonly UNIBET: "unibet";
507
557
  };
508
558
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
509
- declare const VERSION = "0.2.0";
559
+ declare const VERSION = "0.3.0";
510
560
 
511
- export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvResponse, type ExportResolvedPropsOptions, type GetEventEvOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerStat, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type Webhook, type WebhookDelivery };
561
+ export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvResponse, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetEventEvOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerStat, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type Webhook, type WebhookDelivery };
package/dist/index.d.ts CHANGED
@@ -193,6 +193,37 @@ interface EventEvResponse {
193
193
  lines: EvLine[];
194
194
  [k: string]: unknown;
195
195
  }
196
+ interface FuturesOutcome {
197
+ /** Team or player name. */
198
+ name: string;
199
+ /** American odds. */
200
+ price: number | null;
201
+ price_decimal: number | null;
202
+ [k: string]: unknown;
203
+ }
204
+ interface FuturesMarket {
205
+ /** Slugified description, e.g. "world_series_winner". */
206
+ key: string;
207
+ /** Original book label, e.g. "World Series Winner". */
208
+ description: string;
209
+ bookmaker: string;
210
+ bookmaker_title: string;
211
+ last_update: string;
212
+ /** When the book itself reports this market was last updated; null when the book doesn't expose a publish-time signal. */
213
+ book_updated_at: string | null;
214
+ outcomes: FuturesOutcome[];
215
+ [k: string]: unknown;
216
+ }
217
+ interface FuturesEvent {
218
+ id: string;
219
+ sport_key: string;
220
+ /** The futures title from the book, e.g. "World Series 2026". */
221
+ title: string;
222
+ /** Season-end / target resolution time. */
223
+ commence_time: string;
224
+ markets: FuturesMarket[];
225
+ [k: string]: unknown;
226
+ }
196
227
  interface Webhook {
197
228
  id: number;
198
229
  url: string;
@@ -411,6 +442,25 @@ declare class PropLine {
411
442
  * }
412
443
  * ```
413
444
  */
445
+ /**
446
+ * List futures markets for a sport — championship winner, MVP,
447
+ * division winner, etc. Each row is one (futures event, book,
448
+ * market) with every team or player priced. Free tier; pulled from
449
+ * each book's futures feed (Bovada today).
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const futures = await client.getFutures("baseball_mlb");
454
+ * for (const event of futures) {
455
+ * console.log(`${event.title} @ ${event.commence_time}`);
456
+ * for (const m of event.markets) {
457
+ * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);
458
+ * for (const o of top3) console.log(` ${o.name}: ${o.price}`);
459
+ * }
460
+ * }
461
+ * ```
462
+ */
463
+ getFutures(sport: string): Promise<FuturesEvent[]>;
414
464
  getEventEv(sport: string, eventId: number | string, options?: GetEventEvOptions): Promise<EventEvResponse>;
415
465
  /**
416
466
  * Bulk CSV export of resolved prop outcomes (Pro+ tier).
@@ -506,6 +556,6 @@ declare const Bookmakers: {
506
556
  readonly UNIBET: "unibet";
507
557
  };
508
558
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
509
- declare const VERSION = "0.2.0";
559
+ declare const VERSION = "0.3.0";
510
560
 
511
- export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvResponse, type ExportResolvedPropsOptions, type GetEventEvOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerStat, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type Webhook, type WebhookDelivery };
561
+ export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvResponse, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetEventEvOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerStat, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type Webhook, type WebhookDelivery };
package/dist/index.js CHANGED
@@ -221,6 +221,30 @@ var PropLine = class {
221
221
  * }
222
222
  * ```
223
223
  */
224
+ /**
225
+ * List futures markets for a sport — championship winner, MVP,
226
+ * division winner, etc. Each row is one (futures event, book,
227
+ * market) with every team or player priced. Free tier; pulled from
228
+ * each book's futures feed (Bovada today).
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * const futures = await client.getFutures("baseball_mlb");
233
+ * for (const event of futures) {
234
+ * console.log(`${event.title} @ ${event.commence_time}`);
235
+ * for (const m of event.markets) {
236
+ * const top3 = [...m.outcomes].sort((a, b) => a.price - b.price).slice(0, 3);
237
+ * for (const o of top3) console.log(` ${o.name}: ${o.price}`);
238
+ * }
239
+ * }
240
+ * ```
241
+ */
242
+ getFutures(sport) {
243
+ return this._request(
244
+ "GET",
245
+ `/sports/${encodeURIComponent(sport)}/futures`
246
+ );
247
+ }
224
248
  getEventEv(sport, eventId, options = {}) {
225
249
  const params = {};
226
250
  if (options.markets) {
@@ -388,7 +412,7 @@ var Bookmakers = {
388
412
  PRIZEPICKS: "prizepicks",
389
413
  UNIBET: "unibet"
390
414
  };
391
- var VERSION = "0.2.0";
415
+ var VERSION = "0.3.0";
392
416
  export {
393
417
  AuthError,
394
418
  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 ScoreEvent,\n StatsResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n EventEvResponse,\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\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /** Market keys to filter by. */\n markets?: string[];\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\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 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 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 CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: Array<\"line_movement\" | \"resolution\">;\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}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: Array<\"line_movement\" | \"resolution\">;\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: 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 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 * Pro tier: full snapshots. Free tier: redacted (snapshot counts only).\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 return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\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 * 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 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 * 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 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 * 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 // 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 [\"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 GetScoresOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetEventEvOptions,\n ExportResolvedPropsOptions,\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 OddsHistoryResponse,\n ScoreEvent,\n PlayerStat,\n StatsResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n EvOutcome,\n EvLine,\n EventEvResponse,\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 PRIZEPICKS: \"prizepicks\",\n UNIBET: \"unibet\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.2.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 ScoreEvent,\n StatsResponse,\n ResultsResponse,\n PlayerHistoryResponse,\n EventEvResponse,\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\nexport interface GetOddsOptions {\n /** Specific event ID to get odds (with player props) for. Omit for bulk odds. */\n eventId?: number | string;\n /** Market keys to filter by. */\n markets?: string[];\n}\n\nexport interface GetOddsHistoryOptions {\n markets?: string[];\n}\n\nexport interface GetScoresOptions {\n /** Days back to include (default 3). */\n daysFrom?: number;\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 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 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 CreateWebhookOptions {\n /** HTTPS endpoint to receive POSTed events. Required. */\n url: string;\n /** Event types to subscribe to. Default: all. */\n events?: Array<\"line_movement\" | \"resolution\">;\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}\n\nexport interface UpdateWebhookOptions {\n url?: string;\n events?: Array<\"line_movement\" | \"resolution\">;\n filterSportKey?: string;\n filterEventId?: number;\n filterMarketKey?: string;\n filterPlayerName?: string;\n minPriceChangePct?: 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 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 * Pro tier: full snapshots. Free tier: redacted (snapshot counts only).\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 return this._request<OddsHistoryResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/odds/history`,\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 * 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 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 * 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, etc. Each row is one (futures event, book,\n * market) with every team or player priced. Free tier; pulled from\n * each book's futures feed (Bovada today).\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 * 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 // 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 [\"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 GetScoresOptions,\n GetStatsOptions,\n GetResultsOptions,\n GetPlayerHistoryOptions,\n GetEventEvOptions,\n ExportResolvedPropsOptions,\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 OddsHistoryResponse,\n ScoreEvent,\n PlayerStat,\n StatsResponse,\n ResultsMarket,\n ResultsResponse,\n PlayerHistoryEntry,\n PlayerHistoryResponse,\n EvOutcome,\n EvLine,\n EventEvResponse,\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 PRIZEPICKS: \"prizepicks\",\n UNIBET: \"unibet\",\n} as const;\n\nexport type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];\n\nexport const VERSION = \"0.3.0\";\n"],"mappings":";AAAA,SAAS,YAAY,uBAAuB;AAC5C,SAAS,iBAAiB;AAmBnB,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;AA6GA,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,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,EAOA,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,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,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,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;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,EAwCA,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,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;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,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;;;AC9kBO,IAAM,aAAa;AAAA,EACxB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AACV;AAIO,IAAM,UAAU;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "propline",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Node.js / TypeScript SDK for the PropLine player props betting odds API",
5
5
  "keywords": [
6
6
  "sports",