@memberjunction/ng-explorer-core 5.27.0 → 5.27.1
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.
|
@@ -2,43 +2,42 @@ import { OnDestroy } from '@angular/core';
|
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Monitors MJAPI connectivity.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Primary signal is the GraphQLDataProvider's WebSocket state (graphql-ws).
|
|
8
|
+
* When the socket emits 'disconnected' (retries already exhausted by graphql-ws),
|
|
9
|
+
* we fall back to polling /healthcheck until it returns 200, then force a socket
|
|
10
|
+
* reconnect so the next subscription picks up a fresh client.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - 1 success -> connected
|
|
13
|
-
* - Pauses when tab is hidden, checks immediately on focus
|
|
12
|
+
* 'unknown' (no active socket — either never opened or cleanly disposed) is
|
|
13
|
+
* treated as healthy: absence of signal is not a signal of failure.
|
|
14
14
|
*/
|
|
15
15
|
export declare class ServerConnectivityService implements OnDestroy {
|
|
16
|
-
private static readonly
|
|
17
|
-
private static readonly POLL_INTERVAL_DISCONNECTED_MS;
|
|
16
|
+
private static readonly POLL_INTERVAL_MS;
|
|
18
17
|
private static readonly FETCH_TIMEOUT_MS;
|
|
19
|
-
private static readonly FAILURES_BEFORE_DISCONNECT;
|
|
20
18
|
private readonly isConnected;
|
|
21
19
|
readonly IsConnected$: Observable<boolean>;
|
|
22
20
|
private healthCheckUrl;
|
|
23
|
-
private consecutiveFailures;
|
|
24
21
|
private pollingTimerId;
|
|
22
|
+
private socketSubscription;
|
|
25
23
|
private boundVisibilityHandler;
|
|
26
24
|
/** Synchronous getter for the current connectivity state */
|
|
27
25
|
get IsConnected(): boolean;
|
|
28
26
|
/**
|
|
29
|
-
* Begin
|
|
30
|
-
*
|
|
27
|
+
* Begin monitoring connectivity.
|
|
28
|
+
* Subscribes to the graphql-ws socket state and only polls /healthcheck
|
|
29
|
+
* while the socket reports 'disconnected'.
|
|
31
30
|
*/
|
|
32
31
|
Start(healthCheckUrl: string): void;
|
|
33
|
-
/** Stop
|
|
32
|
+
/** Stop monitoring and clean up resources */
|
|
34
33
|
Stop(): void;
|
|
35
34
|
/** Force an immediate health check and return the result */
|
|
36
35
|
CheckNow(): Promise<boolean>;
|
|
37
36
|
ngOnDestroy(): void;
|
|
37
|
+
private subscribeToSocketState;
|
|
38
|
+
private onSocketStateChange;
|
|
38
39
|
private ping;
|
|
39
|
-
private
|
|
40
|
-
private onPingSuccess;
|
|
41
|
-
private onPingFailure;
|
|
40
|
+
private runHealthCheck;
|
|
42
41
|
private scheduleNextPoll;
|
|
43
42
|
private clearPollTimer;
|
|
44
43
|
private attachVisibilityListener;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-connectivity.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/server-connectivity.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,SAAS,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAmB,UAAU,
|
|
1
|
+
{"version":3,"file":"server-connectivity.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/server-connectivity.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,SAAS,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAmB,UAAU,EAAgB,MAAM,MAAM,CAAC;;AAIjE;;;;;;;;;;GAUG;AACH,qBAGa,yBAA0B,YAAW,SAAS;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAEjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,SAAgB,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,CAAmC;IAEpF,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,sBAAsB,CAA6B;IAE3D,4DAA4D;IAC5D,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED;;;;OAIG;IACI,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAW1C,6CAA6C;IACtC,IAAI,IAAI,IAAI;IAUnB,4DAA4D;IAC/C,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAQzC,WAAW,IAAI,IAAI;IAMnB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,mBAAmB;YAiBb,IAAI;YAuBJ,cAAc;IAgB5B,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,kBAAkB;yCAjKf,yBAAyB;6CAAzB,yBAAyB;CA2KrC"}
|
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
2
|
import { BehaviorSubject } from 'rxjs';
|
|
3
3
|
import { LogError, LogStatus } from '@memberjunction/core';
|
|
4
|
+
import { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
+
* Monitors MJAPI connectivity.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Primary signal is the GraphQLDataProvider's WebSocket state (graphql-ws).
|
|
10
|
+
* When the socket emits 'disconnected' (retries already exhausted by graphql-ws),
|
|
11
|
+
* we fall back to polling /healthcheck until it returns 200, then force a socket
|
|
12
|
+
* reconnect so the next subscription picks up a fresh client.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - 1 success -> connected
|
|
14
|
-
* - Pauses when tab is hidden, checks immediately on focus
|
|
14
|
+
* 'unknown' (no active socket — either never opened or cleanly disposed) is
|
|
15
|
+
* treated as healthy: absence of signal is not a signal of failure.
|
|
15
16
|
*/
|
|
16
17
|
export class ServerConnectivityService {
|
|
17
|
-
static
|
|
18
|
-
static POLL_INTERVAL_DISCONNECTED_MS = 10_000;
|
|
18
|
+
static POLL_INTERVAL_MS = 30_000;
|
|
19
19
|
static FETCH_TIMEOUT_MS = 5_000;
|
|
20
|
-
static FAILURES_BEFORE_DISCONNECT = 2;
|
|
21
20
|
isConnected = new BehaviorSubject(true);
|
|
22
21
|
IsConnected$ = this.isConnected.asObservable();
|
|
23
22
|
healthCheckUrl = null;
|
|
24
|
-
consecutiveFailures = 0;
|
|
25
23
|
pollingTimerId = null;
|
|
24
|
+
socketSubscription = null;
|
|
26
25
|
boundVisibilityHandler = null;
|
|
27
26
|
/** Synchronous getter for the current connectivity state */
|
|
28
27
|
get IsConnected() {
|
|
29
28
|
return this.isConnected.value;
|
|
30
29
|
}
|
|
31
30
|
/**
|
|
32
|
-
* Begin
|
|
33
|
-
*
|
|
31
|
+
* Begin monitoring connectivity.
|
|
32
|
+
* Subscribes to the graphql-ws socket state and only polls /healthcheck
|
|
33
|
+
* while the socket reports 'disconnected'.
|
|
34
34
|
*/
|
|
35
35
|
Start(healthCheckUrl) {
|
|
36
36
|
if (this.healthCheckUrl) {
|
|
37
37
|
this.Stop(); // idempotent restart
|
|
38
38
|
}
|
|
39
39
|
this.healthCheckUrl = healthCheckUrl;
|
|
40
|
-
this.consecutiveFailures = 0;
|
|
41
40
|
this.isConnected.next(true);
|
|
42
41
|
this.attachVisibilityListener();
|
|
43
|
-
this.
|
|
42
|
+
this.subscribeToSocketState();
|
|
44
43
|
}
|
|
45
|
-
/** Stop
|
|
44
|
+
/** Stop monitoring and clean up resources */
|
|
46
45
|
Stop() {
|
|
47
46
|
this.clearPollTimer();
|
|
48
47
|
this.detachVisibilityListener();
|
|
48
|
+
if (this.socketSubscription) {
|
|
49
|
+
this.socketSubscription.unsubscribe();
|
|
50
|
+
this.socketSubscription = null;
|
|
51
|
+
}
|
|
49
52
|
this.healthCheckUrl = null;
|
|
50
53
|
}
|
|
51
54
|
/** Force an immediate health check and return the result */
|
|
@@ -53,14 +56,40 @@ export class ServerConnectivityService {
|
|
|
53
56
|
if (!this.healthCheckUrl) {
|
|
54
57
|
return this.isConnected.value;
|
|
55
58
|
}
|
|
56
|
-
|
|
57
|
-
this.applyPingResult(reachable);
|
|
59
|
+
await this.runHealthCheck();
|
|
58
60
|
return this.isConnected.value;
|
|
59
61
|
}
|
|
60
62
|
ngOnDestroy() {
|
|
61
63
|
this.Stop();
|
|
62
64
|
}
|
|
63
65
|
// ── Private helpers ──────────────────────────────────────────────
|
|
66
|
+
subscribeToSocketState() {
|
|
67
|
+
const provider = GraphQLDataProvider.Instance;
|
|
68
|
+
if (!provider) {
|
|
69
|
+
LogError('ServerConnectivityService: GraphQLDataProvider instance not available');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.socketSubscription = provider.SocketConnectivity$.subscribe(state => {
|
|
73
|
+
this.onSocketStateChange(state);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
onSocketStateChange(state) {
|
|
77
|
+
if (state === 'disconnected') {
|
|
78
|
+
if (this.isConnected.value) {
|
|
79
|
+
this.isConnected.next(false);
|
|
80
|
+
LogError('Server connectivity lost (WebSocket closed)');
|
|
81
|
+
}
|
|
82
|
+
this.scheduleNextPoll();
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// 'connected' or 'unknown' — treat as healthy
|
|
86
|
+
this.clearPollTimer();
|
|
87
|
+
if (!this.isConnected.value) {
|
|
88
|
+
this.isConnected.next(true);
|
|
89
|
+
LogStatus('Server connectivity restored (WebSocket reconnected)');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
64
93
|
async ping() {
|
|
65
94
|
if (!this.healthCheckUrl)
|
|
66
95
|
return false;
|
|
@@ -81,27 +110,20 @@ export class ServerConnectivityService {
|
|
|
81
110
|
clearTimeout(timeoutId);
|
|
82
111
|
}
|
|
83
112
|
}
|
|
84
|
-
|
|
113
|
+
async runHealthCheck() {
|
|
114
|
+
const reachable = await this.ping();
|
|
85
115
|
if (reachable) {
|
|
86
|
-
this.
|
|
116
|
+
this.clearPollTimer();
|
|
117
|
+
if (!this.isConnected.value) {
|
|
118
|
+
this.isConnected.next(true);
|
|
119
|
+
LogStatus('Server connectivity restored (/healthcheck OK)');
|
|
120
|
+
}
|
|
121
|
+
// Dispose the stale socket so the next subscription creates a fresh client
|
|
122
|
+
GraphQLDataProvider.Instance?.ForceSocketReconnect();
|
|
87
123
|
}
|
|
88
124
|
else {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
onPingSuccess() {
|
|
93
|
-
this.consecutiveFailures = 0;
|
|
94
|
-
if (!this.isConnected.value) {
|
|
95
|
-
this.isConnected.next(true);
|
|
96
|
-
LogStatus('Server connectivity restored');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
onPingFailure() {
|
|
100
|
-
this.consecutiveFailures++;
|
|
101
|
-
if (this.isConnected.value &&
|
|
102
|
-
this.consecutiveFailures >= ServerConnectivityService.FAILURES_BEFORE_DISCONNECT) {
|
|
103
|
-
this.isConnected.next(false);
|
|
104
|
-
LogError(`Server connectivity lost after ${this.consecutiveFailures} consecutive failures`);
|
|
125
|
+
// Still unreachable — schedule another poll
|
|
126
|
+
this.scheduleNextPoll();
|
|
105
127
|
}
|
|
106
128
|
}
|
|
107
129
|
scheduleNextPoll() {
|
|
@@ -110,13 +132,7 @@ export class ServerConnectivityService {
|
|
|
110
132
|
if (typeof document !== 'undefined' && document.hidden) {
|
|
111
133
|
return;
|
|
112
134
|
}
|
|
113
|
-
|
|
114
|
-
? ServerConnectivityService.POLL_INTERVAL_CONNECTED_MS
|
|
115
|
-
: ServerConnectivityService.POLL_INTERVAL_DISCONNECTED_MS;
|
|
116
|
-
this.pollingTimerId = setTimeout(async () => {
|
|
117
|
-
await this.CheckNow();
|
|
118
|
-
this.scheduleNextPoll();
|
|
119
|
-
}, interval);
|
|
135
|
+
this.pollingTimerId = setTimeout(() => this.runHealthCheck(), ServerConnectivityService.POLL_INTERVAL_MS);
|
|
120
136
|
}
|
|
121
137
|
clearPollTimer() {
|
|
122
138
|
if (this.pollingTimerId != null) {
|
|
@@ -142,9 +158,9 @@ export class ServerConnectivityService {
|
|
|
142
158
|
if (document.hidden) {
|
|
143
159
|
this.clearPollTimer();
|
|
144
160
|
}
|
|
145
|
-
else {
|
|
146
|
-
// Tab became visible — check immediately
|
|
147
|
-
this.
|
|
161
|
+
else if (!this.isConnected.value) {
|
|
162
|
+
// Tab became visible while disconnected — check immediately
|
|
163
|
+
this.runHealthCheck();
|
|
148
164
|
}
|
|
149
165
|
}
|
|
150
166
|
static ɵfac = function ServerConnectivityService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ServerConnectivityService)(); };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-connectivity.service.js","sourceRoot":"","sources":["../../../src/lib/services/server-connectivity.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { LogError, LogStatus } from '@memberjunction/core';\n\n/**\n * Polls MJAPI's /healthcheck endpoint and exposes connectivity state.\n *\n * Start polling after workspace initialization with `Start(healthCheckUrl)`.\n * Subscribe to `IsConnected$` for reactive updates.\n *\n * Rules:\n * - 2 consecutive failures -> disconnected\n * - 1 success -> connected\n * - Pauses when tab is hidden, checks immediately on focus\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class ServerConnectivityService implements OnDestroy {\n private static readonly POLL_INTERVAL_CONNECTED_MS = 30_000;\n private static readonly POLL_INTERVAL_DISCONNECTED_MS = 10_000;\n private static readonly FETCH_TIMEOUT_MS = 5_000;\n private static readonly FAILURES_BEFORE_DISCONNECT = 2;\n\n private readonly isConnected = new BehaviorSubject<boolean>(true);\n public readonly IsConnected$: Observable<boolean> = this.isConnected.asObservable();\n\n private healthCheckUrl: string | null = null;\n private consecutiveFailures = 0;\n private pollingTimerId: ReturnType<typeof setTimeout> | null = null;\n private boundVisibilityHandler: (() => void) | null = null;\n\n /** Synchronous getter for the current connectivity state */\n public get IsConnected(): boolean {\n return this.isConnected.value;\n }\n\n /**\n * Begin polling the health check endpoint.\n * Call once after workspace initialization succeeds.\n */\n public Start(healthCheckUrl: string): void {\n if (this.healthCheckUrl) {\n this.Stop(); // idempotent restart\n }\n this.healthCheckUrl = healthCheckUrl;\n this.consecutiveFailures = 0;\n this.isConnected.next(true);\n\n this.attachVisibilityListener();\n this.scheduleNextPoll();\n }\n\n /** Stop polling and clean up resources */\n public Stop(): void {\n this.clearPollTimer();\n this.detachVisibilityListener();\n this.healthCheckUrl = null;\n }\n\n /** Force an immediate health check and return the result */\n public async CheckNow(): Promise<boolean> {\n if (!this.healthCheckUrl) {\n return this.isConnected.value;\n }\n const reachable = await this.ping();\n this.applyPingResult(reachable);\n return this.isConnected.value;\n }\n\n ngOnDestroy(): void {\n this.Stop();\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async ping(): Promise<boolean> {\n if (!this.healthCheckUrl) return false;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n ServerConnectivityService.FETCH_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.healthCheckUrl, {\n method: 'GET',\n signal: controller.signal,\n cache: 'no-store',\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private applyPingResult(reachable: boolean): void {\n if (reachable) {\n this.onPingSuccess();\n } else {\n this.onPingFailure();\n }\n }\n\n private onPingSuccess(): void {\n this.consecutiveFailures = 0;\n if (!this.isConnected.value) {\n this.isConnected.next(true);\n LogStatus('Server connectivity restored');\n }\n }\n\n private onPingFailure(): void {\n this.consecutiveFailures++;\n if (\n this.isConnected.value &&\n this.consecutiveFailures >= ServerConnectivityService.FAILURES_BEFORE_DISCONNECT\n ) {\n this.isConnected.next(false);\n LogError(`Server connectivity lost after ${this.consecutiveFailures} consecutive failures`);\n }\n }\n\n private scheduleNextPoll(): void {\n this.clearPollTimer();\n\n // Don't poll while the tab is hidden — we'll check on focus\n if (typeof document !== 'undefined' && document.hidden) {\n return;\n }\n\n const interval = this.isConnected.value\n ? ServerConnectivityService.POLL_INTERVAL_CONNECTED_MS\n : ServerConnectivityService.POLL_INTERVAL_DISCONNECTED_MS;\n\n this.pollingTimerId = setTimeout(async () => {\n await this.CheckNow();\n this.scheduleNextPoll();\n }, interval);\n }\n\n private clearPollTimer(): void {\n if (this.pollingTimerId != null) {\n clearTimeout(this.pollingTimerId);\n this.pollingTimerId = null;\n }\n }\n\n private attachVisibilityListener(): void {\n if (typeof document === 'undefined') return;\n\n this.boundVisibilityHandler = () => this.onVisibilityChange();\n document.addEventListener('visibilitychange', this.boundVisibilityHandler);\n }\n\n private detachVisibilityListener(): void {\n if (this.boundVisibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.boundVisibilityHandler);\n this.boundVisibilityHandler = null;\n }\n }\n\n private onVisibilityChange(): void {\n if (!this.healthCheckUrl) return;\n\n if (document.hidden) {\n this.clearPollTimer();\n } else {\n // Tab became visible — check immediately, then resume polling\n this.CheckNow().then(() => this.scheduleNextPoll());\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server-connectivity.service.js","sourceRoot":"","sources":["../../../src/lib/services/server-connectivity.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable, Subscription } from 'rxjs';\nimport { LogError, LogStatus } from '@memberjunction/core';\nimport { GraphQLDataProvider, SocketConnectionState } from '@memberjunction/graphql-dataprovider';\n\n/**\n * Monitors MJAPI connectivity.\n *\n * Primary signal is the GraphQLDataProvider's WebSocket state (graphql-ws).\n * When the socket emits 'disconnected' (retries already exhausted by graphql-ws),\n * we fall back to polling /healthcheck until it returns 200, then force a socket\n * reconnect so the next subscription picks up a fresh client.\n *\n * 'unknown' (no active socket — either never opened or cleanly disposed) is\n * treated as healthy: absence of signal is not a signal of failure.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class ServerConnectivityService implements OnDestroy {\n private static readonly POLL_INTERVAL_MS = 30_000;\n private static readonly FETCH_TIMEOUT_MS = 5_000;\n\n private readonly isConnected = new BehaviorSubject<boolean>(true);\n public readonly IsConnected$: Observable<boolean> = this.isConnected.asObservable();\n\n private healthCheckUrl: string | null = null;\n private pollingTimerId: ReturnType<typeof setTimeout> | null = null;\n private socketSubscription: Subscription | null = null;\n private boundVisibilityHandler: (() => void) | null = null;\n\n /** Synchronous getter for the current connectivity state */\n public get IsConnected(): boolean {\n return this.isConnected.value;\n }\n\n /**\n * Begin monitoring connectivity.\n * Subscribes to the graphql-ws socket state and only polls /healthcheck\n * while the socket reports 'disconnected'.\n */\n public Start(healthCheckUrl: string): void {\n if (this.healthCheckUrl) {\n this.Stop(); // idempotent restart\n }\n this.healthCheckUrl = healthCheckUrl;\n this.isConnected.next(true);\n\n this.attachVisibilityListener();\n this.subscribeToSocketState();\n }\n\n /** Stop monitoring and clean up resources */\n public Stop(): void {\n this.clearPollTimer();\n this.detachVisibilityListener();\n if (this.socketSubscription) {\n this.socketSubscription.unsubscribe();\n this.socketSubscription = null;\n }\n this.healthCheckUrl = null;\n }\n\n /** Force an immediate health check and return the result */\n public async CheckNow(): Promise<boolean> {\n if (!this.healthCheckUrl) {\n return this.isConnected.value;\n }\n await this.runHealthCheck();\n return this.isConnected.value;\n }\n\n ngOnDestroy(): void {\n this.Stop();\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private subscribeToSocketState(): void {\n const provider = GraphQLDataProvider.Instance;\n if (!provider) {\n LogError('ServerConnectivityService: GraphQLDataProvider instance not available');\n return;\n }\n this.socketSubscription = provider.SocketConnectivity$.subscribe(state => {\n this.onSocketStateChange(state);\n });\n }\n\n private onSocketStateChange(state: SocketConnectionState): void {\n if (state === 'disconnected') {\n if (this.isConnected.value) {\n this.isConnected.next(false);\n LogError('Server connectivity lost (WebSocket closed)');\n }\n this.scheduleNextPoll();\n } else {\n // 'connected' or 'unknown' — treat as healthy\n this.clearPollTimer();\n if (!this.isConnected.value) {\n this.isConnected.next(true);\n LogStatus('Server connectivity restored (WebSocket reconnected)');\n }\n }\n }\n\n private async ping(): Promise<boolean> {\n if (!this.healthCheckUrl) return false;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n ServerConnectivityService.FETCH_TIMEOUT_MS\n );\n\n try {\n const response = await fetch(this.healthCheckUrl, {\n method: 'GET',\n signal: controller.signal,\n cache: 'no-store',\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async runHealthCheck(): Promise<void> {\n const reachable = await this.ping();\n if (reachable) {\n this.clearPollTimer();\n if (!this.isConnected.value) {\n this.isConnected.next(true);\n LogStatus('Server connectivity restored (/healthcheck OK)');\n }\n // Dispose the stale socket so the next subscription creates a fresh client\n GraphQLDataProvider.Instance?.ForceSocketReconnect();\n } else {\n // Still unreachable — schedule another poll\n this.scheduleNextPoll();\n }\n }\n\n private scheduleNextPoll(): void {\n this.clearPollTimer();\n\n // Don't poll while the tab is hidden — we'll check on focus\n if (typeof document !== 'undefined' && document.hidden) {\n return;\n }\n\n this.pollingTimerId = setTimeout(\n () => this.runHealthCheck(),\n ServerConnectivityService.POLL_INTERVAL_MS\n );\n }\n\n private clearPollTimer(): void {\n if (this.pollingTimerId != null) {\n clearTimeout(this.pollingTimerId);\n this.pollingTimerId = null;\n }\n }\n\n private attachVisibilityListener(): void {\n if (typeof document === 'undefined') return;\n\n this.boundVisibilityHandler = () => this.onVisibilityChange();\n document.addEventListener('visibilitychange', this.boundVisibilityHandler);\n }\n\n private detachVisibilityListener(): void {\n if (this.boundVisibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.boundVisibilityHandler);\n this.boundVisibilityHandler = null;\n }\n }\n\n private onVisibilityChange(): void {\n if (!this.healthCheckUrl) return;\n\n if (document.hidden) {\n this.clearPollTimer();\n } else if (!this.isConnected.value) {\n // Tab became visible while disconnected — check immediately\n this.runHealthCheck();\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-explorer-core",
|
|
3
|
-
"version": "5.27.
|
|
3
|
+
"version": "5.27.1",
|
|
4
4
|
"description": "MemberJunction Explorer: Core Angular Components",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -32,45 +32,45 @@
|
|
|
32
32
|
"@angular/cdk": "21.1.3",
|
|
33
33
|
"@angular/platform-browser": "21.1.3",
|
|
34
34
|
"@auth0/auth0-angular": "^2.6.0",
|
|
35
|
-
"@memberjunction/ai-core-plus": "5.27.
|
|
36
|
-
"@memberjunction/ai-engine-base": "5.27.
|
|
37
|
-
"@memberjunction/communication-types": "5.27.
|
|
38
|
-
"@memberjunction/core": "5.27.
|
|
39
|
-
"@memberjunction/core-entities": "5.27.
|
|
40
|
-
"@memberjunction/entity-communications-client": "5.27.
|
|
41
|
-
"@memberjunction/export-engine": "5.27.
|
|
42
|
-
"@memberjunction/global": "5.27.
|
|
43
|
-
"@memberjunction/graphql-dataprovider": "5.27.
|
|
44
|
-
"@memberjunction/ng-ai-test-harness": "5.27.
|
|
45
|
-
"@memberjunction/ng-artifacts": "5.27.
|
|
46
|
-
"@memberjunction/ng-auth-services": "5.27.
|
|
47
|
-
"@memberjunction/ng-base-application": "5.27.
|
|
48
|
-
"@memberjunction/ng-base-forms": "5.27.
|
|
49
|
-
"@memberjunction/ng-container-directives": "5.27.
|
|
50
|
-
"@memberjunction/ng-conversations": "5.27.
|
|
51
|
-
"@memberjunction/ng-dashboard-viewer": "5.27.
|
|
52
|
-
"@memberjunction/ng-dashboards": "5.27.
|
|
53
|
-
"@memberjunction/ng-entity-form-dialog": "5.27.
|
|
54
|
-
"@memberjunction/ng-entity-permissions": "5.27.
|
|
55
|
-
"@memberjunction/ng-entity-viewer": "5.27.
|
|
56
|
-
"@memberjunction/ng-explorer-settings": "5.27.
|
|
57
|
-
"@memberjunction/ng-export-service": "5.27.
|
|
58
|
-
"@memberjunction/ng-file-storage": "5.27.
|
|
59
|
-
"@memberjunction/ng-generic-dialog": "5.27.
|
|
60
|
-
"@memberjunction/ng-list-detail-grid": "5.27.
|
|
61
|
-
"@memberjunction/ng-notifications": "5.27.
|
|
62
|
-
"@memberjunction/ng-query-viewer": "5.27.
|
|
63
|
-
"@memberjunction/ng-record-changes": "5.27.
|
|
64
|
-
"@memberjunction/ng-record-selector": "5.27.
|
|
65
|
-
"@memberjunction/ng-record-tags": "5.27.
|
|
66
|
-
"@memberjunction/ng-resource-permissions": "5.27.
|
|
67
|
-
"@memberjunction/ng-search": "5.27.
|
|
68
|
-
"@memberjunction/ng-shared": "5.27.
|
|
69
|
-
"@memberjunction/ng-shared-generic": "5.27.
|
|
70
|
-
"@memberjunction/ng-ui-components": "5.27.
|
|
71
|
-
"@memberjunction/ng-user-avatar": "5.27.
|
|
72
|
-
"@memberjunction/ng-word-cloud": "5.27.
|
|
73
|
-
"@memberjunction/templates-base-types": "5.27.
|
|
35
|
+
"@memberjunction/ai-core-plus": "5.27.1",
|
|
36
|
+
"@memberjunction/ai-engine-base": "5.27.1",
|
|
37
|
+
"@memberjunction/communication-types": "5.27.1",
|
|
38
|
+
"@memberjunction/core": "5.27.1",
|
|
39
|
+
"@memberjunction/core-entities": "5.27.1",
|
|
40
|
+
"@memberjunction/entity-communications-client": "5.27.1",
|
|
41
|
+
"@memberjunction/export-engine": "5.27.1",
|
|
42
|
+
"@memberjunction/global": "5.27.1",
|
|
43
|
+
"@memberjunction/graphql-dataprovider": "5.27.1",
|
|
44
|
+
"@memberjunction/ng-ai-test-harness": "5.27.1",
|
|
45
|
+
"@memberjunction/ng-artifacts": "5.27.1",
|
|
46
|
+
"@memberjunction/ng-auth-services": "5.27.1",
|
|
47
|
+
"@memberjunction/ng-base-application": "5.27.1",
|
|
48
|
+
"@memberjunction/ng-base-forms": "5.27.1",
|
|
49
|
+
"@memberjunction/ng-container-directives": "5.27.1",
|
|
50
|
+
"@memberjunction/ng-conversations": "5.27.1",
|
|
51
|
+
"@memberjunction/ng-dashboard-viewer": "5.27.1",
|
|
52
|
+
"@memberjunction/ng-dashboards": "5.27.1",
|
|
53
|
+
"@memberjunction/ng-entity-form-dialog": "5.27.1",
|
|
54
|
+
"@memberjunction/ng-entity-permissions": "5.27.1",
|
|
55
|
+
"@memberjunction/ng-entity-viewer": "5.27.1",
|
|
56
|
+
"@memberjunction/ng-explorer-settings": "5.27.1",
|
|
57
|
+
"@memberjunction/ng-export-service": "5.27.1",
|
|
58
|
+
"@memberjunction/ng-file-storage": "5.27.1",
|
|
59
|
+
"@memberjunction/ng-generic-dialog": "5.27.1",
|
|
60
|
+
"@memberjunction/ng-list-detail-grid": "5.27.1",
|
|
61
|
+
"@memberjunction/ng-notifications": "5.27.1",
|
|
62
|
+
"@memberjunction/ng-query-viewer": "5.27.1",
|
|
63
|
+
"@memberjunction/ng-record-changes": "5.27.1",
|
|
64
|
+
"@memberjunction/ng-record-selector": "5.27.1",
|
|
65
|
+
"@memberjunction/ng-record-tags": "5.27.1",
|
|
66
|
+
"@memberjunction/ng-resource-permissions": "5.27.1",
|
|
67
|
+
"@memberjunction/ng-search": "5.27.1",
|
|
68
|
+
"@memberjunction/ng-shared": "5.27.1",
|
|
69
|
+
"@memberjunction/ng-shared-generic": "5.27.1",
|
|
70
|
+
"@memberjunction/ng-ui-components": "5.27.1",
|
|
71
|
+
"@memberjunction/ng-user-avatar": "5.27.1",
|
|
72
|
+
"@memberjunction/ng-word-cloud": "5.27.1",
|
|
73
|
+
"@memberjunction/templates-base-types": "5.27.1",
|
|
74
74
|
"golden-layout": "^2.6.0",
|
|
75
75
|
"rxjs": "^7.8.2",
|
|
76
76
|
"tslib": "^2.8.1"
|