@genesislcap/foundation-comms 14.372.0 → 14.373.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.
@@ -27,13 +27,39 @@ export declare class DefaultHttpConnect implements Connect {
27
27
  get isConnected(): boolean;
28
28
  set isConnected(value: boolean);
29
29
  private ongoingTurbo?;
30
+ /**
31
+ * Amount of time (in milliseconds) to wait when another tab is handling session reauthentication.
32
+ */
33
+ private static readonly REAUTH_WAIT_MS;
34
+ /**
35
+ * Amount of time (in milliseconds) to wait before retrying a failed request.
36
+ */
37
+ private static readonly RETRY_DELAY_MS;
38
+ /**
39
+ * Maximum number of retry attempts for failed requests.
40
+ */
41
+ private static readonly MAX_RETRIES;
30
42
  /**
31
43
  * A map of active messages in "polling" mode.
32
44
  * @remarks Equivalent to messaged being streamed in a WebSocket connection.
33
45
  * @internal
34
46
  */
35
47
  private streams;
48
+ /**
49
+ * Promise that tracks an ongoing session refresh to prevent concurrent refreshes from the same instance.
50
+ * @internal
51
+ */
52
+ private ongoingRefresh;
36
53
  constructor(http: Http, messageBuilder: MessageBuilder, session: Session, metaCache: MetaCache, serializer: JSONSerializer, config: HttpConnectConfig);
54
+ /**
55
+ * Attempts to refresh the session when a 401 Unauthorized error is received.
56
+ * Uses a lock mechanism to prevent multiple tabs from trying to reauthenticate simultaneously.
57
+ * Also prevents concurrent refresh attempts from the same instance by reusing an ongoing refresh.
58
+ * If another tab is handling the refresh, waits and retries once to verify the result.
59
+ * @internal
60
+ * @returns A Promise that resolves to `true` if session was refreshed successfully, `false` otherwise.
61
+ */
62
+ private handleSessionRefresh;
37
63
  /**
38
64
  * Checks if the host is available by sending a heartbeat ping message and waiting for the response.
39
65
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"http.connect.d.ts","sourceRoot":"","sources":["../../../src/connect/http.connect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.connect.d.ts","sourceRoot":"","sources":["../../../src/connect/http.connect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAI/D,OAAO,EACL,eAAe,EAMf,UAAU,EAMX,MAAM,MAAM,CAAC;AACd,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EACL,YAAY,EAEZ,OAAO,EACP,cAAc,EAGd,aAAa,EACb,gBAAgB,EACjB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAiB5C;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,OAAO;IA0DjC,IAAI,EAAE,IAAI;IACP,OAAO,CAAC,cAAc;IAC7B,OAAO,CAAC,OAAO;IACb,OAAO,CAAC,SAAS;IACZ,OAAO,CAAC,UAAU;IACf,OAAO,CAAC,MAAM;IA7D5B,IAAI,EAAE,MAAM,CAAC;IAGb,SAAS,UAAS;IAGzB,OAAO,CAAC,YAAY,CAAkB;IAItC,kBAAkB,2BAA0C;IAE5D,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,CAA2C;IAE5E,IACW,WAAW,IAAI,OAAO,CAEhC;IAED,IAAI,WAAW,CAAC,KAAK,EAJK,OAIL,EAGpB;IAGD,OAAO,CAAC,YAAY,CAAC,CAAkC;IAEvD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAEtD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAEtD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAa;IAEhD;;;;OAIG;IACH,OAAO,CAAC,OAAO,CAAsC;IAErD;;;OAGG;IACH,OAAO,CAAC,cAAc,CAAiC;gBAGxC,IAAI,EAAE,IAAI,EACC,cAAc,EAAE,cAAc,EACrC,OAAO,EAAE,OAAO,EACd,SAAS,EAAE,SAAS,EACf,UAAU,EAAE,cAAc,EACvB,MAAM,EAAE,iBAAiB;IAGtD;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAwC5B;;;;OAIG;YACW,SAAS;IAcjB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAe3E,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,OAAO;IAQjB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB7C,UAAU,IAAI,IAAI;IAOZ,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAapE,MAAM,CACX,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,QAAQ,EACnB,OAAO,EAAE,QAAQ,EACjB,MAAM,CAAC,EAAE,GAAG,GACX,gBAAgB,CAAC,OAAO,CAAC;IAqErB,WAAW,CAChB,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,QAAmB,EAC9B,OAAO,GAAE,QAAmB,EAC5B,MAAM,CAAC,EAAE,GAAG,EACZ,YAAY,QAAK,GAChB,UAAU,CAAC,GAAG,EAAE,CAAC;IAMpB,yBAAyB,CACvB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,QAAQ,EACnB,OAAO,EAAE,QAAQ,EACjB,MAAM,CAAC,EAAE,GAAG,GACX,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAKnB,UAAU;IAaX,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD,UAAU,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrD,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IAU9E,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAYlE,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IASxE,qBAAqB,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC;IASvD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAYpF,OAAO,CAAC,UAAU;IAkDlB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,8BAA8B;IAmBtC,OAAO,CAAC,MAAM,CAAC,OAAO;IAItB,OAAO,CAAC,MAAM,CAAC,MAAM;CAUtB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,+DAAsE,CAAC"}
@@ -19,6 +19,14 @@ export interface Http {
19
19
  get<T = any>(url: string, requestInit: HttpRequestInit): Promise<T>;
20
20
  post<T = any>(url: string, requestInit: HttpRequestInit): Promise<T>;
21
21
  }
22
+ /**
23
+ * An error thrown when an HTTP request fails with a non-2xx status code.
24
+ * @public
25
+ */
26
+ export interface HttpError extends Error {
27
+ status: number;
28
+ message: string;
29
+ }
22
30
  /**
23
31
  * The default implementation of the Http interface.
24
32
  * @public
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/connect/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACtE;AAED;;;GAGG;AACH,qBAAa,WAAY,YAAW,IAAI;IACV,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,cAAc;IAEjD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC;IAW7D,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC;CAU5E;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,4DAA4D,CAAC"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/connect/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACtE;AAED;;;GAGG;AACH,MAAM,WAAW,SAAU,SAAQ,KAAK;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AASD;;;GAGG;AACH,qBAAa,WAAY,YAAW,IAAI;IACV,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,cAAc;IAEjD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC;IAc7D,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC;CAa5E;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,4DAA4D,CAAC"}
@@ -1,12 +1,13 @@
1
- export * from './connect.events';
2
1
  export * from './connect';
2
+ export * from './connect.events';
3
3
  export * from './connect.types';
4
- export * from './http.connect';
5
4
  export * from './http';
5
+ export * from './http.connect';
6
6
  export * from './message';
7
+ export * from './reconnectStrategy';
7
8
  export * from './serializers';
8
- export * from './socket.status';
9
+ export * from './sessionRefresh';
9
10
  export * from './socket';
11
+ export * from './socket.status';
10
12
  export * from './socket.types';
11
- export * from './reconnectStrategy';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connect/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connect/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Result of a session refresh attempt.
3
+ * @public
4
+ */
5
+ export declare enum SessionRefreshResult {
6
+ /** Session was successfully refreshed */
7
+ SUCCESS = "SUCCESS",
8
+ /** Refresh failed - page will reload */
9
+ FAILED = "FAILED",
10
+ /** Another tab is handling the refresh */
11
+ HANDLED_BY_OTHER_TAB = "HANDLED_BY_OTHER_TAB",
12
+ /** Session was already valid */
13
+ ALREADY_VALID = "ALREADY_VALID"
14
+ }
15
+ /**
16
+ * Attempts to refresh the session when authentication has expired.
17
+ * Uses a localStorage-based lock mechanism to prevent multiple tabs from
18
+ * trying to reauthenticate simultaneously.
19
+ *
20
+ * @public
21
+ * @param options - Optional configuration
22
+ * @param options.reloadOnFailure - Whether to reload the page if refresh fails (default: true)
23
+ * @returns The result of the refresh attempt
24
+ */
25
+ export declare function tryRefreshSession(options?: {
26
+ reloadOnFailure?: boolean;
27
+ }): Promise<SessionRefreshResult>;
28
+ /**
29
+ * Subscribes to storage events to detect when another tab has finished reauthenticating.
30
+ * @public
31
+ * @param callback - Called when the other tab finishes reauthentication
32
+ * @returns A function to unsubscribe from the storage events
33
+ */
34
+ export declare function onOtherTabReauthComplete(callback: () => void): () => void;
35
+ //# sourceMappingURL=sessionRefresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionRefresh.d.ts","sourceRoot":"","sources":["../../../src/connect/sessionRefresh.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,oBAAY,oBAAoB;IAC9B,yCAAyC;IACzC,OAAO,YAAY;IACnB,wCAAwC;IACxC,MAAM,WAAW;IACjB,0CAA0C;IAC1C,oBAAoB,yBAAyB;IAC7C,gCAAgC;IAChC,aAAa,kBAAkB;CAChC;AA6DD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,OAAO,CAAC,oBAAoB,CAAC,CA+B/B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CASzE"}
@@ -153,9 +153,7 @@ export declare class DefaultSocket implements Socket {
153
153
  * more info here https://docs.genesis.global/docs/develop/server-capabilities/access-control/authentication/#sessiontimeoutmins
154
154
  */
155
155
  private handleExpiredSession;
156
- private static REAUTH_ACTION_LOCK_KEY;
157
156
  private handleCookieBasedReauth;
158
- private onStorageEvent;
159
157
  private updateSessionAndSubscriptions;
160
158
  private prepareHeartbeat;
161
159
  private reconnect;
@@ -1 +1 @@
1
- {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../../src/connect/socket.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../../src/connect/socket.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { __awaiter, __decorate, __param } from "tslib";
2
- import { FORCE_HTTP, GENESIS_SOCKET_URL } from '@genesislcap/foundation-utils';
2
+ import { FORCE_HTTP, GENESIS_SOCKET_URL, isFeatureActivated } from '@genesislcap/foundation-utils';
3
3
  import { observable } from '@microsoft/fast-element';
4
4
  import { DI } from '@microsoft/fast-foundation';
5
5
  import { scan } from 'rxjs';
@@ -214,7 +214,7 @@ DefaultConnect = __decorate([
214
214
  __param(4, ConnectConfig)
215
215
  ], DefaultConnect);
216
216
  export { DefaultConnect };
217
- const useHttpConnect = FORCE_HTTP === 'true' || false;
217
+ const useHttpConnect = FORCE_HTTP === 'true' || isFeatureActivated('forceHttp');
218
218
  /**
219
219
  * The DI token for the Connect interface (WS-only).
220
220
  * @internal
@@ -3,13 +3,15 @@ import { __awaiter, __decorate, __param } from "tslib";
3
3
  import { JSONSerializer } from '@genesislcap/foundation-utils';
4
4
  import { observable, volatile } from '@microsoft/fast-element';
5
5
  import { DI } from '@microsoft/fast-foundation';
6
- import { BehaviorSubject, concat, finalize, from, interval, scan, switchMap, takeWhile, } from 'rxjs';
6
+ import { StatusCodes } from 'http-status-codes';
7
+ import { BehaviorSubject, concat, filter, finalize, from, interval, retry, scan, switchMap, takeWhile, timer, } from 'rxjs';
7
8
  import { MetaCache } from '../metadata';
8
9
  import { Session } from '../session';
9
10
  import { logger } from '../utils';
10
11
  import { Http } from './http';
11
12
  import { HttpConnectConfig } from './http.connect.types';
12
13
  import { EventMessageType, MessageBuilder, MessageType, } from './message';
14
+ import { SessionRefreshResult, tryRefreshSession } from './sessionRefresh';
13
15
  import { updateState } from './updateState';
14
16
  /**
15
17
  * The default implementation for the Connect interface (HTTP-only).
@@ -45,6 +47,49 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
45
47
  * @internal
46
48
  */
47
49
  this.streams = new Map();
50
+ /**
51
+ * Promise that tracks an ongoing session refresh to prevent concurrent refreshes from the same instance.
52
+ * @internal
53
+ */
54
+ this.ongoingRefresh = null;
55
+ }
56
+ /**
57
+ * Attempts to refresh the session when a 401 Unauthorized error is received.
58
+ * Uses a lock mechanism to prevent multiple tabs from trying to reauthenticate simultaneously.
59
+ * Also prevents concurrent refresh attempts from the same instance by reusing an ongoing refresh.
60
+ * If another tab is handling the refresh, waits and retries once to verify the result.
61
+ * @internal
62
+ * @returns A Promise that resolves to `true` if session was refreshed successfully, `false` otherwise.
63
+ */
64
+ handleSessionRefresh() {
65
+ // If a refresh is already in progress, reuse it
66
+ if (this.ongoingRefresh) {
67
+ return this.ongoingRefresh;
68
+ }
69
+ // Start a new refresh and clear the lock when done
70
+ this.ongoingRefresh = (() => __awaiter(this, void 0, void 0, function* () {
71
+ const host = this.host;
72
+ this.disconnect();
73
+ let result = yield tryRefreshSession();
74
+ // If another tab is handling the refresh, wait and check once more
75
+ if (result === SessionRefreshResult.HANDLED_BY_OTHER_TAB) {
76
+ yield new Promise((resolve) => {
77
+ setTimeout(resolve, DefaultHttpConnect_1.REAUTH_WAIT_MS);
78
+ });
79
+ // Check if the other tab succeeded
80
+ result = yield tryRefreshSession();
81
+ }
82
+ // Reconnect if session is valid (either we refreshed it or another tab did)
83
+ if (result === SessionRefreshResult.SUCCESS ||
84
+ result === SessionRefreshResult.ALREADY_VALID) {
85
+ this.connect(host);
86
+ return true;
87
+ }
88
+ return false;
89
+ }))().finally(() => {
90
+ this.ongoingRefresh = null;
91
+ });
92
+ return this.ongoingRefresh;
48
93
  }
49
94
  /**
50
95
  * Checks if the host is available by sending a heartbeat ping message and waiting for the response.
@@ -68,9 +113,18 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
68
113
  });
69
114
  }
70
115
  send(message, needsHandling) {
71
- return this.http.post(DefaultHttpConnect_1.getUrl(this.host, message), {
72
- body: DefaultHttpConnect_1.getBody(message),
73
- headers: this.messageBuilder.createHTTPHeadersFromMessage(message),
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ // Wait for any ongoing session refresh to complete before sending
118
+ if (this.ongoingRefresh) {
119
+ const refreshSucceeded = yield this.ongoingRefresh;
120
+ if (!refreshSucceeded) {
121
+ throw new Error('Cannot send message: session refresh failed');
122
+ }
123
+ }
124
+ return this.http.post(DefaultHttpConnect_1.getUrl(this.host, message), {
125
+ body: DefaultHttpConnect_1.getBody(message),
126
+ headers: this.messageBuilder.createHTTPHeadersFromMessage(message),
127
+ });
74
128
  });
75
129
  }
76
130
  httpMode() {
@@ -85,6 +139,11 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
85
139
  connect(host) {
86
140
  return __awaiter(this, void 0, void 0, function* () {
87
141
  this.host = host;
142
+ // below will convert the host to http one if someone is
143
+ // using the feature flag to force http mode but set api host as ws url
144
+ if (this.host.startsWith('ws')) {
145
+ this.host = this.host.replace('ws', 'http');
146
+ }
88
147
  if (!this.host.endsWith('/')) {
89
148
  this.host = this.host.concat('/');
90
149
  }
@@ -127,9 +186,34 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
127
186
  from(this.send(streamMessage)),
128
187
  // subsequent GET requests using the same SOURCE_REF
129
188
  interval$
189
+ .pipe(filter(() => document.visibilityState === 'visible'))
130
190
  .pipe(switchMap((ms) => interval(ms)))
131
191
  .pipe(switchMap(() => from(this.getMessage(streamMessage))))).pipe(takeWhile(() => this.streams.has(SOURCE_REF)), // ensure it stops emitting once removed
132
- finalize(() => {
192
+ retry({
193
+ count: DefaultHttpConnect_1.MAX_RETRIES,
194
+ delay: (err, retryCount) => {
195
+ const error = err;
196
+ logger.error(`Retry ${retryCount}/${DefaultHttpConnect_1.MAX_RETRIES}:`, error.status, error);
197
+ // Only retry if the error is a connection error or auth error
198
+ const isRetryableError = error.status === 0 ||
199
+ (error.status &&
200
+ error.status >= StatusCodes.BAD_REQUEST &&
201
+ error.status < StatusCodes.INTERNAL_SERVER_ERROR);
202
+ if (!isRetryableError) {
203
+ throw err;
204
+ }
205
+ // Remove SOURCE_REF from the map if the error is a 404, meaning the DS has already expired
206
+ if (error.status === StatusCodes.NOT_FOUND) {
207
+ this.streams.delete(SOURCE_REF);
208
+ }
209
+ // Handle 401 by trying to refresh session, then delay for other errors
210
+ if (error.status === StatusCodes.UNAUTHORIZED) {
211
+ return from(this.handleSessionRefresh());
212
+ }
213
+ // For other errors, just delay
214
+ return timer(DefaultHttpConnect_1.RETRY_DELAY_MS);
215
+ },
216
+ }), finalize(() => {
133
217
  logger.debug(`Terminating stream for ${resourceName} - ${SOURCE_REF}`);
134
218
  this.dataLogoff(SOURCE_REF);
135
219
  }));
@@ -145,6 +229,12 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
145
229
  }
146
230
  getMessage(message) {
147
231
  return __awaiter(this, void 0, void 0, function* () {
232
+ if (this.ongoingRefresh) {
233
+ const refreshSucceeded = yield this.ongoingRefresh;
234
+ if (!refreshSucceeded) {
235
+ throw new Error('Cannot get message: session refresh failed');
236
+ }
237
+ }
148
238
  return this.http.get(DefaultHttpConnect_1.getUrl(this.host, message), {
149
239
  headers: this.messageBuilder.createHTTPHeadersFromMessage(message),
150
240
  });
@@ -290,6 +380,18 @@ let DefaultHttpConnect = DefaultHttpConnect_1 = class DefaultHttpConnect {
290
380
  return `${host}${apiEndpoint}`;
291
381
  }
292
382
  };
383
+ /**
384
+ * Amount of time (in milliseconds) to wait when another tab is handling session reauthentication.
385
+ */
386
+ DefaultHttpConnect.REAUTH_WAIT_MS = 5000;
387
+ /**
388
+ * Amount of time (in milliseconds) to wait before retrying a failed request.
389
+ */
390
+ DefaultHttpConnect.RETRY_DELAY_MS = 1000;
391
+ /**
392
+ * Maximum number of retry attempts for failed requests.
393
+ */
394
+ DefaultHttpConnect.MAX_RETRIES = 3;
293
395
  __decorate([
294
396
  observable
295
397
  ], DefaultHttpConnect.prototype, "host", void 0);
@@ -1,6 +1,12 @@
1
1
  import { __awaiter, __decorate, __param } from "tslib";
2
2
  import { JSONSerializer } from '@genesislcap/foundation-utils';
3
3
  import { DI } from '@microsoft/fast-foundation';
4
+ function createHttpError(response) {
5
+ const error = new Error(`HTTP error: ${response.status} ${response.statusText}`);
6
+ error.status = response.status;
7
+ error.message = response.statusText;
8
+ return error;
9
+ }
4
10
  /**
5
11
  * The default implementation of the Http interface.
6
12
  * @public
@@ -12,6 +18,9 @@ let DefaultHttp = class DefaultHttp {
12
18
  get(url, requestInit) {
13
19
  return __awaiter(this, void 0, void 0, function* () {
14
20
  const response = yield fetch(url, Object.assign(Object.assign({ credentials: 'include', headers: { 'Content-type': 'application/json; charset=UTF-8' } }, requestInit), { method: 'GET' }));
21
+ if (!response.ok) {
22
+ throw createHttpError(response);
23
+ }
15
24
  const serializer = requestInit.serializer || this.serializer;
16
25
  return serializer.deserialize(response);
17
26
  });
@@ -19,6 +28,9 @@ let DefaultHttp = class DefaultHttp {
19
28
  post(url, requestInit) {
20
29
  return __awaiter(this, void 0, void 0, function* () {
21
30
  const response = yield fetch(url, Object.assign(Object.assign({ credentials: 'include', headers: { 'Content-type': 'application/json; charset=UTF-8' } }, requestInit), { method: 'POST' }));
31
+ if (!response.ok) {
32
+ throw createHttpError(response);
33
+ }
22
34
  const serializer = requestInit.serializer || this.serializer;
23
35
  return serializer.deserialize(response);
24
36
  });
@@ -1,11 +1,12 @@
1
- export * from './connect.events';
2
1
  export * from './connect';
2
+ export * from './connect.events';
3
3
  export * from './connect.types';
4
- export * from './http.connect';
5
4
  export * from './http';
5
+ export * from './http.connect';
6
6
  export * from './message';
7
+ export * from './reconnectStrategy';
7
8
  export * from './serializers';
8
- export * from './socket.status';
9
+ export * from './sessionRefresh';
9
10
  export * from './socket';
11
+ export * from './socket.status';
10
12
  export * from './socket.types';
11
- export * from './reconnectStrategy';
@@ -0,0 +1,135 @@
1
+ import { __awaiter } from "tslib";
2
+ import { LOGIN_DETAILS_URL, LOGIN_URL } from '@genesislcap/foundation-utils';
3
+ import { DI } from '@genesislcap/web-core';
4
+ import { Http } from './http';
5
+ const REAUTH_ACTION_LOCK_KEY = 'reauthActionLock';
6
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
7
+ const LOCK_TIMEOUT_MS = 30000;
8
+ /**
9
+ * Result of a session refresh attempt.
10
+ * @public
11
+ */
12
+ export var SessionRefreshResult;
13
+ (function (SessionRefreshResult) {
14
+ /** Session was successfully refreshed */
15
+ SessionRefreshResult["SUCCESS"] = "SUCCESS";
16
+ /** Refresh failed - page will reload */
17
+ SessionRefreshResult["FAILED"] = "FAILED";
18
+ /** Another tab is handling the refresh */
19
+ SessionRefreshResult["HANDLED_BY_OTHER_TAB"] = "HANDLED_BY_OTHER_TAB";
20
+ /** Session was already valid */
21
+ SessionRefreshResult["ALREADY_VALID"] = "ALREADY_VALID";
22
+ })(SessionRefreshResult || (SessionRefreshResult = {}));
23
+ /**
24
+ * Clears stale reauth locks that might have been left behind.
25
+ * @internal
26
+ */
27
+ function clearStaleLock() {
28
+ const lockTimestamp = parseInt(localStorage.getItem(REAUTH_ACTION_LOCK_KEY) || '0');
29
+ if (Date.now() - lockTimestamp > LOCK_TIMEOUT_MS) {
30
+ localStorage.removeItem(REAUTH_ACTION_LOCK_KEY);
31
+ }
32
+ }
33
+ /**
34
+ * Acquires the reauth lock if available.
35
+ * @internal
36
+ * @returns true if lock was acquired, false if another tab holds it
37
+ */
38
+ function tryAcquireLock() {
39
+ if (!localStorage.getItem(REAUTH_ACTION_LOCK_KEY)) {
40
+ localStorage.setItem(REAUTH_ACTION_LOCK_KEY, Date.now().toString());
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ /**
46
+ * Releases the reauth lock.
47
+ * @internal
48
+ */
49
+ function releaseLock() {
50
+ localStorage.removeItem(REAUTH_ACTION_LOCK_KEY);
51
+ }
52
+ /**
53
+ * Checks if the user is currently authenticated.
54
+ * @internal
55
+ */
56
+ function isAuthenticated() {
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ const loginDetailsFetch = yield fetch(LOGIN_DETAILS_URL, { credentials: 'include' });
59
+ const loginDetails = yield loginDetailsFetch.json();
60
+ return loginDetails.MESSAGE_TYPE === 'EVENT_LOGIN_DETAILS_ACK';
61
+ });
62
+ }
63
+ /**
64
+ * Attempts to refresh the session via EVENT_LOGIN_AUTH.
65
+ * @internal
66
+ * @returns true if refresh was successful (HTTP 2xx response)
67
+ * @throws If the network request fails (connection error, timeout, etc.)
68
+ */
69
+ function performRefresh() {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ const http = DI.getOrCreateDOMContainer().get(Http);
72
+ const refreshLoginResponse = yield http.post(LOGIN_URL, {
73
+ body: JSON.stringify({
74
+ DETAILS: {},
75
+ MESSAGE_TYPE: 'EVENT_LOGIN_AUTH',
76
+ }),
77
+ });
78
+ return refreshLoginResponse.MESSAGE_TYPE === 'EVENT_LOGIN_AUTH_ACK';
79
+ });
80
+ }
81
+ /**
82
+ * Attempts to refresh the session when authentication has expired.
83
+ * Uses a localStorage-based lock mechanism to prevent multiple tabs from
84
+ * trying to reauthenticate simultaneously.
85
+ *
86
+ * @public
87
+ * @param options - Optional configuration
88
+ * @param options.reloadOnFailure - Whether to reload the page if refresh fails (default: true)
89
+ * @returns The result of the refresh attempt
90
+ */
91
+ export function tryRefreshSession() {
92
+ return __awaiter(this, arguments, void 0, function* (options = {}) {
93
+ const { reloadOnFailure = true } = options;
94
+ // Clear any stale locks from crashed tabs
95
+ clearStaleLock();
96
+ if (!tryAcquireLock()) {
97
+ // Another tab is handling the refresh
98
+ return SessionRefreshResult.HANDLED_BY_OTHER_TAB;
99
+ }
100
+ try {
101
+ // Session might already be extended on another tab, so we first check if user is
102
+ // authenticated. Cookies are shared between tabs so we can just retry.
103
+ if (yield isAuthenticated()) {
104
+ return SessionRefreshResult.ALREADY_VALID;
105
+ }
106
+ const refreshSuccessful = yield performRefresh();
107
+ if (!refreshSuccessful) {
108
+ if (reloadOnFailure) {
109
+ window.location.reload();
110
+ }
111
+ return SessionRefreshResult.FAILED;
112
+ }
113
+ return SessionRefreshResult.SUCCESS;
114
+ }
115
+ finally {
116
+ releaseLock();
117
+ }
118
+ });
119
+ }
120
+ /**
121
+ * Subscribes to storage events to detect when another tab has finished reauthenticating.
122
+ * @public
123
+ * @param callback - Called when the other tab finishes reauthentication
124
+ * @returns A function to unsubscribe from the storage events
125
+ */
126
+ export function onOtherTabReauthComplete(callback) {
127
+ const handler = (event) => {
128
+ if (event.key === REAUTH_ACTION_LOCK_KEY && event.newValue === null) {
129
+ callback();
130
+ window.removeEventListener('storage', handler);
131
+ }
132
+ };
133
+ window.addEventListener('storage', handler);
134
+ return () => window.removeEventListener('storage', handler);
135
+ }
@@ -1,7 +1,5 @@
1
- var DefaultSocket_1;
2
1
  import { __awaiter, __decorate, __param } from "tslib";
3
2
  import { User } from '@genesislcap/foundation-user';
4
- import { LOGIN_DETAILS_URL, LOGIN_URL } from '@genesislcap/foundation-utils';
5
3
  import { GENESIS_SOCKET_URL, JSONSerializer, UUID } from '@genesislcap/foundation-utils';
6
4
  import { DOM, observable } from '@microsoft/fast-element';
7
5
  import { DI } from '@microsoft/fast-foundation';
@@ -14,6 +12,7 @@ import { ConnectConfig } from './connect.types';
14
12
  import { Http } from './http';
15
13
  import { EventMessageType, MessageBuilder, MessageType, } from './message';
16
14
  import { exponentialScheduler, linearScheduler, MAX_RECONNECT_ATTEMPTS, retryInterval, SocketReconnectStrategy, } from './reconnectStrategy';
15
+ import { onOtherTabReauthComplete, SessionRefreshResult, tryRefreshSession, } from './sessionRefresh';
17
16
  import { SocketStatus } from './socket.status';
18
17
  import { Ticker } from './ticker';
19
18
  /**
@@ -85,7 +84,7 @@ export class SocketMessageHandler {
85
84
  * Default Socket implementation.
86
85
  * @public
87
86
  */
88
- let DefaultSocket = DefaultSocket_1 = class DefaultSocket {
87
+ let DefaultSocket = class DefaultSocket {
89
88
  get isConfigured() {
90
89
  return this.status.isConfigured;
91
90
  }
@@ -292,51 +291,18 @@ let DefaultSocket = DefaultSocket_1 = class DefaultSocket {
292
291
  }
293
292
  handleCookieBasedReauth() {
294
293
  return __awaiter(this, void 0, void 0, function* () {
295
- // checking for stale locks, so we don't block reauth forever
296
- const lockTimestamp = parseInt(localStorage.getItem(DefaultSocket_1.REAUTH_ACTION_LOCK_KEY) || '0');
297
- const LOCK_TIMEOUT_MS = 30000;
298
- if (Date.now() - lockTimestamp > LOCK_TIMEOUT_MS) {
299
- localStorage.removeItem(DefaultSocket_1.REAUTH_ACTION_LOCK_KEY);
294
+ const result = yield tryRefreshSession();
295
+ if (result === SessionRefreshResult.HANDLED_BY_OTHER_TAB) {
296
+ // Session is already being reauthenticated on another tab, wait for it to finish
297
+ onOtherTabReauthComplete(() => this.connect());
298
+ return;
300
299
  }
301
- // this is to prevent multiple tabs from trying to reauthenticate at the same time
302
- if (!localStorage.getItem(DefaultSocket_1.REAUTH_ACTION_LOCK_KEY)) {
303
- localStorage.setItem(DefaultSocket_1.REAUTH_ACTION_LOCK_KEY, Date.now().toString());
304
- // session might already be extended on another tab, so we first check if user is
305
- // authenticated and if it is we just reconnect as cookies are shared between tabs and
306
- // the browser uses latest one when establishing connection with ws
307
- const loginDetailsFetch = yield fetch(LOGIN_DETAILS_URL, { credentials: 'include' });
308
- const loginDetails = yield loginDetailsFetch.json();
309
- if (loginDetails.MESSAGE_TYPE !== 'EVENT_LOGIN_DETAILS_ACK') {
310
- const refreshLoginResponse = yield fetch(LOGIN_URL, {
311
- credentials: 'include',
312
- headers: { 'Content-type': 'application/json; charset=UTF-8' },
313
- method: 'POST',
314
- body: JSON.stringify({
315
- DETAILS: {},
316
- MESSAGE_TYPE: 'EVENT_LOGIN_AUTH',
317
- }),
318
- });
319
- // if the refresh login is not successful we need to reload the page to get back to the login page
320
- if (!refreshLoginResponse.ok) {
321
- window.location.reload();
322
- }
323
- }
324
- localStorage.removeItem(DefaultSocket_1.REAUTH_ACTION_LOCK_KEY);
325
- // connect again to a new stream to use new session token from obtained cookie
300
+ if (result !== SessionRefreshResult.FAILED) {
301
+ // Connect again to use new session token from obtained cookie
326
302
  yield this.connect();
327
303
  }
328
- else {
329
- // session is already being reauthenticated on another tab so we just wait when it finishes and reconnect
330
- window.addEventListener('storage', this.onStorageEvent.bind(this));
331
- }
332
304
  });
333
305
  }
334
- onStorageEvent(event) {
335
- if (event.key === DefaultSocket_1.REAUTH_ACTION_LOCK_KEY && event.newValue === null) {
336
- this.connect();
337
- window.removeEventListener('storage', this.onStorageEvent);
338
- }
339
- }
340
306
  updateSessionAndSubscriptions(refreshTokenResult) {
341
307
  if ((refreshTokenResult === null || refreshTokenResult === void 0 ? void 0 : refreshTokenResult.MESSAGE_TYPE) === EventMessageType.EVENT_LOGIN_AUTH_ACK) {
342
308
  this.wsMessageHandlers.forEach((mh) => {
@@ -552,7 +518,6 @@ let DefaultSocket = DefaultSocket_1 = class DefaultSocket {
552
518
  }
553
519
  }
554
520
  };
555
- DefaultSocket.REAUTH_ACTION_LOCK_KEY = 'reauthActionLock';
556
521
  __decorate([
557
522
  observable
558
523
  ], DefaultSocket.prototype, "websocket", void 0);
@@ -568,7 +533,7 @@ __decorate([
568
533
  __decorate([
569
534
  observable
570
535
  ], DefaultSocket.prototype, "heartbeatIsEnabled", void 0);
571
- DefaultSocket = DefaultSocket_1 = __decorate([
536
+ DefaultSocket = __decorate([
572
537
  __param(0, MessageBuilder),
573
538
  __param(1, Session),
574
539
  __param(2, JSONSerializer),