pmxtjs 2.40.6 → 2.41.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/esm/pmxt/client.d.ts +21 -1
- package/dist/esm/pmxt/client.js +59 -9
- package/dist/esm/pmxt/models.d.ts +11 -0
- package/dist/esm/pmxt/ws-client.d.ts +2 -1
- package/dist/esm/pmxt/ws-client.js +25 -14
- package/dist/pmxt/client.d.ts +21 -1
- package/dist/pmxt/client.js +59 -9
- package/dist/pmxt/models.d.ts +11 -0
- package/dist/pmxt/ws-client.d.ts +2 -1
- package/dist/pmxt/ws-client.js +25 -14
- package/generated/package.json +1 -1
- package/package.json +3 -2
- package/pmxt/client.ts +65 -11
- package/pmxt/models.ts +14 -0
- package/pmxt/ws-client.ts +26 -13
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* OpenAPI client, matching the Python API exactly.
|
|
6
6
|
*/
|
|
7
7
|
import { Configuration, DefaultApi, ExchangeCredentials } from "../generated/src/index.js";
|
|
8
|
-
import { Balance, BuiltOrder, CreateOrderParams, EventFetchParams, EventFilterCriteria, EventFilterFunction, ExecutionPriceResult, MarketFetchParams, MarketFilterCriteria, MarketFilterFunction, MarketOutcome, MyTradesParams, Order, OrderBook, OrderHistoryParams, PaginatedMarketsResult, Position, PriceCandle, SubscribedAddressSnapshot, SubscriptionOption, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from "./models.js";
|
|
8
|
+
import { Balance, BuiltOrder, CreateOrderParams, EventFetchParams, EventFilterCriteria, EventFilterFunction, ExecutionPriceResult, MarketFetchParams, MarketFilterCriteria, MarketFilterFunction, MarketOutcome, MyTradesParams, Order, OrderBook, OrderHistoryParams, PaginatedMarketsResult, Position, PriceCandle, SubscribedAddressSnapshot, SubscriptionOption, Trade, UnifiedEvent, UnifiedMarket, UserTrade, FirehoseEvent } from "./models.js";
|
|
9
9
|
import { ServerManager } from "./server-manager.js";
|
|
10
10
|
/**
|
|
11
11
|
* Base exchange client options.
|
|
@@ -252,6 +252,26 @@ export declare abstract class Exchange {
|
|
|
252
252
|
* ```
|
|
253
253
|
*/
|
|
254
254
|
watchOrderBooks(outcomeIds: (string | MarketOutcome)[], limit?: number): Promise<Record<string, OrderBook>>;
|
|
255
|
+
/**
|
|
256
|
+
* Stream all orderbook updates across venues via the hosted WebSocket API.
|
|
257
|
+
*
|
|
258
|
+
* Returns a promise that resolves with the next book event.
|
|
259
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
260
|
+
* Requires hosted mode (`pmxtApiKey` set).
|
|
261
|
+
*
|
|
262
|
+
* @param venues - Optional venue filter (e.g. ["polymarket", "limitless"])
|
|
263
|
+
* @returns Next firehose event with source, symbol, and orderbook
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const poly = new Polymarket({ pmxtApiKey: "pmxt_xxx" });
|
|
268
|
+
* while (true) {
|
|
269
|
+
* const event = await poly.firehose();
|
|
270
|
+
* console.log(event.source, event.symbol, event.orderbook.bids[0]);
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
firehose(venues?: string[]): Promise<FirehoseEvent>;
|
|
255
275
|
/**
|
|
256
276
|
* Watch real-time trade updates via WebSocket.
|
|
257
277
|
*
|
package/dist/esm/pmxt/client.js
CHANGED
|
@@ -188,9 +188,14 @@ export class Exchange {
|
|
|
188
188
|
this.api = new DefaultApi(this.config);
|
|
189
189
|
}
|
|
190
190
|
catch (error) {
|
|
191
|
-
|
|
191
|
+
const msg = `Failed to start PMXT server: ${error instanceof Error ? error.message : error}\n\n` +
|
|
192
192
|
`Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
|
|
193
|
-
`Or start the server manually: pmxt-server
|
|
193
|
+
`Or start the server manually: pmxt-server`;
|
|
194
|
+
const pmxtError = new PmxtError(msg);
|
|
195
|
+
if (error instanceof Error) {
|
|
196
|
+
pmxtError.cause = error;
|
|
197
|
+
}
|
|
198
|
+
throw pmxtError;
|
|
194
199
|
}
|
|
195
200
|
}
|
|
196
201
|
}
|
|
@@ -289,8 +294,11 @@ export class Exchange {
|
|
|
289
294
|
if (this._wsClient?.connected)
|
|
290
295
|
return this._wsClient;
|
|
291
296
|
const host = this.resolveBaseUrl();
|
|
292
|
-
const accessToken = this.
|
|
293
|
-
|
|
297
|
+
const accessToken = this.isHosted
|
|
298
|
+
? this.pmxtApiKey
|
|
299
|
+
: this.serverManager.getAccessToken();
|
|
300
|
+
const authParamName = this.isHosted ? "apiKey" : "token";
|
|
301
|
+
const client = new SidecarWsClient(host, accessToken || undefined, authParamName);
|
|
294
302
|
try {
|
|
295
303
|
// Trigger connection to validate the endpoint exists.
|
|
296
304
|
// subscribe() calls ensureConnected internally, but we want
|
|
@@ -325,8 +333,12 @@ export class Exchange {
|
|
|
325
333
|
try {
|
|
326
334
|
return await ws.subscribe(this.exchangeName, method, args, this.getCredentials());
|
|
327
335
|
}
|
|
328
|
-
catch {
|
|
329
|
-
|
|
336
|
+
catch (error) {
|
|
337
|
+
// Only fall back to HTTP for transport-level failures
|
|
338
|
+
if (error instanceof PmxtError && /connection failed|no websocket/i.test(error.message)) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
throw error;
|
|
330
342
|
}
|
|
331
343
|
}
|
|
332
344
|
// Low-Level API Access
|
|
@@ -1423,8 +1435,11 @@ export class Exchange {
|
|
|
1423
1435
|
return result;
|
|
1424
1436
|
}
|
|
1425
1437
|
}
|
|
1426
|
-
catch {
|
|
1427
|
-
// fall through to HTTP
|
|
1438
|
+
catch (error) {
|
|
1439
|
+
// Only fall through to HTTP for transport-level WS failures
|
|
1440
|
+
if (!(error instanceof PmxtError) || !/connection failed|no websocket/i.test(error.message)) {
|
|
1441
|
+
throw error;
|
|
1442
|
+
}
|
|
1428
1443
|
}
|
|
1429
1444
|
}
|
|
1430
1445
|
// HTTP fallback
|
|
@@ -1450,7 +1465,7 @@ export class Exchange {
|
|
|
1450
1465
|
}
|
|
1451
1466
|
return result;
|
|
1452
1467
|
}
|
|
1453
|
-
|
|
1468
|
+
throw new PmxtError("watchOrderBooks: unexpected response shape from server");
|
|
1454
1469
|
}
|
|
1455
1470
|
catch (error) {
|
|
1456
1471
|
if (error instanceof PmxtError)
|
|
@@ -1458,6 +1473,41 @@ export class Exchange {
|
|
|
1458
1473
|
throw new PmxtError(`Failed to watch order books: ${error}`);
|
|
1459
1474
|
}
|
|
1460
1475
|
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Stream all orderbook updates across venues via the hosted WebSocket API.
|
|
1478
|
+
*
|
|
1479
|
+
* Returns a promise that resolves with the next book event.
|
|
1480
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
1481
|
+
* Requires hosted mode (`pmxtApiKey` set).
|
|
1482
|
+
*
|
|
1483
|
+
* @param venues - Optional venue filter (e.g. ["polymarket", "limitless"])
|
|
1484
|
+
* @returns Next firehose event with source, symbol, and orderbook
|
|
1485
|
+
*
|
|
1486
|
+
* @example
|
|
1487
|
+
* ```typescript
|
|
1488
|
+
* const poly = new Polymarket({ pmxtApiKey: "pmxt_xxx" });
|
|
1489
|
+
* while (true) {
|
|
1490
|
+
* const event = await poly.firehose();
|
|
1491
|
+
* console.log(event.source, event.symbol, event.orderbook.bids[0]);
|
|
1492
|
+
* }
|
|
1493
|
+
* ```
|
|
1494
|
+
*/
|
|
1495
|
+
async firehose(venues) {
|
|
1496
|
+
await this.initPromise;
|
|
1497
|
+
if (!this.isHosted) {
|
|
1498
|
+
throw new PmxtError("firehose() requires hosted mode (set pmxtApiKey)");
|
|
1499
|
+
}
|
|
1500
|
+
const args = venues ? [venues] : [];
|
|
1501
|
+
const wsData = await this.watchViaWs("firehose", args);
|
|
1502
|
+
if (wsData !== null) {
|
|
1503
|
+
return {
|
|
1504
|
+
source: wsData._source || "",
|
|
1505
|
+
symbol: wsData._symbol || "",
|
|
1506
|
+
orderbook: convertOrderBook(wsData),
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
throw new PmxtError("firehose() requires WebSocket transport — connection failed");
|
|
1510
|
+
}
|
|
1461
1511
|
/**
|
|
1462
1512
|
* Watch real-time trade updates via WebSocket.
|
|
1463
1513
|
*
|
|
@@ -112,6 +112,17 @@ export interface OrderBook {
|
|
|
112
112
|
/** Unix timestamp (milliseconds) */
|
|
113
113
|
timestamp?: number;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* A single event from the firehose stream.
|
|
117
|
+
*/
|
|
118
|
+
export interface FirehoseEvent {
|
|
119
|
+
/** The venue this event originated from (e.g. "polymarket", "limitless") */
|
|
120
|
+
source: string;
|
|
121
|
+
/** The outcome token id / asset id */
|
|
122
|
+
symbol: string;
|
|
123
|
+
/** The order book snapshot */
|
|
124
|
+
orderbook: OrderBook;
|
|
125
|
+
}
|
|
115
126
|
/**
|
|
116
127
|
* Result of an execution price calculation.
|
|
117
128
|
*/
|
|
@@ -16,6 +16,7 @@ export declare class SidecarWsClient {
|
|
|
16
16
|
private ws;
|
|
17
17
|
private host;
|
|
18
18
|
private accessToken;
|
|
19
|
+
private authParamName;
|
|
19
20
|
private closed;
|
|
20
21
|
/** requestId -> latest data payload */
|
|
21
22
|
private dataStore;
|
|
@@ -24,7 +25,7 @@ export declare class SidecarWsClient {
|
|
|
24
25
|
/** (method:symbolKey) -> requestId -- avoids duplicate subscribes */
|
|
25
26
|
private activeSubs;
|
|
26
27
|
private connectPromise;
|
|
27
|
-
constructor(host: string, accessToken?: string);
|
|
28
|
+
constructor(host: string, accessToken?: string, authParamName?: string);
|
|
28
29
|
private ensureConnected;
|
|
29
30
|
private connect;
|
|
30
31
|
private getWebSocketConstructor;
|
|
@@ -17,6 +17,7 @@ export class SidecarWsClient {
|
|
|
17
17
|
ws = null;
|
|
18
18
|
host;
|
|
19
19
|
accessToken;
|
|
20
|
+
authParamName;
|
|
20
21
|
closed = false;
|
|
21
22
|
/** requestId -> latest data payload */
|
|
22
23
|
dataStore = new Map();
|
|
@@ -25,9 +26,10 @@ export class SidecarWsClient {
|
|
|
25
26
|
/** (method:symbolKey) -> requestId -- avoids duplicate subscribes */
|
|
26
27
|
activeSubs = new Map();
|
|
27
28
|
connectPromise = null;
|
|
28
|
-
constructor(host, accessToken) {
|
|
29
|
+
constructor(host, accessToken, authParamName = "token") {
|
|
29
30
|
this.host = host;
|
|
30
31
|
this.accessToken = accessToken;
|
|
32
|
+
this.authParamName = authParamName;
|
|
31
33
|
}
|
|
32
34
|
// ------------------------------------------------------------------
|
|
33
35
|
// Connection lifecycle
|
|
@@ -58,7 +60,7 @@ export class SidecarWsClient {
|
|
|
58
60
|
}
|
|
59
61
|
let url = `${scheme}://${hostPart}/ws`;
|
|
60
62
|
if (this.accessToken) {
|
|
61
|
-
url = `${url}
|
|
63
|
+
url = `${url}?${this.authParamName}=${this.accessToken}`;
|
|
62
64
|
}
|
|
63
65
|
// Use the ws package in Node.js, native WebSocket in browsers
|
|
64
66
|
const WsConstructor = this.getWebSocketConstructor();
|
|
@@ -77,22 +79,30 @@ export class SidecarWsClient {
|
|
|
77
79
|
// Connection failed during handshake
|
|
78
80
|
reject(new PmxtError(`WebSocket connection failed: ${err.message || err}`));
|
|
79
81
|
}
|
|
82
|
+
else {
|
|
83
|
+
// Post-handshake error — propagate to all pending subscribers
|
|
84
|
+
const error = new PmxtError(`WebSocket error: ${err.message || err}`);
|
|
85
|
+
for (const sub of this.subscriptions.values()) {
|
|
86
|
+
if (sub.reject) {
|
|
87
|
+
sub.reject(error);
|
|
88
|
+
sub.reject = null;
|
|
89
|
+
sub.resolve = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
this.closed = true;
|
|
93
|
+
this.ws = null;
|
|
94
|
+
}
|
|
80
95
|
};
|
|
81
96
|
ws.onclose = () => {
|
|
82
97
|
this.closed = true;
|
|
83
98
|
this.ws = null;
|
|
84
99
|
};
|
|
85
100
|
ws.onmessage = (event) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.dispatch(msg);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// Ignore unparseable frames
|
|
95
|
-
}
|
|
101
|
+
const raw = typeof event.data === "string"
|
|
102
|
+
? event.data
|
|
103
|
+
: event.data.toString();
|
|
104
|
+
const msg = JSON.parse(raw);
|
|
105
|
+
this.dispatch(msg);
|
|
96
106
|
};
|
|
97
107
|
});
|
|
98
108
|
}
|
|
@@ -101,14 +111,15 @@ export class SidecarWsClient {
|
|
|
101
111
|
if (typeof globalThis !== "undefined" && globalThis.WebSocket) {
|
|
102
112
|
return globalThis.WebSocket;
|
|
103
113
|
}
|
|
104
|
-
// Node.js --
|
|
114
|
+
// Node.js -- require ws
|
|
105
115
|
try {
|
|
106
116
|
// Dynamic require to avoid bundler issues
|
|
107
117
|
const wsModule = require("ws");
|
|
108
118
|
return wsModule.default || wsModule;
|
|
109
119
|
}
|
|
110
120
|
catch {
|
|
111
|
-
|
|
121
|
+
throw new PmxtError("WebSocket support in Node.js requires the 'ws' package. " +
|
|
122
|
+
"Install it with: npm install ws");
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
dispatch(msg) {
|
package/dist/pmxt/client.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* OpenAPI client, matching the Python API exactly.
|
|
6
6
|
*/
|
|
7
7
|
import { Configuration, DefaultApi, ExchangeCredentials } from "../generated/src/index.js";
|
|
8
|
-
import { Balance, BuiltOrder, CreateOrderParams, EventFetchParams, EventFilterCriteria, EventFilterFunction, ExecutionPriceResult, MarketFetchParams, MarketFilterCriteria, MarketFilterFunction, MarketOutcome, MyTradesParams, Order, OrderBook, OrderHistoryParams, PaginatedMarketsResult, Position, PriceCandle, SubscribedAddressSnapshot, SubscriptionOption, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from "./models.js";
|
|
8
|
+
import { Balance, BuiltOrder, CreateOrderParams, EventFetchParams, EventFilterCriteria, EventFilterFunction, ExecutionPriceResult, MarketFetchParams, MarketFilterCriteria, MarketFilterFunction, MarketOutcome, MyTradesParams, Order, OrderBook, OrderHistoryParams, PaginatedMarketsResult, Position, PriceCandle, SubscribedAddressSnapshot, SubscriptionOption, Trade, UnifiedEvent, UnifiedMarket, UserTrade, FirehoseEvent } from "./models.js";
|
|
9
9
|
import { ServerManager } from "./server-manager.js";
|
|
10
10
|
/**
|
|
11
11
|
* Base exchange client options.
|
|
@@ -252,6 +252,26 @@ export declare abstract class Exchange {
|
|
|
252
252
|
* ```
|
|
253
253
|
*/
|
|
254
254
|
watchOrderBooks(outcomeIds: (string | MarketOutcome)[], limit?: number): Promise<Record<string, OrderBook>>;
|
|
255
|
+
/**
|
|
256
|
+
* Stream all orderbook updates across venues via the hosted WebSocket API.
|
|
257
|
+
*
|
|
258
|
+
* Returns a promise that resolves with the next book event.
|
|
259
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
260
|
+
* Requires hosted mode (`pmxtApiKey` set).
|
|
261
|
+
*
|
|
262
|
+
* @param venues - Optional venue filter (e.g. ["polymarket", "limitless"])
|
|
263
|
+
* @returns Next firehose event with source, symbol, and orderbook
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const poly = new Polymarket({ pmxtApiKey: "pmxt_xxx" });
|
|
268
|
+
* while (true) {
|
|
269
|
+
* const event = await poly.firehose();
|
|
270
|
+
* console.log(event.source, event.symbol, event.orderbook.bids[0]);
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
firehose(venues?: string[]): Promise<FirehoseEvent>;
|
|
255
275
|
/**
|
|
256
276
|
* Watch real-time trade updates via WebSocket.
|
|
257
277
|
*
|
package/dist/pmxt/client.js
CHANGED
|
@@ -191,9 +191,14 @@ class Exchange {
|
|
|
191
191
|
this.api = new index_js_1.DefaultApi(this.config);
|
|
192
192
|
}
|
|
193
193
|
catch (error) {
|
|
194
|
-
|
|
194
|
+
const msg = `Failed to start PMXT server: ${error instanceof Error ? error.message : error}\n\n` +
|
|
195
195
|
`Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
|
|
196
|
-
`Or start the server manually: pmxt-server
|
|
196
|
+
`Or start the server manually: pmxt-server`;
|
|
197
|
+
const pmxtError = new errors_js_1.PmxtError(msg);
|
|
198
|
+
if (error instanceof Error) {
|
|
199
|
+
pmxtError.cause = error;
|
|
200
|
+
}
|
|
201
|
+
throw pmxtError;
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
}
|
|
@@ -292,8 +297,11 @@ class Exchange {
|
|
|
292
297
|
if (this._wsClient?.connected)
|
|
293
298
|
return this._wsClient;
|
|
294
299
|
const host = this.resolveBaseUrl();
|
|
295
|
-
const accessToken = this.
|
|
296
|
-
|
|
300
|
+
const accessToken = this.isHosted
|
|
301
|
+
? this.pmxtApiKey
|
|
302
|
+
: this.serverManager.getAccessToken();
|
|
303
|
+
const authParamName = this.isHosted ? "apiKey" : "token";
|
|
304
|
+
const client = new ws_client_js_1.SidecarWsClient(host, accessToken || undefined, authParamName);
|
|
297
305
|
try {
|
|
298
306
|
// Trigger connection to validate the endpoint exists.
|
|
299
307
|
// subscribe() calls ensureConnected internally, but we want
|
|
@@ -328,8 +336,12 @@ class Exchange {
|
|
|
328
336
|
try {
|
|
329
337
|
return await ws.subscribe(this.exchangeName, method, args, this.getCredentials());
|
|
330
338
|
}
|
|
331
|
-
catch {
|
|
332
|
-
|
|
339
|
+
catch (error) {
|
|
340
|
+
// Only fall back to HTTP for transport-level failures
|
|
341
|
+
if (error instanceof errors_js_1.PmxtError && /connection failed|no websocket/i.test(error.message)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
throw error;
|
|
333
345
|
}
|
|
334
346
|
}
|
|
335
347
|
// Low-Level API Access
|
|
@@ -1426,8 +1438,11 @@ class Exchange {
|
|
|
1426
1438
|
return result;
|
|
1427
1439
|
}
|
|
1428
1440
|
}
|
|
1429
|
-
catch {
|
|
1430
|
-
// fall through to HTTP
|
|
1441
|
+
catch (error) {
|
|
1442
|
+
// Only fall through to HTTP for transport-level WS failures
|
|
1443
|
+
if (!(error instanceof errors_js_1.PmxtError) || !/connection failed|no websocket/i.test(error.message)) {
|
|
1444
|
+
throw error;
|
|
1445
|
+
}
|
|
1431
1446
|
}
|
|
1432
1447
|
}
|
|
1433
1448
|
// HTTP fallback
|
|
@@ -1453,7 +1468,7 @@ class Exchange {
|
|
|
1453
1468
|
}
|
|
1454
1469
|
return result;
|
|
1455
1470
|
}
|
|
1456
|
-
|
|
1471
|
+
throw new errors_js_1.PmxtError("watchOrderBooks: unexpected response shape from server");
|
|
1457
1472
|
}
|
|
1458
1473
|
catch (error) {
|
|
1459
1474
|
if (error instanceof errors_js_1.PmxtError)
|
|
@@ -1461,6 +1476,41 @@ class Exchange {
|
|
|
1461
1476
|
throw new errors_js_1.PmxtError(`Failed to watch order books: ${error}`);
|
|
1462
1477
|
}
|
|
1463
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Stream all orderbook updates across venues via the hosted WebSocket API.
|
|
1481
|
+
*
|
|
1482
|
+
* Returns a promise that resolves with the next book event.
|
|
1483
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
1484
|
+
* Requires hosted mode (`pmxtApiKey` set).
|
|
1485
|
+
*
|
|
1486
|
+
* @param venues - Optional venue filter (e.g. ["polymarket", "limitless"])
|
|
1487
|
+
* @returns Next firehose event with source, symbol, and orderbook
|
|
1488
|
+
*
|
|
1489
|
+
* @example
|
|
1490
|
+
* ```typescript
|
|
1491
|
+
* const poly = new Polymarket({ pmxtApiKey: "pmxt_xxx" });
|
|
1492
|
+
* while (true) {
|
|
1493
|
+
* const event = await poly.firehose();
|
|
1494
|
+
* console.log(event.source, event.symbol, event.orderbook.bids[0]);
|
|
1495
|
+
* }
|
|
1496
|
+
* ```
|
|
1497
|
+
*/
|
|
1498
|
+
async firehose(venues) {
|
|
1499
|
+
await this.initPromise;
|
|
1500
|
+
if (!this.isHosted) {
|
|
1501
|
+
throw new errors_js_1.PmxtError("firehose() requires hosted mode (set pmxtApiKey)");
|
|
1502
|
+
}
|
|
1503
|
+
const args = venues ? [venues] : [];
|
|
1504
|
+
const wsData = await this.watchViaWs("firehose", args);
|
|
1505
|
+
if (wsData !== null) {
|
|
1506
|
+
return {
|
|
1507
|
+
source: wsData._source || "",
|
|
1508
|
+
symbol: wsData._symbol || "",
|
|
1509
|
+
orderbook: convertOrderBook(wsData),
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
throw new errors_js_1.PmxtError("firehose() requires WebSocket transport — connection failed");
|
|
1513
|
+
}
|
|
1464
1514
|
/**
|
|
1465
1515
|
* Watch real-time trade updates via WebSocket.
|
|
1466
1516
|
*
|
package/dist/pmxt/models.d.ts
CHANGED
|
@@ -112,6 +112,17 @@ export interface OrderBook {
|
|
|
112
112
|
/** Unix timestamp (milliseconds) */
|
|
113
113
|
timestamp?: number;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* A single event from the firehose stream.
|
|
117
|
+
*/
|
|
118
|
+
export interface FirehoseEvent {
|
|
119
|
+
/** The venue this event originated from (e.g. "polymarket", "limitless") */
|
|
120
|
+
source: string;
|
|
121
|
+
/** The outcome token id / asset id */
|
|
122
|
+
symbol: string;
|
|
123
|
+
/** The order book snapshot */
|
|
124
|
+
orderbook: OrderBook;
|
|
125
|
+
}
|
|
115
126
|
/**
|
|
116
127
|
* Result of an execution price calculation.
|
|
117
128
|
*/
|
package/dist/pmxt/ws-client.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class SidecarWsClient {
|
|
|
16
16
|
private ws;
|
|
17
17
|
private host;
|
|
18
18
|
private accessToken;
|
|
19
|
+
private authParamName;
|
|
19
20
|
private closed;
|
|
20
21
|
/** requestId -> latest data payload */
|
|
21
22
|
private dataStore;
|
|
@@ -24,7 +25,7 @@ export declare class SidecarWsClient {
|
|
|
24
25
|
/** (method:symbolKey) -> requestId -- avoids duplicate subscribes */
|
|
25
26
|
private activeSubs;
|
|
26
27
|
private connectPromise;
|
|
27
|
-
constructor(host: string, accessToken?: string);
|
|
28
|
+
constructor(host: string, accessToken?: string, authParamName?: string);
|
|
28
29
|
private ensureConnected;
|
|
29
30
|
private connect;
|
|
30
31
|
private getWebSocketConstructor;
|
package/dist/pmxt/ws-client.js
CHANGED
|
@@ -20,6 +20,7 @@ class SidecarWsClient {
|
|
|
20
20
|
ws = null;
|
|
21
21
|
host;
|
|
22
22
|
accessToken;
|
|
23
|
+
authParamName;
|
|
23
24
|
closed = false;
|
|
24
25
|
/** requestId -> latest data payload */
|
|
25
26
|
dataStore = new Map();
|
|
@@ -28,9 +29,10 @@ class SidecarWsClient {
|
|
|
28
29
|
/** (method:symbolKey) -> requestId -- avoids duplicate subscribes */
|
|
29
30
|
activeSubs = new Map();
|
|
30
31
|
connectPromise = null;
|
|
31
|
-
constructor(host, accessToken) {
|
|
32
|
+
constructor(host, accessToken, authParamName = "token") {
|
|
32
33
|
this.host = host;
|
|
33
34
|
this.accessToken = accessToken;
|
|
35
|
+
this.authParamName = authParamName;
|
|
34
36
|
}
|
|
35
37
|
// ------------------------------------------------------------------
|
|
36
38
|
// Connection lifecycle
|
|
@@ -61,7 +63,7 @@ class SidecarWsClient {
|
|
|
61
63
|
}
|
|
62
64
|
let url = `${scheme}://${hostPart}/ws`;
|
|
63
65
|
if (this.accessToken) {
|
|
64
|
-
url = `${url}
|
|
66
|
+
url = `${url}?${this.authParamName}=${this.accessToken}`;
|
|
65
67
|
}
|
|
66
68
|
// Use the ws package in Node.js, native WebSocket in browsers
|
|
67
69
|
const WsConstructor = this.getWebSocketConstructor();
|
|
@@ -80,22 +82,30 @@ class SidecarWsClient {
|
|
|
80
82
|
// Connection failed during handshake
|
|
81
83
|
reject(new errors_js_1.PmxtError(`WebSocket connection failed: ${err.message || err}`));
|
|
82
84
|
}
|
|
85
|
+
else {
|
|
86
|
+
// Post-handshake error — propagate to all pending subscribers
|
|
87
|
+
const error = new errors_js_1.PmxtError(`WebSocket error: ${err.message || err}`);
|
|
88
|
+
for (const sub of this.subscriptions.values()) {
|
|
89
|
+
if (sub.reject) {
|
|
90
|
+
sub.reject(error);
|
|
91
|
+
sub.reject = null;
|
|
92
|
+
sub.resolve = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.closed = true;
|
|
96
|
+
this.ws = null;
|
|
97
|
+
}
|
|
83
98
|
};
|
|
84
99
|
ws.onclose = () => {
|
|
85
100
|
this.closed = true;
|
|
86
101
|
this.ws = null;
|
|
87
102
|
};
|
|
88
103
|
ws.onmessage = (event) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.dispatch(msg);
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
// Ignore unparseable frames
|
|
98
|
-
}
|
|
104
|
+
const raw = typeof event.data === "string"
|
|
105
|
+
? event.data
|
|
106
|
+
: event.data.toString();
|
|
107
|
+
const msg = JSON.parse(raw);
|
|
108
|
+
this.dispatch(msg);
|
|
99
109
|
};
|
|
100
110
|
});
|
|
101
111
|
}
|
|
@@ -104,14 +114,15 @@ class SidecarWsClient {
|
|
|
104
114
|
if (typeof globalThis !== "undefined" && globalThis.WebSocket) {
|
|
105
115
|
return globalThis.WebSocket;
|
|
106
116
|
}
|
|
107
|
-
// Node.js --
|
|
117
|
+
// Node.js -- require ws
|
|
108
118
|
try {
|
|
109
119
|
// Dynamic require to avoid bundler issues
|
|
110
120
|
const wsModule = require("ws");
|
|
111
121
|
return wsModule.default || wsModule;
|
|
112
122
|
}
|
|
113
123
|
catch {
|
|
114
|
-
|
|
124
|
+
throw new errors_js_1.PmxtError("WebSocket support in Node.js requires the 'ws' package. " +
|
|
125
|
+
"Install it with: npm install ws");
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
dispatch(msg) {
|
package/generated/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxtjs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.41.0",
|
|
4
4
|
"description": "Unified prediction market data API - The ccxt for prediction markets",
|
|
5
5
|
"author": "PMXT Contributors",
|
|
6
6
|
"repository": {
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"unified"
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"pmxt-core": "2.
|
|
46
|
+
"pmxt-core": "2.41.0",
|
|
47
|
+
"ws": "^8.18.0"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@types/jest": "^30.0.0",
|
package/pmxt/client.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
UnifiedEvent,
|
|
43
43
|
UnifiedMarket,
|
|
44
44
|
UserTrade,
|
|
45
|
+
FirehoseEvent,
|
|
45
46
|
} from "./models.js";
|
|
46
47
|
|
|
47
48
|
import { ServerManager } from "./server-manager.js";
|
|
@@ -296,11 +297,15 @@ export abstract class Exchange {
|
|
|
296
297
|
});
|
|
297
298
|
this.api = new DefaultApi(this.config);
|
|
298
299
|
} catch (error) {
|
|
299
|
-
|
|
300
|
-
`Failed to start PMXT server: ${error}\n\n` +
|
|
300
|
+
const msg =
|
|
301
|
+
`Failed to start PMXT server: ${error instanceof Error ? error.message : error}\n\n` +
|
|
301
302
|
`Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
|
|
302
|
-
`Or start the server manually: pmxt-server
|
|
303
|
-
);
|
|
303
|
+
`Or start the server manually: pmxt-server`;
|
|
304
|
+
const pmxtError = new PmxtError(msg);
|
|
305
|
+
if (error instanceof Error) {
|
|
306
|
+
(pmxtError as any).cause = error;
|
|
307
|
+
}
|
|
308
|
+
throw pmxtError;
|
|
304
309
|
}
|
|
305
310
|
}
|
|
306
311
|
}
|
|
@@ -408,9 +413,12 @@ export abstract class Exchange {
|
|
|
408
413
|
if (this._wsClient?.connected) return this._wsClient;
|
|
409
414
|
|
|
410
415
|
const host = this.resolveBaseUrl();
|
|
411
|
-
const accessToken = this.
|
|
416
|
+
const accessToken = this.isHosted
|
|
417
|
+
? this.pmxtApiKey
|
|
418
|
+
: this.serverManager.getAccessToken();
|
|
419
|
+
const authParamName = this.isHosted ? "apiKey" : "token";
|
|
412
420
|
|
|
413
|
-
const client = new SidecarWsClient(host, accessToken || undefined);
|
|
421
|
+
const client = new SidecarWsClient(host, accessToken || undefined, authParamName);
|
|
414
422
|
try {
|
|
415
423
|
// Trigger connection to validate the endpoint exists.
|
|
416
424
|
// subscribe() calls ensureConnected internally, but we want
|
|
@@ -459,8 +467,12 @@ export abstract class Exchange {
|
|
|
459
467
|
args,
|
|
460
468
|
this.getCredentials() as Record<string, any> | undefined,
|
|
461
469
|
);
|
|
462
|
-
} catch {
|
|
463
|
-
|
|
470
|
+
} catch (error) {
|
|
471
|
+
// Only fall back to HTTP for transport-level failures
|
|
472
|
+
if (error instanceof PmxtError && /connection failed|no websocket/i.test(error.message)) {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
throw error;
|
|
464
476
|
}
|
|
465
477
|
}
|
|
466
478
|
|
|
@@ -1540,8 +1552,11 @@ export abstract class Exchange {
|
|
|
1540
1552
|
}
|
|
1541
1553
|
return result;
|
|
1542
1554
|
}
|
|
1543
|
-
} catch {
|
|
1544
|
-
// fall through to HTTP
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
// Only fall through to HTTP for transport-level WS failures
|
|
1557
|
+
if (!(error instanceof PmxtError) || !/connection failed|no websocket/i.test(error.message)) {
|
|
1558
|
+
throw error;
|
|
1559
|
+
}
|
|
1545
1560
|
}
|
|
1546
1561
|
}
|
|
1547
1562
|
|
|
@@ -1571,13 +1586,52 @@ export abstract class Exchange {
|
|
|
1571
1586
|
}
|
|
1572
1587
|
return result;
|
|
1573
1588
|
}
|
|
1574
|
-
|
|
1589
|
+
throw new PmxtError("watchOrderBooks: unexpected response shape from server");
|
|
1575
1590
|
} catch (error) {
|
|
1576
1591
|
if (error instanceof PmxtError) throw error;
|
|
1577
1592
|
throw new PmxtError(`Failed to watch order books: ${error}`);
|
|
1578
1593
|
}
|
|
1579
1594
|
}
|
|
1580
1595
|
|
|
1596
|
+
/**
|
|
1597
|
+
* Stream all orderbook updates across venues via the hosted WebSocket API.
|
|
1598
|
+
*
|
|
1599
|
+
* Returns a promise that resolves with the next book event.
|
|
1600
|
+
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
1601
|
+
* Requires hosted mode (`pmxtApiKey` set).
|
|
1602
|
+
*
|
|
1603
|
+
* @param venues - Optional venue filter (e.g. ["polymarket", "limitless"])
|
|
1604
|
+
* @returns Next firehose event with source, symbol, and orderbook
|
|
1605
|
+
*
|
|
1606
|
+
* @example
|
|
1607
|
+
* ```typescript
|
|
1608
|
+
* const poly = new Polymarket({ pmxtApiKey: "pmxt_xxx" });
|
|
1609
|
+
* while (true) {
|
|
1610
|
+
* const event = await poly.firehose();
|
|
1611
|
+
* console.log(event.source, event.symbol, event.orderbook.bids[0]);
|
|
1612
|
+
* }
|
|
1613
|
+
* ```
|
|
1614
|
+
*/
|
|
1615
|
+
async firehose(venues?: string[]): Promise<FirehoseEvent> {
|
|
1616
|
+
await this.initPromise;
|
|
1617
|
+
|
|
1618
|
+
if (!this.isHosted) {
|
|
1619
|
+
throw new PmxtError("firehose() requires hosted mode (set pmxtApiKey)");
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
const args: any[] = venues ? [venues] : [];
|
|
1623
|
+
const wsData = await this.watchViaWs("firehose", args);
|
|
1624
|
+
if (wsData !== null) {
|
|
1625
|
+
return {
|
|
1626
|
+
source: (wsData as any)._source || "",
|
|
1627
|
+
symbol: (wsData as any)._symbol || "",
|
|
1628
|
+
orderbook: convertOrderBook(wsData),
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
throw new PmxtError("firehose() requires WebSocket transport — connection failed");
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1581
1635
|
/**
|
|
1582
1636
|
* Watch real-time trade updates via WebSocket.
|
|
1583
1637
|
*
|
package/pmxt/models.ts
CHANGED
|
@@ -153,6 +153,20 @@ export interface OrderBook {
|
|
|
153
153
|
timestamp?: number;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* A single event from the firehose stream.
|
|
158
|
+
*/
|
|
159
|
+
export interface FirehoseEvent {
|
|
160
|
+
/** The venue this event originated from (e.g. "polymarket", "limitless") */
|
|
161
|
+
source: string;
|
|
162
|
+
|
|
163
|
+
/** The outcome token id / asset id */
|
|
164
|
+
symbol: string;
|
|
165
|
+
|
|
166
|
+
/** The order book snapshot */
|
|
167
|
+
orderbook: OrderBook;
|
|
168
|
+
}
|
|
169
|
+
|
|
156
170
|
/**
|
|
157
171
|
* Result of an execution price calculation.
|
|
158
172
|
*/
|
package/pmxt/ws-client.ts
CHANGED
|
@@ -36,6 +36,7 @@ export class SidecarWsClient {
|
|
|
36
36
|
private ws: WebSocket | null = null;
|
|
37
37
|
private host: string;
|
|
38
38
|
private accessToken: string | undefined;
|
|
39
|
+
private authParamName: string;
|
|
39
40
|
private closed = false;
|
|
40
41
|
|
|
41
42
|
/** requestId -> latest data payload */
|
|
@@ -47,9 +48,10 @@ export class SidecarWsClient {
|
|
|
47
48
|
|
|
48
49
|
private connectPromise: Promise<void> | null = null;
|
|
49
50
|
|
|
50
|
-
constructor(host: string, accessToken?: string) {
|
|
51
|
+
constructor(host: string, accessToken?: string, authParamName: string = "token") {
|
|
51
52
|
this.host = host;
|
|
52
53
|
this.accessToken = accessToken;
|
|
54
|
+
this.authParamName = authParamName;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
// ------------------------------------------------------------------
|
|
@@ -81,7 +83,7 @@ export class SidecarWsClient {
|
|
|
81
83
|
|
|
82
84
|
let url = `${scheme}://${hostPart}/ws`;
|
|
83
85
|
if (this.accessToken) {
|
|
84
|
-
url = `${url}
|
|
86
|
+
url = `${url}?${this.authParamName}=${this.accessToken}`;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
// Use the ws package in Node.js, native WebSocket in browsers
|
|
@@ -103,6 +105,18 @@ export class SidecarWsClient {
|
|
|
103
105
|
if (!this.ws) {
|
|
104
106
|
// Connection failed during handshake
|
|
105
107
|
reject(new PmxtError(`WebSocket connection failed: ${err.message || err}`));
|
|
108
|
+
} else {
|
|
109
|
+
// Post-handshake error — propagate to all pending subscribers
|
|
110
|
+
const error = new PmxtError(`WebSocket error: ${err.message || err}`);
|
|
111
|
+
for (const sub of this.subscriptions.values()) {
|
|
112
|
+
if (sub.reject) {
|
|
113
|
+
sub.reject(error);
|
|
114
|
+
sub.reject = null;
|
|
115
|
+
sub.resolve = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.closed = true;
|
|
119
|
+
this.ws = null;
|
|
106
120
|
}
|
|
107
121
|
};
|
|
108
122
|
|
|
@@ -112,15 +126,11 @@ export class SidecarWsClient {
|
|
|
112
126
|
};
|
|
113
127
|
|
|
114
128
|
ws.onmessage = (event: any) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.dispatch(msg);
|
|
121
|
-
} catch {
|
|
122
|
-
// Ignore unparseable frames
|
|
123
|
-
}
|
|
129
|
+
const raw = typeof event.data === "string"
|
|
130
|
+
? event.data
|
|
131
|
+
: event.data.toString();
|
|
132
|
+
const msg: WsMessage = JSON.parse(raw);
|
|
133
|
+
this.dispatch(msg);
|
|
124
134
|
};
|
|
125
135
|
});
|
|
126
136
|
}
|
|
@@ -130,13 +140,16 @@ export class SidecarWsClient {
|
|
|
130
140
|
if (typeof globalThis !== "undefined" && (globalThis as any).WebSocket) {
|
|
131
141
|
return (globalThis as any).WebSocket;
|
|
132
142
|
}
|
|
133
|
-
// Node.js --
|
|
143
|
+
// Node.js -- require ws
|
|
134
144
|
try {
|
|
135
145
|
// Dynamic require to avoid bundler issues
|
|
136
146
|
const wsModule = require("ws");
|
|
137
147
|
return wsModule.default || wsModule;
|
|
138
148
|
} catch {
|
|
139
|
-
|
|
149
|
+
throw new PmxtError(
|
|
150
|
+
"WebSocket support in Node.js requires the 'ws' package. " +
|
|
151
|
+
"Install it with: npm install ws"
|
|
152
|
+
);
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
|