mdas-jsview-sdk 1.0.17-uat.0 → 1.0.21-uat.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.
@@ -1277,6 +1277,24 @@ const ONBBOLevel2Template = `
1277
1277
  <span class="l2-market-name"></span>
1278
1278
  </div>
1279
1279
  <span class="symbol editable-symbol">--</span>
1280
+ <div class="level1-info">
1281
+ <div class="l1-item">
1282
+ <span class="l1-label">Last:</span>
1283
+ <span class="l1-value l1-last-px">--</span>
1284
+ </div>
1285
+ <div class="l1-item">
1286
+ <span class="l1-label">Low:</span>
1287
+ <span class="l1-value l1-low-px">--</span>
1288
+ </div>
1289
+ <div class="l1-item">
1290
+ <span class="l1-label">High:</span>
1291
+ <span class="l1-value l1-high-px">--</span>
1292
+ </div>
1293
+ <div class="l1-item">
1294
+ <span class="l1-label">Volume:</span>
1295
+ <span class="l1-value l1-volume">--</span>
1296
+ </div>
1297
+ </div>
1280
1298
  </div>
1281
1299
  </div>
1282
1300
 
@@ -1951,6 +1969,38 @@ const ONBBOLevel2Styles = `
1951
1969
  color: #111827;
1952
1970
  }
1953
1971
 
1972
+ /* ========================================
1973
+ LEVEL 1 INFO (INSIDE HEADER)
1974
+ ======================================== */
1975
+
1976
+ .onbbo-level2-widget .level1-info {
1977
+ display: grid;
1978
+ grid-template-columns: repeat(4, 1fr);
1979
+ gap: 12px;
1980
+ margin-top: 10px;
1981
+ }
1982
+
1983
+ .onbbo-level2-widget .l1-item {
1984
+ display: flex;
1985
+ flex-direction: column;
1986
+ gap: 3px;
1987
+ }
1988
+
1989
+ .onbbo-level2-widget .l1-label {
1990
+ font-size: 11px;
1991
+ font-weight: 600;
1992
+ color: #6b7280;
1993
+ text-transform: uppercase;
1994
+ letter-spacing: 0.5px;
1995
+ }
1996
+
1997
+ .onbbo-level2-widget .l1-value {
1998
+ font-size: 15px;
1999
+ font-weight: 700;
2000
+ color: #111827;
2001
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2002
+ }
2003
+
1954
2004
  /* ========================================
1955
2005
  ORDER BOOK CONTAINER
1956
2006
  ======================================== */
@@ -2335,6 +2385,11 @@ const ONBBOLevel2Styles = `
2335
2385
  .onbbo-level2-widget .orderbook-panel:last-child {
2336
2386
  border-bottom: none;
2337
2387
  }
2388
+
2389
+ .onbbo-level2-widget .level1-info {
2390
+ grid-template-columns: repeat(2, 1fr);
2391
+ gap: 6px;
2392
+ }
2338
2393
  }
2339
2394
 
2340
2395
  @media (max-width: 480px) {
@@ -2353,6 +2408,19 @@ const ONBBOLevel2Styles = `
2353
2408
  text-align: left;
2354
2409
  }
2355
2410
 
2411
+ .onbbo-level2-widget .level1-info {
2412
+ grid-template-columns: repeat(2, 1fr);
2413
+ gap: 4px;
2414
+ }
2415
+
2416
+ .onbbo-level2-widget .l1-label {
2417
+ font-size: 9px;
2418
+ }
2419
+
2420
+ .onbbo-level2-widget .l1-value {
2421
+ font-size: 11px;
2422
+ }
2423
+
2356
2424
  .onbbo-level2-widget .panel-header {
2357
2425
  font-size: 10px;
2358
2426
  padding: 6px 2px;
@@ -4501,13 +4569,14 @@ class MarketDataWidget extends BaseWidget {
4501
4569
  } */
4502
4570
 
4503
4571
  handleData(message) {
4572
+ console.log('DEBUG', message);
4504
4573
  if (this.loadingTimeout) {
4505
4574
  clearTimeout(this.loadingTimeout);
4506
4575
  this.loadingTimeout = null;
4507
4576
  }
4508
4577
 
4509
4578
  // Handle error messages from server (plain text converted to structured format)
4510
- if (message.type === 'error' && message.noData) {
4579
+ if (message.type === 'error') {
4511
4580
  if (this.debug) {
4512
4581
  console.log('[MarketDataWidget] Received no data message:', message.message);
4513
4582
  }
@@ -5277,13 +5346,16 @@ class NightSessionWidget extends BaseWidget {
5277
5346
  // to avoid duplicate timeouts
5278
5347
  }
5279
5348
  handleData(message) {
5349
+ console.log('DEBUG NIGHT', message);
5350
+ //message = message.data || message.Data
5351
+
5280
5352
  if (this.loadingTimeout) {
5281
5353
  clearTimeout(this.loadingTimeout);
5282
5354
  this.loadingTimeout = null;
5283
5355
  }
5284
5356
 
5285
5357
  // Handle error messages from server (plain text converted to structured format)
5286
- if (message.type === 'error' && message.noData) {
5358
+ if (message.type === 'error' || message.error == true) {
5287
5359
  if (this.debug) {
5288
5360
  console.log('[NightSessionWidget] Received no data message:', message.message);
5289
5361
  }
@@ -5339,9 +5411,10 @@ class NightSessionWidget extends BaseWidget {
5339
5411
  }
5340
5412
 
5341
5413
  // Filter for night session data
5342
- if (Array.isArray(message)) {
5414
+
5415
+ if (Array.isArray(message.data)) {
5343
5416
  // First, try to find data matching our symbol regardless of MarketName
5344
- const symbolData = message.find(item => item.Symbol === this.symbol);
5417
+ const symbolData = message.data.find(item => item.Symbol === this.symbol);
5345
5418
  if (symbolData) {
5346
5419
  if (this.debug) {
5347
5420
  console.log('[NightSessionWidget] Found data for symbol:', symbolData);
@@ -5381,34 +5454,35 @@ class NightSessionWidget extends BaseWidget {
5381
5454
  }
5382
5455
  }
5383
5456
  // Handle wrapped format
5384
- else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1' || message.type === 'queryonbbol1') {
5385
- if (message['0']?.Symbol === this.symbol) {
5386
- // For onbbo source, skip MarketName check
5387
- const isOnbbo = message.type === 'queryonbbol1';
5388
- const shouldShowNoData = message['0'].NotFound === true || !isOnbbo && (!message['0'].MarketName || message['0'].MarketName !== 'BLUE');
5389
- if (shouldShowNoData) {
5390
- // Only show no data state if we don't have cached data
5391
- if (!this.data) {
5392
- this.showNoDataState(message['0']);
5393
- } else {
5394
- this.hideLoading();
5395
- if (this.debug) {
5396
- console.log('[NightSessionWidget] No new data, keeping cached data visible');
5457
+ /* else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1' || message.type === 'queryonbbol1') {
5458
+ if (message.data['0']?.Symbol === this.symbol) {
5459
+ // For onbbo source, skip MarketName check
5460
+ const isOnbbo = message.type === 'queryonbbol1';
5461
+ const shouldShowNoData = message['0'].NotFound === true ||
5462
+ (!isOnbbo && (!message['0'].MarketName || message['0'].MarketName !== 'BLUE'));
5463
+ if (shouldShowNoData) {
5464
+ // Only show no data state if we don't have cached data
5465
+ if (!this.data) {
5466
+ this.showNoDataState(message['0']);
5467
+ } else {
5468
+ this.hideLoading();
5469
+ if (this.debug) {
5470
+ console.log('[NightSessionWidget] No new data, keeping cached data visible');
5471
+ }
5472
+ }
5473
+ } else {
5474
+ const model = new NightSessionModel(message['0']);
5475
+ this.data = model; // Store for caching
5476
+ this.updateWidget(model);
5397
5477
  }
5398
- }
5399
5478
  } else {
5400
- const model = new NightSessionModel(message['0']);
5401
- this.data = model; // Store for caching
5402
- this.updateWidget(model);
5403
- }
5404
- } else {
5405
- // No matching symbol - keep cached data if available
5406
- if (this.debug) {
5407
- console.log('[NightSessionWidget] No matching symbol in response, keeping cached data');
5479
+ // No matching symbol - keep cached data if available
5480
+ if (this.debug) {
5481
+ console.log('[NightSessionWidget] No matching symbol in response, keeping cached data');
5482
+ }
5483
+ this.hideLoading();
5408
5484
  }
5409
- this.hideLoading();
5410
- }
5411
- }
5485
+ } */
5412
5486
  if (message._cached) ;
5413
5487
  }
5414
5488
  updateWidget(data) {
@@ -39085,7 +39159,7 @@ class IntradayChartWidget extends BaseWidget {
39085
39159
  let priceData = null;
39086
39160
 
39087
39161
  // Handle array format (standard night session format)
39088
- if (Array.isArray(message)) {
39162
+ if (Array.isArray(message.data)) {
39089
39163
  console.log('[IntradayChartWidget] Processing array format, length:', message.length);
39090
39164
  const symbolData = message.find(item => item.Symbol === this.symbol);
39091
39165
  console.log('[IntradayChartWidget] Found symbol data:', symbolData);
@@ -40056,8 +40130,10 @@ class ONBBOLevel2Widget extends BaseWidget {
40056
40130
  this.styled = options.styled !== undefined ? options.styled : true;
40057
40131
  this.maxLevels = options.maxLevels || 10; // Number of levels to display
40058
40132
  this.data = null;
40133
+ this.level1Data = null; // Store Level 1 data separately
40059
40134
  this.isDestroyed = false;
40060
- this.unsubscribe = null;
40135
+ this.unsubscribeL2 = null;
40136
+ this.unsubscribeL1 = null;
40061
40137
  this.symbolEditor = null;
40062
40138
  this.loadingTimeout = null;
40063
40139
 
@@ -40158,13 +40234,22 @@ class ONBBOLevel2Widget extends BaseWidget {
40158
40234
  this.showError(`No data received for ${upperSymbol}. Please try again.`);
40159
40235
  }, 10000);
40160
40236
 
40161
- // Unsubscribe from old symbol
40162
- if (this.unsubscribe) {
40237
+ // Unsubscribe from old symbol's L2 and L1 data
40238
+ if (this.unsubscribeL2) {
40163
40239
  if (this.debug) {
40164
- console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol}`);
40240
+ console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol} L2`);
40165
40241
  }
40166
- this.unsubscribe();
40167
- this.unsubscribe = null;
40242
+ this.wsManager.sendUnsubscribe('queryonbbol2', this.symbol);
40243
+ this.unsubscribeL2();
40244
+ this.unsubscribeL2 = null;
40245
+ }
40246
+ if (this.unsubscribeL1) {
40247
+ if (this.debug) {
40248
+ console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol} L1`);
40249
+ }
40250
+ this.wsManager.sendUnsubscribe('queryonbbol1', this.symbol);
40251
+ this.unsubscribeL1();
40252
+ this.unsubscribeL1 = null;
40168
40253
  }
40169
40254
 
40170
40255
  // Update internal symbol
@@ -40234,10 +40319,51 @@ class ONBBOLevel2Widget extends BaseWidget {
40234
40319
  }
40235
40320
  }
40236
40321
  subscribeToData() {
40237
- // Subscribe to ONBBO Level 2 data
40238
- this.unsubscribe = this.wsManager.subscribe(this.widgetId, ['queryonbbol2'],
40239
- // ONBBO Level 2 subscription type
40240
- this.handleMessage.bind(this), this.symbol);
40322
+ // Subscribe to ONBBO Level 2 data (order book)
40323
+ // Use separate widget IDs to avoid "already active" duplicate detection
40324
+ this.unsubscribeL2 = this.wsManager.subscribe(`${this.widgetId}-l2`, ['queryonbbol2'], messageWrapper => {
40325
+ const {
40326
+ event,
40327
+ data
40328
+ } = messageWrapper;
40329
+
40330
+ // Handle connection events
40331
+ if (event === 'connection') {
40332
+ this.handleConnectionStatus(data);
40333
+ return;
40334
+ }
40335
+
40336
+ // For data events, add type context
40337
+ if (event === 'data') {
40338
+ data._dataType = 'level2';
40339
+ this.handleMessage({
40340
+ event,
40341
+ data
40342
+ });
40343
+ }
40344
+ }, this.symbol);
40345
+
40346
+ // Subscribe to ONBBO Level 1 data (statistics: LastPx, LowPx, HighPx, Volume)
40347
+ this.unsubscribeL1 = this.wsManager.subscribe(`${this.widgetId}-l1`, ['queryonbbol1'], messageWrapper => {
40348
+ const {
40349
+ event,
40350
+ data
40351
+ } = messageWrapper;
40352
+
40353
+ // Connection already handled by L2 subscription
40354
+ if (event === 'connection') {
40355
+ return;
40356
+ }
40357
+
40358
+ // For data events, add type context
40359
+ if (event === 'data') {
40360
+ data._dataType = 'level1';
40361
+ this.handleMessage({
40362
+ event,
40363
+ data
40364
+ });
40365
+ }
40366
+ }, this.symbol);
40241
40367
  }
40242
40368
  handleData(message) {
40243
40369
  if (this.loadingTimeout) {
@@ -40245,8 +40371,14 @@ class ONBBOLevel2Widget extends BaseWidget {
40245
40371
  this.loadingTimeout = null;
40246
40372
  }
40247
40373
 
40374
+ // Extract data type from metadata (added by subscription callback)
40375
+ const dataType = message._dataType;
40376
+ if (this.debug) {
40377
+ console.log(`[ONBBOLevel2Widget] handleData called with type: ${dataType}`, message);
40378
+ }
40379
+
40248
40380
  // Handle error messages
40249
- if (message.type === 'error') {
40381
+ if (message.type === 'error' || message.error == true) {
40250
40382
  const errorMsg = message.message || 'Server error';
40251
40383
  if (this.debug) {
40252
40384
  console.log('[ONBBOLevel2Widget] Error:', errorMsg);
@@ -40255,16 +40387,54 @@ class ONBBOLevel2Widget extends BaseWidget {
40255
40387
  return;
40256
40388
  }
40257
40389
 
40390
+ // Handle Level 1 data (statistics: LastPx, LowPx, HighPx, Volume)
40391
+ if (message.type === 'queryonbbol1') {
40392
+ if (this.debug) {
40393
+ console.log('[ONBBOLevel2Widget] Processing Level 1 data:', message);
40394
+ }
40395
+
40396
+ // Handle array format (new format with Data wrapper)
40397
+ if (message.data && Array.isArray(message.data)) {
40398
+ const l1Data = message.data.find(d => d.Symbol === this.symbol);
40399
+ if (l1Data) {
40400
+ this.level1Data = {
40401
+ lastPx: l1Data.LastPx || 0,
40402
+ lowPx: l1Data.LowPx || 0,
40403
+ highPx: l1Data.HighPx || 0,
40404
+ volume: l1Data.Volume || 0
40405
+ };
40406
+ this.updateLevel1Display();
40407
+ }
40408
+ }
40409
+ // Handle direct array format (standard format)
40410
+ else if (Array.isArray(message)) {
40411
+ const l1Data = message.find(d => d.Symbol === this.symbol);
40412
+ if (l1Data) {
40413
+ this.level1Data = {
40414
+ lastPx: l1Data.LastPx || 0,
40415
+ lowPx: l1Data.LowPx || 0,
40416
+ highPx: l1Data.HighPx || 0,
40417
+ volume: l1Data.Volume || 0
40418
+ };
40419
+ this.updateLevel1Display();
40420
+ }
40421
+ }
40422
+
40423
+ // Clean up metadata
40424
+ delete message._dataType;
40425
+ return;
40426
+ }
40427
+
40258
40428
  // Handle Level 2 data - Array of MMID quotes
40259
- if (Array.isArray(message)) {
40429
+ if (Array.isArray(message.data)) {
40260
40430
  // Check if it's an array of MMID objects (ONBBO format)
40261
- if (message.length > 0 && message[0].MMID) {
40431
+ if (message.data.length > 0 && message.data[0].MMID) {
40262
40432
  if (this.debug) {
40263
40433
  console.log('[ONBBOLevel2Widget] Received MMID array:', message);
40264
40434
  }
40265
40435
 
40266
40436
  // Create model from MMID array
40267
- const model = new ONBBOLevel2Model(message);
40437
+ const model = new ONBBOLevel2Model(message.data);
40268
40438
  model.symbol = this.symbol; // Set symbol from widget
40269
40439
  this.data = model;
40270
40440
  this.updateWidget(model);
@@ -40272,7 +40442,7 @@ class ONBBOLevel2Widget extends BaseWidget {
40272
40442
  }
40273
40443
 
40274
40444
  // Try to find symbol in array (alternative format)
40275
- const symbolData = message.find(item => item.Symbol === this.symbol);
40445
+ const symbolData = message.data.find(item => item.Symbol === this.symbol);
40276
40446
  if (symbolData) {
40277
40447
  if (symbolData.NotFound === true) {
40278
40448
  this.showError(`No Level 2 data available for ${this.symbol}`);
@@ -40286,32 +40456,31 @@ class ONBBOLevel2Widget extends BaseWidget {
40286
40456
  this.showError(`No Level 2 data available for ${this.symbol}`);
40287
40457
  }
40288
40458
  // Handle wrapped format
40289
- else if (message.type === 'queryonbbol2') {
40290
- // Check if wrapped data contains MMID array
40291
- if (message['0'] && Array.isArray(message['0'])) {
40292
- if (this.debug) {
40293
- console.log('[ONBBOLevel2Widget] Received wrapped MMID array:', message['0']);
40459
+ /* else if (message.type === 'queryonbbol2') {
40460
+ // Check if wrapped data contains MMID array
40461
+ if (message['0'] && Array.isArray(message['0'])) {
40462
+ if (this.debug) {
40463
+ console.log('[ONBBOLevel2Widget] Received wrapped MMID array:', message['0']);
40464
+ }
40465
+ const model = new ONBBOLevel2Model(message['0']);
40466
+ model.symbol = this.symbol;
40467
+ this.data = model;
40468
+ this.updateWidget(model);
40469
+ return;
40294
40470
  }
40295
- const model = new ONBBOLevel2Model(message['0']);
40296
- model.symbol = this.symbol;
40297
- this.data = model;
40298
- this.updateWidget(model);
40299
- return;
40300
- }
40301
-
40302
- // Check for symbol match in wrapped format
40303
- if (message['0']?.Symbol === this.symbol) {
40304
- if (message['0'].NotFound === true) {
40305
- this.showError(`No Level 2 data available for ${this.symbol}`);
40471
+ // Check for symbol match in wrapped format
40472
+ if (message['0']?.Symbol === this.symbol) {
40473
+ if (message['0'].NotFound === true) {
40474
+ this.showError(`No Level 2 data available for ${this.symbol}`);
40475
+ } else {
40476
+ const model = new ONBBOLevel2Model(message['0']);
40477
+ this.data = model;
40478
+ this.updateWidget(model);
40479
+ }
40306
40480
  } else {
40307
- const model = new ONBBOLevel2Model(message['0']);
40308
- this.data = model;
40309
- this.updateWidget(model);
40481
+ this.showError(`No Level 2 data available for ${this.symbol}`);
40310
40482
  }
40311
- } else {
40312
- this.showError(`No Level 2 data available for ${this.symbol}`);
40313
- }
40314
- }
40483
+ } */
40315
40484
  }
40316
40485
  updateWidget(data) {
40317
40486
  if (this.isDestroyed) return;
@@ -40409,6 +40578,43 @@ class ONBBOLevel2Widget extends BaseWidget {
40409
40578
  askBody.appendChild(row);
40410
40579
  });
40411
40580
  }
40581
+ updateLevel1Display() {
40582
+ if (!this.level1Data) return;
40583
+ const formatPrice = price => {
40584
+ return price ? price.toFixed(2) : '--';
40585
+ };
40586
+ const formatVolume = volume => {
40587
+ if (!volume) return '--';
40588
+ return volume.toLocaleString();
40589
+ };
40590
+
40591
+ // Update Last Price
40592
+ const lastPxElement = this.container.querySelector('.l1-last-px');
40593
+ if (lastPxElement) {
40594
+ lastPxElement.textContent = formatPrice(this.level1Data.lastPx);
40595
+ }
40596
+
40597
+ // Update Low Price
40598
+ const lowPxElement = this.container.querySelector('.l1-low-px');
40599
+ if (lowPxElement) {
40600
+ lowPxElement.textContent = formatPrice(this.level1Data.lowPx);
40601
+ }
40602
+
40603
+ // Update High Price
40604
+ const highPxElement = this.container.querySelector('.l1-high-px');
40605
+ if (highPxElement) {
40606
+ highPxElement.textContent = formatPrice(this.level1Data.highPx);
40607
+ }
40608
+
40609
+ // Update Volume
40610
+ const volumeElement = this.container.querySelector('.l1-volume');
40611
+ if (volumeElement) {
40612
+ volumeElement.textContent = formatVolume(this.level1Data.volume);
40613
+ }
40614
+ if (this.debug) {
40615
+ console.log('[ONBBOLevel2Widget] Updated Level 1 display:', this.level1Data);
40616
+ }
40617
+ }
40412
40618
  showLoading() {
40413
40619
  const loadingOverlay = this.container.querySelector('.widget-loading-overlay');
40414
40620
  if (loadingOverlay) {
@@ -40455,9 +40661,17 @@ class ONBBOLevel2Widget extends BaseWidget {
40455
40661
  this.clearTimeout(this.loadingTimeout);
40456
40662
  this.loadingTimeout = null;
40457
40663
  }
40458
- if (this.unsubscribe) {
40459
- this.unsubscribe();
40460
- this.unsubscribe = null;
40664
+
40665
+ // Unsubscribe from L2 data
40666
+ if (this.unsubscribeL2) {
40667
+ this.unsubscribeL2();
40668
+ this.unsubscribeL2 = null;
40669
+ }
40670
+
40671
+ // Unsubscribe from L1 data
40672
+ if (this.unsubscribeL1) {
40673
+ this.unsubscribeL1();
40674
+ this.unsubscribeL1 = null;
40461
40675
  }
40462
40676
 
40463
40677
  // Destroy the symbol editor
@@ -41583,6 +41797,7 @@ class WebSocketManager {
41583
41797
  }
41584
41798
  }
41585
41799
  handleMessage(event) {
41800
+ //console.log('EVENT', event)
41586
41801
  try {
41587
41802
  // Update activity tracking - connection is alive!
41588
41803
  this.lastMessageReceived = Date.now();
@@ -41607,22 +41822,20 @@ class WebSocketManager {
41607
41822
  return;
41608
41823
  }
41609
41824
 
41610
- // Determine target type based on error content
41611
- const targetType = this._getTargetTypeFromErrorMessage(textMessage);
41825
+ // Determine type based on error content, default to 'error'
41826
+ const errorType = this._getTargetTypeFromErrorMessage(textMessage) || 'error';
41612
41827
  if (textMessage.toLowerCase().includes('no night session') || textMessage.toLowerCase().includes('no data')) {
41613
41828
  message = {
41614
- type: 'error',
41829
+ type: errorType,
41615
41830
  message: textMessage,
41616
- error: textMessage,
41617
- noData: true,
41618
- targetType: targetType // Will be 'querynightsession' for night session errors
41831
+ error: true,
41832
+ noData: true
41619
41833
  };
41620
41834
  } else {
41621
41835
  message = {
41622
- type: 'error',
41836
+ type: errorType,
41623
41837
  message: textMessage,
41624
- error: textMessage,
41625
- targetType: targetType // Could be null for general errors
41838
+ error: true
41626
41839
  };
41627
41840
  }
41628
41841
  }
@@ -41630,9 +41843,16 @@ class WebSocketManager {
41630
41843
  console.log('[WebSocketManager] Processed message:', message);
41631
41844
  }
41632
41845
 
41633
- // Handle new message format with type and data fields (case-insensitive)
41634
- const normalizedMessage = this._normalizeMessage(message);
41635
- this._routeMessage(normalizedMessage);
41846
+ // Extract data field for night session messages to avoid nested structure
41847
+ // Night session format: { type: 'queryonbbol1', error: false, data: [...] }
41848
+ // We want to send just the data array to widgets
41849
+ let dataToRoute = message;
41850
+ /* if (message.data !== undefined && message.type !== undefined) {
41851
+ // This is night session format - extract the data
41852
+ dataToRoute = message.data;
41853
+ } */
41854
+
41855
+ this._routeMessage(dataToRoute);
41636
41856
  } catch (error) {
41637
41857
  console.error('[WebSocketManager] Error handling message:', error);
41638
41858
  this._notifyWidgets('error', {
@@ -41771,7 +41991,31 @@ class WebSocketManager {
41771
41991
  }
41772
41992
  } else {
41773
41993
  if (this.config.debug) {
41774
- console.log(`[WebSocketManager] Subscription ${subscriptionKey} already active, skipping`);
41994
+ console.log(`[WebSocketManager] Subscription ${subscriptionKey} already active, skipping server subscription`);
41995
+ }
41996
+
41997
+ // Subscription already active, but send cached data to newly subscribing widgets
41998
+ const cachedMessage = this.lastMessageCache.get(subscriptionKey);
41999
+ console.log('LAST', this.lastMessageCache);
42000
+ if (cachedMessage) {
42001
+ if (this.config.debug) {
42002
+ console.log(`[WebSocketManager] Sending cached data to newly subscribing widgets for ${subscriptionKey}`);
42003
+ }
42004
+
42005
+ // Find all widgets subscribed to this type:symbol combination
42006
+ this.subscriptions.forEach((subscription, widgetId) => {
42007
+ if (subscription.types.has(type) && subscription.symbol === symbol) {
42008
+ try {
42009
+ subscription.callback({
42010
+ event: 'data',
42011
+ data: cachedMessage,
42012
+ widgetId
42013
+ });
42014
+ } catch (error) {
42015
+ console.error(`[WebSocketManager] Error sending cached data to widget ${widgetId}:`, error);
42016
+ }
42017
+ }
42018
+ });
41775
42019
  }
41776
42020
  }
41777
42021
  });
@@ -41823,11 +42067,6 @@ class WebSocketManager {
41823
42067
  * The data field structure varies based on the type
41824
42068
  */
41825
42069
  _normalizeMessage(message) {
41826
- // If message already has targetType (error messages), return as is
41827
- if (message.targetType) {
41828
- return message;
41829
- }
41830
-
41831
42070
  // Check for error field (case-insensitive)
41832
42071
  const errorField = message.error || message.Error;
41833
42072
  const typeField = message.type || message.Type;
@@ -41838,13 +42077,12 @@ class WebSocketManager {
41838
42077
  if (this.config.debug) {
41839
42078
  console.log(`[WebSocketManager] Detected error message with type: ${typeField}`);
41840
42079
  }
41841
-
41842
- // Set targetType for routing to specific widget types
41843
42080
  return {
41844
42081
  ...message,
41845
- targetType: typeField,
41846
- type: typeField,
41847
- isError: true
42082
+ // Only add type if message doesn't already have one
42083
+ ...(!message.type && !message.Type && typeField ? {
42084
+ type: typeField
42085
+ } : {})
41848
42086
  };
41849
42087
  }
41850
42088
 
@@ -41862,10 +42100,11 @@ class WebSocketManager {
41862
42100
  // Treat as error and route based on type
41863
42101
  return {
41864
42102
  ...message,
41865
- targetType: typeField,
41866
- type: typeField,
41867
- isError: true,
41868
- error: true // Mark as error even if it was false
42103
+ // Only add type if message doesn't already have one
42104
+ ...(!message.type && !message.Type && typeField ? {
42105
+ type: typeField
42106
+ } : {}),
42107
+ error: true // Mark as error since it was detected as implicit error
41869
42108
  };
41870
42109
  }
41871
42110
  }
@@ -41899,8 +42138,8 @@ class WebSocketManager {
41899
42138
  };
41900
42139
  }
41901
42140
 
41902
- // Also preserve the original type at the root level for backward compatibility
41903
- if (typeof normalizedData === 'object' && !normalizedData.type) {
42141
+ // Only add type field if the normalized data doesn't already have one
42142
+ if (typeof normalizedData === 'object' && !normalizedData.type && !normalizedData.Type) {
41904
42143
  normalizedData.type = typeField;
41905
42144
  }
41906
42145
  return normalizedData;
@@ -41922,32 +42161,7 @@ class WebSocketManager {
41922
42161
  // Cache the message for later use by new subscribers
41923
42162
  this._cacheMessage(message);
41924
42163
 
41925
- // Check if message has a specific target type (like night session errors)
41926
- if (message.targetType) {
41927
- const targetWidgets = this.typeSubscriptions.get(message.targetType);
41928
- if (targetWidgets && targetWidgets.size > 0) {
41929
- if (this.config.debug) {
41930
- console.log(`[WebSocketManager] Routing ${message.targetType} error to specific widgets:`, [...targetWidgets]);
41931
- }
41932
- targetWidgets.forEach(widgetId => {
41933
- const subscription = this.subscriptions.get(widgetId);
41934
- if (subscription) {
41935
- try {
41936
- subscription.callback({
41937
- event: 'data',
41938
- data: message,
41939
- widgetId
41940
- });
41941
- } catch (error) {
41942
- console.error(`[WebSocketManager] Error in widget ${widgetId} callback:`, error);
41943
- }
41944
- }
41945
- });
41946
- return; // Don't fall through to broadcast
41947
- }
41948
- }
41949
-
41950
- //console.log('here');
42164
+ // Get relevant widgets based on message type and symbol
41951
42165
  const relevantWidgets = this._getRelevantWidgets(message);
41952
42166
  if (this.config.debug && relevantWidgets.size > 0) {
41953
42167
  console.log(`[WebSocketManager] Routing message to ${relevantWidgets.size} relevant widgets`);
@@ -41990,42 +42204,49 @@ class WebSocketManager {
41990
42204
  _cacheMessage(message) {
41991
42205
  try {
41992
42206
  // Extract symbol and message type to create cache key
41993
- const symbol = this._extractSymbol(message);
41994
- const messageType = this._extractMessageType(message);
42207
+ // This allows new widgets to get instant data when subscribing
42208
+ // check in message.data or message.Data
42209
+ let data;
42210
+ if (message.data) {
42211
+ data = message.data;
42212
+ } else if (message.Data) {
42213
+ data = message.Data;
42214
+ } else {
42215
+ data = message;
42216
+ }
42217
+ const symbol = this._extractSymbol(data);
42218
+ const messageType = message.type;
42219
+ if (this.config.debug) {
42220
+ console.log(`[WebSocketManager] _cacheMessage - extracted symbol: "${symbol}", messageType: "${messageType}"`);
42221
+ }
42222
+
42223
+ // Cache by type:symbol combination
41995
42224
  if (messageType && symbol) {
41996
42225
  const cacheKey = `${messageType}:${symbol}`;
41997
42226
  this.lastMessageCache.set(cacheKey, message);
41998
42227
  if (this.config.debug) {
41999
42228
  console.log(`[WebSocketManager] Cached message for ${cacheKey}`);
42000
42229
  }
42001
- }
42002
-
42003
- // Also handle array messages (for night session / blueocean data)
42004
- if (Array.isArray(message) && message.length > 0 && message[0].Symbol) {
42005
- const firstItem = message[0];
42006
- const symbol = firstItem.Symbol;
42007
- const messageType = this._extractMessageType(message);
42008
- if (messageType && symbol) {
42009
- const cacheKey = `${messageType}:${symbol}`;
42010
- this.lastMessageCache.set(cacheKey, message);
42230
+ } else {
42231
+ // If we can't extract symbol from message, try to cache by type only
42232
+ // This helps with data like ONBBO L2 which doesn't have symbol in the message
42233
+ if (messageType) {
42011
42234
  if (this.config.debug) {
42012
- console.log(`[WebSocketManager] Cached array message for ${cacheKey}`);
42235
+ console.log(`[WebSocketManager] No symbol found in message, attempting to infer from subscriptions for type: ${messageType}`);
42013
42236
  }
42014
- }
42015
- }
42016
42237
 
42017
- // Handle Data array format (for queryl1 data)
42018
- if (message.Data && Array.isArray(message.Data)) {
42019
- message.Data.forEach(dataItem => {
42020
- if (dataItem.Symbol) {
42021
- const cacheKey = `queryl1:${dataItem.Symbol}`;
42022
- // Cache the whole message, not just the item
42023
- this.lastMessageCache.set(cacheKey, message);
42024
- if (this.config.debug) {
42025
- console.log(`[WebSocketManager] Cached data item for ${cacheKey}`);
42238
+ // Find active subscriptions for this message type and cache for each
42239
+ this.activeSubscriptions.forEach(activeKey => {
42240
+ const [type, sym] = activeKey.split(':');
42241
+ if (type === messageType && sym) {
42242
+ const cacheKey = activeKey;
42243
+ this.lastMessageCache.set(cacheKey, message);
42244
+ if (this.config.debug) {
42245
+ console.log(`[WebSocketManager] Cached message for ${cacheKey} (inferred from active subscription)`);
42246
+ }
42026
42247
  }
42027
- }
42028
- });
42248
+ });
42249
+ }
42029
42250
  }
42030
42251
  } catch (error) {
42031
42252
  console.error('[WebSocketManager] Error caching message:', error);
@@ -42192,7 +42413,7 @@ class WebSocketManager {
42192
42413
  _extractSymbol(item) {
42193
42414
  // Handle new format where symbol might be in nested data structure
42194
42415
  // Check if this is an array with data that has _messageType (normalized new format)
42195
- if (Array.isArray(item) && item._messageType) {
42416
+ if (Array.isArray(item)) {
42196
42417
  // The data is an array, check first item for symbol
42197
42418
  if (item.length > 0 && item[0]) {
42198
42419
  return this._extractSymbolFromItem(item[0]);