propline 0.3.0 → 0.4.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
@@ -230,11 +230,14 @@ for (const market of history.markets) {
230
230
  }
231
231
  ```
232
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
233
+ Each snapshot carries up to three change-detection signals:
234
+ `recorded_at` (when our scraper saw the odds), `book_updated_at` (when
235
+ the book itself reports the price was last set Bovada today), and
236
+ `book_version` (per-market monotonic counter — Pinnacle today). The
237
+ gap between `recorded_at` and `book_updated_at` is per-book scraper
238
+ latency; deltas in `book_version` between two snapshots tell you how
239
+ many distinct market updates the book recorded between them, even
240
+ when the visible price didn't change. See
238
241
  <https://prop-line.com/docs#timestamps> for the full semantic.
239
242
 
240
243
  ### Get player prop history (Pro full, Free redacted)
package/dist/index.cjs CHANGED
@@ -287,6 +287,40 @@ var PropLine = class {
287
287
  { params }
288
288
  );
289
289
  }
290
+ /**
291
+ * Calculate EV% for a user-supplied price against the event's
292
+ * no-vig fair anchor. Useful for books PropLine doesn't carry —
293
+ * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a
294
+ * price in hand and want to know if it's +EV against the sharp
295
+ * consensus we do carry.
296
+ *
297
+ * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,
298
+ * no-vig devigging) but takes one user price as input. Pro tier.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * const r = await client.calcEventEv("baseball_mlb", 12614, {
303
+ * market: "h2h",
304
+ * name: "Pittsburgh Pirates",
305
+ * price: -118,
306
+ * });
307
+ * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);
308
+ * ```
309
+ */
310
+ calcEventEv(sport, eventId, options) {
311
+ const params = {
312
+ market: options.market,
313
+ name: options.name,
314
+ price: options.price
315
+ };
316
+ if (options.point !== void 0) params.point = options.point;
317
+ if (options.description) params.description = options.description;
318
+ return this._request(
319
+ "GET",
320
+ `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,
321
+ { params }
322
+ );
323
+ }
290
324
  async exportResolvedProps(options) {
291
325
  const params = { sport: options.sport };
292
326
  if (options.market) params.market = options.market;
@@ -443,7 +477,7 @@ var Bookmakers = {
443
477
  PRIZEPICKS: "prizepicks",
444
478
  UNIBET: "unibet"
445
479
  };
446
- var VERSION = "0.3.0";
480
+ var VERSION = "0.4.0";
447
481
  // Annotate the CommonJS export names for ESM import in node:
448
482
  0 && (module.exports = {
449
483
  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 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":[]}
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 CalcEventEvOptions,\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 EventEvCalcResponse,\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.4.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 EventEvCalcResponse,\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 CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface 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 * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // 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,22 @@ interface EventEvResponse {
193
193
  lines: EvLine[];
194
194
  [k: string]: unknown;
195
195
  }
196
+ interface EventEvCalcResponse {
197
+ market: string;
198
+ name: string;
199
+ point: number | null;
200
+ description: string;
201
+ price: number;
202
+ /** Which book the no-vig fair anchor came from (pinnacle, bovada, ...). */
203
+ fair_source: string;
204
+ /** No-vig fair win probability for `name` at `point`. */
205
+ fair_prob: number;
206
+ /** Win probability implied by the user's `price`. */
207
+ implied_prob: number;
208
+ ev_pct: number;
209
+ is_plus_ev: boolean;
210
+ [k: string]: unknown;
211
+ }
196
212
  interface FuturesOutcome {
197
213
  /** Team or player name. */
198
214
  name: string;
@@ -307,6 +323,18 @@ interface GetEventEvOptions {
307
323
  */
308
324
  markets?: string | string[];
309
325
  }
326
+ interface CalcEventEvOptions {
327
+ /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */
328
+ market: string;
329
+ /** Outcome name. Team for h2h/spreads; "Over" or "Under" for totals/props. */
330
+ name: string;
331
+ /** American odds at your book, e.g. -118 or 145. */
332
+ price: number;
333
+ /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */
334
+ point?: number;
335
+ /** Player name for player-prop markets. Omit for game-line markets. */
336
+ description?: string;
337
+ }
310
338
  interface ExportResolvedPropsOptions {
311
339
  /** Sport key (e.g. `"baseball_mlb"`). Required. */
312
340
  sport: string;
@@ -462,6 +490,27 @@ declare class PropLine {
462
490
  */
463
491
  getFutures(sport: string): Promise<FuturesEvent[]>;
464
492
  getEventEv(sport: string, eventId: number | string, options?: GetEventEvOptions): Promise<EventEvResponse>;
493
+ /**
494
+ * Calculate EV% for a user-supplied price against the event's
495
+ * no-vig fair anchor. Useful for books PropLine doesn't carry —
496
+ * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a
497
+ * price in hand and want to know if it's +EV against the sharp
498
+ * consensus we do carry.
499
+ *
500
+ * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,
501
+ * no-vig devigging) but takes one user price as input. Pro tier.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * const r = await client.calcEventEv("baseball_mlb", 12614, {
506
+ * market: "h2h",
507
+ * name: "Pittsburgh Pirates",
508
+ * price: -118,
509
+ * });
510
+ * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);
511
+ * ```
512
+ */
513
+ calcEventEv(sport: string, eventId: number | string, options: CalcEventEvOptions): Promise<EventEvCalcResponse>;
465
514
  /**
466
515
  * Bulk CSV export of resolved prop outcomes (Pro+ tier).
467
516
  *
@@ -556,6 +605,6 @@ declare const Bookmakers: {
556
605
  readonly UNIBET: "unibet";
557
606
  };
558
607
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
559
- declare const VERSION = "0.3.0";
608
+ declare const VERSION = "0.4.0";
560
609
 
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 };
610
+ export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvCalcResponse, 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,22 @@ interface EventEvResponse {
193
193
  lines: EvLine[];
194
194
  [k: string]: unknown;
195
195
  }
196
+ interface EventEvCalcResponse {
197
+ market: string;
198
+ name: string;
199
+ point: number | null;
200
+ description: string;
201
+ price: number;
202
+ /** Which book the no-vig fair anchor came from (pinnacle, bovada, ...). */
203
+ fair_source: string;
204
+ /** No-vig fair win probability for `name` at `point`. */
205
+ fair_prob: number;
206
+ /** Win probability implied by the user's `price`. */
207
+ implied_prob: number;
208
+ ev_pct: number;
209
+ is_plus_ev: boolean;
210
+ [k: string]: unknown;
211
+ }
196
212
  interface FuturesOutcome {
197
213
  /** Team or player name. */
198
214
  name: string;
@@ -307,6 +323,18 @@ interface GetEventEvOptions {
307
323
  */
308
324
  markets?: string | string[];
309
325
  }
326
+ interface CalcEventEvOptions {
327
+ /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */
328
+ market: string;
329
+ /** Outcome name. Team for h2h/spreads; "Over" or "Under" for totals/props. */
330
+ name: string;
331
+ /** American odds at your book, e.g. -118 or 145. */
332
+ price: number;
333
+ /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */
334
+ point?: number;
335
+ /** Player name for player-prop markets. Omit for game-line markets. */
336
+ description?: string;
337
+ }
310
338
  interface ExportResolvedPropsOptions {
311
339
  /** Sport key (e.g. `"baseball_mlb"`). Required. */
312
340
  sport: string;
@@ -462,6 +490,27 @@ declare class PropLine {
462
490
  */
463
491
  getFutures(sport: string): Promise<FuturesEvent[]>;
464
492
  getEventEv(sport: string, eventId: number | string, options?: GetEventEvOptions): Promise<EventEvResponse>;
493
+ /**
494
+ * Calculate EV% for a user-supplied price against the event's
495
+ * no-vig fair anchor. Useful for books PropLine doesn't carry —
496
+ * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a
497
+ * price in hand and want to know if it's +EV against the sharp
498
+ * consensus we do carry.
499
+ *
500
+ * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,
501
+ * no-vig devigging) but takes one user price as input. Pro tier.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * const r = await client.calcEventEv("baseball_mlb", 12614, {
506
+ * market: "h2h",
507
+ * name: "Pittsburgh Pirates",
508
+ * price: -118,
509
+ * });
510
+ * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);
511
+ * ```
512
+ */
513
+ calcEventEv(sport: string, eventId: number | string, options: CalcEventEvOptions): Promise<EventEvCalcResponse>;
465
514
  /**
466
515
  * Bulk CSV export of resolved prop outcomes (Pro+ tier).
467
516
  *
@@ -556,6 +605,6 @@ declare const Bookmakers: {
556
605
  readonly UNIBET: "unibet";
557
606
  };
558
607
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
559
- declare const VERSION = "0.3.0";
608
+ declare const VERSION = "0.4.0";
560
609
 
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 };
610
+ export { AuthError, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type CreateWebhookOptions, type EvLine, type EvOutcome, type Event, type EventEvCalcResponse, 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
@@ -256,6 +256,40 @@ var PropLine = class {
256
256
  { params }
257
257
  );
258
258
  }
259
+ /**
260
+ * Calculate EV% for a user-supplied price against the event's
261
+ * no-vig fair anchor. Useful for books PropLine doesn't carry —
262
+ * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a
263
+ * price in hand and want to know if it's +EV against the sharp
264
+ * consensus we do carry.
265
+ *
266
+ * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,
267
+ * no-vig devigging) but takes one user price as input. Pro tier.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * const r = await client.calcEventEv("baseball_mlb", 12614, {
272
+ * market: "h2h",
273
+ * name: "Pittsburgh Pirates",
274
+ * price: -118,
275
+ * });
276
+ * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);
277
+ * ```
278
+ */
279
+ calcEventEv(sport, eventId, options) {
280
+ const params = {
281
+ market: options.market,
282
+ name: options.name,
283
+ price: options.price
284
+ };
285
+ if (options.point !== void 0) params.point = options.point;
286
+ if (options.description) params.description = options.description;
287
+ return this._request(
288
+ "GET",
289
+ `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,
290
+ { params }
291
+ );
292
+ }
259
293
  async exportResolvedProps(options) {
260
294
  const params = { sport: options.sport };
261
295
  if (options.market) params.market = options.market;
@@ -412,7 +446,7 @@ var Bookmakers = {
412
446
  PRIZEPICKS: "prizepicks",
413
447
  UNIBET: "unibet"
414
448
  };
415
- var VERSION = "0.3.0";
449
+ var VERSION = "0.4.0";
416
450
  export {
417
451
  AuthError,
418
452
  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 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":"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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 EventEvCalcResponse,\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 CalcEventEvOptions {\n /** Market key — h2h / spreads / totals / pitcher_strikeouts / etc. */\n market: string;\n /** Outcome name. Team for h2h/spreads; \"Over\" or \"Under\" for totals/props. */\n name: string;\n /** American odds at your book, e.g. -118 or 145. */\n price: number;\n /** Line/point for spreads, totals, player props. Sign matters for spreads (-1.5 favorite). Omit for h2h. */\n point?: number;\n /** Player name for player-prop markets. Omit for game-line markets. */\n description?: string;\n}\n\nexport interface ExportResolvedPropsOptions {\n /** Sport key (e.g. `\"baseball_mlb\"`). Required. */\n sport: string;\n /** Optional market filter. */\n market?: string;\n /** Optional bookmaker filter. */\n bookmaker?: string;\n /** ISO datetime lower bound on `resolved_at`. */\n since?: string;\n /** ISO datetime upper bound on `resolved_at`. */\n until?: string;\n /** If set, stream the CSV to this path and resolve to the path. Otherwise resolve to the CSV bytes. */\n outPath?: string;\n}\n\nexport interface 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 * Calculate EV% for a user-supplied price against the event's\n * no-vig fair anchor. Useful for books PropLine doesn't carry —\n * Caesars, BetMGM, Fanatics, BetUS, Hard Rock — where you have a\n * price in hand and want to know if it's +EV against the sharp\n * consensus we do carry.\n *\n * Same fair-line math as `getEventEv` (Pinnacle-preferred anchor,\n * no-vig devigging) but takes one user price as input. Pro tier.\n *\n * @example\n * ```ts\n * const r = await client.calcEventEv(\"baseball_mlb\", 12614, {\n * market: \"h2h\",\n * name: \"Pittsburgh Pirates\",\n * price: -118,\n * });\n * console.log(`EV ${r.ev_pct}% fair=${r.fair_prob}`);\n * ```\n */\n calcEventEv(\n sport: string,\n eventId: number | string,\n options: CalcEventEvOptions\n ): Promise<EventEvCalcResponse> {\n const params: Record<string, string | number | undefined> = {\n market: options.market,\n name: options.name,\n price: options.price,\n };\n if (options.point !== undefined) params.point = options.point;\n if (options.description) params.description = options.description;\n return this._request<EventEvCalcResponse>(\n \"GET\",\n `/sports/${encodeURIComponent(sport)}/events/${encodeURIComponent(String(eventId))}/ev/calc`,\n { params }\n );\n }\n\n /**\n * Bulk CSV export of resolved prop outcomes (Pro+ tier).\n *\n * If `outPath` is provided, streams the CSV to disk and resolves to the\n * path. Otherwise resolves to the full CSV bytes as a `Uint8Array`.\n *\n * @example\n * ```ts\n * await client.exportResolvedProps({\n * sport: \"baseball_mlb\",\n * market: \"pitcher_strikeouts\",\n * since: \"2026-04-01T00:00:00Z\",\n * outPath: \"./mlb-strikeouts.csv\",\n * });\n * ```\n */\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath: string }\n ): Promise<string>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions & { outPath?: undefined }\n ): Promise<Uint8Array>;\n async exportResolvedProps(\n options: ExportResolvedPropsOptions\n ): Promise<string | Uint8Array> {\n const params: Record<string, string | undefined> = { sport: options.sport };\n if (options.market) params.market = options.market;\n if (options.bookmaker) params.bookmaker = options.bookmaker;\n if (options.since) params.since = options.since;\n if (options.until) params.until = options.until;\n\n const url = this._buildUrl(\"/exports/resolved-props\", params);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this._fetch(url, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (resp.status === 401) {\n throw new AuthError();\n }\n if (resp.status === 403) {\n throw new PropLineError(403, await readDetail(resp, \"Pro tier required\"));\n }\n if (resp.status >= 400) {\n throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));\n }\n\n const buf = new Uint8Array(await resp.arrayBuffer());\n if (options.outPath) {\n await writeFile(options.outPath, buf);\n return options.outPath;\n }\n return buf;\n }\n\n // ------------------------------------------------------------------\n // 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 CalcEventEvOptions,\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 EventEvCalcResponse,\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.4.0\";\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "propline",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Node.js / TypeScript SDK for the PropLine player props betting odds API",
5
5
  "keywords": [
6
6
  "sports",