propline 0.44.0 → 0.46.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 +70 -0
- package/dist/index.cjs +134 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -2
- package/dist/index.d.ts +119 -2
- package/dist/index.js +134 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -947,8 +947,52 @@ interface WebhookDelivery {
|
|
|
947
947
|
attempts: number;
|
|
948
948
|
delivered_at: string | null;
|
|
949
949
|
payload: Record<string, unknown>;
|
|
950
|
+
/**
|
|
951
|
+
* This subscription's own event counter — the value sent as the
|
|
952
|
+
* `X-PropLine-Sequence` header. Null on deliveries enqueued before the
|
|
953
|
+
* sequence shipped; those cannot be replayed.
|
|
954
|
+
*/
|
|
955
|
+
seq: number | null;
|
|
950
956
|
[k: string]: unknown;
|
|
951
957
|
}
|
|
958
|
+
/** One event from `replayWebhookEvents`. */
|
|
959
|
+
interface ReplayEvent {
|
|
960
|
+
/** Cursor position. Monotonic within this subscription. */
|
|
961
|
+
seq: number;
|
|
962
|
+
delivery_id: number;
|
|
963
|
+
event_type: string;
|
|
964
|
+
created_at: string;
|
|
965
|
+
/** The canonical payload that was (or would have been) POSTed. */
|
|
966
|
+
data: Record<string, unknown>;
|
|
967
|
+
}
|
|
968
|
+
interface ReplayPage {
|
|
969
|
+
webhook_id: number;
|
|
970
|
+
since_seq: number;
|
|
971
|
+
/** Oldest first, so you can replay them forward. */
|
|
972
|
+
events: ReplayEvent[];
|
|
973
|
+
/**
|
|
974
|
+
* Cursor for the next call. Equals the `since_seq` you sent when the page
|
|
975
|
+
* is empty, so a paging loop needs no special case.
|
|
976
|
+
*/
|
|
977
|
+
next_seq: number;
|
|
978
|
+
has_more: boolean;
|
|
979
|
+
/** Bounds of what is still retained. Null when nothing is. */
|
|
980
|
+
oldest_available_seq: number | null;
|
|
981
|
+
newest_available_seq: number | null;
|
|
982
|
+
/**
|
|
983
|
+
* The most recent sequence ever issued to this subscription. NOT subject to
|
|
984
|
+
* retention, so `latest_seq - next_seq` is an honest "how far behind am I"
|
|
985
|
+
* even after the rows themselves are pruned.
|
|
986
|
+
*/
|
|
987
|
+
latest_seq: number;
|
|
988
|
+
/**
|
|
989
|
+
* TRUE when events after your cursor have already aged out and are gone.
|
|
990
|
+
* Check this: without it a short `events` array is indistinguishable from
|
|
991
|
+
* "nothing to catch up on".
|
|
992
|
+
*/
|
|
993
|
+
truncated: boolean;
|
|
994
|
+
retention_note: string | null;
|
|
995
|
+
}
|
|
952
996
|
interface DfsPayoutTier {
|
|
953
997
|
correct: number;
|
|
954
998
|
multiplier: number;
|
|
@@ -1423,6 +1467,33 @@ interface ListWebhookDeliveriesOptions {
|
|
|
1423
1467
|
*/
|
|
1424
1468
|
beforeId?: number;
|
|
1425
1469
|
}
|
|
1470
|
+
interface ReplayWebhookEventsOptions {
|
|
1471
|
+
/**
|
|
1472
|
+
* Read events after this cursor — the highest `X-PropLine-Sequence` you
|
|
1473
|
+
* have processed. Defaults to 0 (from the oldest retained event).
|
|
1474
|
+
*/
|
|
1475
|
+
sinceSeq?: number;
|
|
1476
|
+
/** Max events per page. Default 100, max 500. */
|
|
1477
|
+
limit?: number;
|
|
1478
|
+
}
|
|
1479
|
+
interface StreamOptions {
|
|
1480
|
+
/** Subscription to stream. Must be transport="websocket". */
|
|
1481
|
+
webhookId: number;
|
|
1482
|
+
/** Resume point — the last `seq` you processed. Default 0. */
|
|
1483
|
+
sinceSeq?: number;
|
|
1484
|
+
/** Auto-reconnect and resume from the last seq. Default true. */
|
|
1485
|
+
reconnect?: boolean;
|
|
1486
|
+
/** Override the websocket origin (default: derived from baseUrl). */
|
|
1487
|
+
wsUrl?: string;
|
|
1488
|
+
/** Called on every successful handshake with the `ready` frame. */
|
|
1489
|
+
onReady?: (ready: ReplayPage) => void;
|
|
1490
|
+
/**
|
|
1491
|
+
* Called when the server reports events after your cursor have aged out of
|
|
1492
|
+
* retention. This is the one case the stream cannot make you whole —
|
|
1493
|
+
* resync from the REST endpoints.
|
|
1494
|
+
*/
|
|
1495
|
+
onTruncated?: (ready: ReplayPage) => void;
|
|
1496
|
+
}
|
|
1426
1497
|
interface VerifySignatureOptions {
|
|
1427
1498
|
/** Webhook signing secret (returned once from `createWebhook`). */
|
|
1428
1499
|
secret: string;
|
|
@@ -1852,6 +1923,52 @@ declare class PropLine {
|
|
|
1852
1923
|
testWebhook(webhookId: number): Promise<unknown>;
|
|
1853
1924
|
/** Last 50 (default) delivery attempts for a webhook. */
|
|
1854
1925
|
listWebhookDeliveries(webhookId: number, options?: ListWebhookDeliveriesOptions): Promise<WebhookDelivery[]>;
|
|
1926
|
+
/**
|
|
1927
|
+
* Re-read this subscription's events in order, from a cursor.
|
|
1928
|
+
*
|
|
1929
|
+
* Answers "my endpoint was down — what did I miss?". Every delivery carries
|
|
1930
|
+
* an `X-PropLine-Sequence` header: a counter monotonic *within your
|
|
1931
|
+
* subscription*. Store the highest one you processed and pass it as
|
|
1932
|
+
* `sinceSeq`.
|
|
1933
|
+
*
|
|
1934
|
+
* Do NOT use `X-PropLine-Delivery` as the cursor — that id is global across
|
|
1935
|
+
* every subscription, so its gaps are other customers' traffic.
|
|
1936
|
+
*
|
|
1937
|
+
* Events come back oldest-first (the opposite of `listWebhookDeliveries`,
|
|
1938
|
+
* which is a newest-first debugging log). Page by passing `next_seq` back
|
|
1939
|
+
* as `sinceSeq` while `has_more` is true.
|
|
1940
|
+
*
|
|
1941
|
+
* **Check `truncated`.** True means events after your cursor have aged out
|
|
1942
|
+
* of retention (2 days, max 5,000 deliveries per subscription) and are gone
|
|
1943
|
+
* — resync from the REST endpoints instead of assuming you are current.
|
|
1944
|
+
*
|
|
1945
|
+
* Does not count against your daily request quota.
|
|
1946
|
+
*/
|
|
1947
|
+
replayWebhookEvents(webhookId: number, options?: ReplayWebhookEventsOptions): Promise<ReplayPage>;
|
|
1948
|
+
/**
|
|
1949
|
+
* Stream a websocket subscription as an async iterable.
|
|
1950
|
+
*
|
|
1951
|
+
* ```ts
|
|
1952
|
+
* for await (const ev of client.stream({ webhookId: 12, sinceSeq: 4180 })) {
|
|
1953
|
+
* console.log(ev.seq, ev.event_type, ev.data);
|
|
1954
|
+
* }
|
|
1955
|
+
* ```
|
|
1956
|
+
*
|
|
1957
|
+
* The subscription must have been created with `transport: "websocket"`.
|
|
1958
|
+
* Same events, same filters, same `seq` as an HTTP webhook — one
|
|
1959
|
+
* subscription, different transport.
|
|
1960
|
+
*
|
|
1961
|
+
* **Reconnects automatically and resumes from the last `seq` it saw**, which
|
|
1962
|
+
* is the whole point of the sequence: a dropped connection does not become a
|
|
1963
|
+
* gap in your data. Set `reconnect: false` to get a single connection that
|
|
1964
|
+
* ends when the socket closes.
|
|
1965
|
+
*
|
|
1966
|
+
* If the server reports `truncated` — events after your cursor aged out of
|
|
1967
|
+
* retention and are gone — `onTruncated` fires. Handle it: that is the one
|
|
1968
|
+
* case where the stream cannot make you whole and you should resync from the
|
|
1969
|
+
* REST endpoints.
|
|
1970
|
+
*/
|
|
1971
|
+
stream(options: StreamOptions): AsyncGenerator<ReplayEvent, void, void>;
|
|
1855
1972
|
/**
|
|
1856
1973
|
* Grade placed bets against their closing lines (CLV).
|
|
1857
1974
|
*
|
|
@@ -1953,6 +2070,6 @@ declare const Bookmakers: {
|
|
|
1953
2070
|
readonly PRIZEPICKS: "prizepicks";
|
|
1954
2071
|
};
|
|
1955
2072
|
type BookmakerKey = (typeof Bookmakers)[keyof typeof Bookmakers];
|
|
1956
|
-
declare const VERSION = "0.
|
|
2073
|
+
declare const VERSION = "0.46.0";
|
|
1957
2074
|
|
|
1958
|
-
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ClvBetInput, type ClvGradeResponse, type ClvGradedBet, type ClvSummary, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type EventProjectionsResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetEventProjectionsOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerGamesOptions, 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 PlayerGame, type PlayerGameLog, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, type ProjectionRow, 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 };
|
|
2075
|
+
export { AuthError, type BestLine, type BestLineSide, type BestPrice, type Bookmaker, type BookmakerKey, Bookmakers, type CalcEventEvOptions, type ClosingBookmaker, type ClosingMarket, type ClosingOutcome, type ClvBetInput, type ClvGradeResponse, type ClvGradedBet, type ClvSummary, type ContextResponse, type CreateWebhookOptions, type DfsPayoutTier, type DfsPayoutsResponse, type DfsPlayPayout, type EvLine, type EvOutcome, type Event, type EventBestLineResponse, type EventEvCalcResponse, type EventEvResponse, type EventProjectionsResponse, type ExportOddsHistoryOptions, type ExportResolvedPropsOptions, type FuturesEvent, type FuturesMarket, type FuturesOutcome, type GetDfsPayoutsOptions, type GetEventBestLineOptions, type GetEventEvOptions, type GetEventProjectionsOptions, type GetMlbGrandSalamiOptions, type GetNhlDailyGoalsTotalOptions, type GetOddsClosingOptions, type GetOddsHistoryOptions, type GetOddsOptions, type GetPlayerGamesOptions, 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 PlayerGame, type PlayerGameLog, type PlayerHistoryEntry, type PlayerHistoryResponse, type PlayerMarketTrend, type PlayerStat, type PlayerTrends, type ProjectionRow, PropLine, PropLineError, type PropLineErrorInfo, type PropLineOptions, type QuotaStatus, RateLimitError, type ReplayEvent, type ReplayPage, type ReplayWebhookEventsOptions, type ResolutionSummary, type ResolutionSummaryMarket, type ResolutionSummarySport, type ResolvedOutcome, type ResultsMarket, type ResultsResponse, type ScoreEvent, type Sport, type StatsResponse, type SteamMove, type StreamOptions, type TrendLastGame, type TrendStreak, type UpdateWebhookOptions, VERSION, type VerifySignatureOptions, type WeatherInfo, type Webhook, type WebhookDelivery, type WebhookEventType };
|
package/dist/index.js
CHANGED
|
@@ -787,6 +787,139 @@ var PropLine = class {
|
|
|
787
787
|
{ params: { limit: options.limit ?? 50, before_id: options.beforeId } }
|
|
788
788
|
);
|
|
789
789
|
}
|
|
790
|
+
/**
|
|
791
|
+
* Re-read this subscription's events in order, from a cursor.
|
|
792
|
+
*
|
|
793
|
+
* Answers "my endpoint was down — what did I miss?". Every delivery carries
|
|
794
|
+
* an `X-PropLine-Sequence` header: a counter monotonic *within your
|
|
795
|
+
* subscription*. Store the highest one you processed and pass it as
|
|
796
|
+
* `sinceSeq`.
|
|
797
|
+
*
|
|
798
|
+
* Do NOT use `X-PropLine-Delivery` as the cursor — that id is global across
|
|
799
|
+
* every subscription, so its gaps are other customers' traffic.
|
|
800
|
+
*
|
|
801
|
+
* Events come back oldest-first (the opposite of `listWebhookDeliveries`,
|
|
802
|
+
* which is a newest-first debugging log). Page by passing `next_seq` back
|
|
803
|
+
* as `sinceSeq` while `has_more` is true.
|
|
804
|
+
*
|
|
805
|
+
* **Check `truncated`.** True means events after your cursor have aged out
|
|
806
|
+
* of retention (2 days, max 5,000 deliveries per subscription) and are gone
|
|
807
|
+
* — resync from the REST endpoints instead of assuming you are current.
|
|
808
|
+
*
|
|
809
|
+
* Does not count against your daily request quota.
|
|
810
|
+
*/
|
|
811
|
+
replayWebhookEvents(webhookId, options = {}) {
|
|
812
|
+
return this._request(
|
|
813
|
+
"GET",
|
|
814
|
+
`/webhooks/${webhookId}/replay`,
|
|
815
|
+
{ params: { since_seq: options.sinceSeq ?? 0, limit: options.limit ?? 100 } }
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Stream a websocket subscription as an async iterable.
|
|
820
|
+
*
|
|
821
|
+
* ```ts
|
|
822
|
+
* for await (const ev of client.stream({ webhookId: 12, sinceSeq: 4180 })) {
|
|
823
|
+
* console.log(ev.seq, ev.event_type, ev.data);
|
|
824
|
+
* }
|
|
825
|
+
* ```
|
|
826
|
+
*
|
|
827
|
+
* The subscription must have been created with `transport: "websocket"`.
|
|
828
|
+
* Same events, same filters, same `seq` as an HTTP webhook — one
|
|
829
|
+
* subscription, different transport.
|
|
830
|
+
*
|
|
831
|
+
* **Reconnects automatically and resumes from the last `seq` it saw**, which
|
|
832
|
+
* is the whole point of the sequence: a dropped connection does not become a
|
|
833
|
+
* gap in your data. Set `reconnect: false` to get a single connection that
|
|
834
|
+
* ends when the socket closes.
|
|
835
|
+
*
|
|
836
|
+
* If the server reports `truncated` — events after your cursor aged out of
|
|
837
|
+
* retention and are gone — `onTruncated` fires. Handle it: that is the one
|
|
838
|
+
* case where the stream cannot make you whole and you should resync from the
|
|
839
|
+
* REST endpoints.
|
|
840
|
+
*/
|
|
841
|
+
async *stream(options) {
|
|
842
|
+
const wsBase = (options.wsUrl ?? this.baseUrl).replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/v1\/?$/, "");
|
|
843
|
+
const url = `${wsBase}/v1/stream`;
|
|
844
|
+
let cursor = options.sinceSeq ?? 0;
|
|
845
|
+
let attempt = 0;
|
|
846
|
+
for (; ; ) {
|
|
847
|
+
const queue = [];
|
|
848
|
+
let notify = null;
|
|
849
|
+
let closed = null;
|
|
850
|
+
let opened = false;
|
|
851
|
+
const ws = new WebSocket(url);
|
|
852
|
+
const wake = () => {
|
|
853
|
+
const n = notify;
|
|
854
|
+
notify = null;
|
|
855
|
+
n?.();
|
|
856
|
+
};
|
|
857
|
+
ws.addEventListener("open", () => {
|
|
858
|
+
opened = true;
|
|
859
|
+
ws.send(JSON.stringify({
|
|
860
|
+
type: "auth",
|
|
861
|
+
api_key: this.apiKey,
|
|
862
|
+
webhook_id: options.webhookId,
|
|
863
|
+
since_seq: cursor
|
|
864
|
+
}));
|
|
865
|
+
});
|
|
866
|
+
ws.addEventListener("message", (e) => {
|
|
867
|
+
let msg;
|
|
868
|
+
try {
|
|
869
|
+
msg = JSON.parse(String(e.data));
|
|
870
|
+
} catch {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (msg.type === "ready") {
|
|
874
|
+
attempt = 0;
|
|
875
|
+
if (msg.truncated) options.onTruncated?.(msg);
|
|
876
|
+
options.onReady?.(msg);
|
|
877
|
+
} else if (msg.type === "event") {
|
|
878
|
+
queue.push(msg);
|
|
879
|
+
wake();
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
ws.addEventListener("close", (e) => {
|
|
883
|
+
const terminal = [4400, 4401, 4403, 4404].includes(e.code);
|
|
884
|
+
closed = new PropLineError(
|
|
885
|
+
e.code,
|
|
886
|
+
`stream closed${e.reason ? `: ${e.reason}` : ""}`
|
|
887
|
+
);
|
|
888
|
+
closed.terminal = terminal;
|
|
889
|
+
wake();
|
|
890
|
+
});
|
|
891
|
+
ws.addEventListener("error", () => {
|
|
892
|
+
if (!closed) closed = new PropLineError(0, "stream connection error");
|
|
893
|
+
wake();
|
|
894
|
+
});
|
|
895
|
+
try {
|
|
896
|
+
for (; ; ) {
|
|
897
|
+
while (queue.length) {
|
|
898
|
+
const ev = queue.shift();
|
|
899
|
+
cursor = ev.seq;
|
|
900
|
+
yield ev;
|
|
901
|
+
}
|
|
902
|
+
if (closed) break;
|
|
903
|
+
await new Promise((r) => {
|
|
904
|
+
notify = r;
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
} finally {
|
|
908
|
+
try {
|
|
909
|
+
ws.close();
|
|
910
|
+
} catch {
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const err = closed;
|
|
914
|
+
if (err?.terminal) throw err;
|
|
915
|
+
if (options.reconnect === false) {
|
|
916
|
+
if (err && !opened) throw err;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const delayMs = Math.min(3e4, 500 * 2 ** attempt++);
|
|
920
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
921
|
+
}
|
|
922
|
+
}
|
|
790
923
|
/**
|
|
791
924
|
* Grade placed bets against their closing lines (CLV).
|
|
792
925
|
*
|
|
@@ -927,7 +1060,7 @@ var Bookmakers = {
|
|
|
927
1060
|
POLYMARKET: "polymarket",
|
|
928
1061
|
PRIZEPICKS: "prizepicks"
|
|
929
1062
|
};
|
|
930
|
-
var VERSION = "0.
|
|
1063
|
+
var VERSION = "0.46.0";
|
|
931
1064
|
export {
|
|
932
1065
|
AuthError,
|
|
933
1066
|
Bookmakers,
|