propline 0.22.1 → 0.24.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/dist/index.d.cts CHANGED
@@ -653,19 +653,53 @@ interface GetDfsPayoutsOptions {
653
653
  */
654
654
  legWinProb?: number;
655
655
  }
656
+ /**
657
+ * Structured error body returned by gated/throttled endpoints
658
+ * (see https://prop-line.com/docs#errors). Branch on `error` — the
659
+ * codes are stable — and follow the URLs instead of parsing prose.
660
+ */
661
+ interface PropLineErrorInfo {
662
+ /** Stable machine-readable code, e.g. "upgrade_required", "daily_limit_exceeded". */
663
+ error?: string;
664
+ /** Human-readable sentence. */
665
+ message?: string;
666
+ /** Cheapest tier that unlocks a gated feature (403s). */
667
+ required_tier?: string;
668
+ /** Where to unlock it — pre-filled one-click URL on daily-cap 429s. */
669
+ upgrade_url?: string;
670
+ docs_url?: string;
671
+ signup_url?: string;
672
+ backfill_url?: string;
673
+ /** Burst-limit backoff hint (429s). */
674
+ retry_after_seconds?: number;
675
+ /** Daily-cap 429s: recommended next plan incl. its own upgrade_url. */
676
+ recommended?: {
677
+ plan?: string;
678
+ upgrade_url?: string;
679
+ [key: string]: unknown;
680
+ };
681
+ [key: string]: unknown;
682
+ }
656
683
  /** Base error for all PropLine API failures. */
657
684
  declare class PropLineError extends Error {
658
685
  readonly statusCode: number;
686
+ /** Human-readable detail message. */
659
687
  readonly detail: string;
660
- constructor(statusCode: number, detail: string);
688
+ /** Structured error body, when the API returned one. */
689
+ readonly info?: PropLineErrorInfo;
690
+ constructor(statusCode: number, detail: string, info?: PropLineErrorInfo);
691
+ /** Stable machine-readable code (e.g. "upgrade_required"), if present. */
692
+ get errorCode(): string | undefined;
693
+ /** The URL that unlocks a gated feature or lifts a cap, if present. */
694
+ get upgradeUrl(): string | undefined;
661
695
  }
662
696
  /** Thrown when the API key is missing or invalid (HTTP 401). */
663
697
  declare class AuthError extends PropLineError {
664
- constructor(detail?: string);
698
+ constructor(detail?: string, info?: PropLineErrorInfo);
665
699
  }
666
700
  /** Thrown when the daily request limit is exceeded (HTTP 429). */
667
701
  declare class RateLimitError extends PropLineError {
668
- constructor(detail?: string);
702
+ constructor(detail?: string, info?: PropLineErrorInfo);
669
703
  }
670
704
  interface PropLineOptions {
671
705
  /** API base URL. Default: `https://api.prop-line.com/v1`. */
@@ -886,6 +920,22 @@ interface VerifySignatureOptions {
886
920
  /** Value of the `X-PropLine-Signature` header. */
887
921
  signature: string;
888
922
  }
923
+ /**
924
+ * Live daily-quota state, parsed from the `X-Daily-*` headers the API
925
+ * returns on every authenticated response.
926
+ */
927
+ interface QuotaStatus {
928
+ /** Your tier's daily request cap. */
929
+ limit: number;
930
+ /** Requests used today (including the request that produced this). */
931
+ used: number;
932
+ /** Requests left before the cap. */
933
+ remaining: number;
934
+ /** Unix seconds when the quota resets (00:00 UTC — a hard reset, not a rolling window). */
935
+ resetEpoch: number;
936
+ /** Quota reset time as a `Date`. */
937
+ resetAt: Date;
938
+ }
889
939
  /**
890
940
  * Client for the PropLine player props API.
891
941
  *
@@ -901,9 +951,24 @@ declare class PropLine {
901
951
  readonly apiKey: string;
902
952
  readonly baseUrl: string;
903
953
  readonly timeoutMs: number;
954
+ /**
955
+ * Daily-quota state from the most recent API response, or `null` before
956
+ * the first request. Updated on every call (including 429s):
957
+ *
958
+ * ```ts
959
+ * await client.getSports();
960
+ * console.log(client.lastQuota?.remaining); // 999
961
+ * ```
962
+ */
963
+ lastQuota: QuotaStatus | null;
904
964
  private readonly _fetch;
905
965
  constructor(apiKey: string, options?: PropLineOptions);
906
966
  private _buildUrl;
967
+ /**
968
+ * Record the X-Daily-* quota headers when present (absent on
969
+ * unauthenticated errors, e.g. an invalid key's 401).
970
+ */
971
+ private _captureQuota;
907
972
  private _request;
908
973
  /** List all available sports. */
909
974
  getSports(): Promise<Sport[]>;
@@ -1264,6 +1329,6 @@ declare const Bookmakers: {
1264
1329
  readonly PRIZEPICKS: "prizepicks";
1265
1330
  };
1266
1331
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
1267
- declare const VERSION = "0.22.1";
1332
+ declare const VERSION = "0.24.0";
1268
1333
 
1269
- export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
1334
+ export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineErrorInfo, type PropLineOptions, type QuotaStatus, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
package/dist/index.d.ts CHANGED
@@ -653,19 +653,53 @@ interface GetDfsPayoutsOptions {
653
653
  */
654
654
  legWinProb?: number;
655
655
  }
656
+ /**
657
+ * Structured error body returned by gated/throttled endpoints
658
+ * (see https://prop-line.com/docs#errors). Branch on `error` — the
659
+ * codes are stable — and follow the URLs instead of parsing prose.
660
+ */
661
+ interface PropLineErrorInfo {
662
+ /** Stable machine-readable code, e.g. "upgrade_required", "daily_limit_exceeded". */
663
+ error?: string;
664
+ /** Human-readable sentence. */
665
+ message?: string;
666
+ /** Cheapest tier that unlocks a gated feature (403s). */
667
+ required_tier?: string;
668
+ /** Where to unlock it — pre-filled one-click URL on daily-cap 429s. */
669
+ upgrade_url?: string;
670
+ docs_url?: string;
671
+ signup_url?: string;
672
+ backfill_url?: string;
673
+ /** Burst-limit backoff hint (429s). */
674
+ retry_after_seconds?: number;
675
+ /** Daily-cap 429s: recommended next plan incl. its own upgrade_url. */
676
+ recommended?: {
677
+ plan?: string;
678
+ upgrade_url?: string;
679
+ [key: string]: unknown;
680
+ };
681
+ [key: string]: unknown;
682
+ }
656
683
  /** Base error for all PropLine API failures. */
657
684
  declare class PropLineError extends Error {
658
685
  readonly statusCode: number;
686
+ /** Human-readable detail message. */
659
687
  readonly detail: string;
660
- constructor(statusCode: number, detail: string);
688
+ /** Structured error body, when the API returned one. */
689
+ readonly info?: PropLineErrorInfo;
690
+ constructor(statusCode: number, detail: string, info?: PropLineErrorInfo);
691
+ /** Stable machine-readable code (e.g. "upgrade_required"), if present. */
692
+ get errorCode(): string | undefined;
693
+ /** The URL that unlocks a gated feature or lifts a cap, if present. */
694
+ get upgradeUrl(): string | undefined;
661
695
  }
662
696
  /** Thrown when the API key is missing or invalid (HTTP 401). */
663
697
  declare class AuthError extends PropLineError {
664
- constructor(detail?: string);
698
+ constructor(detail?: string, info?: PropLineErrorInfo);
665
699
  }
666
700
  /** Thrown when the daily request limit is exceeded (HTTP 429). */
667
701
  declare class RateLimitError extends PropLineError {
668
- constructor(detail?: string);
702
+ constructor(detail?: string, info?: PropLineErrorInfo);
669
703
  }
670
704
  interface PropLineOptions {
671
705
  /** API base URL. Default: `https://api.prop-line.com/v1`. */
@@ -886,6 +920,22 @@ interface VerifySignatureOptions {
886
920
  /** Value of the `X-PropLine-Signature` header. */
887
921
  signature: string;
888
922
  }
923
+ /**
924
+ * Live daily-quota state, parsed from the `X-Daily-*` headers the API
925
+ * returns on every authenticated response.
926
+ */
927
+ interface QuotaStatus {
928
+ /** Your tier's daily request cap. */
929
+ limit: number;
930
+ /** Requests used today (including the request that produced this). */
931
+ used: number;
932
+ /** Requests left before the cap. */
933
+ remaining: number;
934
+ /** Unix seconds when the quota resets (00:00 UTC — a hard reset, not a rolling window). */
935
+ resetEpoch: number;
936
+ /** Quota reset time as a `Date`. */
937
+ resetAt: Date;
938
+ }
889
939
  /**
890
940
  * Client for the PropLine player props API.
891
941
  *
@@ -901,9 +951,24 @@ declare class PropLine {
901
951
  readonly apiKey: string;
902
952
  readonly baseUrl: string;
903
953
  readonly timeoutMs: number;
954
+ /**
955
+ * Daily-quota state from the most recent API response, or `null` before
956
+ * the first request. Updated on every call (including 429s):
957
+ *
958
+ * ```ts
959
+ * await client.getSports();
960
+ * console.log(client.lastQuota?.remaining); // 999
961
+ * ```
962
+ */
963
+ lastQuota: QuotaStatus | null;
904
964
  private readonly _fetch;
905
965
  constructor(apiKey: string, options?: PropLineOptions);
906
966
  private _buildUrl;
967
+ /**
968
+ * Record the X-Daily-* quota headers when present (absent on
969
+ * unauthenticated errors, e.g. an invalid key's 401).
970
+ */
971
+ private _captureQuota;
907
972
  private _request;
908
973
  /** List all available sports. */
909
974
  getSports(): Promise<Sport[]>;
@@ -1264,6 +1329,6 @@ declare const Bookmakers: {
1264
1329
  readonly PRIZEPICKS: "prizepicks";
1265
1330
  };
1266
1331
  type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
1267
- declare const VERSION = "0.22.1";
1332
+ declare const VERSION = "0.24.0";
1268
1333
 
1269
- export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineOptions, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
1334
+ export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerHistoryOptions, type GetPlayerTrendsOptions, type GetResultsOptions, type GetScoresOptions, type GetStatsOptions, type HitRateSplit, type ListWebhookDeliveriesOptions, type Market, type MarketSummary, type MlbGrandSalamiBook, type MlbGrandSalamiResponse, type MovementBookmaker, type MovementMarket, type MovementOutcome, type MovementResponse, type NhlDailyGoalsTotalBook, type NhlDailyGoalsTotalResponse, type OddsClosingResponse, type OddsHistoryBookmaker, type OddsHistoryMarket, type OddsHistoryOutcome, type OddsHistoryResponse, type OddsResponse, type Outcome, type OutcomeSnapshot, type PeriodFilter, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, PropLine, PropLineError, type PropLineErrorInfo, type PropLineOptions, type QuotaStatus, RateLimitError, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
package/dist/index.js CHANGED
@@ -3,25 +3,37 @@ import { createHmac, timingSafeEqual } from "crypto";
3
3
  import { writeFile } from "fs/promises";
4
4
  var PropLineError = class extends Error {
5
5
  statusCode;
6
+ /** Human-readable detail message. */
6
7
  detail;
7
- constructor(statusCode, detail) {
8
+ /** Structured error body, when the API returned one. */
9
+ info;
10
+ constructor(statusCode, detail, info) {
8
11
  super(`[${statusCode}] ${detail}`);
9
12
  this.name = "PropLineError";
10
13
  this.statusCode = statusCode;
11
14
  this.detail = detail;
15
+ this.info = info;
12
16
  Object.setPrototypeOf(this, new.target.prototype);
13
17
  }
18
+ /** Stable machine-readable code (e.g. "upgrade_required"), if present. */
19
+ get errorCode() {
20
+ return this.info?.error;
21
+ }
22
+ /** The URL that unlocks a gated feature or lifts a cap, if present. */
23
+ get upgradeUrl() {
24
+ return this.info?.upgrade_url ?? this.info?.recommended?.upgrade_url;
25
+ }
14
26
  };
15
27
  var AuthError = class extends PropLineError {
16
- constructor(detail = "Invalid API key") {
17
- super(401, detail);
28
+ constructor(detail = "Invalid API key", info) {
29
+ super(401, detail, info);
18
30
  this.name = "AuthError";
19
31
  Object.setPrototypeOf(this, new.target.prototype);
20
32
  }
21
33
  };
22
34
  var RateLimitError = class extends PropLineError {
23
- constructor(detail = "Rate limit exceeded") {
24
- super(429, detail);
35
+ constructor(detail = "Rate limit exceeded", info) {
36
+ super(429, detail, info);
25
37
  this.name = "RateLimitError";
26
38
  Object.setPrototypeOf(this, new.target.prototype);
27
39
  }
@@ -40,6 +52,16 @@ var PropLine = class {
40
52
  apiKey;
41
53
  baseUrl;
42
54
  timeoutMs;
55
+ /**
56
+ * Daily-quota state from the most recent API response, or `null` before
57
+ * the first request. Updated on every call (including 429s):
58
+ *
59
+ * ```ts
60
+ * await client.getSports();
61
+ * console.log(client.lastQuota?.remaining); // 999
62
+ * ```
63
+ */
64
+ lastQuota = null;
43
65
  _fetch;
44
66
  constructor(apiKey, options = {}) {
45
67
  if (!apiKey) {
@@ -69,6 +91,25 @@ var PropLine = class {
69
91
  }
70
92
  return url.toString();
71
93
  }
94
+ /**
95
+ * Record the X-Daily-* quota headers when present (absent on
96
+ * unauthenticated errors, e.g. an invalid key's 401).
97
+ */
98
+ _captureQuota(resp) {
99
+ const limit = Number(resp.headers.get("X-Daily-Limit"));
100
+ const used = Number(resp.headers.get("X-Daily-Used"));
101
+ const remaining = Number(resp.headers.get("X-Daily-Remaining"));
102
+ const resetEpoch = Number(resp.headers.get("X-Daily-Reset"));
103
+ if (resp.headers.has("X-Daily-Limit") && Number.isFinite(limit) && Number.isFinite(used) && Number.isFinite(remaining) && Number.isFinite(resetEpoch)) {
104
+ this.lastQuota = {
105
+ limit,
106
+ used,
107
+ remaining,
108
+ resetEpoch,
109
+ resetAt: new Date(resetEpoch * 1e3)
110
+ };
111
+ }
112
+ }
72
113
  async _request(method, path, init = {}) {
73
114
  const url = this._buildUrl(path, init.params);
74
115
  const controller = new AbortController();
@@ -87,14 +128,18 @@ var PropLine = class {
87
128
  } finally {
88
129
  clearTimeout(timer);
89
130
  }
131
+ this._captureQuota(resp);
90
132
  if (resp.status === 401) {
91
- throw new AuthError(await readDetail(resp, "Invalid API key"));
133
+ const d = await readDetail(resp, "Invalid API key");
134
+ throw new AuthError(d.message, d.info);
92
135
  }
93
136
  if (resp.status === 429) {
94
- throw new RateLimitError(await readDetail(resp, "Rate limit exceeded"));
137
+ const d = await readDetail(resp, "Rate limit exceeded");
138
+ throw new RateLimitError(d.message, d.info);
95
139
  }
96
140
  if (resp.status >= 400) {
97
- throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));
141
+ const d = await readDetail(resp, resp.statusText);
142
+ throw new PropLineError(resp.status, d.message, d.info);
98
143
  }
99
144
  if (resp.status === 204) {
100
145
  return void 0;
@@ -541,14 +586,17 @@ var PropLine = class {
541
586
  } finally {
542
587
  clearTimeout(timer);
543
588
  }
589
+ this._captureQuota(resp);
544
590
  if (resp.status === 401) {
545
591
  throw new AuthError();
546
592
  }
547
593
  if (resp.status === 403) {
548
- throw new PropLineError(403, await readDetail(resp, "Pro tier required"));
594
+ const d = await readDetail(resp, "Pro tier required");
595
+ throw new PropLineError(403, d.message, d.info);
549
596
  }
550
597
  if (resp.status >= 400) {
551
- throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));
598
+ const d = await readDetail(resp, resp.statusText);
599
+ throw new PropLineError(resp.status, d.message, d.info);
552
600
  }
553
601
  const buf = new Uint8Array(await resp.arrayBuffer());
554
602
  if (options.outPath) {
@@ -576,17 +624,20 @@ var PropLine = class {
576
624
  } finally {
577
625
  clearTimeout(timer);
578
626
  }
627
+ this._captureQuota(resp);
579
628
  if (resp.status === 401) {
580
629
  throw new AuthError();
581
630
  }
582
631
  if (resp.status === 403) {
583
- throw new PropLineError(
584
- 403,
585
- await readDetail(resp, "Historical Backfill pass or Enterprise required")
632
+ const d = await readDetail(
633
+ resp,
634
+ "Historical Backfill pass or Enterprise required"
586
635
  );
636
+ throw new PropLineError(403, d.message, d.info);
587
637
  }
588
638
  if (resp.status >= 400) {
589
- throw new PropLineError(resp.status, await readDetail(resp, resp.statusText));
639
+ const d = await readDetail(resp, resp.statusText);
640
+ throw new PropLineError(resp.status, d.message, d.info);
590
641
  }
591
642
  const buf = new Uint8Array(await resp.arrayBuffer());
592
643
  if (options.outPath) {
@@ -696,15 +747,22 @@ function webhookBody(options) {
696
747
  async function readDetail(resp, fallback) {
697
748
  try {
698
749
  const text = await resp.text();
699
- if (!text) return fallback;
750
+ if (!text) return { message: fallback };
700
751
  try {
701
752
  const json = JSON.parse(text);
702
- if (typeof json.detail === "string") return json.detail;
753
+ if (typeof json.detail === "string") return { message: json.detail };
754
+ if (json.detail && typeof json.detail === "object") {
755
+ const info = json.detail;
756
+ return {
757
+ message: typeof info.message === "string" ? info.message : text,
758
+ info
759
+ };
760
+ }
703
761
  } catch {
704
762
  }
705
- return text || fallback;
763
+ return { message: text || fallback };
706
764
  } catch {
707
- return fallback;
765
+ return { message: fallback };
708
766
  }
709
767
  }
710
768
 
@@ -720,7 +778,7 @@ var Bookmakers = {
720
778
  POLYMARKET: "polymarket",
721
779
  PRIZEPICKS: "prizepicks"
722
780
  };
723
- var VERSION = "0.22.1";
781
+ var VERSION = "0.24.0";
724
782
  export {
725
783
  AuthError,
726
784
  Bookmakers,