@mrdoge/node 0.1.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.
@@ -0,0 +1,417 @@
1
+ import { WelcomeParams, MethodName, MethodParams, MethodResult, SubscriptionEventParams, SubscriptionClosedParams, SubscriptionMethodName, PushEventsOf, SubscriptionClosedReason, ErrorCode } from '@mrdoge/protocol';
2
+ export { AiPick, BetItem, Clock, Competition, ErrorCode, Market, Match, MatchDetail, MatchDetailSelect, MatchSelect, MatchState, MatchStats, MatchStatus, Pagination, Period, PickConfidence, PickLeg, PickResult, Recommendation, Region, Selector, Sport, StatPlayer, SubscriptionClosedReason, Team, TeamDetail, TeamForm, TeamFormMatch, TeamFormResult, WelcomeParams } from '@mrdoge/protocol';
3
+ import { MrDogeHttpClient } from '@mrdoge/http';
4
+
5
+ interface BackoffConfig {
6
+ minMs: number;
7
+ maxMs: number;
8
+ /** Jitter as a fraction of the computed delay (0.2 = ±20%). */
9
+ jitter: number;
10
+ }
11
+
12
+ /**
13
+ * Per-call options accepted by every method that makes a request. Mirrors
14
+ * the `fetch` convention so callers can plug in a standard `AbortController`.
15
+ *
16
+ * When `signal` fires, the in-flight request is dropped client-side and the
17
+ * returned promise rejects with `AbortError` (`err.name === "AbortError"`).
18
+ */
19
+ interface CallOptions {
20
+ signal?: AbortSignal;
21
+ }
22
+
23
+ interface ConnectionConfig {
24
+ baseUrl: string;
25
+ apiKey: string;
26
+ /** Default locale applied to method params. Per-call overrides win. */
27
+ locale?: string;
28
+ /** Default timezone applied to method params. Per-call overrides win. */
29
+ timezone?: string;
30
+ requestTimeoutMs: number;
31
+ maxReconnectAttempts: number;
32
+ reconnectBackoff: BackoffConfig;
33
+ compression: boolean;
34
+ /** Auth must complete (auth ack + welcome) within this many ms. */
35
+ authTimeoutMs: number;
36
+ }
37
+ interface ConnectionEvents {
38
+ connected: {
39
+ welcome: WelcomeParams;
40
+ };
41
+ disconnected: {
42
+ reason: string;
43
+ code: number;
44
+ };
45
+ reconnecting: {
46
+ attempt: number;
47
+ delayMs: number;
48
+ };
49
+ }
50
+ /**
51
+ * Active subscriptions tracked by the connection. The handle's `subId` is
52
+ * mutable — it changes on reconnect when the server issues a fresh id.
53
+ */
54
+ interface SubscriptionRegistration {
55
+ /** Current server-issued sub id. Changes on reconnect. */
56
+ subId: string;
57
+ method: MethodName;
58
+ params: unknown;
59
+ onEvent: (event: SubscriptionEventParams) => void;
60
+ onClosed: (params: SubscriptionClosedParams) => void;
61
+ /** Called by the connection when a reconnect resubscribe produced a fresh snapshot. */
62
+ onSnapshot: (newSubId: string, snapshot: unknown) => void;
63
+ }
64
+ /**
65
+ * Long-lived WebSocket connection with JSON-RPC dispatch and automatic
66
+ * reconnection. Subscriptions are resubscribed transparently on reconnect.
67
+ */
68
+ declare class Connection {
69
+ private readonly emitter;
70
+ private readonly config;
71
+ private ws;
72
+ private nextRequestId;
73
+ private readonly pending;
74
+ private readonly subscriptions;
75
+ private connectingPromise;
76
+ private welcome;
77
+ private closed;
78
+ private reconnectAttempt;
79
+ private reconnectAbort;
80
+ constructor(config: ConnectionConfig);
81
+ on: <E extends keyof ConnectionEvents>(event: E, fn: (payload: ConnectionEvents[E]) => void) => () => void;
82
+ off: <E extends keyof ConnectionEvents>(event: E, fn: (payload: ConnectionEvents[E]) => void) => void;
83
+ get isConnected(): boolean;
84
+ /**
85
+ * Idempotent. Returns the welcome payload once connected + authed.
86
+ */
87
+ connect(): Promise<WelcomeParams>;
88
+ call<M extends MethodName>(method: M, params: MethodParams<M>, options?: CallOptions): Promise<MethodResult<M>>;
89
+ /**
90
+ * Registers a subscription with the connection. The connection sends the
91
+ * subscribe call, stores the registration for reconnect, and routes incoming
92
+ * push events to the handler.
93
+ */
94
+ registerSubscription<M extends MethodName>(method: M, params: MethodParams<M>, handlers: {
95
+ onEvent: SubscriptionRegistration["onEvent"];
96
+ onClosed: SubscriptionRegistration["onClosed"];
97
+ onSnapshot: SubscriptionRegistration["onSnapshot"];
98
+ }, options?: CallOptions): Promise<{
99
+ subId: string;
100
+ snapshot: unknown;
101
+ }>;
102
+ /**
103
+ * Cancels a subscription server-side and removes the registration locally.
104
+ */
105
+ cancelSubscription(subId: string): Promise<void>;
106
+ close(): Promise<void>;
107
+ private openAndAuth;
108
+ private performAuth;
109
+ /**
110
+ * Set during `performAuth` so we can resolve when the `welcome` notification arrives.
111
+ */
112
+ private pendingWelcome;
113
+ private send;
114
+ private handleMessage;
115
+ private handleNotification;
116
+ private handleClose;
117
+ private handleSocketError;
118
+ private scheduleReconnect;
119
+ private resubscribeAll;
120
+ }
121
+
122
+ declare class Regions {
123
+ private readonly conn;
124
+ private readonly defaults;
125
+ constructor(conn: Connection, defaults: {
126
+ locale?: string;
127
+ });
128
+ list(params?: MethodParams<"regions.list">, options?: CallOptions): Promise<MethodResult<"regions.list">>;
129
+ }
130
+
131
+ declare class Competitions {
132
+ private readonly conn;
133
+ private readonly defaults;
134
+ constructor(conn: Connection, defaults: {
135
+ locale?: string;
136
+ });
137
+ list(params?: MethodParams<"competitions.list">, options?: CallOptions): Promise<MethodResult<"competitions.list">>;
138
+ }
139
+
140
+ declare class Teams {
141
+ private readonly conn;
142
+ private readonly defaults;
143
+ constructor(conn: Connection, defaults: {
144
+ locale?: string;
145
+ });
146
+ list(params?: MethodParams<"teams.list">, options?: CallOptions): Promise<MethodResult<"teams.list">>;
147
+ get(params: MethodParams<"teams.get">, options?: CallOptions): Promise<MethodResult<"teams.get">>;
148
+ form(params: MethodParams<"teams.form">, options?: CallOptions): Promise<MethodResult<"teams.form">>;
149
+ }
150
+
151
+ /**
152
+ * Maps each push event name to the typed `data` payload it carries.
153
+ * Derived from the discriminated union in `@mrdoge/protocol`.
154
+ */
155
+ type EventDataMap = {
156
+ [E in SubscriptionEventParams as E["event"]]: E["data"];
157
+ };
158
+ type SnapshotOf<M extends SubscriptionMethodName> = MethodResult<M> extends {
159
+ snapshot: infer S;
160
+ } ? S : never;
161
+ type SubEventMap<M extends SubscriptionMethodName> = {
162
+ [E in PushEventsOf<M> & keyof EventDataMap]: EventDataMap[E];
163
+ } & {
164
+ snapshot: SnapshotOf<M>;
165
+ closed: {
166
+ reason: SubscriptionClosedReason;
167
+ message?: string;
168
+ };
169
+ };
170
+ /**
171
+ * Handle returned by `mrdoge.matches.subscribe(...)` and `subscribeLive(...)`.
172
+ *
173
+ * Stable across reconnects: the underlying server-issued `sub` id may change,
174
+ * but this handle and its listeners persist. On reconnect, the SDK refreshes
175
+ * `snapshot` and emits a `snapshot` event so callers can replace their local
176
+ * state.
177
+ */
178
+ declare class Subscription<M extends SubscriptionMethodName> {
179
+ private readonly emitter;
180
+ private readonly connection;
181
+ private internalSubId;
182
+ private currentSnapshot;
183
+ private cancelled;
184
+ /**
185
+ * True when `cancel()` was called before the WS subscribe arrived (subId
186
+ * still pending). Once WS resolves, `_deliverSnapshot` issues the
187
+ * server-side cancel so we don't leak a registered subscription.
188
+ */
189
+ private pendingCancel;
190
+ /** @internal — constructed by the SDK, not by user code. */
191
+ constructor(connection: Connection, subId: string, initialSnapshot: SnapshotOf<M>);
192
+ /** The latest snapshot the server has emitted (initial, or post-reconnect). */
193
+ get snapshot(): SnapshotOf<M>;
194
+ /** Current server-issued subscription id. Changes on reconnect. */
195
+ get id(): string;
196
+ /**
197
+ * Listen for a push event from this subscription. Returns an unsubscribe
198
+ * function that removes only this listener.
199
+ *
200
+ * In addition to protocol push events, two synthetic events fire:
201
+ * - `snapshot` — when a reconnect produced a fresh snapshot
202
+ * - `closed` — when the server terminates the subscription (or cancel)
203
+ */
204
+ on<E extends keyof SubEventMap<M>>(event: E, fn: (payload: SubEventMap<M>[E]) => void): () => void;
205
+ /**
206
+ * Cancel the subscription server-side. Idempotent — calling twice is safe.
207
+ */
208
+ cancel(): Promise<void>;
209
+ /** @internal */
210
+ _deliverEvent(params: SubscriptionEventParams): void;
211
+ /** @internal */
212
+ _deliverSnapshot(newSubId: string, snapshot: SnapshotOf<M>): void;
213
+ /** @internal */
214
+ _deliverClosed(reason: SubscriptionClosedReason, message?: string): void;
215
+ }
216
+
217
+ interface Defaults {
218
+ locale?: string;
219
+ timezone?: string;
220
+ }
221
+ declare class Matches {
222
+ private readonly conn;
223
+ private readonly defaults;
224
+ private readonly http;
225
+ constructor(conn: Connection, defaults: Defaults, http: MrDogeHttpClient);
226
+ list(params?: MethodParams<"matches.list">, options?: CallOptions): Promise<MethodResult<"matches.list">>;
227
+ get(params: MethodParams<"matches.get">, options?: CallOptions): Promise<MethodResult<"matches.get">>;
228
+ trending(params?: MethodParams<"matches.trending">, options?: CallOptions): Promise<MethodResult<"matches.trending">>;
229
+ search(params: MethodParams<"matches.search">, options?: CallOptions): Promise<MethodResult<"matches.search">>;
230
+ subscribeLive(params?: MethodParams<"matches.subscribeLive">, options?: CallOptions): Promise<Subscription<"matches.subscribeLive">>;
231
+ subscribe(params: MethodParams<"matches.subscribe">, options?: CallOptions): Promise<Subscription<"matches.subscribe">>;
232
+ }
233
+
234
+ declare class Picks {
235
+ private readonly conn;
236
+ private readonly defaults;
237
+ constructor(conn: Connection, defaults: {
238
+ locale?: string;
239
+ });
240
+ list(params?: MethodParams<"ai.picks.list">, options?: CallOptions): Promise<MethodResult<"ai.picks.list">>;
241
+ }
242
+ declare class Recommendations {
243
+ private readonly conn;
244
+ private readonly defaults;
245
+ constructor(conn: Connection, defaults: {
246
+ locale?: string;
247
+ });
248
+ list(params?: MethodParams<"ai.recommendations.list">, options?: CallOptions): Promise<MethodResult<"ai.recommendations.list">>;
249
+ }
250
+ declare class Ai {
251
+ readonly picks: Picks;
252
+ readonly recommendations: Recommendations;
253
+ constructor(conn: Connection, defaults: {
254
+ locale?: string;
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Server-side resource for minting short-lived auth tokens for client-side use.
260
+ *
261
+ * Typical use: customer's backend has the `sk_live_...` key and exposes an
262
+ * HTTP route that mints tokens for their frontend (browser / React Native):
263
+ *
264
+ * ```ts
265
+ * app.post("/api/mrdoge/token", async (req, res) => {
266
+ * const { token, expiresAt } = await mrdoge.tokens.create({ ttl: 600 })
267
+ * res.json({ token, expiresAt })
268
+ * })
269
+ * ```
270
+ *
271
+ * The frontend uses these tokens with `@mrdoge/client` — the sk_live_... key
272
+ * never leaves the backend.
273
+ */
274
+ declare class Tokens {
275
+ private readonly conn;
276
+ constructor(conn: Connection);
277
+ /**
278
+ * Mint a short-lived JWT auth token.
279
+ *
280
+ * @param params.ttl Token lifetime in seconds. Default 600 (10 min).
281
+ * Server-enforced bounds: min 60, max 86400 (24h).
282
+ * @returns { token, expiresAt } — `token` is opaque to the customer; pass
283
+ * it to `@mrdoge/client`. `expiresAt` is ISO-8601.
284
+ */
285
+ create(params?: MethodParams<"tokens.create">, options?: CallOptions): Promise<MethodResult<"tokens.create">>;
286
+ }
287
+
288
+ declare const DEFAULT_BASE_URL = "wss://api.mrdoge.co/sdk/v1";
289
+ interface MrDogeOptions {
290
+ apiKey: string;
291
+ /** Override the server URL (e.g. for local dev). Defaults to production. */
292
+ baseUrl?: string;
293
+ /** Default locale applied to every call; overridable per-call. */
294
+ locale?: string;
295
+ /** Default timezone applied to every call; overridable per-call. */
296
+ timezone?: string;
297
+ /** Request-level timeout in milliseconds. Default 10s. */
298
+ requestTimeoutMs?: number;
299
+ /** Max reconnect attempts on transient disconnects. Default Infinity. */
300
+ maxReconnectAttempts?: number;
301
+ /** Exponential backoff config for reconnects. */
302
+ reconnectBackoff?: Partial<BackoffConfig>;
303
+ /**
304
+ * Whether to negotiate `permessage-deflate` compression at the WS handshake.
305
+ * Default `true`. Disable for CPU-constrained environments.
306
+ */
307
+ compression?: boolean;
308
+ }
309
+ /**
310
+ * Mr. Doge SDK entrypoint.
311
+ *
312
+ * ```ts
313
+ * const mrdoge = new MrDoge({ apiKey: process.env.MRDOGE_API_KEY! })
314
+ * const matches = await mrdoge.matches.list({ date: "2026-05-12" })
315
+ * ```
316
+ *
317
+ * The constructor does not open a connection. The WebSocket is opened lazily
318
+ * on the first method call and reused for the lifetime of the client.
319
+ */
320
+ declare class MrDoge {
321
+ readonly regions: Regions;
322
+ readonly competitions: Competitions;
323
+ readonly teams: Teams;
324
+ readonly matches: Matches;
325
+ readonly ai: Ai;
326
+ readonly tokens: Tokens;
327
+ private readonly connection;
328
+ private readonly http;
329
+ constructor(options: MrDogeOptions);
330
+ /**
331
+ * Listen to connection lifecycle events.
332
+ */
333
+ on<E extends keyof ConnectionEvents>(event: E, fn: (payload: ConnectionEvents[E]) => void): () => void;
334
+ /**
335
+ * Force-open the connection now instead of waiting for the first call.
336
+ * Returns the welcome payload from the server.
337
+ */
338
+ connect(): Promise<{
339
+ protocolVersion: number;
340
+ serverVersion: string;
341
+ tier: string;
342
+ rateLimit: {
343
+ requestsPerMinute: number;
344
+ subscriptionsMax: number;
345
+ };
346
+ }>;
347
+ /**
348
+ * Close the connection and cancel every active subscription. The client is
349
+ * unusable after `close()`.
350
+ */
351
+ close(): Promise<void>;
352
+ }
353
+
354
+ /**
355
+ * Base class for every SDK-thrown error. Map by `instanceof` or by `code`.
356
+ */
357
+ declare class MrDogeError extends Error {
358
+ readonly code: ErrorCode | "connection_error" | "disconnected" | "timeout" | "aborted" | "unknown";
359
+ readonly data?: unknown;
360
+ constructor(code: MrDogeError["code"], message: string, data?: unknown);
361
+ }
362
+ declare class UnauthorizedError extends MrDogeError {
363
+ constructor(message: string, data?: unknown);
364
+ }
365
+ declare class ForbiddenError extends MrDogeError {
366
+ constructor(message: string, data?: unknown);
367
+ }
368
+ declare class NotFoundError extends MrDogeError {
369
+ constructor(message: string, data?: unknown);
370
+ }
371
+ declare class ValidationError extends MrDogeError {
372
+ constructor(message: string, data?: unknown);
373
+ }
374
+ declare class RateLimitError extends MrDogeError {
375
+ readonly retryAfterMs: number;
376
+ readonly limit?: number;
377
+ readonly remaining?: number;
378
+ readonly resetAt?: Date;
379
+ constructor(message: string, data?: unknown);
380
+ }
381
+ declare class SubscriptionLimitError extends MrDogeError {
382
+ constructor(message: string, data?: unknown);
383
+ }
384
+ declare class ConnectionLimitError extends MrDogeError {
385
+ readonly current?: number;
386
+ readonly max?: number;
387
+ constructor(message: string, data?: unknown);
388
+ }
389
+ declare class UnavailableError extends MrDogeError {
390
+ constructor(message: string, data?: unknown);
391
+ }
392
+ declare class InternalError extends MrDogeError {
393
+ constructor(message: string, data?: unknown);
394
+ }
395
+ declare class ProtocolError extends MrDogeError {
396
+ constructor(message: string, data?: unknown);
397
+ }
398
+ /** Raised when the SDK can't reach or stay connected to the server. */
399
+ declare class ConnectionError extends MrDogeError {
400
+ constructor(message: string, data?: unknown);
401
+ }
402
+ /** A request was in flight when the connection dropped. The caller should retry if safe. */
403
+ declare class DisconnectedError extends MrDogeError {
404
+ constructor(message?: string, data?: unknown);
405
+ }
406
+ declare class TimeoutError extends MrDogeError {
407
+ constructor(message: string, data?: unknown);
408
+ }
409
+ /**
410
+ * Thrown when a request is aborted via the customer-supplied
411
+ * `options.signal`. Matches `fetch` convention — `err.name === "AbortError"`.
412
+ */
413
+ declare class AbortError extends MrDogeError {
414
+ constructor(message?: string);
415
+ }
416
+
417
+ export { AbortError, type CallOptions, ConnectionError, ConnectionLimitError, DEFAULT_BASE_URL, DisconnectedError, ForbiddenError, InternalError, MrDoge, MrDogeError, type MrDogeOptions, NotFoundError, ProtocolError, RateLimitError, Subscription, SubscriptionLimitError, TimeoutError, UnauthorizedError, UnavailableError, ValidationError };