pmxt-core 2.43.19 → 2.43.24

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.
Files changed (88) hide show
  1. package/dist/exchanges/baozi/errors.d.ts +1 -1
  2. package/dist/exchanges/baozi/errors.js +1 -1
  3. package/dist/exchanges/gemini-titan/errors.d.ts +3 -3
  4. package/dist/exchanges/gemini-titan/errors.js +1 -1
  5. package/dist/exchanges/gemini-titan/index.js +1 -1
  6. package/dist/exchanges/gemini-titan/normalizer.js +2 -2
  7. package/dist/exchanges/gemini-titan/websocket.js +16 -3
  8. package/dist/exchanges/hyperliquid/errors.d.ts +3 -3
  9. package/dist/exchanges/hyperliquid/errors.js +1 -1
  10. package/dist/exchanges/hyperliquid/index.js +1 -1
  11. package/dist/exchanges/kalshi/api.d.ts +1 -1
  12. package/dist/exchanges/kalshi/api.js +1 -1
  13. package/dist/exchanges/kalshi/auth.js +3 -0
  14. package/dist/exchanges/kalshi/errors.d.ts +2 -2
  15. package/dist/exchanges/kalshi/fetcher.d.ts +1 -0
  16. package/dist/exchanges/kalshi/fetcher.js +3 -3
  17. package/dist/exchanges/kalshi/index.js +1 -1
  18. package/dist/exchanges/kalshi/normalizer.d.ts +1 -1
  19. package/dist/exchanges/kalshi/normalizer.js +4 -4
  20. package/dist/exchanges/kalshi/websocket.d.ts +1 -0
  21. package/dist/exchanges/kalshi/websocket.js +29 -13
  22. package/dist/exchanges/limitless/api.d.ts +1 -1
  23. package/dist/exchanges/limitless/api.js +1 -1
  24. package/dist/exchanges/limitless/auth.js +3 -0
  25. package/dist/exchanges/limitless/client.js +9 -0
  26. package/dist/exchanges/limitless/errors.d.ts +2 -2
  27. package/dist/exchanges/limitless/index.js +1 -1
  28. package/dist/exchanges/limitless/normalizer.js +4 -2
  29. package/dist/exchanges/limitless/utils.js +3 -0
  30. package/dist/exchanges/limitless/websocket.js +37 -7
  31. package/dist/exchanges/metaculus/cancelOrder.d.ts +1 -1
  32. package/dist/exchanges/metaculus/cancelOrder.js +3 -3
  33. package/dist/exchanges/metaculus/errors.d.ts +3 -3
  34. package/dist/exchanges/mock/index.js +32 -15
  35. package/dist/exchanges/myriad/api.d.ts +1 -1
  36. package/dist/exchanges/myriad/api.js +1 -1
  37. package/dist/exchanges/myriad/errors.d.ts +2 -2
  38. package/dist/exchanges/myriad/websocket.js +16 -4
  39. package/dist/exchanges/opinion/api.d.ts +1 -1
  40. package/dist/exchanges/opinion/api.js +1 -1
  41. package/dist/exchanges/opinion/errors.d.ts +2 -2
  42. package/dist/exchanges/opinion/errors.js +4 -3
  43. package/dist/exchanges/opinion/fetcher.js +1 -1
  44. package/dist/exchanges/opinion/index.js +1 -1
  45. package/dist/exchanges/opinion/utils.d.ts +1 -1
  46. package/dist/exchanges/opinion/utils.js +2 -2
  47. package/dist/exchanges/opinion/websocket.js +35 -6
  48. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  49. package/dist/exchanges/polymarket/api-clob.js +1 -1
  50. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  51. package/dist/exchanges/polymarket/api-data.js +1 -1
  52. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  53. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  54. package/dist/exchanges/polymarket/auth.js +22 -3
  55. package/dist/exchanges/polymarket/errors.d.ts +3 -3
  56. package/dist/exchanges/polymarket/fetcher.js +3 -0
  57. package/dist/exchanges/polymarket/index.js +1 -1
  58. package/dist/exchanges/polymarket/normalizer.js +6 -4
  59. package/dist/exchanges/polymarket/websocket.d.ts +2 -0
  60. package/dist/exchanges/polymarket/websocket.js +53 -27
  61. package/dist/exchanges/polymarket_us/normalizer.js +3 -3
  62. package/dist/exchanges/polymarket_us/websocket.js +6 -0
  63. package/dist/exchanges/probable/api.d.ts +1 -1
  64. package/dist/exchanges/probable/api.js +1 -1
  65. package/dist/exchanges/probable/errors.d.ts +2 -2
  66. package/dist/exchanges/probable/errors.js +1 -1
  67. package/dist/exchanges/probable/index.js +2 -2
  68. package/dist/exchanges/smarkets/auth.js +6 -0
  69. package/dist/exchanges/smarkets/errors.d.ts +3 -3
  70. package/dist/exchanges/smarkets/errors.js +7 -2
  71. package/dist/exchanges/smarkets/fetcher.js +16 -6
  72. package/dist/feeds/binance/binance-feed.js +20 -2
  73. package/dist/feeds/chainlink/chainlink-feed.js +18 -3
  74. package/dist/router/Router.js +1 -3
  75. package/dist/router/client.d.ts +16 -8
  76. package/dist/router/client.js +7 -3
  77. package/dist/server/index.js +6 -5
  78. package/dist/server/openapi.yaml +1 -1
  79. package/dist/subscriber/external/goldsky.d.ts +2 -1
  80. package/dist/subscriber/external/goldsky.js +33 -14
  81. package/dist/subscriber/watcher.js +6 -8
  82. package/dist/types.d.ts +1 -1
  83. package/dist/utils/error-mapper.d.ts +7 -7
  84. package/dist/utils/error-mapper.js +54 -47
  85. package/dist/utils/market-utils.js +4 -4
  86. package/dist/utils/throttler.d.ts +2 -0
  87. package/dist/utils/throttler.js +8 -0
  88. package/package.json +3 -3
@@ -5,7 +5,7 @@ import { BaseError } from '../../errors';
5
5
  */
6
6
  export declare class BaoziErrorMapper extends ErrorMapper {
7
7
  constructor();
8
- mapError(error: any): BaseError;
8
+ mapError(error: unknown): BaseError;
9
9
  private extractAnchorError;
10
10
  }
11
11
  export declare const baoziErrorMapper: BaoziErrorMapper;
@@ -22,7 +22,7 @@ class BaoziErrorMapper extends error_mapper_1.ErrorMapper {
22
22
  }
23
23
  mapError(error) {
24
24
  // Handle Solana transaction errors
25
- if (error?.message) {
25
+ if (error instanceof Error) {
26
26
  const msg = error.message;
27
27
  // Solana insufficient funds
28
28
  if (msg.includes('Attempt to debit an account but found no record of a prior credit') ||
@@ -13,8 +13,8 @@ import { BadRequest } from '../../errors';
13
13
  */
14
14
  export declare class GeminiErrorMapper extends ErrorMapper {
15
15
  constructor();
16
- protected extractErrorMessage(error: any): string;
17
- protected mapBadRequestError(message: string, data: any): BadRequest;
18
- mapError(error: any): ReturnType<ErrorMapper['mapError']>;
16
+ protected extractErrorMessage(error: unknown): string;
17
+ protected mapBadRequestError(message: string, data: unknown): BadRequest;
18
+ mapError(error: unknown): ReturnType<ErrorMapper['mapError']>;
19
19
  }
20
20
  export declare const geminiErrorMapper: GeminiErrorMapper;
@@ -38,7 +38,7 @@ class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
38
38
  return super.extractErrorMessage(error);
39
39
  }
40
40
  mapBadRequestError(message, data) {
41
- const reason = typeof data === 'object' && data?.reason
41
+ const reason = typeof data === 'object' && data !== null && 'reason' in data
42
42
  ? String(data.reason)
43
43
  : '';
44
44
  const lowerReason = reason.toLowerCase();
@@ -125,7 +125,7 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
125
125
  side: 'buy',
126
126
  type: 'limit',
127
127
  amount: 0,
128
- status: 'cancelled',
128
+ status: 'canceled',
129
129
  filled: 0,
130
130
  remaining: 0,
131
131
  timestamp: Date.now(),
@@ -23,8 +23,8 @@ function mapOrderStatus(geminiStatus) {
23
23
  case 'open': return 'open';
24
24
  case 'accepted': return 'open';
25
25
  case 'filled': return 'filled';
26
- case 'cancelled': return 'cancelled';
27
- case 'canceled': return 'cancelled';
26
+ case 'cancelled': return 'canceled';
27
+ case 'canceled': return 'canceled';
28
28
  case 'rejected': return 'rejected';
29
29
  default: return 'open';
30
30
  }
@@ -50,7 +50,10 @@ class GeminiWebSocket {
50
50
  const headers = this.auth
51
51
  ? this.auth.buildWsHeaders()
52
52
  : {};
53
- this.ws = new ws_1.default(this.config.wsUrl, { headers });
53
+ this.ws = new ws_1.default(this.config.wsUrl, {
54
+ headers,
55
+ handshakeTimeout: 30_000,
56
+ });
54
57
  this.ws.on('open', () => {
55
58
  this.isConnected = true;
56
59
  this.isConnecting = false;
@@ -236,7 +239,12 @@ class GeminiWebSocket {
236
239
  if (!this.orderBookResolvers.has(symbol)) {
237
240
  this.orderBookResolvers.set(symbol, []);
238
241
  }
239
- this.orderBookResolvers.get(symbol).push({ resolve, reject });
242
+ const resolvers = this.orderBookResolvers.get(symbol);
243
+ if (!resolvers) {
244
+ reject(new Error(`[gemini-titan] resolver queue missing for ${symbol}`));
245
+ return;
246
+ }
247
+ resolvers.push({ resolve, reject });
240
248
  });
241
249
  return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${symbol}')`);
242
250
  }
@@ -257,7 +265,12 @@ class GeminiWebSocket {
257
265
  if (!this.tradeResolvers.has(symbol)) {
258
266
  this.tradeResolvers.set(symbol, []);
259
267
  }
260
- this.tradeResolvers.get(symbol).push({ resolve, reject });
268
+ const resolvers = this.tradeResolvers.get(symbol);
269
+ if (!resolvers) {
270
+ reject(new Error(`[gemini-titan] resolver queue missing for ${symbol}`));
271
+ return;
272
+ }
273
+ resolvers.push({ resolve, reject });
261
274
  });
262
275
  return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${symbol}')`);
263
276
  }
@@ -11,8 +11,8 @@ import { BadRequest } from '../../errors';
11
11
  */
12
12
  export declare class HyperliquidErrorMapper extends ErrorMapper {
13
13
  constructor();
14
- protected extractErrorMessage(error: any): string;
15
- protected mapBadRequestError(message: string, data: any): BadRequest;
16
- mapError(error: any): ReturnType<ErrorMapper['mapError']>;
14
+ protected extractErrorMessage(error: unknown): string;
15
+ protected mapBadRequestError(message: string, data: unknown): BadRequest;
16
+ mapError(error: unknown): ReturnType<ErrorMapper['mapError']>;
17
17
  }
18
18
  export declare const hyperliquidErrorMapper: HyperliquidErrorMapper;
@@ -34,7 +34,7 @@ class HyperliquidErrorMapper extends error_mapper_1.ErrorMapper {
34
34
  }
35
35
  mapBadRequestError(message, data) {
36
36
  const lowerMessage = message.toLowerCase();
37
- const responseStr = typeof data === 'object' && data?.response
37
+ const responseStr = typeof data === 'object' && data !== null && 'response' in data
38
38
  ? String(data.response).toLowerCase()
39
39
  : lowerMessage;
40
40
  if (responseStr.includes('insufficient margin') || responseStr.includes('not enough')) {
@@ -205,7 +205,7 @@ class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
205
205
  side: 'buy',
206
206
  type: 'limit',
207
207
  amount: 0,
208
- status: 'cancelled',
208
+ status: 'canceled',
209
209
  filled: 0,
210
210
  remaining: 0,
211
211
  timestamp: Date.now(),
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
3
- * Generated at: 2026-05-24T16:33:23.368Z
3
+ * Generated at: 2026-05-24T18:05:59.921Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const kalshiApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.kalshiApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
6
- * Generated at: 2026-05-24T16:33:23.368Z
6
+ * Generated at: 2026-05-24T18:05:59.921Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -83,6 +83,9 @@ class KalshiAuth {
83
83
  // Allow input of private key in both raw string or PEM format
84
84
  // If it's a raw key without headers, accessing it might be tricky with implicit types,
85
85
  // but standard PEM is best. We assume the user provides a valid PEM.
86
+ if (!this.credentials.privateKey) {
87
+ throw new Error('[kalshi] privateKey is required for authenticated requests');
88
+ }
86
89
  let privateKey = this.credentials.privateKey;
87
90
  // Fix for common .env issue where newlines are escaped
88
91
  if (privateKey.includes('\\n')) {
@@ -10,10 +10,10 @@ export declare class KalshiErrorMapper extends ErrorMapper {
10
10
  /**
11
11
  * Override to handle Kalshi-specific error patterns
12
12
  */
13
- protected extractErrorMessage(error: any): string;
13
+ protected extractErrorMessage(error: unknown): string;
14
14
  /**
15
15
  * Override to detect Kalshi-specific error patterns
16
16
  */
17
- protected mapBadRequestError(message: string, data: any): BadRequest;
17
+ protected mapBadRequestError(message: string, data: unknown): BadRequest;
18
18
  }
19
19
  export declare const kalshiErrorMapper: KalshiErrorMapper;
@@ -19,6 +19,7 @@ export interface KalshiRawMarket {
19
19
  volume_24h?: number;
20
20
  volume?: number;
21
21
  liquidity?: number;
22
+ liquidity_dollars?: string;
22
23
  open_interest?: number;
23
24
  volume_24h_fp?: string;
24
25
  volume_fp?: string;
@@ -308,7 +308,7 @@ class KalshiFetcher {
308
308
  const events = data.events || [];
309
309
  if (events.length === 0)
310
310
  break;
311
- allEvents = allEvents.concat(events);
311
+ allEvents.push(...events);
312
312
  if (targetMarketCount) {
313
313
  for (const event of events) {
314
314
  totalMarketCount += (event.markets || []).length;
@@ -343,7 +343,7 @@ class KalshiFetcher {
343
343
  const events = data.events || [];
344
344
  if (events.length === 0)
345
345
  break;
346
- allEvents = allEvents.concat(events);
346
+ allEvents.push(...events);
347
347
  cursor = data.cursor;
348
348
  page++;
349
349
  } while (cursor && page < MAX_PAGES);
@@ -370,7 +370,7 @@ class KalshiFetcher {
370
370
  page++;
371
371
  if (events.length === 0)
372
372
  break;
373
- allEvents = allEvents.concat(events);
373
+ allEvents.push(...events);
374
374
  } while (cursor && allEvents.length < maxEvents && page < MAX_PAGES);
375
375
  return {
376
376
  events: allEvents.slice(0, maxEvents),
@@ -260,7 +260,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
260
260
  side: order.side === 'yes' ? 'buy' : 'sell',
261
261
  type: 'limit',
262
262
  amount: order.count,
263
- status: 'cancelled',
263
+ status: 'canceled',
264
264
  filled: order.count - (order.remaining_count || 0),
265
265
  remaining: 0,
266
266
  timestamp: new Date(order.created_time).getTime(),
@@ -25,4 +25,4 @@ export declare class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEv
25
25
  private cleanLabel;
26
26
  private templateRule;
27
27
  }
28
- export declare function sortRawEvents(events: any[], sort: string): any[];
28
+ export declare function sortRawEvents(events: KalshiRawEvent[], sort: string): KalshiRawEvent[];
@@ -125,9 +125,9 @@ class KalshiNormalizer {
125
125
  const pf = p[field];
126
126
  const af = ask[field];
127
127
  const bf = bid[field];
128
- if (pf !== null && pf !== undefined)
128
+ if (pf != null)
129
129
  return pf;
130
- if (af !== null && af !== undefined && bf !== null && bf !== undefined) {
130
+ if (af != null && bf != null) {
131
131
  return (af + bf) / 2;
132
132
  }
133
133
  return p.previous || 0;
@@ -257,7 +257,7 @@ class KalshiNormalizer {
257
257
  switch ((status ?? '').toLowerCase()) {
258
258
  case 'resting': return 'open';
259
259
  case 'canceled':
260
- case 'cancelled': return 'cancelled';
260
+ case 'cancelled': return 'canceled';
261
261
  case 'executed':
262
262
  case 'filled': return 'filled';
263
263
  default: return 'open';
@@ -337,7 +337,7 @@ function eventVolume(event) {
337
337
  return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.volume_fp ?? '') || Number(m.volume || 0)), 0);
338
338
  }
339
339
  function eventLiquidity(event) {
340
- return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.open_interest_fp ?? '') || parseFloat(m.liquidity_dollars || m.open_interest || m.liquidity || '0') || 0), 0);
340
+ return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.open_interest_fp ?? '') || parseFloat(String(m.liquidity_dollars || m.open_interest || m.liquidity || '0')) || 0), 0);
341
341
  }
342
342
  function eventNewest(event) {
343
343
  const times = (event.markets || [])
@@ -28,6 +28,7 @@ export declare class KalshiWebSocket {
28
28
  private reconnectTimer?;
29
29
  private connectionPromise?;
30
30
  private isTerminated;
31
+ private static readonly CONNECTION_TIMEOUT_MS;
31
32
  constructor(auth: KalshiAuth, config?: KalshiWebSocketConfig);
32
33
  private connect;
33
34
  private scheduleReconnect;
@@ -27,10 +27,14 @@ class KalshiWebSocket {
27
27
  reconnectTimer;
28
28
  connectionPromise;
29
29
  isTerminated = false;
30
+ static CONNECTION_TIMEOUT_MS = 30_000;
30
31
  constructor(auth, config = {}) {
31
32
  this.auth = auth;
32
33
  this.config = config;
33
- this.wsUrl = config.wsUrl; // wsUrl must be provided by caller (from KalshiExchange)
34
+ if (!config.wsUrl) {
35
+ throw new Error('KalshiWebSocket: wsUrl is required in config');
36
+ }
37
+ this.wsUrl = config.wsUrl;
34
38
  }
35
39
  async connect() {
36
40
  if (this.isConnected) {
@@ -52,7 +56,20 @@ class KalshiWebSocket {
52
56
  // Get authentication headers
53
57
  const headers = this.auth.getHeaders("GET", path);
54
58
  this.ws = new ws_1.default(this.wsUrl, { headers });
59
+ // Connection timeout: close the socket if not connected within 30s
60
+ const connectionTimeout = setTimeout(() => {
61
+ if (!this.isConnected && this.ws) {
62
+ logger_1.logger.error("Kalshi WebSocket connection timed out", {
63
+ timeoutMs: KalshiWebSocket.CONNECTION_TIMEOUT_MS,
64
+ });
65
+ this.ws.close();
66
+ this.isConnecting = false;
67
+ this.connectionPromise = undefined;
68
+ reject(new Error(`Kalshi WebSocket connection timed out after ${KalshiWebSocket.CONNECTION_TIMEOUT_MS}ms`));
69
+ }
70
+ }, KalshiWebSocket.CONNECTION_TIMEOUT_MS);
55
71
  this.ws.on("open", () => {
72
+ clearTimeout(connectionTimeout);
56
73
  this.isConnected = true;
57
74
  this.isConnecting = false;
58
75
  this.connectionPromise = undefined;
@@ -76,12 +93,14 @@ class KalshiWebSocket {
76
93
  }
77
94
  });
78
95
  this.ws.on("error", (error) => {
96
+ clearTimeout(connectionTimeout);
79
97
  logger_1.logger.error("Kalshi WebSocket error", { error: String(error) });
80
98
  this.isConnecting = false;
81
99
  this.connectionPromise = undefined;
82
100
  reject(error);
83
101
  });
84
102
  this.ws.on("close", () => {
103
+ clearTimeout(connectionTimeout);
85
104
  if (!this.isTerminated) {
86
105
  logger_1.logger.info("Kalshi WebSocket closed");
87
106
  this.scheduleReconnect();
@@ -394,10 +413,9 @@ class KalshiWebSocket {
394
413
  }
395
414
  // Return a promise that resolves on the next orderbook update
396
415
  const dataPromise = new Promise((resolve, reject) => {
397
- if (!this.orderBookResolvers.has(ticker)) {
398
- this.orderBookResolvers.set(ticker, []);
399
- }
400
- this.orderBookResolvers.get(ticker).push({ resolve, reject });
416
+ const resolvers = this.orderBookResolvers.get(ticker) ?? [];
417
+ resolvers.push({ resolve, reject });
418
+ this.orderBookResolvers.set(ticker, resolvers);
401
419
  });
402
420
  return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${ticker}')`);
403
421
  }
@@ -421,15 +439,14 @@ class KalshiWebSocket {
421
439
  }
422
440
  // Wait for all tickers to receive at least one snapshot/update
423
441
  const dataPromise = Promise.all(tickers.map((ticker) => new Promise((resolve, reject) => {
424
- if (!this.orderBookResolvers.has(ticker)) {
425
- this.orderBookResolvers.set(ticker, []);
426
- }
427
- this.orderBookResolvers.get(ticker).push({
442
+ const resolvers = this.orderBookResolvers.get(ticker) ?? [];
443
+ resolvers.push({
428
444
  resolve: (book) => {
429
445
  Promise.resolve(book).then((b) => resolve([ticker, b]));
430
446
  },
431
447
  reject,
432
448
  });
449
+ this.orderBookResolvers.set(ticker, resolvers);
433
450
  })));
434
451
  const entries = await (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBooks(${JSON.stringify(tickers)})`);
435
452
  const result = {};
@@ -454,10 +471,9 @@ class KalshiWebSocket {
454
471
  this.subscribeToTrades([ticker]);
455
472
  }
456
473
  const dataPromise = new Promise((resolve, reject) => {
457
- if (!this.tradeResolvers.has(ticker)) {
458
- this.tradeResolvers.set(ticker, []);
459
- }
460
- this.tradeResolvers.get(ticker).push({ resolve, reject });
474
+ const resolvers = this.tradeResolvers.get(ticker) ?? [];
475
+ resolvers.push({ resolve, reject });
476
+ this.tradeResolvers.set(ticker, resolvers);
461
477
  });
462
478
  return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${ticker}')`);
463
479
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
3
- * Generated at: 2026-05-24T16:33:23.408Z
3
+ * Generated at: 2026-05-24T18:05:59.968Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const limitlessApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.limitlessApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
6
- * Generated at: 2026-05-24T16:33:23.408Z
6
+ * Generated at: 2026-05-24T18:05:59.968Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -44,6 +44,9 @@ class LimitlessAuth {
44
44
  }
45
45
  }
46
46
  getApiKey() {
47
+ if (!this.apiKey) {
48
+ throw new Error('[limitless] apiKey is required for authenticated requests');
49
+ }
47
50
  return this.apiKey;
48
51
  }
49
52
  /**
@@ -242,12 +242,18 @@ class LimitlessClient {
242
242
  * Cancel a specific order by ID.
243
243
  */
244
244
  async cancelOrder(orderId) {
245
+ if (!this.orderClient) {
246
+ throw new Error('[limitless] Order client not initialized -- trading credentials required');
247
+ }
245
248
  return await this.orderClient.cancel(orderId);
246
249
  }
247
250
  /**
248
251
  * Cancel all orders for a specific market.
249
252
  */
250
253
  async cancelAllOrders(marketSlug) {
254
+ if (!this.orderClient) {
255
+ throw new Error('[limitless] Order client not initialized -- trading credentials required');
256
+ }
251
257
  return await this.orderClient.cancelAll(marketSlug);
252
258
  }
253
259
  /**
@@ -304,6 +310,9 @@ class LimitlessClient {
304
310
  name: 'base',
305
311
  });
306
312
  const contract = new ethers_1.Contract(USDC_ADDRESS, ABI, provider);
313
+ if (!this.signer) {
314
+ throw new Error('[limitless] Signer not initialized -- wallet private key required');
315
+ }
307
316
  const balance = await contract.balanceOf(this.signer.address);
308
317
  const decimals = await contract.decimals(); // Should be 6
309
318
  return parseFloat(ethers_1.utils.formatUnits(balance, decimals));
@@ -10,10 +10,10 @@ export declare class LimitlessErrorMapper extends ErrorMapper {
10
10
  /**
11
11
  * Override to handle Limitless-specific error patterns
12
12
  */
13
- protected extractErrorMessage(error: any): string;
13
+ protected extractErrorMessage(error: unknown): string;
14
14
  /**
15
15
  * Override to detect Limitless-specific error patterns
16
16
  */
17
- protected mapBadRequestError(message: string, data: any): BadRequest;
17
+ protected mapBadRequestError(message: string, data: unknown): BadRequest;
18
18
  }
19
19
  export declare const limitlessErrorMapper: LimitlessErrorMapper;
@@ -306,7 +306,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
306
306
  side: 'buy',
307
307
  type: 'limit',
308
308
  amount: 0,
309
- status: 'cancelled',
309
+ status: 'canceled',
310
310
  filled: 0,
311
311
  remaining: 0,
312
312
  timestamp: Date.now(),
@@ -56,10 +56,12 @@ class LimitlessNormalizer {
56
56
  };
57
57
  }).sort((a, b) => a.timestamp - b.timestamp);
58
58
  if (params.start) {
59
- candles = candles.filter((c) => c.timestamp >= params.start.getTime());
59
+ const start = params.start;
60
+ candles = candles.filter((c) => c.timestamp >= start.getTime());
60
61
  }
61
62
  if (params.end) {
62
- candles = candles.filter((c) => c.timestamp <= params.end.getTime());
63
+ const end = params.end;
64
+ candles = candles.filter((c) => c.timestamp <= end.getTime());
63
65
  }
64
66
  if (params.limit) {
65
67
  candles = candles.slice(0, params.limit);
@@ -16,6 +16,9 @@ function mapMarketToUnified(market) {
16
16
  // Use explicit key lookup — Object.entries order is not guaranteed to
17
17
  // match the prices array.
18
18
  if (market.tokens) {
19
+ if (!market.tokens.yes || !market.tokens.no) {
20
+ throw new Error(`[limitless] Market "${market.slug}" is missing token addresses`);
21
+ }
19
22
  const prices = Array.isArray(market.prices) ? market.prices : [];
20
23
  const yesPrice = prices[0] || 0;
21
24
  const noPrice = prices[1] || 0;
@@ -81,7 +81,9 @@ class LimitlessWebSocket {
81
81
  // 1. If we have buffered data, return it immediately
82
82
  const buffer = this.orderbookBuffers.get(marketSlug);
83
83
  if (buffer && buffer.length > 0) {
84
- return buffer.shift();
84
+ const entry = buffer.shift();
85
+ if (entry)
86
+ return entry;
85
87
  }
86
88
  // 2. Special case: If this is the FIRST call for this market and we have no data,
87
89
  // fetch a snapshot to get things moving.
@@ -113,14 +115,30 @@ class LimitlessWebSocket {
113
115
  }
114
116
  // Wait for WebSocket update with timeout
115
117
  try {
118
+ const resolverEntry = {
119
+ resolve: () => { },
120
+ reject: () => { },
121
+ };
116
122
  const wsUpdatePromise = new Promise((resolve, reject) => {
123
+ resolverEntry.resolve = resolve;
124
+ resolverEntry.reject = reject;
117
125
  if (!this.orderbookResolvers.has(marketSlug)) {
118
126
  this.orderbookResolvers.set(marketSlug, []);
119
127
  }
120
- this.orderbookResolvers.get(marketSlug).push({ resolve, reject });
128
+ const resolvers = this.orderbookResolvers.get(marketSlug);
129
+ if (resolvers) {
130
+ resolvers.push(resolverEntry);
131
+ }
121
132
  });
122
133
  const timeoutPromise = new Promise((resolve) => {
123
134
  setTimeout(async () => {
135
+ // Timeout won the race -- remove the stale resolver (#372)
136
+ const resolvers = this.orderbookResolvers.get(marketSlug);
137
+ if (resolvers) {
138
+ const idx = resolvers.indexOf(resolverEntry);
139
+ if (idx !== -1)
140
+ resolvers.splice(idx, 1);
141
+ }
124
142
  // Timeout: fetch REST snapshot as fallback
125
143
  try {
126
144
  this.lastOrderbookTimestamps.set(marketSlug, Date.now());
@@ -222,6 +240,14 @@ class LimitlessWebSocket {
222
240
  async close() {
223
241
  this.orderbookCallbacks.clear();
224
242
  this.priceCallbacks.clear();
243
+ // Reject any pending resolvers before clearing (#372)
244
+ for (const [, resolvers] of this.orderbookResolvers) {
245
+ for (const resolver of resolvers) {
246
+ resolver.reject(new Error('WebSocket closed'));
247
+ }
248
+ }
249
+ this.orderbookResolvers.clear();
250
+ this.orderbookBuffers.clear();
225
251
  await this.client.disconnect();
226
252
  this.watcher.close();
227
253
  }
@@ -254,7 +280,9 @@ class LimitlessWebSocket {
254
280
  if (resolvers.length > 0) {
255
281
  // If someone is waiting, give it to them immediately
256
282
  const resolver = resolvers.shift();
257
- resolver.resolve(pmxtOrderbook);
283
+ if (resolver) {
284
+ resolver.resolve(pmxtOrderbook);
285
+ }
258
286
  }
259
287
  else {
260
288
  // Otherwise, buffer it for the next call
@@ -262,10 +290,12 @@ class LimitlessWebSocket {
262
290
  this.orderbookBuffers.set(marketSlug, []);
263
291
  }
264
292
  const buffer = this.orderbookBuffers.get(marketSlug);
265
- buffer.push(pmxtOrderbook);
266
- // Keep buffer size reasonable
267
- if (buffer.length > 100)
268
- buffer.shift();
293
+ if (buffer) {
294
+ buffer.push(pmxtOrderbook);
295
+ // Keep buffer size reasonable
296
+ if (buffer.length > 100)
297
+ buffer.shift();
298
+ }
269
299
  }
270
300
  });
271
301
  // Handle AMM price updates
@@ -32,7 +32,7 @@ export interface CancelOrderContext {
32
32
  *
33
33
  * @param orderId The Metaculus question ID to withdraw the forecast from.
34
34
  * @param ctx HTTP client and auth context.
35
- * @returns A synthetic Order with status "cancelled".
35
+ * @returns A synthetic Order with status "canceled".
36
36
  *
37
37
  * @throws {AuthenticationError} If no API token is configured.
38
38
  * @throws {ValidationError} If the orderId is not a valid numeric question ID.
@@ -24,7 +24,7 @@ const errors_2 = require("./errors");
24
24
  *
25
25
  * @param orderId The Metaculus question ID to withdraw the forecast from.
26
26
  * @param ctx HTTP client and auth context.
27
- * @returns A synthetic Order with status "cancelled".
27
+ * @returns A synthetic Order with status "canceled".
28
28
  *
29
29
  * @throws {AuthenticationError} If no API token is configured.
30
30
  * @throws {ValidationError} If the orderId is not a valid numeric question ID.
@@ -51,7 +51,7 @@ async function cancelOrder(orderId, ctx) {
51
51
  data: [{ question: questionId }],
52
52
  headers: { "Content-Type": "application/json", ...headers },
53
53
  });
54
- // 4. Return synthetic cancelled order
54
+ // 4. Return synthetic canceled order
55
55
  return {
56
56
  id: `mc-withdraw-${questionId}-${Date.now()}`,
57
57
  marketId: orderId,
@@ -59,7 +59,7 @@ async function cancelOrder(orderId, ctx) {
59
59
  side: "buy",
60
60
  type: "market",
61
61
  amount: 1,
62
- status: "cancelled",
62
+ status: "canceled",
63
63
  filled: 0,
64
64
  remaining: 0,
65
65
  timestamp: Date.now(),
@@ -11,11 +11,11 @@ import { NotFound, BadRequest, BaseError } from '../../errors';
11
11
  */
12
12
  export declare class MetaculusErrorMapper extends ErrorMapper {
13
13
  constructor();
14
- protected mapNotFoundError(message: string, _data: any): NotFound;
15
- protected mapBadRequestError(message: string, data: any): BadRequest;
14
+ protected mapNotFoundError(message: string, _data: unknown): NotFound;
15
+ protected mapBadRequestError(message: string, data: unknown): BadRequest;
16
16
  /**
17
17
  * Override the top-level mapByStatusCode for Metaculus-specific auth messages.
18
18
  */
19
- protected mapByStatusCode(status: number, message: string, data: any, response?: any): BaseError;
19
+ protected mapByStatusCode(status: number, message: string, data: unknown, response?: unknown): BaseError;
20
20
  }
21
21
  export declare const metaculusErrorMapper: MetaculusErrorMapper;