@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.
- package/changelog.md +12 -0
- package/dist/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +591 -147
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +599 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/readme.md +99 -97
- package/src/AuthContext.tsx +513 -411
- package/src/context.tsx +19 -1
- package/src/core/auth/AuthManager.ts +1239 -726
- package/src/sync/syncProtocol.js +49 -3
- package/src/utils/network.ts +1 -1
package/src/sync/syncProtocol.js
CHANGED
|
@@ -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 (
|
|
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()];
|
package/src/utils/network.ts
CHANGED
|
@@ -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.
|
|
113
|
+
window.history.replaceState({}, document.title, url.pathname + url.search)
|
|
114
114
|
log('Cleaned OAuth parameters from URL')
|
|
115
115
|
}
|
|
116
116
|
}
|