@basictech/react 0.8.0-beta.2 → 0.8.0-beta.4

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.
@@ -38,6 +38,7 @@ export const syncProtocol = function () {
38
38
  var requestId = 0;
39
39
  var acceptCallbacks = {};
40
40
  var refreshTimer = null;
41
+ var pendingTokenUpdate = null;
41
42
 
42
43
  // Connect the WebSocket to given url:
43
44
  log("Connecting to", url)
@@ -81,6 +82,13 @@ export const syncProtocol = function () {
81
82
  }
82
83
  }
83
84
 
85
+ function sendTokenUpdate(token) {
86
+ if (ws.readyState !== WebSocket.OPEN) return false;
87
+ pendingTokenUpdate = token;
88
+ ws.send(JSON.stringify({ type: "tokenUpdate", authToken: token }));
89
+ return true;
90
+ }
91
+
84
92
  // Resolve the getToken function from the module-level registry.
85
93
  // It's stored there (not in options) because dexie-syncable serializes
86
94
  // options into IndexedDB, and functions can't survive structured clone.
@@ -103,10 +111,8 @@ export const syncProtocol = function () {
103
111
  refreshTimer = setTimeout(async function () {
104
112
  try {
105
113
  var newToken = await resolveGetToken()({ forceRefresh: true });
106
- if (ws.readyState === WebSocket.OPEN) {
114
+ if (sendTokenUpdate(newToken)) {
107
115
  log("Sending tokenUpdate on existing WebSocket");
108
- ws.send(JSON.stringify({ type: "tokenUpdate", authToken: newToken }));
109
- scheduleTokenRefresh(newToken);
110
116
  }
111
117
  } catch (err) {
112
118
  log("Proactive token refresh failed (non-fatal):", err);
@@ -137,9 +143,34 @@ export const syncProtocol = function () {
137
143
  }
138
144
  };
139
145
 
146
+ // When the page becomes visible again (e.g. PWA/mobile browser resuming
147
+ // from background), the scheduled setTimeout for token refresh may have
148
+ // been frozen by the browser. Force-refresh the token and re-send it to
149
+ // the server so the WebSocket connection stays authenticated.
150
+ function handleVisibilityResume() {
151
+ if (document.visibilityState === 'visible' && ws.readyState === WebSocket.OPEN) {
152
+ log("Page became visible - refreshing token for WebSocket");
153
+ resolveGetToken()({ forceRefresh: true }).then(function(newToken) {
154
+ sendTokenUpdate(newToken);
155
+ }).catch(function(err) {
156
+ log("Token refresh on visibility resume failed:", err);
157
+ });
158
+ }
159
+ }
160
+ if (typeof document !== 'undefined') {
161
+ document.addEventListener('visibilitychange', handleVisibilityResume);
162
+ }
163
+
164
+ function cleanupVisibilityListener() {
165
+ if (typeof document !== 'undefined') {
166
+ document.removeEventListener('visibilitychange', handleVisibilityResume);
167
+ }
168
+ }
169
+
140
170
  // If network down or other error, tell the framework to reconnect again in some time:
141
171
  ws.onerror = function (event) {
142
172
  clearRefreshTimer();
173
+ cleanupVisibilityListener();
143
174
  ws.close();
144
175
  log("ws.onerror", event);
145
176
  onError(event?.message, RECONNECT_DELAY);
@@ -148,6 +179,7 @@ export const syncProtocol = function () {
148
179
  // If socket is closed (network disconnected), inform framework and make it reconnect
149
180
  ws.onclose = function (event) {
150
181
  clearRefreshTimer();
182
+ cleanupVisibilityListener();
151
183
  onError("Socket closed: " + event.reason, RECONNECT_DELAY);
152
184
  };
153
185
 
@@ -211,11 +243,25 @@ export const syncProtocol = function () {
211
243
  },
212
244
  disconnect: function () {
213
245
  clearRefreshTimer();
246
+ cleanupVisibilityListener();
214
247
  ws.close();
215
248
  },
216
249
  });
217
250
  isFirstRound = false;
218
251
  }
252
+ } else if (requestFromServer.type == "tokenUpdateAck") {
253
+ if (requestFromServer.ok) {
254
+ scheduleTokenRefresh(requestFromServer.authToken || pendingTokenUpdate);
255
+ pendingTokenUpdate = null;
256
+ } else {
257
+ log("tokenUpdate rejected by server:", requestFromServer.code || requestFromServer.message);
258
+ pendingTokenUpdate = null;
259
+ ws.close(4001, requestFromServer.code || "token_update_failed");
260
+ onError(
261
+ requestFromServer.message || "Authentication refresh failed",
262
+ RECONNECT_DELAY,
263
+ );
264
+ }
219
265
  } else if (requestFromServer.type == "ack") {
220
266
  var requestId = requestFromServer.requestId;
221
267
  var acceptCallback = acceptCallbacks[requestId.toString()];
@@ -110,7 +110,7 @@ export function cleanOAuthParamsFromUrl(): void {
110
110
  const url = new URL(window.location.href)
111
111
  url.searchParams.delete('code')
112
112
  url.searchParams.delete('state')
113
- window.history.pushState({}, document.title, url.pathname + url.search)
113
+ window.history.replaceState({}, document.title, url.pathname + url.search)
114
114
  log('Cleaned OAuth parameters from URL')
115
115
  }
116
116
  }