http-request-manager 18.15.27 → 18.15.31

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.
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, APP_ID, Inject, InjectionToken, isDevMode, signal, effect, computed, Injector, Optional, EventEmitter, Input, Output, ViewEncapsulation, Component, NgModule, ViewChild } from '@angular/core';
2
+ import { inject, Injectable, APP_ID, Inject, InjectionToken, isDevMode, signal, effect, computed, Injector, Optional, DestroyRef, EventEmitter, Input, Output, ViewEncapsulation, Component, NgModule, ViewChild } from '@angular/core';
3
3
  import { ComponentStore } from '@ngrx/component-store';
4
- import { map, catchError, filter, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, take, delay, scan, distinctUntilChanged } from 'rxjs/operators';
4
+ import { map, catchError, filter, take, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, delay, scan, distinctUntilChanged } from 'rxjs/operators';
5
5
  import { HttpClient, HttpHeaders, HttpEventType, HttpHeaderResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
6
6
  import * as CryptoJS from 'crypto-js';
7
- import { from, BehaviorSubject, EMPTY, throwError, defer, interval, timer, Subject, of, merge, Subscription, take as take$1, catchError as catchError$1, map as map$1, tap as tap$1, switchMap as switchMap$1, startWith as startWith$1, distinctUntilChanged as distinctUntilChanged$1, combineLatest, filter as filter$1, takeUntil as takeUntil$1, ReplaySubject } from 'rxjs';
8
- import { toObservable } from '@angular/core/rxjs-interop';
7
+ import { from, BehaviorSubject, EMPTY, timer, throwError, defer, interval, Subject, of, merge, Subscription, take as take$1, catchError as catchError$1, map as map$1, tap as tap$1, switchMap as switchMap$1, startWith as startWith$1, distinctUntilChanged as distinctUntilChanged$1, combineLatest, filter as filter$1, takeUntil as takeUntil$1, ReplaySubject } from 'rxjs';
8
+ import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
9
  import { ToastMessageDisplayService, ToastDisplay, ToastColors, ToastMessageDisplayModule } from 'toast-message-display';
10
10
  import Dexie from 'dexie';
11
11
  import * as i1 from '@ngx-translate/core';
@@ -1854,6 +1854,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
1854
1854
  */
1855
1855
  class WebSocketManagerService {
1856
1856
  constructor() {
1857
+ this.retryCount$ = WebSocketManagerService.retryCountSubject.asObservable();
1858
+ this.maxRetries$ = WebSocketManagerService.maxRetriesSubject.asObservable();
1857
1859
  this.messages$ = WebSocketManagerService.messages.asObservable();
1858
1860
  this.connectionStatus$ = WebSocketManagerService.connectionStatus.asObservable();
1859
1861
  this.subscribedChannels$ = WebSocketManagerService.subscribedChannels.asObservable();
@@ -1870,7 +1872,11 @@ class WebSocketManagerService {
1870
1872
  static { this.lastJwtToken = ''; }
1871
1873
  // Retry configuration
1872
1874
  static { this.retryCount = 0; }
1873
- static { this.jwtInvalidClose = false; }
1875
+ static { this.retryDelay = 5000; }
1876
+ static { this.retrySubscription = null; }
1877
+ static { this.maxRetries = 10; }
1878
+ static { this.retryCountSubject = new BehaviorSubject(0); }
1879
+ static { this.maxRetriesSubject = new BehaviorSubject(10); }
1874
1880
  // ═══════════════════════════════════════════════════════════════════════════
1875
1881
  // INSTANCE OBSERVABLES (Shared across ALL instances via static BehaviorSubjects)
1876
1882
  // ═══════════════════════════════════════════════════════════════════════════
@@ -1915,6 +1921,9 @@ class WebSocketManagerService {
1915
1921
  * @param jwtToken - Optional JWT token for authentication
1916
1922
  */
1917
1923
  connect(options, jwtToken) {
1924
+ // Cancel any pending retry timer before attempting a new connection
1925
+ WebSocketManagerService.retrySubscription?.unsubscribe();
1926
+ WebSocketManagerService.retrySubscription = null;
1918
1927
  // Check if connection already exists and is open
1919
1928
  if (WebSocketManagerService.socket) {
1920
1929
  if (WebSocketManagerService.socket.readyState === WebSocket.OPEN) {
@@ -1941,82 +1950,105 @@ class WebSocketManagerService {
1941
1950
  // Store jwt token for retry
1942
1951
  WebSocketManagerService.lastJwtToken = jwtToken;
1943
1952
  WebSocketManagerService.lastOptions = options;
1944
- WebSocketManagerService.jwtInvalidClose = false;
1945
- // Mark as connecting
1953
+ // Always set maxRetries and delay from latest options
1954
+ if (options?.retry?.times) {
1955
+ WebSocketManagerService.maxRetries = options.retry.times;
1956
+ WebSocketManagerService.maxRetriesSubject.next(options.retry.times);
1957
+ }
1958
+ else {
1959
+ WebSocketManagerService.maxRetries = 10;
1960
+ WebSocketManagerService.maxRetriesSubject.next(10);
1961
+ }
1962
+ if (options?.retry?.delay) {
1963
+ WebSocketManagerService.retryDelay = options.retry.delay * 1000;
1964
+ }
1965
+ else {
1966
+ WebSocketManagerService.retryDelay = 5000;
1967
+ }
1968
+ // Mark as connecting — connectionStatus true while connecting/retrying, false only when exhausted
1946
1969
  WebSocketManagerService.isConnecting = true;
1947
1970
  WebSocketManagerService.isSubscribed = false;
1948
1971
  WebSocketManagerService.subscribedChannels.next(new Set());
1972
+ WebSocketManagerService.connectionStatus.next(true);
1949
1973
  const sessionId = this.getSessionId();
1950
1974
  const URL = (jwtToken) ? `${options.wsServer}?token=${jwtToken}&sessionId=${sessionId}` : `${options.wsServer}?sessionId=${sessionId}`;
1951
1975
  console.log(`🔌 Initiating WebSocket connection to: ${options.wsServer}`);
1952
1976
  // Create new WebSocket instance (static)
1953
1977
  WebSocketManagerService.socket = new WebSocket(URL);
1954
1978
  WebSocketManagerService.socket.onopen = () => {
1955
- console.log(`📡 Connected to WebSocket`);
1979
+ console.log(`📡 WebSocket handshake complete — awaiting server confirmation`);
1956
1980
  // Force clear subscribedChannels on new connection - server lost our subscriptions
1957
1981
  console.log('🧹 Clearing subscribedChannels on connect (was:', WebSocketManagerService.subscribedChannels.value.size, 'channels)');
1958
1982
  WebSocketManagerService.subscribedChannels.next(new Set());
1959
- WebSocketManagerService.retryCount = 0;
1960
- WebSocketManagerService.connectionStatus.next(true);
1983
+ // Do NOT reset retryCount here — reset happens only on confirmed stable connection
1984
+ // (onmessage first valid response). Resetting here would cause the counter to always
1985
+ // show 1/N because onopen fires before onclose increments the counter.
1961
1986
  WebSocketManagerService.isConnecting = false;
1962
1987
  WebSocketManagerService.connectionInitialized = true;
1963
- // Emit reconnect event - MessageTrackerService will handle subscriptions with lastSeenId
1964
- console.log(`🔄 Emitting reconnect event for MessageTrackerService`);
1965
- WebSocketManagerService.onReconnect.next();
1966
1988
  };
1967
1989
  WebSocketManagerService.socket.onmessage = (event) => {
1968
1990
  try {
1969
1991
  const data = JSON.parse(event.data);
1970
- if (data.error && (data.error === 'JWT_INVALID' || data.error === 'AUTH_BLOCKED')) {
1971
- console.error(`🚫 ${data.error}: ${data.message || 'Authentication error'}`);
1972
- WebSocketManagerService.jwtInvalidClose = true;
1992
+ if (data.error) {
1973
1993
  WebSocketManagerService.messages.next(data);
1974
- WebSocketManagerService.connectionStatus.next(false);
1994
+ // Log the server error and close — onclose will drive the retry logic
1995
+ if (data.error === 'JWT_INVALID' || data.error === 'AUTH_BLOCKED') {
1996
+ console.error(`🚫 ${data.error}: ${data.message || 'Authentication error'} — will retry`);
1997
+ }
1998
+ else {
1999
+ console.error(`❌ Server error: ${data.error} — will retry`);
2000
+ }
1975
2001
  WebSocketManagerService.socket?.close();
1976
2002
  return;
1977
2003
  }
2004
+ // First valid (non-error) message confirms the server accepted auth.
2005
+ // Reset retry counter now that we have a stable connection.
2006
+ if (WebSocketManagerService.retryCount > 0) {
2007
+ console.log(`✅ Server confirmed connection after ${WebSocketManagerService.retryCount} retries.`);
2008
+ WebSocketManagerService.retryCount = 0;
2009
+ WebSocketManagerService.retryCountSubject.next(0);
2010
+ console.log(`🔄 Emitting reconnect event for MessageTrackerService`);
2011
+ WebSocketManagerService.onReconnect.next();
2012
+ }
1978
2013
  WebSocketManagerService.messages.next(data);
1979
2014
  }
1980
2015
  catch (error) {
1981
2016
  console.error('Error parsing WebSocket message:', event.data);
1982
2017
  }
1983
2018
  };
1984
- WebSocketManagerService.socket.onclose = () => {
1985
- console.log('🔴 WebSocket connection closed');
1986
- console.log('🧹 Clearing subscribedChannels (was:', WebSocketManagerService.subscribedChannels.value.size, 'channels)');
1987
- WebSocketManagerService.connectionStatus.next(false);
2019
+ WebSocketManagerService.socket.onclose = (event) => {
2020
+ console.log(`🔴 WebSocket closed (code: ${event.code})`);
1988
2021
  WebSocketManagerService.isConnecting = false;
1989
2022
  WebSocketManagerService.socket = null;
1990
- // Clear subscribed channels - server lost our subscriptions
1991
2023
  WebSocketManagerService.subscribedChannels.next(new Set());
1992
- console.log('✅ subscribedChannels cleared');
1993
- if (WebSocketManagerService.jwtInvalidClose) {
1994
- console.error('🚫 WebSocket closed due to authentication block/invalid token. Not retrying.');
1995
- WebSocketManagerService.retryCount = 0;
1996
- return;
1997
- }
1998
- const maxRetries = WebSocketManagerService.lastOptions?.retry?.times || 3;
1999
- const retryDelay = (WebSocketManagerService.lastOptions?.retry?.delay && WebSocketManagerService.lastOptions.retry.delay * 1000) || 5000;
2024
+ const maxRetries = WebSocketManagerService.maxRetries;
2025
+ const retryDelay = WebSocketManagerService.retryDelay;
2000
2026
  if (WebSocketManagerService.lastOptions && WebSocketManagerService.retryCount < maxRetries) {
2001
2027
  WebSocketManagerService.retryCount++;
2028
+ WebSocketManagerService.retryCountSubject.next(WebSocketManagerService.retryCount);
2002
2029
  console.log(`🔄 Reconnect attempt ${WebSocketManagerService.retryCount}/${maxRetries}...`);
2003
- setTimeout(() => this.connect(WebSocketManagerService.lastOptions, WebSocketManagerService.lastJwtToken), retryDelay);
2030
+ // connectionStatus stays true — still actively retrying
2031
+ WebSocketManagerService.retrySubscription = timer(retryDelay).pipe(take(1)).subscribe(() => this.connect(WebSocketManagerService.lastOptions, WebSocketManagerService.lastJwtToken));
2004
2032
  }
2005
- else if (WebSocketManagerService.retryCount >= maxRetries) {
2006
- console.error(`❌ WebSocket failed after ${maxRetries} attempts. Giving up.`);
2007
- WebSocketManagerService.retryCount = 0;
2033
+ else {
2034
+ console.error(`❌ WebSocket failed after ${maxRetries} retries. Giving up.`);
2035
+ WebSocketManagerService.connectionStatus.next(false);
2036
+ WebSocketManagerService.retryCount = maxRetries;
2037
+ WebSocketManagerService.retryCountSubject.next(maxRetries);
2008
2038
  }
2009
2039
  };
2010
2040
  WebSocketManagerService.socket.onerror = (error) => {
2011
2041
  console.error('❌ WebSocket error:', error);
2012
- WebSocketManagerService.connectionStatus.next(false);
2013
2042
  WebSocketManagerService.isConnecting = false;
2043
+ // onclose will fire after onerror and drive retry/give-up logic
2014
2044
  };
2015
2045
  }
2016
2046
  /**
2017
2047
  * Disconnect from WebSocket server
2018
2048
  */
2019
2049
  disconnect() {
2050
+ WebSocketManagerService.retrySubscription?.unsubscribe();
2051
+ WebSocketManagerService.retrySubscription = null;
2020
2052
  if (WebSocketManagerService.socket) {
2021
2053
  console.log('🔌 Disconnecting WebSocket...');
2022
2054
  WebSocketManagerService.socket.close();
@@ -3374,10 +3406,10 @@ class MessageTrackerService {
3374
3406
  * Map<channel, retryCount>
3375
3407
  */
3376
3408
  this.gapRetryCount = new Map();
3377
- /** Batch acknowledgment timer */
3378
- this.batchAckTimer = null;
3379
3409
  /** Subscription to WebSocket messages */
3380
3410
  this.messagesSubscription = null;
3411
+ /** Subject to signal destroy for takeUntil */
3412
+ this.destroy$ = new Subject();
3381
3413
  /**
3382
3414
  * Track channels we want to be subscribed to (survives disconnect/reconnect)
3383
3415
  * This is separate from WebSocketManagerService.subscribedChannels which tracks
@@ -3391,7 +3423,9 @@ class MessageTrackerService {
3391
3423
  this.restoreLastSeen();
3392
3424
  this.restoreIntendedChannels();
3393
3425
  // Start batch acknowledgment timer
3394
- this.startBatchAckTimer();
3426
+ interval(this.BATCH_ACK_INTERVAL_MS)
3427
+ .pipe(takeUntil(this.destroy$))
3428
+ .subscribe(() => this.sendAllBatchAcks());
3395
3429
  // Subscribe to all incoming WebSocket messages
3396
3430
  this.messagesSubscription = this.wsManager.messages$.subscribe(msg => {
3397
3431
  if (msg) {
@@ -3406,7 +3440,8 @@ class MessageTrackerService {
3406
3440
  console.log('✅ MessageTrackerService initialized');
3407
3441
  }
3408
3442
  ngOnDestroy() {
3409
- this.stopBatchAckTimer();
3443
+ this.destroy$.next();
3444
+ this.destroy$.complete();
3410
3445
  this.persistLastSeen(); // This now also persists intendedChannels
3411
3446
  this.messagesSubscription?.unsubscribe();
3412
3447
  console.log('🛑 MessageTrackerService destroyed');
@@ -3492,23 +3527,6 @@ class MessageTrackerService {
3492
3527
  }
3493
3528
  this.pendingAcks.get(channel).add(messageId);
3494
3529
  }
3495
- /**
3496
- * Start batch acknowledgment timer
3497
- */
3498
- startBatchAckTimer() {
3499
- this.batchAckTimer = setInterval(() => {
3500
- this.sendAllBatchAcks();
3501
- }, this.BATCH_ACK_INTERVAL_MS);
3502
- }
3503
- /**
3504
- * Stop batch acknowledgment timer
3505
- */
3506
- stopBatchAckTimer() {
3507
- if (this.batchAckTimer) {
3508
- clearInterval(this.batchAckTimer);
3509
- this.batchAckTimer = null;
3510
- }
3511
- }
3512
3530
  /**
3513
3531
  * Send batch acknowledgments for all channels with pending acks
3514
3532
  */
@@ -3745,6 +3763,8 @@ class HTTPManagerService extends RequestService {
3745
3763
  this.connectionStatus$ = this.wsManager.connectionStatus$;
3746
3764
  this.messages$ = this.messageTracker.messages$; // Messages flow through MessageTrackerService
3747
3765
  this.subscribedChannels$ = this.wsManager.subscribedChannels$;
3766
+ this.retryCount$ = this.wsManager.retryCount$;
3767
+ this.maxRetries$ = this.wsManager.maxRetries$;
3748
3768
  this.countdown = new BehaviorSubject(0);
3749
3769
  this.countdown$ = this.countdown.asObservable();
3750
3770
  this.error = new BehaviorSubject(false);
@@ -5664,6 +5684,7 @@ class LocalStorageSignalsManagerService {
5664
5684
  this.defaultOptions = SettingOptions.adapt();
5665
5685
  this.stateRetrieved = false;
5666
5686
  this.encrypted = false;
5687
+ this.destroyRef = inject(DestroyRef);
5667
5688
  this.app = inject(AppService);
5668
5689
  this.utils = inject(UtilsService);
5669
5690
  this.objectMergerService = inject(ObjectMergerService);
@@ -5726,7 +5747,7 @@ class LocalStorageSignalsManagerService {
5726
5747
  }
5727
5748
  });
5728
5749
  // Expiration timer every 3s
5729
- setInterval(() => {
5750
+ interval(3000).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
5730
5751
  const state = this.state();
5731
5752
  const expired = this.expired(state);
5732
5753
  if (expired.length > 0) {
@@ -5740,7 +5761,7 @@ class LocalStorageSignalsManagerService {
5740
5761
  this.state.set(updated);
5741
5762
  this.persistState(updated);
5742
5763
  }
5743
- }, 3000);
5764
+ });
5744
5765
  }
5745
5766
  // --- STATE MUTATORS ---
5746
5767
  setStore(store) {
@@ -6518,53 +6539,40 @@ class QueryParamsTrackerService {
6518
6539
  sessionStorage.removeItem(TRACKER_SESSION_INIT_KEY);
6519
6540
  }
6520
6541
  }
6521
- clearPath(pathKey) {
6522
- this.deletePathFromStore(pathKey);
6523
- }
6524
- deletePathFromStore(pathKey) {
6525
- this.localStorageManager.store$(TRACKER_STORE_NAME)
6526
- .pipe(take(1))
6527
- .subscribe((storedState) => {
6528
- if (!storedState?.paths?.[pathKey])
6529
- return;
6530
- const updatedPaths = { ...storedState.paths };
6531
- delete updatedPaths[pathKey];
6532
- const updatedState = { ...storedState, paths: updatedPaths };
6533
- this.localStorageManager.updateStore({ name: TRACKER_STORE_NAME, data: updatedState });
6534
- if (this.stateRestored && this.state.paths[pathKey]) {
6535
- delete this.state.paths[pathKey];
6536
- }
6537
- });
6542
+ clearTrackingForPath(pathKey) {
6543
+ this.ensureStateRestored();
6544
+ if (this.state.paths[pathKey]) {
6545
+ delete this.state.paths[pathKey];
6546
+ this.persistState();
6547
+ }
6538
6548
  }
6539
6549
  checkRequestOptions(requestOptions = [], options = {}) {
6540
6550
  this.ensureStateRestored();
6541
- return this.ready$.pipe(filter(ready => ready === true), take(1), map(() => {
6542
- const normalized = this.normalizeRequestOptions(requestOptions);
6543
- if (!normalized.pathKey)
6544
- return false;
6545
- this.cleanupExpiredEntries();
6546
- if (!normalized.hasQuery) {
6547
- const pathState = this.ensurePathState(normalized.pathKey);
6548
- const pathSeenBefore = pathState.consumedValuesByKey.hasOwnProperty('__path_seen__');
6549
- if (!pathSeenBefore) {
6550
- pathState.consumedValuesByKey['__path_seen__'] = ['true'];
6551
- this.persistState();
6552
- }
6553
- return !pathSeenBefore;
6554
- }
6555
- if (options.mode === 'exact') {
6556
- return this.checkExact(normalized, options);
6551
+ if (!this.ready$.value) {
6552
+ return true;
6553
+ }
6554
+ const normalized = this.normalizeRequestOptions(requestOptions);
6555
+ if (!normalized.pathKey)
6556
+ return false;
6557
+ this.cleanupExpiredEntries();
6558
+ if (!normalized.hasQuery) {
6559
+ const pathState = this.ensurePathState(normalized.pathKey);
6560
+ const pathSeenBefore = Object.keys(pathState.consumedValuesByKey).length > 0;
6561
+ if (!pathSeenBefore) {
6562
+ this.persistState();
6557
6563
  }
6558
- return this.checkVariation(normalized, options);
6559
- }));
6564
+ return !pathSeenBefore;
6565
+ }
6566
+ if (options.mode === 'exact') {
6567
+ return this.checkExact(normalized, options);
6568
+ }
6569
+ return this.checkVariation(normalized, options);
6560
6570
  }
6561
6571
  matchesPath(requestOptions = [], expectedPathOptions = []) {
6562
6572
  this.ensureStateRestored();
6563
- return this.ready$.pipe(filter(ready => ready === true), take(1), map(() => {
6564
- const requestPath = this.normalizeRequestOptions(requestOptions).pathKey;
6565
- const expectedPath = this.normalizeRequestOptions(expectedPathOptions).pathKey;
6566
- return Boolean(requestPath) && requestPath === expectedPath;
6567
- }));
6573
+ const requestPath = this.normalizeRequestOptions(requestOptions).pathKey;
6574
+ const expectedPath = this.normalizeRequestOptions(expectedPathOptions).pathKey;
6575
+ return Boolean(requestPath) && requestPath === expectedPath;
6568
6576
  }
6569
6577
  checkExact(normalized, options) {
6570
6578
  const pathState = this.ensurePathState(normalized.pathKey);
@@ -6956,6 +6964,8 @@ class HTTPManagerStateService extends ComponentStore {
6956
6964
  this.wsOptions = WSOptions.adapt();
6957
6965
  // Expose raw WS connection status directly to UI (from singleton WebSocketManagerService)
6958
6966
  this.connectionStatus$ = this.httpManagerService.connectionStatus$;
6967
+ this.wsRetryCount$ = this.httpManagerService.retryCount$;
6968
+ this.wsMaxRetries$ = this.httpManagerService.maxRetries$;
6959
6969
  // WebSocket
6960
6970
  this.initWS = this.effect((wsOptions$) => wsOptions$.pipe(switchMap((wsOptions) => merge(this.httpManagerService.connectionStatus$.pipe(tap((isConnected) => {
6961
6971
  if (isConnected) {
@@ -8464,7 +8474,7 @@ class HTTPManagerStateService extends ComponentStore {
8464
8474
  }
8465
8475
  }
8466
8476
  if (Array.isArray(pathArray) && pathArray.length > 0) {
8467
- this.queryParamsTrackerService.clearPath(pathArray.join('/'));
8477
+ this.queryParamsTrackerService.clearTrackingForPath(pathArray.join('/'));
8468
8478
  this._requestCachePaths.delete(tableName);
8469
8479
  }
8470
8480
  if (!storeData)
@@ -8602,10 +8612,10 @@ class HTTPManagerStateService extends ComponentStore {
8602
8612
  }
8603
8613
  checkTrackerAllowsRequest(tableName, path, options, storeData) {
8604
8614
  if (options && 'watchParams' in options) {
8605
- return this.queryParamsTrackerService.checkRequestOptions(path, {
8615
+ return of(this.queryParamsTrackerService.checkRequestOptions(path, {
8606
8616
  watchParams: options.watchParams,
8607
8617
  watchExpiresAt: options?.watchExpiresAt,
8608
- });
8618
+ }));
8609
8619
  }
8610
8620
  const normalized = this.trackerNormalizePath(path);
8611
8621
  const ignoreQueryParams = Array.isArray(options?.ignoreQueryParams) ? options.ignoreQueryParams : [];
@@ -11701,11 +11711,8 @@ class WsMessagingComponent {
11701
11711
  this.stateService.updateConnection(this.server, this.wsServer, this.jwtToken, this.user, this.path);
11702
11712
  // Only trigger once when connection becomes true
11703
11713
  this.connectionStatus$.pipe(filter$1(status => status === true), take$1(1), takeUntil$1(this.destroy$)).subscribe(() => {
11704
- // Wait a moment for subscription to be processed, then fetch channels
11705
- setTimeout(() => {
11706
- console.log('📋 Fetching channels after connection...');
11707
- this.messageService.getAllChannels();
11708
- }, 500); // 500ms delay to ensure subscription is processed
11714
+ console.log('📋 Fetching channels after connection...');
11715
+ this.messageService.getAllChannels();
11709
11716
  });
11710
11717
  // Subscribe to latest messages and display using rule-based routing
11711
11718
  this.latestCommunicationMessages$.pipe(filter$1(message => !!message), takeUntil$1(this.destroy$)).subscribe((message) => {
@@ -12051,8 +12058,8 @@ class RequestManagerWsDemoComponent {
12051
12058
  this.fb = inject(FormBuilder);
12052
12059
  this.path = ['ai', 'tests'];
12053
12060
  this.user$ = this.stateService.user$;
12054
- this.attempts$ = this.stateService.wsRetryAttempts$;
12055
- this.nextRetry$ = this.stateService.wsNextRetry$;
12061
+ this.retryCount$ = this.stateService.wsRetryCount$;
12062
+ this.maxRetries$ = this.stateService.wsMaxRetries$;
12056
12063
  this.connectionStatus$ = this.stateService.connectionStatus$;
12057
12064
  this.data$ = this.stateService.data$;
12058
12065
  this.isPending$ = this.stateService.isPending$;
@@ -12061,11 +12068,11 @@ class RequestManagerWsDemoComponent {
12061
12068
  this.stateService.updateConnection(this.server, this.wsServer, this.jwtToken, this.user, this.path);
12062
12069
  }
12063
12070
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12064
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: ["server", "wsServer", "jwtToken", "user"] }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "component", type: WsChatsComponent, selector: "app-ws-chats" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
12071
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: ["server", "wsServer", "jwtToken", "user"] }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "component", type: WsChatsComponent, selector: "app-ws-chats" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
12065
12072
  }
12066
12073
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, decorators: [{
12067
12074
  type: Component,
12068
- args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
12075
+ args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ retryCount$ | async }} / {{ maxRetries$ | async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if (!(connectionStatus$ | async) && ((retryCount$ | async) ?? 0) < ((maxRetries$ | async) ?? 0)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"!(connectionStatus$ | async)\">\n <!-- WS - Notifications -->\n <app-ws-notifications\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - Chats\" [disabled]=\"true\">\n <!-- WS - Chats -->\n <app-ws-chats></app-ws-chats>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
12069
12076
  }], propDecorators: { server: [{
12070
12077
  type: Input
12071
12078
  }], wsServer: [{