pmxtjs 2.44.6 → 2.45.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.
@@ -6,7 +6,7 @@
6
6
  * OpenAPI client, matching the Python API exactly.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Mock = exports.Hyperliquid = exports.GeminiTitan = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Baozi = exports.Probable = exports.Myriad = exports.KalshiDemo = exports.Limitless = exports.Kalshi = exports.Polymarket = exports.Exchange = void 0;
9
+ exports.Mock = exports.SuiBets = exports.Hyperliquid = exports.GeminiTitan = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Baozi = exports.Probable = exports.Myriad = exports.KalshiDemo = exports.Limitless = exports.Kalshi = exports.Polymarket = exports.Exchange = void 0;
10
10
  const index_js_1 = require("../generated/src/index.js");
11
11
  const models_js_1 = require("./models.js");
12
12
  const server_manager_js_1 = require("./server-manager.js");
@@ -300,7 +300,7 @@ class Exchange {
300
300
  * Return the shared WebSocket client, creating it on first use.
301
301
  *
302
302
  * Returns `null` if the sidecar /ws endpoint was previously found
303
- * to be unavailable, letting callers fall back to HTTP.
303
+ * to be unavailable.
304
304
  */
305
305
  async getOrCreateWs() {
306
306
  if (this._wsUnsupported)
@@ -348,13 +348,65 @@ class Exchange {
348
348
  return await ws.subscribe(this.exchangeName, method, args, this.getCredentials());
349
349
  }
350
350
  catch (error) {
351
- // Only fall back to HTTP for transport-level failures
352
- if (error instanceof errors_js_1.PmxtError && /connection failed|no websocket/i.test(error.message)) {
351
+ if (this.isWsTransportUnavailableError(error)) {
353
352
  return null;
354
353
  }
355
354
  throw error;
356
355
  }
357
356
  }
357
+ wsTransportUnavailableError(method) {
358
+ return new errors_js_1.PmxtError(`${method}() requires WebSocket transport — connection failed`);
359
+ }
360
+ isWsTransportUnavailableError(error) {
361
+ return error instanceof errors_js_1.PmxtError
362
+ && /connection failed|no websocket|websocket.*not connected/i.test(error.message);
363
+ }
364
+ getWsInternals(ws) {
365
+ return ws;
366
+ }
367
+ wsSubscriptionKey(method, args) {
368
+ const firstArg = args[0] ?? "";
369
+ return Array.isArray(firstArg)
370
+ ? `${method}:${[...firstArg].sort().join(",")}`
371
+ : `${method}:${firstArg}`;
372
+ }
373
+ getWsSubscriptionId(ws, method, args) {
374
+ const internals = this.getWsInternals(ws);
375
+ const subKey = this.wsSubscriptionKey(method, args);
376
+ return internals.activeSubs.get(subKey);
377
+ }
378
+ clearWsSubscription(ws, method, args) {
379
+ const internals = this.getWsInternals(ws);
380
+ const subKey = this.wsSubscriptionKey(method, args);
381
+ const requestId = internals.activeSubs.get(subKey);
382
+ if (!requestId)
383
+ return;
384
+ const sub = internals.subscriptions.get(requestId);
385
+ if (sub?.reject) {
386
+ sub.reject(new errors_js_1.PmxtError(`${method} subscription cancelled`));
387
+ }
388
+ internals.activeSubs.delete(subKey);
389
+ internals.subscriptions.delete(requestId);
390
+ internals.dataStore.delete(requestId);
391
+ const firstArg = args[0] ?? "";
392
+ const symbols = Array.isArray(firstArg)
393
+ ? firstArg.map(String)
394
+ : firstArg
395
+ ? [String(firstArg)]
396
+ : [];
397
+ for (const symbol of symbols) {
398
+ internals.dataStore.delete(`${requestId}:${symbol}`);
399
+ }
400
+ }
401
+ async sendWsMessage(ws, message) {
402
+ const internals = this.getWsInternals(ws);
403
+ await internals.ensureConnected();
404
+ const socket = internals.ws;
405
+ if (!socket) {
406
+ throw new errors_js_1.PmxtError('[ws-client] Cannot send: WebSocket not connected');
407
+ }
408
+ socket.send(JSON.stringify(message));
409
+ }
358
410
  // Low-Level API Access
359
411
  /**
360
412
  * Call an exchange-specific REST endpoint by its operationId.
@@ -1005,25 +1057,28 @@ class Exchange {
1005
1057
  }
1006
1058
  async unwatchOrderBook(outcomeId) {
1007
1059
  await this.initPromise;
1060
+ const resolvedOutcomeId = resolveOutcomeId(outcomeId);
1061
+ const args = [resolvedOutcomeId];
1008
1062
  try {
1009
- const args = [];
1010
- args.push(resolveOutcomeId(outcomeId));
1011
- const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/unwatchOrderBook`, {
1012
- method: 'POST',
1013
- headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1014
- body: JSON.stringify({ args, credentials: this.getCredentials() }),
1063
+ const ws = await this.getOrCreateWs();
1064
+ if (!ws) {
1065
+ throw this.wsTransportUnavailableError("unwatchOrderBook");
1066
+ }
1067
+ const requestId = this.getWsSubscriptionId(ws, "watchOrderBook", args)
1068
+ ?? `req-${Math.random().toString(36).slice(2, 14)}`;
1069
+ await this.sendWsMessage(ws, {
1070
+ id: requestId,
1071
+ action: "unsubscribe",
1072
+ exchange: this.exchangeName,
1073
+ method: "unwatchOrderBook",
1074
+ args,
1015
1075
  });
1016
- if (!response.ok) {
1017
- const body = await response.json().catch(() => ({}));
1018
- if (body.error && typeof body.error === "object") {
1019
- throw (0, errors_js_1.fromServerError)(body.error);
1020
- }
1021
- throw new errors_js_1.PmxtError(body.error?.message || response.statusText);
1022
- }
1023
- const json = await response.json();
1024
- this.handleResponse(json);
1076
+ this.clearWsSubscription(ws, "watchOrderBook", args);
1025
1077
  }
1026
1078
  catch (error) {
1079
+ if (this.isWsTransportUnavailableError(error)) {
1080
+ throw this.wsTransportUnavailableError("unwatchOrderBook");
1081
+ }
1027
1082
  if (error instanceof errors_js_1.PmxtError)
1028
1083
  throw error;
1029
1084
  throw new errors_js_1.PmxtError(`Failed to unwatchOrderBook: ${error}`);
@@ -1436,34 +1491,11 @@ class Exchange {
1436
1491
  }
1437
1492
  args.push(params);
1438
1493
  }
1439
- // Try WebSocket transport first
1440
1494
  const wsData = await this.watchViaWs("watchOrderBook", args);
1441
1495
  if (wsData !== null) {
1442
1496
  return convertOrderBook(wsData);
1443
1497
  }
1444
- // HTTP fallback
1445
- try {
1446
- const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchOrderBook`, {
1447
- method: 'POST',
1448
- headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1449
- body: JSON.stringify({ args, credentials: this.getCredentials() }),
1450
- });
1451
- if (!response.ok) {
1452
- const body = await response.json().catch(() => ({}));
1453
- if (body.error && typeof body.error === "object") {
1454
- throw (0, errors_js_1.fromServerError)(body.error);
1455
- }
1456
- throw new errors_js_1.PmxtError(body.error?.message || response.statusText);
1457
- }
1458
- const json = await response.json();
1459
- const data = this.handleResponse(json);
1460
- return convertOrderBook(data);
1461
- }
1462
- catch (error) {
1463
- if (error instanceof errors_js_1.PmxtError)
1464
- throw error;
1465
- throw new errors_js_1.PmxtError(`Failed to watch order book: ${error}`);
1466
- }
1498
+ throw this.wsTransportUnavailableError("watchOrderBook");
1467
1499
  }
1468
1500
  /**
1469
1501
  * Watch real-time order book updates for multiple outcomes at once.
@@ -1472,9 +1504,6 @@ class Exchange {
1472
1504
  * order book snapshot. Call repeatedly in a loop to stream updates
1473
1505
  * (CCXT Pro pattern).
1474
1506
  *
1475
- * Prefers the sidecar WebSocket transport when available, falling
1476
- * back to HTTP POST for older sidecars.
1477
- *
1478
1507
  * @param outcomeIds - Array of outcome IDs (or MarketOutcome objects)
1479
1508
  * @param limit - Optional depth limit for each order book
1480
1509
  * @param params - Optional exchange-specific parameters
@@ -1504,54 +1533,27 @@ class Exchange {
1504
1533
  }
1505
1534
  args.push(params);
1506
1535
  }
1507
- // Try WebSocket transport first
1508
- const ws = await this.getOrCreateWs();
1509
- if (ws) {
1510
- try {
1511
- const rawResult = await ws.subscribeBatch(this.exchangeName, "watchOrderBooks", args, this.getCredentials());
1512
- if (rawResult && typeof rawResult === "object") {
1513
- const result = {};
1514
- for (const [k, v] of Object.entries(rawResult)) {
1515
- if (v && typeof v === "object") {
1516
- result[k] = convertOrderBook(v);
1517
- }
1518
- }
1519
- return result;
1520
- }
1521
- }
1522
- catch (error) {
1523
- // Only fall through to HTTP for transport-level WS failures
1524
- if (!(error instanceof errors_js_1.PmxtError) || !/connection failed|no websocket/i.test(error.message)) {
1525
- throw error;
1526
- }
1527
- }
1528
- }
1529
- // HTTP fallback
1530
1536
  try {
1531
- const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchOrderBooks`, {
1532
- method: 'POST',
1533
- headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1534
- body: JSON.stringify({ args, credentials: this.getCredentials() }),
1535
- });
1536
- if (!response.ok) {
1537
- const body = await response.json().catch(() => ({}));
1538
- if (body.error && typeof body.error === "object") {
1539
- throw (0, errors_js_1.fromServerError)(body.error);
1540
- }
1541
- throw new errors_js_1.PmxtError(body.error?.message || response.statusText);
1537
+ const ws = await this.getOrCreateWs();
1538
+ if (!ws) {
1539
+ throw this.wsTransportUnavailableError("watchOrderBooks");
1542
1540
  }
1543
- const json = await response.json();
1544
- const data = this.handleResponse(json);
1545
- if (data && typeof data === "object") {
1541
+ const rawResult = await ws.subscribeBatch(this.exchangeName, "watchOrderBooks", args, this.getCredentials());
1542
+ if (rawResult && typeof rawResult === "object") {
1546
1543
  const result = {};
1547
- for (const [k, v] of Object.entries(data)) {
1548
- result[k] = convertOrderBook(v);
1544
+ for (const [k, v] of Object.entries(rawResult)) {
1545
+ if (v && typeof v === "object") {
1546
+ result[k] = convertOrderBook(v);
1547
+ }
1549
1548
  }
1550
1549
  return result;
1551
1550
  }
1552
1551
  throw new errors_js_1.PmxtError("watchOrderBooks: unexpected response shape from server");
1553
1552
  }
1554
1553
  catch (error) {
1554
+ if (this.isWsTransportUnavailableError(error)) {
1555
+ throw this.wsTransportUnavailableError("watchOrderBooks");
1556
+ }
1555
1557
  if (error instanceof errors_js_1.PmxtError)
1556
1558
  throw error;
1557
1559
  throw new errors_js_1.PmxtError(`Failed to watch order books: ${error}`);
@@ -1590,7 +1592,7 @@ class Exchange {
1590
1592
  orderbook: convertOrderBook(wsData),
1591
1593
  };
1592
1594
  }
1593
- throw new errors_js_1.PmxtError("watchAllOrderBooks() requires WebSocket transport — connection failed");
1595
+ throw this.wsTransportUnavailableError("watchAllOrderBooks");
1594
1596
  }
1595
1597
  /** @deprecated Use {@link watchAllOrderBooks} instead. */
1596
1598
  async firehose(venues) {
@@ -1622,38 +1624,21 @@ class Exchange {
1622
1624
  async watchTrades(outcomeId, address, since, limit) {
1623
1625
  await this.initPromise;
1624
1626
  const resolvedOutcomeId = resolveOutcomeId(outcomeId);
1625
- try {
1626
- const args = [resolvedOutcomeId];
1627
- if (address !== undefined) {
1628
- args.push(address);
1629
- }
1630
- if (since !== undefined) {
1631
- args.push(since);
1632
- }
1633
- if (limit !== undefined) {
1634
- args.push(limit);
1635
- }
1636
- const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchTrades`, {
1637
- method: 'POST',
1638
- headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1639
- body: JSON.stringify({ args, credentials: this.getCredentials() }),
1640
- });
1641
- if (!response.ok) {
1642
- const body = await response.json().catch(() => ({}));
1643
- if (body.error && typeof body.error === "object") {
1644
- throw (0, errors_js_1.fromServerError)(body.error);
1645
- }
1646
- throw new errors_js_1.PmxtError(body.error?.message || response.statusText);
1647
- }
1648
- const json = await response.json();
1649
- const data = this.handleResponse(json);
1650
- return data.map(convertTrade);
1627
+ const args = [resolvedOutcomeId];
1628
+ if (address !== undefined) {
1629
+ args.push(address);
1651
1630
  }
1652
- catch (error) {
1653
- if (error instanceof errors_js_1.PmxtError)
1654
- throw error;
1655
- throw new errors_js_1.PmxtError(`Failed to watch trades: ${error}`);
1631
+ if (since !== undefined) {
1632
+ args.push(since);
1633
+ }
1634
+ if (limit !== undefined) {
1635
+ args.push(limit);
1636
+ }
1637
+ const wsData = await this.watchViaWs("watchTrades", args);
1638
+ if (wsData !== null) {
1639
+ return wsData.map(convertTrade);
1656
1640
  }
1641
+ throw this.wsTransportUnavailableError("watchTrades");
1657
1642
  }
1658
1643
  /**
1659
1644
  * Watch real-time updates of a public wallet via WebSocket.
@@ -2533,6 +2518,21 @@ class Hyperliquid extends Exchange {
2533
2518
  }
2534
2519
  }
2535
2520
  exports.Hyperliquid = Hyperliquid;
2521
+ /**
2522
+ * SuiBets exchange client.
2523
+ *
2524
+ * @example
2525
+ * ```typescript
2526
+ * const suibets = new SuiBets();
2527
+ * const markets = await suibets.fetchMarkets();
2528
+ * ```
2529
+ */
2530
+ class SuiBets extends Exchange {
2531
+ constructor(options = {}) {
2532
+ super("suibets", options);
2533
+ }
2534
+ }
2535
+ exports.SuiBets = SuiBets;
2536
2536
  /**
2537
2537
  * Mock exchange client.
2538
2538
  *
@@ -195,11 +195,11 @@ class Router extends client_js_1.Exchange {
195
195
  const params = 'title' in marketOrParams ? { market: marketOrParams } : marketOrParams;
196
196
  await this.initPromise;
197
197
  const query = {};
198
- const marketId = params.marketId ?? params.market?.marketId;
198
+ const marketId = params.marketId ?? (!params.market?.slug ? params.market?.marketId : undefined);
199
199
  if (marketId)
200
200
  query.marketId = marketId;
201
- if (params.slug)
202
- query.slug = params.slug;
201
+ if (params.slug ?? params.market?.slug)
202
+ query.slug = params.slug ?? params.market?.slug;
203
203
  if (params.url)
204
204
  query.url = params.url;
205
205
  if (params.relation)
@@ -215,6 +215,9 @@ class Router extends client_js_1.Exchange {
215
215
  const data = this.handleResponse(json);
216
216
  if (!data)
217
217
  return [];
218
+ if (!Array.isArray(data)) {
219
+ throw new Error('fetchMarketMatches returned an unexpected response shape: expected an array');
220
+ }
218
221
  return data.map(parseMatchResult);
219
222
  }
220
223
  catch (error) {
@@ -231,11 +234,11 @@ class Router extends client_js_1.Exchange {
231
234
  const params = 'title' in eventOrParams && 'markets' in eventOrParams ? { event: eventOrParams } : eventOrParams;
232
235
  await this.initPromise;
233
236
  const query = {};
234
- const eventId = params.eventId ?? params.event?.id;
237
+ const eventId = params.eventId ?? (!params.event?.slug ? params.event?.id : undefined);
235
238
  if (eventId)
236
239
  query.eventId = eventId;
237
- if (params.slug)
238
- query.slug = params.slug;
240
+ if (params.slug ?? params.event?.slug)
241
+ query.slug = params.slug ?? params.event?.slug;
239
242
  if (params.relation)
240
243
  query.relation = params.relation;
241
244
  if (params.minConfidence !== undefined)
@@ -249,6 +252,9 @@ class Router extends client_js_1.Exchange {
249
252
  const data = this.handleResponse(json);
250
253
  if (!data)
251
254
  return [];
255
+ if (!Array.isArray(data)) {
256
+ throw new Error('fetchEventMatches returned an unexpected response shape: expected an array');
257
+ }
252
258
  return data.map((entry) => {
253
259
  const event = convertEvent(entry.event || {});
254
260
  return {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "2.44.6",
3
+ "version": "2.45.0",
4
4
  "description": "OpenAPI client for pmxtjs",
5
5
  "author": "OpenAPI-Generator",
6
6
  "repository": {
package/index.ts CHANGED
@@ -25,9 +25,10 @@ import { ServerManager } from "./pmxt/server-manager.js";
25
25
  import * as models from "./pmxt/models.js";
26
26
  import * as errors from "./pmxt/errors.js";
27
27
 
28
- export { Exchange, Polymarket, Kalshi, KalshiDemo, Limitless, Myriad, Probable, Baozi, Opinion, Metaculus, Smarkets, PolymarketUS, GeminiTitan, Hyperliquid, Mock, PolymarketOptions } from "./pmxt/client.js";
28
+ export { Exchange, Polymarket, Kalshi, KalshiDemo, Limitless, Myriad, Probable, Baozi, Opinion, Metaculus, Smarkets, PolymarketUS, GeminiTitan, Hyperliquid, SuiBets, Mock, PolymarketOptions } from "./pmxt/client.js";
29
29
  export { Router } from "./pmxt/router.js";
30
30
  export { ServerManager } from "./pmxt/server-manager.js";
31
+ export { HOSTED_URL, LOCAL_URL, ENV, resolvePmxtBaseUrl } from "./pmxt/constants.js";
31
32
  export { MarketList } from "./pmxt/models.js";
32
33
  export type * from "./pmxt/models.js";
33
34
  export * from "./pmxt/errors.js";
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "2.44.6",
3
+ "version": "2.45.0",
4
4
  "description": "Unified prediction market data API - The ccxt for prediction markets",
5
5
  "author": "PMXT Contributors",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/pmxt-dev/pmxt.git"
8
+ "url": "git+https://github.com/pmxt-dev/pmxt.git"
9
9
  },
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  "prebuild": "npm run clean",
33
33
  "build": "tsc && tsc -p tsconfig.esm.json && node scripts/fix-esm.js && echo '{ \"type\": \"module\" }' > dist/esm/package.json",
34
34
  "prepack": "npm run build",
35
- "test": "jest"
35
+ "test": "jest --passWithNoTests"
36
36
  },
37
37
  "keywords": [
38
38
  "prediction-markets",
@@ -43,7 +43,7 @@
43
43
  "unified"
44
44
  ],
45
45
  "dependencies": {
46
- "pmxt-core": "2.44.6",
46
+ "pmxt-core": "2.45.0",
47
47
  "ws": "^8.18.0"
48
48
  },
49
49
  "devDependencies": {