@stryke-xyz/premarket-sdk 1.2.1 → 1.2.2
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/cjs/config/index.js +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/sync/clients/activity-client.js +325 -454
- package/dist/cjs/sync/clients/order-client.js +609 -429
- package/dist/esm/config/index.js +3 -3
- package/dist/esm/package.json +1 -1
- package/dist/esm/sync/clients/activity-client.js +325 -454
- package/dist/esm/sync/clients/order-client.js +609 -429
- package/dist/types/sync/clients/activity-client.d.ts +39 -32
- package/dist/types/sync/clients/order-client.d.ts +79 -58
- package/dist/types/sync/index.d.ts +2 -2
- package/package.json +58 -58
- package/dist/cjs/api/README.md +0 -296
- package/dist/cjs/config/README.md +0 -112
- package/dist/cjs/exchange/README.md +0 -280
- package/dist/cjs/registry/README.md +0 -150
- package/dist/cjs/shared/README.md +0 -235
- package/dist/cjs/shared/utils.js +0 -1
- package/dist/cjs/sync/README.md +0 -215
- package/dist/cjs/sync/clients/base-client.js +0 -518
- package/dist/cjs/sync/redis-ws-client.js +0 -235
- package/dist/cjs/utils/README.md +0 -89
- package/dist/cjs/vault/README.md +0 -268
- package/dist/esm/api/README.md +0 -296
- package/dist/esm/config/README.md +0 -112
- package/dist/esm/exchange/README.md +0 -280
- package/dist/esm/registry/README.md +0 -150
- package/dist/esm/shared/README.md +0 -235
- package/dist/esm/shared/utils.js +0 -0
- package/dist/esm/sync/README.md +0 -215
- package/dist/esm/sync/clients/base-client.js +0 -508
- package/dist/esm/sync/redis-ws-client.js +0 -225
- package/dist/esm/utils/README.md +0 -89
- package/dist/esm/vault/README.md +0 -268
- package/dist/types/shared/utils.d.ts +0 -0
- package/dist/types/sync/clients/base-client.d.ts +0 -56
- package/dist/types/sync/redis-ws-client.d.ts +0 -21
|
@@ -124,6 +124,10 @@ function _sliced_to_array(arr, i) {
|
|
|
124
124
|
function _to_consumable_array(arr) {
|
|
125
125
|
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
126
126
|
}
|
|
127
|
+
function _type_of(obj) {
|
|
128
|
+
"@swc/helpers - typeof";
|
|
129
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
130
|
+
}
|
|
127
131
|
function _unsupported_iterable_to_array(o, minLen) {
|
|
128
132
|
if (!o) return;
|
|
129
133
|
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
@@ -238,21 +242,40 @@ function scheduleDeferred(callback) {
|
|
|
238
242
|
}
|
|
239
243
|
setTimeout(callback, 0);
|
|
240
244
|
}
|
|
241
|
-
|
|
242
|
-
* Normalize a price string to a canonical form for consistent map key lookups.
|
|
243
|
-
* Converts "1.000000" and "1" both to "1" (removes trailing zeros after decimal).
|
|
244
|
-
*/ function normalizePrice(price) {
|
|
245
|
+
function normalizePrice(price) {
|
|
245
246
|
var num = parseFloat(price);
|
|
246
247
|
if (isNaN(num)) return price;
|
|
247
248
|
return num.toString();
|
|
248
249
|
}
|
|
249
|
-
/** Current depth state for a token within a market subscription. */ /** One bid or ask level change in the normalized SDK format. */ /** Raw level shape inside the wire `depth_update.levels[]` array. */ /** Raw `depth_update` frame as published by the depth projector. */ /** Consolidated depth update emitted to SDK listeners after normalization. */ /** Last applied executor sequenceId for this token (== endSeq of the frame). */ /** Range covered by this frame; clients can detect gaps via startSeq != prevSeq + 1. */ /**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
/** A market + its token IDs to subscribe to. */ /** Current depth state for a token within a market subscription. */ /** One bid or ask level change in the normalized SDK format. */ /** Raw level shape inside the wire `depth_update.levels[]` array. */ /** Raw `depth_update` frame as published by the depth projector. */ /** Consolidated depth update emitted to SDK listeners after normalization. */ /** Last applied executor sequenceId for this token (== endSeq of the frame). */ /** Range covered by this frame; clients can detect gaps via startSeq != prevSeq + 1. */ /**
|
|
251
|
+
* Configuration for the market depth websocket client.
|
|
252
|
+
*
|
|
253
|
+
* Supports multiple markets over a single WebSocket connection.
|
|
254
|
+
* Markets can be added/removed dynamically via subscribeMarket / unsubscribeMarket
|
|
255
|
+
* without closing the connection.
|
|
256
|
+
*/ /** Initial market subscriptions. More can be added later via subscribeMarket(). */ /** Heartbeat interval in ms (default: 30000) */ /** Heartbeat timeout - if no pong received within this time, reconnect (default: 10000) */ /** Max reconnection attempts before giving up (default: Infinity) */ /** Initial reconnect delay in ms (default: 1000) */ /** Max reconnect delay in ms (default: 30000) */ /** @deprecated Use MarketDepthClientConfig with markets[] instead of marketId+tokenIds. */ // Internal state per token
|
|
257
|
+
// price → depth
|
|
258
|
+
// price → depth
|
|
253
259
|
/**
|
|
254
|
-
* Client for syncing orderbook depth data for
|
|
255
|
-
*
|
|
260
|
+
* Client for syncing orderbook depth data for one or more markets over a
|
|
261
|
+
* single persistent WebSocket connection.
|
|
262
|
+
*
|
|
263
|
+
* Usage:
|
|
264
|
+
* const client = new MarketDepthSyncClient({ wsUrl: "wss://..." });
|
|
265
|
+
* await client.connect();
|
|
266
|
+
*
|
|
267
|
+
* // Subscribe to markets dynamically (e.g. when they scroll into view)
|
|
268
|
+
* client.subscribeMarket("5", ["0xabc..."]);
|
|
269
|
+
*
|
|
270
|
+
* // Unsubscribe without closing the connection (e.g. scrolled out of view)
|
|
271
|
+
* client.unsubscribeMarket("5");
|
|
272
|
+
*
|
|
273
|
+
* // Listen for updates
|
|
274
|
+
* const offDelta = client.onDelta((marketId, update) => { ... });
|
|
275
|
+
* const offSnap = client.onSnapshot((marketId, snapshots) => { ... });
|
|
276
|
+
*
|
|
277
|
+
* // Full disconnect (app teardown)
|
|
278
|
+
* await client.disconnect();
|
|
256
279
|
*/ export var MarketDepthSyncClient = /*#__PURE__*/ function() {
|
|
257
280
|
"use strict";
|
|
258
281
|
function MarketDepthSyncClient(config) {
|
|
@@ -260,11 +283,18 @@ function scheduleDeferred(callback) {
|
|
|
260
283
|
_define_property(this, "ws", null);
|
|
261
284
|
_define_property(this, "config", void 0);
|
|
262
285
|
_define_property(this, "status", "disconnected");
|
|
263
|
-
// Per-token depth state (
|
|
286
|
+
// Per-token depth state (tokenIds are globally unique across all subscribed markets)
|
|
264
287
|
_define_property(this, "tokenStates", new Map());
|
|
265
|
-
//
|
|
266
|
-
_define_property(this, "
|
|
267
|
-
|
|
288
|
+
// Active subscriptions: marketId → tokenIds[]
|
|
289
|
+
_define_property(this, "activeSubscriptions", new Map());
|
|
290
|
+
// Reverse map: tokenId → marketId (for gap resync targeting)
|
|
291
|
+
_define_property(this, "tokenToMarket", new Map());
|
|
292
|
+
// Markets that have received their initial snapshot on this connection
|
|
293
|
+
_define_property(this, "syncedMarkets", new Set());
|
|
294
|
+
// Per-market frame queues: buffer depth_updates received before snapshot
|
|
295
|
+
_define_property(this, "frameQueues", new Map());
|
|
296
|
+
// Per-market processing mutex
|
|
297
|
+
_define_property(this, "processingMarkets", new Set());
|
|
268
298
|
// Listeners
|
|
269
299
|
_define_property(this, "statusListeners", new Set());
|
|
270
300
|
_define_property(this, "snapshotListeners", new Set());
|
|
@@ -273,121 +303,186 @@ function scheduleDeferred(callback) {
|
|
|
273
303
|
_define_property(this, "shouldBeConnected", false);
|
|
274
304
|
_define_property(this, "reconnectAttempts", 0);
|
|
275
305
|
_define_property(this, "reconnectTimeoutId", null);
|
|
276
|
-
// Heartbeat
|
|
306
|
+
// Heartbeat
|
|
277
307
|
_define_property(this, "heartbeatIntervalId", null);
|
|
278
308
|
_define_property(this, "heartbeatTimeoutId", null);
|
|
279
309
|
_define_property(this, "lastPongTime", 0);
|
|
280
|
-
// Visibility
|
|
310
|
+
// Visibility handler
|
|
281
311
|
_define_property(this, "visibilityChangeHandler", null);
|
|
312
|
+
// Support legacy single-market config (marketId + tokenIds)
|
|
313
|
+
var normalizedConfig = _object_spread({}, config);
|
|
314
|
+
if (config.marketId && config.tokenIds) {
|
|
315
|
+
var _config_markets;
|
|
316
|
+
normalizedConfig.markets = [
|
|
317
|
+
{
|
|
318
|
+
marketId: config.marketId,
|
|
319
|
+
tokenIds: config.tokenIds
|
|
320
|
+
}
|
|
321
|
+
].concat(_to_consumable_array((_config_markets = config.markets) !== null && _config_markets !== void 0 ? _config_markets : []));
|
|
322
|
+
}
|
|
282
323
|
this.config = _object_spread({
|
|
324
|
+
markets: [],
|
|
283
325
|
heartbeatIntervalMs: 30000,
|
|
284
326
|
heartbeatTimeoutMs: 10000,
|
|
285
327
|
maxReconnectAttempts: Infinity,
|
|
286
328
|
initialReconnectDelayMs: 1000,
|
|
287
329
|
maxReconnectDelayMs: 30000
|
|
288
|
-
},
|
|
330
|
+
}, normalizedConfig);
|
|
331
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
332
|
+
try {
|
|
333
|
+
// Seed initial subscriptions from config
|
|
334
|
+
for(var _iterator = this.config.markets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
335
|
+
var spec = _step.value;
|
|
336
|
+
this.activeSubscriptions.set(spec.marketId, _to_consumable_array(spec.tokenIds));
|
|
337
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
338
|
+
try {
|
|
339
|
+
for(var _iterator1 = spec.tokenIds[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
340
|
+
var tokenId = _step1.value;
|
|
341
|
+
this.tokenToMarket.set(tokenId, spec.marketId);
|
|
342
|
+
}
|
|
343
|
+
} catch (err) {
|
|
344
|
+
_didIteratorError1 = true;
|
|
345
|
+
_iteratorError1 = err;
|
|
346
|
+
} finally{
|
|
347
|
+
try {
|
|
348
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
349
|
+
_iterator1.return();
|
|
350
|
+
}
|
|
351
|
+
} finally{
|
|
352
|
+
if (_didIteratorError1) {
|
|
353
|
+
throw _iteratorError1;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
_didIteratorError = true;
|
|
360
|
+
_iteratorError = err;
|
|
361
|
+
} finally{
|
|
362
|
+
try {
|
|
363
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
364
|
+
_iterator.return();
|
|
365
|
+
}
|
|
366
|
+
} finally{
|
|
367
|
+
if (_didIteratorError) {
|
|
368
|
+
throw _iteratorError;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
289
372
|
}
|
|
290
373
|
_create_class(MarketDepthSyncClient, [
|
|
291
374
|
{
|
|
292
375
|
key: "connect",
|
|
293
|
-
value:
|
|
376
|
+
value: // ---------------------------------------------------------------------------
|
|
377
|
+
// Public API
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
/** Open the WebSocket connection and subscribe to all configured markets. */ function connect() {
|
|
294
380
|
return _async_to_generator(function() {
|
|
295
381
|
var _this, wsUrl;
|
|
296
382
|
return _ts_generator(this, function(_state) {
|
|
297
383
|
_this = this;
|
|
298
|
-
// Clean up any existing connection first
|
|
299
384
|
this.stopHeartbeat();
|
|
300
385
|
this.clearReconnectTimeout();
|
|
301
386
|
if (this.ws) {
|
|
302
387
|
this.ws.onclose = null;
|
|
303
|
-
// Prevent triggering reconnect
|
|
304
388
|
this.ws.close();
|
|
305
389
|
this.ws = null;
|
|
306
390
|
}
|
|
307
391
|
this.shouldBeConnected = true;
|
|
392
|
+
this.syncedMarkets.clear();
|
|
393
|
+
this.frameQueues.clear();
|
|
394
|
+
this.processingMarkets.clear();
|
|
308
395
|
this.setStatus("connecting");
|
|
309
396
|
wsUrl = this.config.wsUrl;
|
|
310
397
|
if (!wsUrl.startsWith("ws://") && !wsUrl.startsWith("wss://")) {
|
|
311
398
|
throw new Error("Invalid WebSocket URL: ".concat(wsUrl));
|
|
312
399
|
}
|
|
313
|
-
// Prevent queue processing until snapshots are received
|
|
314
|
-
this.isProcessing = true;
|
|
315
|
-
// Set up visibility change handler
|
|
316
400
|
this.setupVisibilityChangeHandler();
|
|
317
401
|
return [
|
|
318
402
|
2,
|
|
319
403
|
new Promise(function(resolve, reject) {
|
|
320
404
|
var settled = false;
|
|
321
405
|
var resolveOnce = function() {
|
|
322
|
-
if (settled)
|
|
323
|
-
|
|
324
|
-
|
|
406
|
+
if (!settled) {
|
|
407
|
+
settled = true;
|
|
408
|
+
resolve();
|
|
409
|
+
}
|
|
325
410
|
};
|
|
326
|
-
var rejectOnce = function(
|
|
327
|
-
if (settled)
|
|
328
|
-
|
|
329
|
-
|
|
411
|
+
var rejectOnce = function(e) {
|
|
412
|
+
if (!settled) {
|
|
413
|
+
settled = true;
|
|
414
|
+
reject(e);
|
|
415
|
+
}
|
|
330
416
|
};
|
|
331
417
|
_this.ws = new WebSocket(wsUrl);
|
|
332
418
|
_this.ws.onopen = function() {
|
|
333
|
-
// Reset reconnect attempts on successful connection
|
|
334
419
|
_this.reconnectAttempts = 0;
|
|
335
420
|
_this.lastPongTime = Date.now();
|
|
336
|
-
// Send subscribe_market message
|
|
337
|
-
_this.ws.send(JSON.stringify({
|
|
338
|
-
type: "subscribe_market",
|
|
339
|
-
marketId: _this.config.marketId,
|
|
340
|
-
tokenIds: _this.config.tokenIds
|
|
341
|
-
}));
|
|
342
|
-
// Start heartbeat
|
|
343
421
|
_this.startHeartbeat();
|
|
422
|
+
if (_this.activeSubscriptions.size === 0) {
|
|
423
|
+
_this.setStatus("synced");
|
|
424
|
+
resolveOnce();
|
|
425
|
+
} else {
|
|
426
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
427
|
+
try {
|
|
428
|
+
for(var _iterator = _this.activeSubscriptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
429
|
+
var _step_value = _sliced_to_array(_step.value, 2), marketId = _step_value[0], tokenIds = _step_value[1];
|
|
430
|
+
_this._sendSubscribeMarket(marketId, tokenIds);
|
|
431
|
+
}
|
|
432
|
+
} catch (err) {
|
|
433
|
+
_didIteratorError = true;
|
|
434
|
+
_iteratorError = err;
|
|
435
|
+
} finally{
|
|
436
|
+
try {
|
|
437
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
438
|
+
_iterator.return();
|
|
439
|
+
}
|
|
440
|
+
} finally{
|
|
441
|
+
if (_didIteratorError) {
|
|
442
|
+
throw _iteratorError;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Resolve immediately once WS is open — snapshots arrive async
|
|
447
|
+
resolveOnce();
|
|
448
|
+
}
|
|
344
449
|
};
|
|
345
450
|
_this.ws.onmessage = function(event) {
|
|
346
451
|
try {
|
|
347
452
|
var msg = JSON.parse(event.data);
|
|
348
|
-
// Handle pong response
|
|
349
453
|
if (msg.type === "pong") {
|
|
350
454
|
_this.lastPongTime = Date.now();
|
|
351
455
|
_this.clearHeartbeatTimeout();
|
|
352
456
|
return;
|
|
353
457
|
}
|
|
354
458
|
if (msg.type === "subscribed_market") {
|
|
355
|
-
// Received initial snapshots
|
|
356
459
|
_this.handleSubscribedMarket(msg);
|
|
357
|
-
_this.isProcessing = false;
|
|
358
|
-
_this.setStatus("synced");
|
|
359
|
-
void _this.processFrameQueue();
|
|
360
|
-
resolveOnce();
|
|
361
460
|
} else if (msg.type === "depth_update") {
|
|
362
461
|
_this.handleDepthUpdate(msg);
|
|
363
462
|
} else if (msg.type === "market_state") {
|
|
364
|
-
// Received market state (bestBid, bestAsk, lastPrice) - preferred over last_price
|
|
365
463
|
_this.handleMarketStateUpdate(msg);
|
|
366
464
|
} else if (msg.type === "last_price") {
|
|
367
|
-
// Legacy: last price only (still supported)
|
|
368
465
|
_this.handleLastPriceUpdate(msg);
|
|
369
466
|
} else if (msg.type === "error") {
|
|
370
|
-
console.error("
|
|
467
|
+
console.error("[MarketDepthSyncClient] server error:", msg.message);
|
|
371
468
|
rejectOnce(new Error(msg.message));
|
|
372
469
|
}
|
|
373
470
|
} catch (error) {
|
|
374
|
-
console.error("
|
|
471
|
+
console.error("[MarketDepthSyncClient] parse error:", error);
|
|
375
472
|
}
|
|
376
473
|
};
|
|
377
474
|
_this.ws.onerror = function(error) {
|
|
378
|
-
console.error("WebSocket error:", error);
|
|
379
|
-
// Only reject if this is the initial connection
|
|
380
475
|
if (_this.status === "connecting") {
|
|
381
476
|
rejectOnce(error);
|
|
382
477
|
}
|
|
383
478
|
};
|
|
384
479
|
_this.ws.onclose = function() {
|
|
385
480
|
_this.stopHeartbeat();
|
|
481
|
+
_this.syncedMarkets.clear();
|
|
386
482
|
_this.setStatus("disconnected");
|
|
387
483
|
if (!settled) {
|
|
388
|
-
rejectOnce(new Error("WebSocket closed before
|
|
484
|
+
rejectOnce(new Error("[MarketDepthSyncClient] WebSocket closed before connecting"));
|
|
389
485
|
}
|
|
390
|
-
// Attempt to reconnect if we should still be connected
|
|
391
486
|
if (_this.shouldBeConnected) {
|
|
392
487
|
_this.scheduleReconnect();
|
|
393
488
|
}
|
|
@@ -399,210 +494,257 @@ function scheduleDeferred(callback) {
|
|
|
399
494
|
}
|
|
400
495
|
},
|
|
401
496
|
{
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
this.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Subscribe to a market's depth. If the WebSocket is already open, sends the
|
|
499
|
+
* `subscribe_market` message immediately. Otherwise, the subscription is queued
|
|
500
|
+
* and sent when the connection opens. Calling this for an already-subscribed
|
|
501
|
+
* market re-requests a fresh snapshot.
|
|
502
|
+
*/ key: "subscribeMarket",
|
|
503
|
+
value: function subscribeMarket(marketId, tokenIds) {
|
|
504
|
+
this.activeSubscriptions.set(marketId, _to_consumable_array(tokenIds));
|
|
505
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
506
|
+
try {
|
|
507
|
+
for(var _iterator = tokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
508
|
+
var tokenId = _step.value;
|
|
509
|
+
this.tokenToMarket.set(tokenId, marketId);
|
|
413
510
|
}
|
|
414
|
-
}
|
|
415
|
-
|
|
511
|
+
} catch (err) {
|
|
512
|
+
_didIteratorError = true;
|
|
513
|
+
_iteratorError = err;
|
|
514
|
+
} finally{
|
|
515
|
+
try {
|
|
516
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
517
|
+
_iterator.return();
|
|
518
|
+
}
|
|
519
|
+
} finally{
|
|
520
|
+
if (_didIteratorError) {
|
|
521
|
+
throw _iteratorError;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
526
|
+
this._sendSubscribeMarket(marketId, tokenIds);
|
|
527
|
+
}
|
|
416
528
|
}
|
|
417
529
|
},
|
|
418
530
|
{
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
531
|
+
/**
|
|
532
|
+
* Unsubscribe from a market. Sends `unsubscribe_market` to the server and
|
|
533
|
+
* clears local state for that market's tokens. The WebSocket connection is
|
|
534
|
+
* kept open for other subscriptions.
|
|
535
|
+
*/ key: "unsubscribeMarket",
|
|
536
|
+
value: function unsubscribeMarket(marketId) {
|
|
537
|
+
var _this_activeSubscriptions_get;
|
|
538
|
+
var tokenIds = (_this_activeSubscriptions_get = this.activeSubscriptions.get(marketId)) !== null && _this_activeSubscriptions_get !== void 0 ? _this_activeSubscriptions_get : [];
|
|
539
|
+
this.activeSubscriptions.delete(marketId);
|
|
540
|
+
this.syncedMarkets.delete(marketId);
|
|
541
|
+
this.frameQueues.delete(marketId);
|
|
542
|
+
this.processingMarkets.delete(marketId);
|
|
543
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
544
|
+
try {
|
|
545
|
+
for(var _iterator = tokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
546
|
+
var tokenId = _step.value;
|
|
547
|
+
this.tokenStates.delete(tokenId);
|
|
548
|
+
this.tokenToMarket.delete(tokenId);
|
|
549
|
+
}
|
|
550
|
+
} catch (err) {
|
|
551
|
+
_didIteratorError = true;
|
|
552
|
+
_iteratorError = err;
|
|
553
|
+
} finally{
|
|
554
|
+
try {
|
|
555
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
556
|
+
_iterator.return();
|
|
557
|
+
}
|
|
558
|
+
} finally{
|
|
559
|
+
if (_didIteratorError) {
|
|
560
|
+
throw _iteratorError;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
565
|
+
try {
|
|
566
|
+
this.ws.send(JSON.stringify({
|
|
567
|
+
type: "unsubscribe_market",
|
|
568
|
+
marketId: marketId
|
|
569
|
+
}));
|
|
570
|
+
} catch (unused) {}
|
|
425
571
|
}
|
|
426
572
|
}
|
|
427
573
|
},
|
|
428
574
|
{
|
|
429
|
-
key: "
|
|
430
|
-
value:
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
// Send failed, connection is dead
|
|
451
|
-
this.handleConnectionLost();
|
|
452
|
-
}
|
|
575
|
+
key: "disconnect",
|
|
576
|
+
value: // Ignore — connection closing
|
|
577
|
+
/** Fully disconnect and stop reconnecting. */ function disconnect() {
|
|
578
|
+
return _async_to_generator(function() {
|
|
579
|
+
return _ts_generator(this, function(_state) {
|
|
580
|
+
this.shouldBeConnected = false;
|
|
581
|
+
this.stopHeartbeat();
|
|
582
|
+
this.clearReconnectTimeout();
|
|
583
|
+
this.removeVisibilityChangeHandler();
|
|
584
|
+
if (this.ws) {
|
|
585
|
+
this.ws.onclose = null;
|
|
586
|
+
this.ws.close();
|
|
587
|
+
this.ws = null;
|
|
588
|
+
}
|
|
589
|
+
this.syncedMarkets.clear();
|
|
590
|
+
this.setStatus("disconnected");
|
|
591
|
+
return [
|
|
592
|
+
2
|
|
593
|
+
];
|
|
594
|
+
});
|
|
595
|
+
}).call(this);
|
|
453
596
|
}
|
|
454
597
|
},
|
|
455
598
|
{
|
|
456
|
-
key: "
|
|
457
|
-
value: function
|
|
458
|
-
this.
|
|
459
|
-
if (this.ws) {
|
|
460
|
-
this.ws.onclose = null;
|
|
461
|
-
// Prevent double-triggering
|
|
462
|
-
this.ws.close();
|
|
463
|
-
this.ws = null;
|
|
464
|
-
}
|
|
465
|
-
this.setStatus("disconnected");
|
|
466
|
-
if (this.shouldBeConnected) {
|
|
467
|
-
this.scheduleReconnect();
|
|
468
|
-
}
|
|
599
|
+
key: "getStatus",
|
|
600
|
+
value: function getStatus() {
|
|
601
|
+
return this.status;
|
|
469
602
|
}
|
|
470
603
|
},
|
|
471
604
|
{
|
|
472
|
-
key: "
|
|
473
|
-
value: function
|
|
474
|
-
|
|
475
|
-
this.
|
|
476
|
-
this.heartbeatIntervalId = setInterval(function() {
|
|
477
|
-
if (!_this.ws || _this.ws.readyState !== WebSocket.OPEN) {
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
try {
|
|
481
|
-
_this.ws.send(JSON.stringify({
|
|
482
|
-
type: "ping"
|
|
483
|
-
}));
|
|
484
|
-
// Set timeout for pong response
|
|
485
|
-
_this.clearHeartbeatTimeout();
|
|
486
|
-
_this.heartbeatTimeoutId = setTimeout(function() {
|
|
487
|
-
console.log("Heartbeat timeout, reconnecting...");
|
|
488
|
-
_this.handleConnectionLost();
|
|
489
|
-
}, _this.config.heartbeatTimeoutMs);
|
|
490
|
-
} catch (unused) {
|
|
491
|
-
// Send failed, connection is dead
|
|
492
|
-
_this.handleConnectionLost();
|
|
493
|
-
}
|
|
494
|
-
}, this.config.heartbeatIntervalMs);
|
|
605
|
+
key: "isSynced",
|
|
606
|
+
value: function isSynced() {
|
|
607
|
+
if (this.activeSubscriptions.size === 0) return this.status === "synced";
|
|
608
|
+
return this.activeSubscriptions.size === this.syncedMarkets.size;
|
|
495
609
|
}
|
|
496
610
|
},
|
|
497
611
|
{
|
|
498
|
-
key: "
|
|
499
|
-
value: function
|
|
500
|
-
|
|
501
|
-
clearInterval(this.heartbeatIntervalId);
|
|
502
|
-
this.heartbeatIntervalId = null;
|
|
503
|
-
}
|
|
504
|
-
this.clearHeartbeatTimeout();
|
|
612
|
+
key: "isMarketSynced",
|
|
613
|
+
value: function isMarketSynced(marketId) {
|
|
614
|
+
return this.syncedMarkets.has(marketId);
|
|
505
615
|
}
|
|
506
616
|
},
|
|
507
617
|
{
|
|
508
|
-
key: "
|
|
509
|
-
value: function
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
618
|
+
key: "getBids",
|
|
619
|
+
value: function getBids(tokenId) {
|
|
620
|
+
var state = this.tokenStates.get(tokenId);
|
|
621
|
+
if (!state) return [];
|
|
622
|
+
return Array.from(state.bids.entries()).map(function(param) {
|
|
623
|
+
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
624
|
+
return {
|
|
625
|
+
price: price,
|
|
626
|
+
depth: depth
|
|
627
|
+
};
|
|
628
|
+
});
|
|
514
629
|
}
|
|
515
630
|
},
|
|
516
631
|
{
|
|
517
|
-
key: "
|
|
518
|
-
value: function
|
|
519
|
-
var
|
|
520
|
-
if (!
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
var maxDelay = this.config.maxReconnectDelayMs;
|
|
529
|
-
var delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts) + Math.random() * 1000, maxDelay);
|
|
530
|
-
this.reconnectAttempts++;
|
|
531
|
-
this.setStatus("recovering");
|
|
532
|
-
console.log("Scheduling reconnect attempt ".concat(this.reconnectAttempts, " in ").concat(Math.round(delay), "ms"));
|
|
533
|
-
this.clearReconnectTimeout();
|
|
534
|
-
this.reconnectTimeoutId = setTimeout(function() {
|
|
535
|
-
_this.performReconnect();
|
|
536
|
-
}, delay);
|
|
632
|
+
key: "getAsks",
|
|
633
|
+
value: function getAsks(tokenId) {
|
|
634
|
+
var state = this.tokenStates.get(tokenId);
|
|
635
|
+
if (!state) return [];
|
|
636
|
+
return Array.from(state.asks.entries()).map(function(param) {
|
|
637
|
+
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
638
|
+
return {
|
|
639
|
+
price: price,
|
|
640
|
+
depth: depth
|
|
641
|
+
};
|
|
642
|
+
});
|
|
537
643
|
}
|
|
538
644
|
},
|
|
539
645
|
{
|
|
540
|
-
key: "
|
|
541
|
-
value: function
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
646
|
+
key: "getBestBid",
|
|
647
|
+
value: function getBestBid(tokenId) {
|
|
648
|
+
var _ref;
|
|
649
|
+
var _this_tokenStates_get;
|
|
650
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestBid) !== null && _ref !== void 0 ? _ref : null;
|
|
546
651
|
}
|
|
547
652
|
},
|
|
548
653
|
{
|
|
549
|
-
key: "
|
|
550
|
-
value: function
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
switch(_state.label){
|
|
555
|
-
case 0:
|
|
556
|
-
if (!this.shouldBeConnected) return [
|
|
557
|
-
2
|
|
558
|
-
];
|
|
559
|
-
_state.label = 1;
|
|
560
|
-
case 1:
|
|
561
|
-
_state.trys.push([
|
|
562
|
-
1,
|
|
563
|
-
3,
|
|
564
|
-
,
|
|
565
|
-
4
|
|
566
|
-
]);
|
|
567
|
-
// Clear state before reconnecting
|
|
568
|
-
this.tokenStates.clear();
|
|
569
|
-
this.frameQueue = [];
|
|
570
|
-
return [
|
|
571
|
-
4,
|
|
572
|
-
this.connect()
|
|
573
|
-
];
|
|
574
|
-
case 2:
|
|
575
|
-
_state.sent();
|
|
576
|
-
console.log("Reconnected successfully");
|
|
577
|
-
return [
|
|
578
|
-
3,
|
|
579
|
-
4
|
|
580
|
-
];
|
|
581
|
-
case 3:
|
|
582
|
-
error = _state.sent();
|
|
583
|
-
console.error("Reconnection failed:", error);
|
|
584
|
-
return [
|
|
585
|
-
3,
|
|
586
|
-
4
|
|
587
|
-
];
|
|
588
|
-
case 4:
|
|
589
|
-
return [
|
|
590
|
-
2
|
|
591
|
-
];
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
}).call(this);
|
|
654
|
+
key: "getBestAsk",
|
|
655
|
+
value: function getBestAsk(tokenId) {
|
|
656
|
+
var _ref;
|
|
657
|
+
var _this_tokenStates_get;
|
|
658
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestAsk) !== null && _ref !== void 0 ? _ref : null;
|
|
595
659
|
}
|
|
596
660
|
},
|
|
597
661
|
{
|
|
598
|
-
key: "
|
|
599
|
-
value:
|
|
600
|
-
|
|
601
|
-
var
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
662
|
+
key: "getLastPrice",
|
|
663
|
+
value: function getLastPrice(tokenId) {
|
|
664
|
+
var _ref;
|
|
665
|
+
var _this_tokenStates_get;
|
|
666
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.lastPrice) !== null && _ref !== void 0 ? _ref : null;
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
key: "onSnapshot",
|
|
671
|
+
value: function onSnapshot(listener) {
|
|
672
|
+
var _this = this;
|
|
673
|
+
this.snapshotListeners.add(listener);
|
|
674
|
+
return function() {
|
|
675
|
+
return _this.snapshotListeners.delete(listener);
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
key: "onDelta",
|
|
681
|
+
value: function onDelta(listener) {
|
|
682
|
+
var _this = this;
|
|
683
|
+
this.deltaListeners.add(listener);
|
|
684
|
+
return function() {
|
|
685
|
+
return _this.deltaListeners.delete(listener);
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
key: "onStatus",
|
|
691
|
+
value: function onStatus(listener) {
|
|
692
|
+
var _this = this;
|
|
693
|
+
this.statusListeners.add(listener);
|
|
694
|
+
return function() {
|
|
695
|
+
return _this.statusListeners.delete(listener);
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
key: "_sendSubscribeMarket",
|
|
701
|
+
value: // ---------------------------------------------------------------------------
|
|
702
|
+
// Private — WS message handlers
|
|
703
|
+
// ---------------------------------------------------------------------------
|
|
704
|
+
function _sendSubscribeMarket(marketId, tokenIds) {
|
|
705
|
+
var _this_activeSubscriptions_get;
|
|
706
|
+
// Clear stale state so we get a clean snapshot
|
|
707
|
+
var existingTokenIds = (_this_activeSubscriptions_get = this.activeSubscriptions.get(marketId)) !== null && _this_activeSubscriptions_get !== void 0 ? _this_activeSubscriptions_get : tokenIds;
|
|
708
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
709
|
+
try {
|
|
710
|
+
for(var _iterator = existingTokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
711
|
+
var tokenId = _step.value;
|
|
712
|
+
this.tokenStates.delete(tokenId);
|
|
713
|
+
}
|
|
714
|
+
} catch (err) {
|
|
715
|
+
_didIteratorError = true;
|
|
716
|
+
_iteratorError = err;
|
|
717
|
+
} finally{
|
|
718
|
+
try {
|
|
719
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
720
|
+
_iterator.return();
|
|
721
|
+
}
|
|
722
|
+
} finally{
|
|
723
|
+
if (_didIteratorError) {
|
|
724
|
+
throw _iteratorError;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
this.syncedMarkets.delete(marketId);
|
|
729
|
+
this.frameQueues.delete(marketId);
|
|
730
|
+
try {
|
|
731
|
+
this.ws.send(JSON.stringify({
|
|
732
|
+
type: "subscribe_market",
|
|
733
|
+
marketId: marketId,
|
|
734
|
+
tokenIds: tokenIds
|
|
735
|
+
}));
|
|
736
|
+
} catch (e) {
|
|
737
|
+
console.warn("[MarketDepthSyncClient] failed to send subscribe_market:", e);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
key: "handleSubscribedMarket",
|
|
743
|
+
value: function handleSubscribedMarket(msg) {
|
|
744
|
+
var marketId = msg.marketId, snapshots = msg.snapshots;
|
|
745
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
746
|
+
try {
|
|
747
|
+
for(var _iterator = snapshots[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
606
748
|
var snapshot = _step.value;
|
|
607
749
|
var state = {
|
|
608
750
|
bids: new Map(),
|
|
@@ -668,20 +810,38 @@ function scheduleDeferred(callback) {
|
|
|
668
810
|
}
|
|
669
811
|
}
|
|
670
812
|
}
|
|
813
|
+
this.syncedMarkets.add(marketId);
|
|
814
|
+
if (this.activeSubscriptions.size > 0 && this.syncedMarkets.size >= this.activeSubscriptions.size) {
|
|
815
|
+
this.setStatus("synced");
|
|
816
|
+
}
|
|
671
817
|
this.snapshotListeners.forEach(function(listener) {
|
|
672
818
|
try {
|
|
673
|
-
listener(
|
|
674
|
-
} catch (
|
|
675
|
-
console.error(
|
|
819
|
+
listener(marketId, snapshots);
|
|
820
|
+
} catch (e) {
|
|
821
|
+
console.error(e);
|
|
676
822
|
}
|
|
677
823
|
});
|
|
824
|
+
// Process any buffered depth_updates for this market
|
|
825
|
+
void this.processFrameQueue(marketId);
|
|
678
826
|
}
|
|
679
827
|
},
|
|
680
828
|
{
|
|
681
829
|
key: "handleDepthUpdate",
|
|
682
830
|
value: function handleDepthUpdate(msg) {
|
|
683
|
-
|
|
684
|
-
|
|
831
|
+
var marketId = msg.marketId;
|
|
832
|
+
if (!this.syncedMarkets.has(marketId)) {
|
|
833
|
+
// Buffer until snapshot arrives
|
|
834
|
+
if (!this.frameQueues.has(marketId)) {
|
|
835
|
+
this.frameQueues.set(marketId, []);
|
|
836
|
+
}
|
|
837
|
+
this.frameQueues.get(marketId).push(msg);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (!this.frameQueues.has(marketId)) {
|
|
841
|
+
this.frameQueues.set(marketId, []);
|
|
842
|
+
}
|
|
843
|
+
this.frameQueues.get(marketId).push(msg);
|
|
844
|
+
void this.processFrameQueue(marketId);
|
|
685
845
|
}
|
|
686
846
|
},
|
|
687
847
|
{
|
|
@@ -689,83 +849,88 @@ function scheduleDeferred(callback) {
|
|
|
689
849
|
value: function handleMarketStateUpdate(msg) {
|
|
690
850
|
var _msg_bestBid, _msg_bestAsk, _msg_lastPrice;
|
|
691
851
|
var state = this.tokenStates.get(String(msg.tokenId));
|
|
692
|
-
if (!state)
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
852
|
+
if (!state) return;
|
|
695
853
|
state.bestBid = (_msg_bestBid = msg.bestBid) !== null && _msg_bestBid !== void 0 ? _msg_bestBid : state.bestBid;
|
|
696
854
|
state.bestAsk = (_msg_bestAsk = msg.bestAsk) !== null && _msg_bestAsk !== void 0 ? _msg_bestAsk : state.bestAsk;
|
|
697
855
|
state.lastPrice = (_msg_lastPrice = msg.lastPrice) !== null && _msg_lastPrice !== void 0 ? _msg_lastPrice : state.lastPrice;
|
|
698
|
-
this.emitOutOfBandUpdate(msg.tokenId, state);
|
|
856
|
+
this.emitOutOfBandUpdate(msg.marketId, msg.tokenId, state);
|
|
699
857
|
}
|
|
700
858
|
},
|
|
701
859
|
{
|
|
702
860
|
key: "handleLastPriceUpdate",
|
|
703
861
|
value: function handleLastPriceUpdate(msg) {
|
|
862
|
+
var _ref, _msg_marketId;
|
|
704
863
|
var state = this.tokenStates.get(String(msg.tokenId));
|
|
705
|
-
if (!state)
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
864
|
+
if (!state) return;
|
|
708
865
|
state.lastPrice = msg.lastPrice;
|
|
709
|
-
this.
|
|
866
|
+
var marketId = (_ref = (_msg_marketId = msg.marketId) !== null && _msg_marketId !== void 0 ? _msg_marketId : this.tokenToMarket.get(msg.tokenId)) !== null && _ref !== void 0 ? _ref : "";
|
|
867
|
+
this.emitOutOfBandUpdate(marketId, msg.tokenId, state);
|
|
710
868
|
}
|
|
711
869
|
},
|
|
712
870
|
{
|
|
713
871
|
key: "emitOutOfBandUpdate",
|
|
714
|
-
value:
|
|
715
|
-
var
|
|
872
|
+
value: function emitOutOfBandUpdate(marketId, tokenId, state) {
|
|
873
|
+
var update = {
|
|
874
|
+
tokenId: tokenId,
|
|
875
|
+
levels: [],
|
|
876
|
+
bestBid: state.bestBid,
|
|
877
|
+
bestAsk: state.bestAsk,
|
|
878
|
+
lastPrice: state.lastPrice,
|
|
879
|
+
seq: state.seq,
|
|
880
|
+
startSeq: state.seq,
|
|
881
|
+
endSeq: state.seq
|
|
882
|
+
};
|
|
716
883
|
this.deltaListeners.forEach(function(listener) {
|
|
717
884
|
try {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
bestBid: state.bestBid,
|
|
722
|
-
bestAsk: state.bestAsk,
|
|
723
|
-
lastPrice: state.lastPrice,
|
|
724
|
-
seq: state.seq,
|
|
725
|
-
startSeq: state.seq,
|
|
726
|
-
endSeq: state.seq
|
|
727
|
-
};
|
|
728
|
-
listener(_this.config.marketId, update);
|
|
729
|
-
} catch (error) {
|
|
730
|
-
console.error("Error in delta listener:", error);
|
|
885
|
+
listener(marketId, update);
|
|
886
|
+
} catch (e) {
|
|
887
|
+
console.error(e);
|
|
731
888
|
}
|
|
732
889
|
});
|
|
733
890
|
}
|
|
734
891
|
},
|
|
735
892
|
{
|
|
736
893
|
key: "processFrameQueue",
|
|
737
|
-
value: function processFrameQueue() {
|
|
894
|
+
value: function processFrameQueue(marketId) {
|
|
738
895
|
return _async_to_generator(function() {
|
|
739
|
-
var _this, _this1, _loop;
|
|
896
|
+
var _this, _this1, _loop, queue, _ret, queue1;
|
|
740
897
|
return _ts_generator(this, function(_state) {
|
|
741
898
|
_this = this;
|
|
742
|
-
if (this.
|
|
899
|
+
if (this.processingMarkets.has(marketId)) return [
|
|
743
900
|
2
|
|
744
901
|
];
|
|
745
|
-
this.
|
|
902
|
+
this.processingMarkets.add(marketId);
|
|
746
903
|
try {
|
|
747
904
|
_loop = function() {
|
|
748
|
-
var frame =
|
|
905
|
+
var frame = queue.shift();
|
|
749
906
|
var state = _this1.tokenStates.get(String(frame.tokenId));
|
|
750
907
|
if (!state) return "continue";
|
|
751
908
|
var startSeq = parseInt(frame.startSeq, 10);
|
|
752
909
|
var endSeq = parseInt(frame.endSeq, 10);
|
|
753
|
-
//
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
//
|
|
757
|
-
if (state.seq > 0 && startSeq > state.seq + 1) {
|
|
758
|
-
console.warn("[
|
|
910
|
+
// endSeq=0 is a sentinel meaning "no sequence tracking" — always apply.
|
|
911
|
+
// Non-zero endSeq <= state.seq means this frame was already applied.
|
|
912
|
+
if (endSeq !== 0 && endSeq <= state.seq) return "continue";
|
|
913
|
+
// Gap detection: warn and trigger resync for that specific market.
|
|
914
|
+
if (state.seq > 0 && startSeq > 0 && startSeq > state.seq + 1) {
|
|
915
|
+
console.warn("[MarketDepthSyncClient] gap detected tokenId=".concat(frame.tokenId, " ") + "expected=".concat(state.seq + 1, " got=").concat(startSeq, " — resyncing market ").concat(marketId));
|
|
916
|
+
// Re-subscribe to get a fresh snapshot (clears state, re-sends subscribe)
|
|
917
|
+
var tokenIds = _this1.activeSubscriptions.get(marketId);
|
|
918
|
+
if (tokenIds && _this1.ws && _this1.ws.readyState === WebSocket.OPEN) {
|
|
919
|
+
_this1._sendSubscribeMarket(marketId, tokenIds);
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
v: void void 0
|
|
923
|
+
};
|
|
759
924
|
}
|
|
925
|
+
// Stop processing stale queue; new snapshot will restart it
|
|
760
926
|
var emittedLevels = [];
|
|
761
927
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
762
928
|
try {
|
|
763
929
|
for(var _iterator = frame.levels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
764
930
|
var lv = _step.value;
|
|
765
|
-
// Per-level dedup against state.seq guards against re-applying
|
|
766
|
-
// levels from a partially-applied previous frame.
|
|
767
931
|
var lvSeq = parseInt(lv.seq, 10);
|
|
768
|
-
|
|
932
|
+
// lvSeq=0 means no sequence → always apply (same sentinel logic as endSeq)
|
|
933
|
+
if (lvSeq !== 0 && lvSeq <= state.seq) continue;
|
|
769
934
|
_this1.applyLevel(lv, state);
|
|
770
935
|
emittedLevels.push({
|
|
771
936
|
side: lv.side,
|
|
@@ -787,32 +952,44 @@ function scheduleDeferred(callback) {
|
|
|
787
952
|
}
|
|
788
953
|
}
|
|
789
954
|
}
|
|
790
|
-
|
|
955
|
+
// Advance seq only when endSeq is meaningful
|
|
956
|
+
if (endSeq > state.seq) state.seq = endSeq;
|
|
791
957
|
if (emittedLevels.length === 0) return "continue";
|
|
958
|
+
var update = {
|
|
959
|
+
tokenId: frame.tokenId,
|
|
960
|
+
levels: emittedLevels,
|
|
961
|
+
bestBid: state.bestBid,
|
|
962
|
+
bestAsk: state.bestAsk,
|
|
963
|
+
lastPrice: state.lastPrice,
|
|
964
|
+
seq: state.seq,
|
|
965
|
+
startSeq: startSeq,
|
|
966
|
+
endSeq: endSeq
|
|
967
|
+
};
|
|
792
968
|
_this1.deltaListeners.forEach(function(listener) {
|
|
793
969
|
try {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
bestBid: state.bestBid,
|
|
798
|
-
bestAsk: state.bestAsk,
|
|
799
|
-
lastPrice: state.lastPrice,
|
|
800
|
-
seq: endSeq,
|
|
801
|
-
startSeq: startSeq,
|
|
802
|
-
endSeq: endSeq
|
|
803
|
-
};
|
|
804
|
-
listener(_this.config.marketId, update);
|
|
805
|
-
} catch (error) {
|
|
806
|
-
console.error("Error in delta listener:", error);
|
|
970
|
+
listener(marketId, update);
|
|
971
|
+
} catch (e) {
|
|
972
|
+
console.error(e);
|
|
807
973
|
}
|
|
808
974
|
});
|
|
809
975
|
};
|
|
810
|
-
|
|
976
|
+
queue = this.frameQueues.get(marketId);
|
|
977
|
+
if (!queue) return [
|
|
978
|
+
2
|
|
979
|
+
];
|
|
980
|
+
while(queue.length > 0){
|
|
981
|
+
_ret = (_this1 = this, _loop());
|
|
982
|
+
if (_type_of(_ret) === "object") return [
|
|
983
|
+
2,
|
|
984
|
+
_ret.v
|
|
985
|
+
];
|
|
986
|
+
}
|
|
811
987
|
} finally{
|
|
812
|
-
this.
|
|
813
|
-
|
|
988
|
+
this.processingMarkets.delete(marketId);
|
|
989
|
+
queue1 = this.frameQueues.get(marketId);
|
|
990
|
+
if (queue1 && queue1.length > 0) {
|
|
814
991
|
scheduleDeferred(function() {
|
|
815
|
-
void _this.processFrameQueue();
|
|
992
|
+
void _this.processFrameQueue(marketId);
|
|
816
993
|
});
|
|
817
994
|
}
|
|
818
995
|
}
|
|
@@ -834,7 +1011,6 @@ function scheduleDeferred(callback) {
|
|
|
834
1011
|
} else {
|
|
835
1012
|
map.set(key, level.depth);
|
|
836
1013
|
}
|
|
837
|
-
// Recalculate best prices from current state
|
|
838
1014
|
if (level.side === "bid") {
|
|
839
1015
|
var _Math;
|
|
840
1016
|
var bidPrices = Array.from(state.bids.keys()).map(function(p) {
|
|
@@ -858,186 +1034,190 @@ function scheduleDeferred(callback) {
|
|
|
858
1034
|
this.statusListeners.forEach(function(listener) {
|
|
859
1035
|
try {
|
|
860
1036
|
listener(status);
|
|
861
|
-
} catch (
|
|
862
|
-
console.error(
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
console.error(e);
|
|
863
1039
|
}
|
|
864
1040
|
});
|
|
865
1041
|
}
|
|
866
1042
|
},
|
|
867
1043
|
{
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
value: function getTokenIds() {
|
|
883
|
-
return Array.from(this.tokenStates.keys());
|
|
884
|
-
}
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
/** Returns raw internal token state for advanced integrations. */ key: "getTokenState",
|
|
888
|
-
value: function getTokenState(tokenId) {
|
|
889
|
-
return this.tokenStates.get(tokenId);
|
|
890
|
-
}
|
|
891
|
-
},
|
|
892
|
-
{
|
|
893
|
-
/** Returns the current bid ladder for a token, sorted from highest to lowest price. */ key: "getBids",
|
|
894
|
-
value: function getBids(tokenId) {
|
|
895
|
-
var state = this.tokenStates.get(tokenId);
|
|
896
|
-
if (!state) return [];
|
|
897
|
-
return Array.from(state.bids.entries()).map(function(param) {
|
|
898
|
-
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
899
|
-
return {
|
|
900
|
-
price: price,
|
|
901
|
-
depth: depth
|
|
902
|
-
};
|
|
903
|
-
}).sort(function(a, b) {
|
|
904
|
-
return parseFloat(b.price) - parseFloat(a.price);
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
// Highest first
|
|
910
|
-
/** Returns the current ask ladder for a token, sorted from lowest to highest price. */ key: "getAsks",
|
|
911
|
-
value: function getAsks(tokenId) {
|
|
912
|
-
var state = this.tokenStates.get(tokenId);
|
|
913
|
-
if (!state) return [];
|
|
914
|
-
return Array.from(state.asks.entries()).map(function(param) {
|
|
915
|
-
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
916
|
-
return {
|
|
917
|
-
price: price,
|
|
918
|
-
depth: depth
|
|
919
|
-
};
|
|
920
|
-
}).sort(function(a, b) {
|
|
921
|
-
return parseFloat(a.price) - parseFloat(b.price);
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
},
|
|
925
|
-
{
|
|
926
|
-
// Lowest first
|
|
927
|
-
/** Returns the best bid price for a token, if known. */ key: "getBestBid",
|
|
928
|
-
value: function getBestBid(tokenId) {
|
|
929
|
-
var _ref;
|
|
930
|
-
var _this_tokenStates_get;
|
|
931
|
-
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestBid) !== null && _ref !== void 0 ? _ref : null;
|
|
1044
|
+
key: "setupVisibilityChangeHandler",
|
|
1045
|
+
value: // ---------------------------------------------------------------------------
|
|
1046
|
+
// Private — reconnection & heartbeat
|
|
1047
|
+
// ---------------------------------------------------------------------------
|
|
1048
|
+
function setupVisibilityChangeHandler() {
|
|
1049
|
+
var _this = this;
|
|
1050
|
+
if (typeof document === "undefined") return;
|
|
1051
|
+
this.removeVisibilityChangeHandler();
|
|
1052
|
+
this.visibilityChangeHandler = function() {
|
|
1053
|
+
if (document.visibilityState === "visible" && _this.shouldBeConnected) {
|
|
1054
|
+
_this.checkConnectionHealth();
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
932
1058
|
}
|
|
933
1059
|
},
|
|
934
1060
|
{
|
|
935
|
-
|
|
936
|
-
value: function
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1061
|
+
key: "removeVisibilityChangeHandler",
|
|
1062
|
+
value: function removeVisibilityChangeHandler() {
|
|
1063
|
+
if (typeof document === "undefined") return;
|
|
1064
|
+
if (this.visibilityChangeHandler) {
|
|
1065
|
+
document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
1066
|
+
this.visibilityChangeHandler = null;
|
|
1067
|
+
}
|
|
940
1068
|
}
|
|
941
1069
|
},
|
|
942
1070
|
{
|
|
943
|
-
|
|
944
|
-
value: function
|
|
945
|
-
var
|
|
946
|
-
|
|
947
|
-
|
|
1071
|
+
key: "checkConnectionHealth",
|
|
1072
|
+
value: function checkConnectionHealth() {
|
|
1073
|
+
var _this = this;
|
|
1074
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
1075
|
+
this.handleConnectionLost();
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
try {
|
|
1079
|
+
this.ws.send(JSON.stringify({
|
|
1080
|
+
type: "ping"
|
|
1081
|
+
}));
|
|
1082
|
+
this.clearHeartbeatTimeout();
|
|
1083
|
+
this.heartbeatTimeoutId = setTimeout(function() {
|
|
1084
|
+
_this.handleConnectionLost();
|
|
1085
|
+
}, this.config.heartbeatTimeoutMs);
|
|
1086
|
+
} catch (unused) {
|
|
1087
|
+
this.handleConnectionLost();
|
|
1088
|
+
}
|
|
948
1089
|
}
|
|
949
1090
|
},
|
|
950
1091
|
{
|
|
951
|
-
|
|
952
|
-
value: function
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1092
|
+
key: "handleConnectionLost",
|
|
1093
|
+
value: function handleConnectionLost() {
|
|
1094
|
+
this.stopHeartbeat();
|
|
1095
|
+
if (this.ws) {
|
|
1096
|
+
this.ws.onclose = null;
|
|
1097
|
+
this.ws.close();
|
|
1098
|
+
this.ws = null;
|
|
1099
|
+
}
|
|
1100
|
+
this.setStatus("disconnected");
|
|
1101
|
+
if (this.shouldBeConnected) {
|
|
1102
|
+
this.scheduleReconnect();
|
|
1103
|
+
}
|
|
956
1104
|
}
|
|
957
1105
|
},
|
|
958
1106
|
{
|
|
959
|
-
|
|
960
|
-
value: function
|
|
961
|
-
var
|
|
962
|
-
|
|
963
|
-
|
|
1107
|
+
key: "startHeartbeat",
|
|
1108
|
+
value: function startHeartbeat() {
|
|
1109
|
+
var _this = this;
|
|
1110
|
+
this.stopHeartbeat();
|
|
1111
|
+
this.heartbeatIntervalId = setInterval(function() {
|
|
1112
|
+
if (!_this.ws || _this.ws.readyState !== WebSocket.OPEN) return;
|
|
1113
|
+
try {
|
|
1114
|
+
_this.ws.send(JSON.stringify({
|
|
1115
|
+
type: "ping"
|
|
1116
|
+
}));
|
|
1117
|
+
_this.clearHeartbeatTimeout();
|
|
1118
|
+
_this.heartbeatTimeoutId = setTimeout(function() {
|
|
1119
|
+
_this.handleConnectionLost();
|
|
1120
|
+
}, _this.config.heartbeatTimeoutMs);
|
|
1121
|
+
} catch (unused) {
|
|
1122
|
+
_this.handleConnectionLost();
|
|
1123
|
+
}
|
|
1124
|
+
}, this.config.heartbeatIntervalMs);
|
|
964
1125
|
}
|
|
965
1126
|
},
|
|
966
1127
|
{
|
|
967
|
-
|
|
968
|
-
value: function
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1128
|
+
key: "stopHeartbeat",
|
|
1129
|
+
value: function stopHeartbeat() {
|
|
1130
|
+
if (this.heartbeatIntervalId) {
|
|
1131
|
+
clearInterval(this.heartbeatIntervalId);
|
|
1132
|
+
this.heartbeatIntervalId = null;
|
|
1133
|
+
}
|
|
1134
|
+
this.clearHeartbeatTimeout();
|
|
973
1135
|
}
|
|
974
1136
|
},
|
|
975
1137
|
{
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
return _this.statusListeners.delete(callback);
|
|
983
|
-
};
|
|
1138
|
+
key: "clearHeartbeatTimeout",
|
|
1139
|
+
value: function clearHeartbeatTimeout() {
|
|
1140
|
+
if (this.heartbeatTimeoutId) {
|
|
1141
|
+
clearTimeout(this.heartbeatTimeoutId);
|
|
1142
|
+
this.heartbeatTimeoutId = null;
|
|
1143
|
+
}
|
|
984
1144
|
}
|
|
985
1145
|
},
|
|
986
1146
|
{
|
|
987
|
-
|
|
988
|
-
value: function
|
|
1147
|
+
key: "scheduleReconnect",
|
|
1148
|
+
value: function scheduleReconnect() {
|
|
989
1149
|
var _this = this;
|
|
990
|
-
this.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1150
|
+
if (!this.shouldBeConnected) return;
|
|
1151
|
+
var maxAttempts = this.config.maxReconnectAttempts;
|
|
1152
|
+
if (this.reconnectAttempts >= maxAttempts) {
|
|
1153
|
+
console.error("[MarketDepthSyncClient] max reconnect attempts (".concat(maxAttempts, ") reached"));
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
var baseDelay = this.config.initialReconnectDelayMs;
|
|
1157
|
+
var maxDelay = this.config.maxReconnectDelayMs;
|
|
1158
|
+
var delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts) + Math.random() * 1000, maxDelay);
|
|
1159
|
+
this.reconnectAttempts++;
|
|
1160
|
+
this.setStatus("recovering");
|
|
1161
|
+
this.clearReconnectTimeout();
|
|
1162
|
+
this.reconnectTimeoutId = setTimeout(function() {
|
|
1163
|
+
void _this.performReconnect();
|
|
1164
|
+
}, delay);
|
|
994
1165
|
}
|
|
995
1166
|
},
|
|
996
1167
|
{
|
|
997
|
-
|
|
998
|
-
value: function
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
};
|
|
1168
|
+
key: "clearReconnectTimeout",
|
|
1169
|
+
value: function clearReconnectTimeout() {
|
|
1170
|
+
if (this.reconnectTimeoutId) {
|
|
1171
|
+
clearTimeout(this.reconnectTimeoutId);
|
|
1172
|
+
this.reconnectTimeoutId = null;
|
|
1173
|
+
}
|
|
1004
1174
|
}
|
|
1005
1175
|
},
|
|
1006
1176
|
{
|
|
1007
|
-
key: "
|
|
1008
|
-
value:
|
|
1177
|
+
key: "performReconnect",
|
|
1178
|
+
value: function performReconnect() {
|
|
1009
1179
|
return _async_to_generator(function() {
|
|
1180
|
+
var error;
|
|
1010
1181
|
return _ts_generator(this, function(_state) {
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1182
|
+
switch(_state.label){
|
|
1183
|
+
case 0:
|
|
1184
|
+
if (!this.shouldBeConnected) return [
|
|
1185
|
+
2
|
|
1186
|
+
];
|
|
1187
|
+
_state.label = 1;
|
|
1188
|
+
case 1:
|
|
1189
|
+
_state.trys.push([
|
|
1190
|
+
1,
|
|
1191
|
+
3,
|
|
1192
|
+
,
|
|
1193
|
+
4
|
|
1194
|
+
]);
|
|
1195
|
+
// Clear snapshot state — will be re-populated from fresh snapshots
|
|
1196
|
+
this.tokenStates.clear();
|
|
1197
|
+
this.frameQueues.clear();
|
|
1198
|
+
this.processingMarkets.clear();
|
|
1199
|
+
return [
|
|
1200
|
+
4,
|
|
1201
|
+
this.connect()
|
|
1202
|
+
];
|
|
1203
|
+
case 2:
|
|
1204
|
+
_state.sent();
|
|
1205
|
+
return [
|
|
1206
|
+
3,
|
|
1207
|
+
4
|
|
1208
|
+
];
|
|
1209
|
+
case 3:
|
|
1210
|
+
error = _state.sent();
|
|
1211
|
+
console.error("[MarketDepthSyncClient] reconnection failed:", error);
|
|
1212
|
+
return [
|
|
1213
|
+
3,
|
|
1214
|
+
4
|
|
1215
|
+
];
|
|
1216
|
+
case 4:
|
|
1217
|
+
return [
|
|
1218
|
+
2
|
|
1219
|
+
];
|
|
1034
1220
|
}
|
|
1035
|
-
this.frameQueue = [];
|
|
1036
|
-
this.tokenStates.clear();
|
|
1037
|
-
this.setStatus("disconnected");
|
|
1038
|
-
return [
|
|
1039
|
-
2
|
|
1040
|
-
];
|
|
1041
1221
|
});
|
|
1042
1222
|
}).call(this);
|
|
1043
1223
|
}
|