dino-markets 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.
package/dist/index.js ADDED
@@ -0,0 +1,278 @@
1
+ // src/errors.ts
2
+ var DinoError = class extends Error {
3
+ /** HTTP status code of the failed response. */
4
+ status;
5
+ /** Parsed JSON response body, or null if unavailable/unparseable. */
6
+ body;
7
+ constructor(message, status, body = null) {
8
+ super(message);
9
+ this.name = "DinoError";
10
+ this.status = status;
11
+ this.body = body;
12
+ }
13
+ };
14
+ var AuthenticationError = class extends DinoError {
15
+ constructor(message, status, body = null) {
16
+ super(message, status, body);
17
+ this.name = "AuthenticationError";
18
+ }
19
+ };
20
+ var PlanError = class extends DinoError {
21
+ constructor(message, status, body = null) {
22
+ super(message, status, body);
23
+ this.name = "PlanError";
24
+ }
25
+ };
26
+ var RateLimitError = class extends DinoError {
27
+ retryAfter;
28
+ constructor(message, status, body = null, retryAfter) {
29
+ super(message, status, body);
30
+ this.name = "RateLimitError";
31
+ this.retryAfter = retryAfter;
32
+ }
33
+ };
34
+ var ServerError = class extends DinoError {
35
+ constructor(message, status, body = null) {
36
+ super(message, status, body);
37
+ this.name = "ServerError";
38
+ }
39
+ };
40
+ function extractRetryAfter(headers, body) {
41
+ const header = headers?.get?.("retry-after");
42
+ if (header) {
43
+ const n = Number(header);
44
+ if (Number.isFinite(n)) return n;
45
+ }
46
+ if (body && typeof body === "object" && "retry_after" in body) {
47
+ const n = Number(body.retry_after);
48
+ if (Number.isFinite(n)) return n;
49
+ }
50
+ return void 0;
51
+ }
52
+ function errorMessage(status, body, fallback) {
53
+ if (body && typeof body === "object" && "error" in body) {
54
+ const msg = body.error;
55
+ if (typeof msg === "string" && msg) return msg;
56
+ }
57
+ return fallback;
58
+ }
59
+ function mapError(status, body, headers) {
60
+ if (status === 401) {
61
+ return new AuthenticationError(errorMessage(status, body, "authentication failed"), status, body);
62
+ }
63
+ if (status === 402) {
64
+ return new PlanError(errorMessage(status, body, "plan does not allow this request"), status, body);
65
+ }
66
+ if (status === 429) {
67
+ const retryAfter = extractRetryAfter(headers, body);
68
+ return new RateLimitError(errorMessage(status, body, "rate limit exceeded"), status, body, retryAfter);
69
+ }
70
+ if (status >= 500) {
71
+ return new ServerError(errorMessage(status, body, "server error"), status, body);
72
+ }
73
+ return new DinoError(errorMessage(status, body, `request failed with status ${status}`), status, body);
74
+ }
75
+
76
+ // src/client.ts
77
+ var SDK_VERSION = "0.1.0";
78
+ var DEFAULT_BASE_URL = "https://api.dino.markets";
79
+ var DEFAULT_TIMEOUT_MS = 1e4;
80
+ var DEFAULT_MAX_RETRIES = 2;
81
+ var RETRY_BACKOFF_MS = [500, 1e3, 2e3];
82
+ function resolveApiKey(apiKey) {
83
+ if (apiKey) return apiKey;
84
+ if (typeof process !== "undefined" && process.env && process.env.DINO_API_KEY) {
85
+ return process.env.DINO_API_KEY;
86
+ }
87
+ throw new Error(
88
+ "dino-markets: no API key provided. Pass { apiKey } or set the DINO_API_KEY environment variable. Get a free key at https://dino.markets"
89
+ );
90
+ }
91
+ function validateLimit(limit) {
92
+ if (limit === void 0) return;
93
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1e3) {
94
+ throw new Error(`dino-markets: limit must be an integer between 1 and 1000, got ${limit}`);
95
+ }
96
+ }
97
+ function buildQuery(params) {
98
+ const usp = new URLSearchParams();
99
+ for (const [key, value] of Object.entries(params)) {
100
+ if (value === void 0 || value === null) continue;
101
+ usp.set(key, String(value));
102
+ }
103
+ const qs = usp.toString();
104
+ return qs ? `?${qs}` : "";
105
+ }
106
+ function sleep(ms) {
107
+ return new Promise((resolve) => setTimeout(resolve, ms));
108
+ }
109
+ var Dino = class {
110
+ apiKey;
111
+ baseUrl;
112
+ timeoutMs;
113
+ maxRetries;
114
+ fetchImpl;
115
+ constructor(opts = {}) {
116
+ this.apiKey = resolveApiKey(opts.apiKey);
117
+ this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
118
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
119
+ this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
120
+ this.fetchImpl = opts.fetchImpl ?? fetch;
121
+ }
122
+ /** Matched cross-venue catalog. See MarketsOptions for filters. */
123
+ async markets(opts = {}) {
124
+ validateLimit(opts.limit);
125
+ const qs = buildQuery({
126
+ sport: opts.sport,
127
+ league: opts.league,
128
+ status: opts.status,
129
+ signal: opts.signal,
130
+ sort: opts.sort,
131
+ include: opts.include,
132
+ market_type: opts.market_type,
133
+ game_id: opts.game_id,
134
+ limit: opts.limit
135
+ });
136
+ return this.request("GET", `/v2/markets${qs}`);
137
+ }
138
+ /** One canonical market object. Accepts a "dino_<uuid>" id or a bare uuid. */
139
+ async market(marketId) {
140
+ if (!marketId) throw new Error("dino-markets: market(marketId) requires a non-empty marketId");
141
+ return this.request("GET", `/v2/pairs/${encodeURIComponent(marketId)}`);
142
+ }
143
+ /** Per-outcome Kalshi/Polymarket price history for one market. */
144
+ async history(marketId) {
145
+ if (!marketId) throw new Error("dino-markets: history(marketId) requires a non-empty marketId");
146
+ return this.request("GET", `/v2/pairs/${encodeURIComponent(marketId)}/history`);
147
+ }
148
+ /** Sports and leagues currently in season, with lifecycle counts. */
149
+ async leagues() {
150
+ return this.request("GET", "/v2/leagues");
151
+ }
152
+ /** Sugar for markets({ ...opts, signal: "arb" }). */
153
+ async findArbitrage(opts = {}) {
154
+ return this.markets({ ...opts, signal: "arb" });
155
+ }
156
+ /** Flag a bad or incorrect arbitrage signal. At least one field of body is required. */
157
+ async reportBadArb(body) {
158
+ const hasField = body && Object.values(body).some((v) => v !== void 0 && v !== null);
159
+ if (!hasField) {
160
+ throw new Error(
161
+ "dino-markets: reportBadArb(body) requires at least one of opp_id/reason/sport/market/detail"
162
+ );
163
+ }
164
+ return this.request("POST", "/v2/report-bad-arb", body);
165
+ }
166
+ /**
167
+ * Mint a short-lived WebSocket connect ticket (Basic/Pro only; 402 on Free).
168
+ * See stream.ts's `watch()` helper to consume it directly.
169
+ */
170
+ async streamToken() {
171
+ return this.request("POST", "/v1/stream/token");
172
+ }
173
+ async request(method, path, body) {
174
+ const url = `${this.baseUrl}${path}`;
175
+ const init = {
176
+ method,
177
+ headers: {
178
+ Authorization: `Bearer ${this.apiKey}`,
179
+ Accept: "application/json",
180
+ "User-Agent": `dino-markets-js/${SDK_VERSION}`,
181
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
182
+ },
183
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
184
+ };
185
+ let attempt = 0;
186
+ for (; ; ) {
187
+ const controller = new AbortController();
188
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
189
+ let response;
190
+ try {
191
+ response = await this.fetchImpl(url, { ...init, signal: controller.signal });
192
+ } catch (err) {
193
+ clearTimeout(timer);
194
+ if (attempt < this.maxRetries) {
195
+ const delayMs = RETRY_BACKOFF_MS[attempt] ?? 2e3;
196
+ attempt += 1;
197
+ await sleep(delayMs);
198
+ continue;
199
+ }
200
+ if (err instanceof Error && err.name === "AbortError") {
201
+ throw new Error(`dino-markets: request to ${path} timed out after ${this.timeoutMs}ms`);
202
+ }
203
+ throw err;
204
+ }
205
+ clearTimeout(timer);
206
+ if (response.ok) {
207
+ if (response.status === 202 || response.status === 204) {
208
+ return await safeJson(response);
209
+ }
210
+ return await response.json();
211
+ }
212
+ const retryable = response.status === 429 || response.status >= 500;
213
+ if (retryable && attempt < this.maxRetries) {
214
+ const body3 = await safeJson(response);
215
+ const headerRetry = Number(response.headers.get("retry-after"));
216
+ const bodyRetry = body3 && typeof body3 === "object" && "retry_after" in body3 ? Number(body3.retry_after) : NaN;
217
+ const retryAfterS = Number.isFinite(headerRetry) ? headerRetry : Number.isFinite(bodyRetry) ? bodyRetry : void 0;
218
+ const delayMs = retryAfterS !== void 0 ? retryAfterS * 1e3 : RETRY_BACKOFF_MS[attempt] ?? 2e3;
219
+ attempt += 1;
220
+ await sleep(delayMs);
221
+ continue;
222
+ }
223
+ const body2 = await safeJson(response);
224
+ throw mapError(response.status, body2, response.headers);
225
+ }
226
+ }
227
+ };
228
+ async function safeJson(response) {
229
+ try {
230
+ const text = await response.text();
231
+ if (!text) return null;
232
+ return JSON.parse(text);
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+
238
+ // src/stream.ts
239
+ async function watch(client, opts) {
240
+ const centrifugeModuleName = "centrifuge";
241
+ let CentrifugeCtor;
242
+ try {
243
+ const mod = await import(
244
+ /* @vite-ignore */
245
+ centrifugeModuleName
246
+ );
247
+ CentrifugeCtor = mod.Centrifuge ?? mod.default?.Centrifuge ?? mod.default;
248
+ } catch {
249
+ throw new Error(
250
+ 'dino-markets: streaming requires the "centrifuge" package. Install it with `npm install centrifuge`.'
251
+ );
252
+ }
253
+ const { ticket, ws_url } = await client.streamToken();
254
+ const centrifuge = new CentrifugeCtor(ws_url, {
255
+ data: { t: ticket }
256
+ });
257
+ centrifuge.on("publication", (ctx) => {
258
+ opts.onFrame(ctx.data, ctx.channel);
259
+ });
260
+ if (opts.onError) {
261
+ centrifuge.on("error", opts.onError);
262
+ }
263
+ centrifuge.connect();
264
+ return {
265
+ close: () => centrifuge.disconnect()
266
+ };
267
+ }
268
+ export {
269
+ AuthenticationError,
270
+ Dino,
271
+ DinoError,
272
+ PlanError,
273
+ RateLimitError,
274
+ ServerError,
275
+ mapError,
276
+ watch
277
+ };
278
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/stream.ts"],"sourcesContent":["/**\n * Error types raised by the dino-markets client.\n *\n * Every non-2xx HTTP response from the API is mapped to one of these via\n * `mapError`. `body` is the parsed JSON response body when available (the\n * API returns `{ error: string, ... }` on failures), or `null` if the body\n * could not be parsed as JSON.\n */\n\nexport class DinoError extends Error {\n /** HTTP status code of the failed response. */\n status: number;\n /** Parsed JSON response body, or null if unavailable/unparseable. */\n body: unknown;\n\n constructor(message: string, status: number, body: unknown = null) {\n super(message);\n this.name = \"DinoError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/** 401 - missing, malformed, or revoked API key. */\nexport class AuthenticationError extends DinoError {\n constructor(message: string, status: number, body: unknown = null) {\n super(message, status, body);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** 402 - subscription inactive, or the endpoint needs a paid plan (e.g. streaming on Free). */\nexport class PlanError extends DinoError {\n constructor(message: string, status: number, body: unknown = null) {\n super(message, status, body);\n this.name = \"PlanError\";\n }\n}\n\n/** 429 - rate limit exceeded. `retryAfter` is in seconds when the server reports one. */\nexport class RateLimitError extends DinoError {\n retryAfter?: number;\n\n constructor(message: string, status: number, body: unknown = null, retryAfter?: number) {\n super(message, status, body);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfter;\n }\n}\n\n/** 5xx - server-side failure. */\nexport class ServerError extends DinoError {\n constructor(message: string, status: number, body: unknown = null) {\n super(message, status, body);\n this.name = \"ServerError\";\n }\n}\n\nfunction extractRetryAfter(headers: Headers | undefined, body: unknown): number | undefined {\n const header = headers?.get?.(\"retry-after\");\n if (header) {\n const n = Number(header);\n if (Number.isFinite(n)) return n;\n }\n if (body && typeof body === \"object\" && \"retry_after\" in (body as Record<string, unknown>)) {\n const n = Number((body as Record<string, unknown>).retry_after);\n if (Number.isFinite(n)) return n;\n }\n return undefined;\n}\n\nfunction errorMessage(status: number, body: unknown, fallback: string): string {\n if (body && typeof body === \"object\" && \"error\" in (body as Record<string, unknown>)) {\n const msg = (body as Record<string, unknown>).error;\n if (typeof msg === \"string\" && msg) return msg;\n }\n return fallback;\n}\n\n/**\n * Map an HTTP response's status/body to a DinoError subclass. status must be\n * a non-2xx code; headers is optional and only used to read Retry-After.\n */\nexport function mapError(status: number, body: unknown, headers?: Headers): DinoError {\n if (status === 401) {\n return new AuthenticationError(errorMessage(status, body, \"authentication failed\"), status, body);\n }\n if (status === 402) {\n return new PlanError(errorMessage(status, body, \"plan does not allow this request\"), status, body);\n }\n if (status === 429) {\n const retryAfter = extractRetryAfter(headers, body);\n return new RateLimitError(errorMessage(status, body, \"rate limit exceeded\"), status, body, retryAfter);\n }\n if (status >= 500) {\n return new ServerError(errorMessage(status, body, \"server error\"), status, body);\n }\n return new DinoError(errorMessage(status, body, `request failed with status ${status}`), status, body);\n}\n","import { mapError } from \"./errors.js\";\nimport type {\n FindArbitrageOptions,\n HistoryResponse,\n LeaguesResponse,\n Market,\n MarketsOptions,\n MarketsResponse,\n ReportBadArbBody,\n ReportBadArbResponse,\n StreamTokenResponse,\n} from \"./types.js\";\n\nconst SDK_VERSION = \"0.1.0\";\nconst DEFAULT_BASE_URL = \"https://api.dino.markets\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RETRIES = 2;\nconst RETRY_BACKOFF_MS = [500, 1000, 2000];\n\nexport type FetchLike = typeof fetch;\n\nexport interface DinoOptions {\n /** API key. Falls back to process.env.DINO_API_KEY when process is available. */\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n maxRetries?: number;\n /** Injectable fetch implementation, mainly for tests. Defaults to the global fetch. */\n fetchImpl?: FetchLike;\n}\n\nfunction resolveApiKey(apiKey?: string): string {\n if (apiKey) return apiKey;\n if (typeof process !== \"undefined\" && process.env && process.env.DINO_API_KEY) {\n return process.env.DINO_API_KEY;\n }\n throw new Error(\n \"dino-markets: no API key provided. Pass { apiKey } or set the DINO_API_KEY environment variable. \" +\n \"Get a free key at https://dino.markets\",\n );\n}\n\nfunction validateLimit(limit: number | undefined): void {\n if (limit === undefined) return;\n if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {\n throw new Error(`dino-markets: limit must be an integer between 1 and 1000, got ${limit}`);\n }\n}\n\nfunction buildQuery(params: Record<string, string | number | undefined>): string {\n const usp = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n usp.set(key, String(value));\n }\n const qs = usp.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class Dino {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly fetchImpl: FetchLike;\n\n constructor(opts: DinoOptions = {}) {\n this.apiKey = resolveApiKey(opts.apiKey);\n this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n }\n\n /** Matched cross-venue catalog. See MarketsOptions for filters. */\n async markets(opts: MarketsOptions = {}): Promise<MarketsResponse> {\n validateLimit(opts.limit);\n const qs = buildQuery({\n sport: opts.sport,\n league: opts.league,\n status: opts.status,\n signal: opts.signal,\n sort: opts.sort,\n include: opts.include,\n market_type: opts.market_type,\n game_id: opts.game_id,\n limit: opts.limit,\n });\n return this.request<MarketsResponse>(\"GET\", `/v2/markets${qs}`);\n }\n\n /** One canonical market object. Accepts a \"dino_<uuid>\" id or a bare uuid. */\n async market(marketId: string): Promise<Market> {\n if (!marketId) throw new Error(\"dino-markets: market(marketId) requires a non-empty marketId\");\n return this.request<Market>(\"GET\", `/v2/pairs/${encodeURIComponent(marketId)}`);\n }\n\n /** Per-outcome Kalshi/Polymarket price history for one market. */\n async history(marketId: string): Promise<HistoryResponse> {\n if (!marketId) throw new Error(\"dino-markets: history(marketId) requires a non-empty marketId\");\n return this.request<HistoryResponse>(\"GET\", `/v2/pairs/${encodeURIComponent(marketId)}/history`);\n }\n\n /** Sports and leagues currently in season, with lifecycle counts. */\n async leagues(): Promise<LeaguesResponse> {\n return this.request<LeaguesResponse>(\"GET\", \"/v2/leagues\");\n }\n\n /** Sugar for markets({ ...opts, signal: \"arb\" }). */\n async findArbitrage(opts: FindArbitrageOptions = {}): Promise<MarketsResponse> {\n return this.markets({ ...opts, signal: \"arb\" });\n }\n\n /** Flag a bad or incorrect arbitrage signal. At least one field of body is required. */\n async reportBadArb(body: ReportBadArbBody): Promise<ReportBadArbResponse> {\n // Mirrors the server's own gate (and the Python SDK): any non-null value counts,\n // empty strings included.\n const hasField = body && Object.values(body).some((v) => v !== undefined && v !== null);\n if (!hasField) {\n throw new Error(\n \"dino-markets: reportBadArb(body) requires at least one of opp_id/reason/sport/market/detail\",\n );\n }\n return this.request<ReportBadArbResponse>(\"POST\", \"/v2/report-bad-arb\", body);\n }\n\n /**\n * Mint a short-lived WebSocket connect ticket (Basic/Pro only; 402 on Free).\n * See stream.ts's `watch()` helper to consume it directly.\n */\n async streamToken(): Promise<StreamTokenResponse> {\n return this.request<StreamTokenResponse>(\"POST\", \"/v1/stream/token\");\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const init: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n \"User-Agent\": `dino-markets-js/${SDK_VERSION}`,\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n };\n\n let attempt = 0;\n for (;;) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n let response: Response;\n try {\n response = await this.fetchImpl(url, { ...init, signal: controller.signal });\n } catch (err) {\n clearTimeout(timer);\n // Transient transport failure (network error or timeout): retry on the same\n // backoff schedule as 429/5xx, matching the Python SDK's URLError handling.\n if (attempt < this.maxRetries) {\n const delayMs = RETRY_BACKOFF_MS[attempt] ?? 2000;\n attempt += 1;\n await sleep(delayMs);\n continue;\n }\n if (err instanceof Error && err.name === \"AbortError\") {\n throw new Error(`dino-markets: request to ${path} timed out after ${this.timeoutMs}ms`);\n }\n throw err;\n }\n clearTimeout(timer);\n\n if (response.ok) {\n if (response.status === 202 || response.status === 204) {\n return (await safeJson(response)) as T;\n }\n return (await response.json()) as T;\n }\n\n const retryable = response.status === 429 || response.status >= 500;\n if (retryable && attempt < this.maxRetries) {\n const body = await safeJson(response);\n const headerRetry = Number(response.headers.get(\"retry-after\"));\n const bodyRetry =\n body && typeof body === \"object\" && \"retry_after\" in (body as Record<string, unknown>)\n ? Number((body as Record<string, unknown>).retry_after)\n : NaN;\n const retryAfterS = Number.isFinite(headerRetry)\n ? headerRetry\n : Number.isFinite(bodyRetry)\n ? bodyRetry\n : undefined;\n const delayMs = retryAfterS !== undefined ? retryAfterS * 1000 : RETRY_BACKOFF_MS[attempt] ?? 2000;\n attempt += 1;\n await sleep(delayMs);\n continue;\n }\n\n const body = await safeJson(response);\n throw mapError(response.status, body, response.headers);\n }\n }\n}\n\nasync function safeJson(response: Response): Promise<unknown> {\n try {\n const text = await response.text();\n if (!text) return null;\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n","import type { Dino } from \"./client.js\";\n\n/**\n * A market/price frame published on the markets:* channels. Frames are flat\n * and self-describing (type discriminator, no wrapper) - see the WebSocket\n * docs at https://dino.markets/docs/websocket. This SDK does not parse the\n * frame further; it is passed through as-is.\n */\nexport type StreamFrame = Record<string, unknown>;\n\nexport interface WatchOptions {\n /** Called for every publication received on the subscribed channels. */\n onFrame: (frame: StreamFrame, channel: string) => void;\n /** Called on a connection error. Optional. */\n onError?: (error: unknown) => void;\n}\n\nexport interface WatchHandle {\n /** Disconnect the stream. */\n close: () => void;\n}\n\n/**\n * Connect to the dino.markets real-time stream and forward publications to\n * onFrame. Requires a Basic or Pro plan; a Free key's streamToken() call\n * throws a PlanError (402).\n *\n * Requires the optional \"centrifuge\" peer dependency: `npm install centrifuge`.\n */\nexport async function watch(client: Dino, opts: WatchOptions): Promise<WatchHandle> {\n // Dynamic + untyped on purpose: \"centrifuge\" is an optional peer dependency\n // (streaming only), so this module must build and type-check without it\n // installed. The import specifier is not statically analyzable by tsup's\n // bundler, which is what keeps it out of the required dependency graph.\n const centrifugeModuleName = \"centrifuge\";\n let CentrifugeCtor: new (url: string, opts: unknown) => CentrifugeClient;\n try {\n const mod: any = await import(/* @vite-ignore */ centrifugeModuleName);\n CentrifugeCtor = mod.Centrifuge ?? mod.default?.Centrifuge ?? mod.default;\n } catch {\n throw new Error(\n 'dino-markets: streaming requires the \"centrifuge\" package. Install it with `npm install centrifuge`.',\n );\n }\n\n const { ticket, ws_url } = await client.streamToken();\n\n const centrifuge = new CentrifugeCtor(ws_url, {\n data: { t: ticket },\n });\n\n centrifuge.on(\"publication\", (ctx: { channel: string; data: StreamFrame }) => {\n opts.onFrame(ctx.data, ctx.channel);\n });\n\n if (opts.onError) {\n centrifuge.on(\"error\", opts.onError);\n }\n\n centrifuge.connect();\n\n return {\n close: () => centrifuge.disconnect(),\n };\n}\n\n/** Minimal shape of the centrifuge Client this SDK relies on, kept local so no type dependency is needed. */\ninterface CentrifugeClient {\n on(event: \"publication\", cb: (ctx: { channel: string; data: StreamFrame }) => void): void;\n on(event: \"error\", cb: (error: unknown) => void): void;\n connect(): void;\n disconnect(): void;\n}\n"],"mappings":"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","names":["body"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "dino-markets",
3
+ "version": "0.1.0",
4
+ "description": "Official TypeScript SDK for dino.markets - cross-venue prediction market data (Kalshi + Polymarket, matched and arbitrage signals).",
5
+ "license": "MIT",
6
+ "author": "Nusantara Ventures LLC <support@dino.markets>",
7
+ "homepage": "https://dino.markets",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/nusantara-ventures/dino-markets-typescript.git"
11
+ },
12
+ "keywords": [
13
+ "prediction-markets",
14
+ "kalshi",
15
+ "polymarket",
16
+ "sports",
17
+ "arbitrage",
18
+ "api-client"
19
+ ],
20
+ "type": "module",
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.cts",
35
+ "default": "./dist/index.cjs"
36
+ }
37
+ }
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "test": "npm run build && node --test"
45
+ },
46
+ "peerDependencies": {
47
+ "centrifuge": "^5"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "centrifuge": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "tsup": "^8.5.1",
56
+ "typescript": "^5.9.3"
57
+ }
58
+ }