@basictech/react 0.8.0-beta.1 → 0.8.0-beta.3
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/.turbo/turbo-build.log +10 -10
- package/changelog.md +12 -0
- package/dist/index.d.mts +59 -43
- package/dist/index.d.ts +59 -43
- package/dist/index.js +1015 -200
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1006 -193
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
- package/readme.md +33 -0
- package/src/AuthContext.tsx +157 -177
- package/src/context.tsx +104 -0
- package/src/core/auth/AuthManager.ts +64 -40
- package/src/dev/BasicDevToolbar.tsx +665 -0
- package/src/index.ts +3 -2
- package/src/sync/syncProtocol.js +30 -0
- package/src/utils/network.ts +69 -16
package/src/sync/syncProtocol.js
CHANGED
|
@@ -137,9 +137,37 @@ export const syncProtocol = function () {
|
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
// When the page becomes visible again (e.g. PWA/mobile browser resuming
|
|
141
|
+
// from background), the scheduled setTimeout for token refresh may have
|
|
142
|
+
// been frozen by the browser. Force-refresh the token and re-send it to
|
|
143
|
+
// the server so the WebSocket connection stays authenticated.
|
|
144
|
+
function handleVisibilityResume() {
|
|
145
|
+
if (document.visibilityState === 'visible' && ws.readyState === WebSocket.OPEN) {
|
|
146
|
+
log("Page became visible - refreshing token for WebSocket");
|
|
147
|
+
resolveGetToken()({ forceRefresh: true }).then(function(newToken) {
|
|
148
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
149
|
+
ws.send(JSON.stringify({ type: "tokenUpdate", authToken: newToken }));
|
|
150
|
+
scheduleTokenRefresh(newToken);
|
|
151
|
+
}
|
|
152
|
+
}).catch(function(err) {
|
|
153
|
+
log("Token refresh on visibility resume failed:", err);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (typeof document !== 'undefined') {
|
|
158
|
+
document.addEventListener('visibilitychange', handleVisibilityResume);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function cleanupVisibilityListener() {
|
|
162
|
+
if (typeof document !== 'undefined') {
|
|
163
|
+
document.removeEventListener('visibilitychange', handleVisibilityResume);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
140
167
|
// If network down or other error, tell the framework to reconnect again in some time:
|
|
141
168
|
ws.onerror = function (event) {
|
|
142
169
|
clearRefreshTimer();
|
|
170
|
+
cleanupVisibilityListener();
|
|
143
171
|
ws.close();
|
|
144
172
|
log("ws.onerror", event);
|
|
145
173
|
onError(event?.message, RECONNECT_DELAY);
|
|
@@ -148,6 +176,7 @@ export const syncProtocol = function () {
|
|
|
148
176
|
// If socket is closed (network disconnected), inform framework and make it reconnect
|
|
149
177
|
ws.onclose = function (event) {
|
|
150
178
|
clearRefreshTimer();
|
|
179
|
+
cleanupVisibilityListener();
|
|
151
180
|
onError("Socket closed: " + event.reason, RECONNECT_DELAY);
|
|
152
181
|
};
|
|
153
182
|
|
|
@@ -211,6 +240,7 @@ export const syncProtocol = function () {
|
|
|
211
240
|
},
|
|
212
241
|
disconnect: function () {
|
|
213
242
|
clearRefreshTimer();
|
|
243
|
+
cleanupVisibilityListener();
|
|
214
244
|
ws.close();
|
|
215
245
|
},
|
|
216
246
|
});
|
package/src/utils/network.ts
CHANGED
|
@@ -1,44 +1,97 @@
|
|
|
1
1
|
// Network utilities for Basic React package
|
|
2
|
+
import semver from 'semver'
|
|
2
3
|
import { log } from '../config'
|
|
3
|
-
import { version as
|
|
4
|
+
import { version as pkgVersion } from '../../package.json'
|
|
4
5
|
|
|
5
6
|
export function isDevelopment(debug?: boolean): boolean {
|
|
7
|
+
if (debug === true) return true
|
|
8
|
+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') return true
|
|
9
|
+
if (typeof window === 'undefined' || !window.location) return false
|
|
10
|
+
const host = window.location.hostname
|
|
6
11
|
return (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
process.env.NODE_ENV === 'development' ||
|
|
13
|
-
debug === true
|
|
12
|
+
host === 'localhost' ||
|
|
13
|
+
host === '127.0.0.1' ||
|
|
14
|
+
host.includes('localhost') ||
|
|
15
|
+
host.includes('127.0.0.1') ||
|
|
16
|
+
host.includes('.local')
|
|
14
17
|
)
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
function normalizeVersion(v: string | null | undefined): string | null {
|
|
21
|
+
if (v == null) return null
|
|
22
|
+
const t = String(v).trim()
|
|
23
|
+
return t.length ? t : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function versionsMatch(a: string, b: string): boolean {
|
|
27
|
+
const na = a.trim()
|
|
28
|
+
const nb = b.trim()
|
|
29
|
+
if (na === nb) return true
|
|
30
|
+
const va = semver.valid(na)
|
|
31
|
+
const vb = semver.valid(nb)
|
|
32
|
+
if (va && vb) return semver.eq(va, vb)
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Use npm `beta` dist-tag when the installed version is a semver prerelease whose first id is `beta`. */
|
|
37
|
+
function usesBetaDistTag(version: string): boolean {
|
|
38
|
+
const pre = semver.prerelease(version)
|
|
39
|
+
const id = pre?.[0]
|
|
40
|
+
return typeof id === 'string' && id.toLowerCase() === 'beta'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type NpmInstallMeta = {
|
|
44
|
+
'dist-tags'?: { latest?: string; beta?: string }
|
|
45
|
+
}
|
|
46
|
+
|
|
17
47
|
export async function checkForNewVersion(): Promise<{
|
|
18
48
|
hasNewVersion: boolean,
|
|
19
49
|
latestVersion: string | null,
|
|
20
50
|
currentVersion: string | null
|
|
21
51
|
}> {
|
|
22
52
|
try {
|
|
23
|
-
const
|
|
53
|
+
const currentVersion = normalizeVersion(pkgVersion)
|
|
54
|
+
if (!currentVersion) {
|
|
55
|
+
return { hasNewVersion: false, latestVersion: null, currentVersion: null }
|
|
56
|
+
}
|
|
24
57
|
|
|
25
|
-
const response = await fetch(
|
|
58
|
+
const response = await fetch('https://registry.npmjs.org/@basictech/react', {
|
|
59
|
+
headers: { Accept: 'application/vnd.npm.install-v1+json' },
|
|
60
|
+
})
|
|
26
61
|
if (!response.ok) {
|
|
27
62
|
throw new Error('Failed to fetch version from npm');
|
|
28
63
|
}
|
|
29
64
|
|
|
30
|
-
const data = await response.json()
|
|
31
|
-
const
|
|
65
|
+
const data = (await response.json()) as NpmInstallMeta
|
|
66
|
+
const distTags = data['dist-tags'] ?? {}
|
|
67
|
+
const rawRegistry =
|
|
68
|
+
usesBetaDistTag(currentVersion)
|
|
69
|
+
? distTags.beta ?? distTags.latest
|
|
70
|
+
: distTags.latest
|
|
71
|
+
const latestVersion = normalizeVersion(rawRegistry ?? null)
|
|
72
|
+
if (!latestVersion) {
|
|
73
|
+
throw new Error('Missing dist-tags from npm registry')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const same = versionsMatch(currentVersion, latestVersion)
|
|
77
|
+
|
|
78
|
+
if (!same && isDevelopment()) {
|
|
79
|
+
log('[basic] version check mismatch:', {
|
|
80
|
+
currentVersion,
|
|
81
|
+
registryVersion: latestVersion,
|
|
82
|
+
channel: usesBetaDistTag(currentVersion) ? 'beta' : 'latest',
|
|
83
|
+
})
|
|
84
|
+
}
|
|
32
85
|
|
|
33
|
-
if (
|
|
86
|
+
if (!same) {
|
|
34
87
|
console.warn('[basic] New version available:', latestVersion, `\nrun "npm install @basictech/react@${latestVersion}" to update`);
|
|
35
88
|
}
|
|
36
|
-
if (
|
|
89
|
+
if (usesBetaDistTag(currentVersion)) {
|
|
37
90
|
log('thank you for being on basictech/react beta :)')
|
|
38
91
|
}
|
|
39
92
|
|
|
40
93
|
return {
|
|
41
|
-
hasNewVersion:
|
|
94
|
+
hasNewVersion: !same,
|
|
42
95
|
latestVersion,
|
|
43
96
|
currentVersion
|
|
44
97
|
};
|
|
@@ -57,7 +110,7 @@ export function cleanOAuthParamsFromUrl(): void {
|
|
|
57
110
|
const url = new URL(window.location.href)
|
|
58
111
|
url.searchParams.delete('code')
|
|
59
112
|
url.searchParams.delete('state')
|
|
60
|
-
window.history.
|
|
113
|
+
window.history.replaceState({}, document.title, url.pathname + url.search)
|
|
61
114
|
log('Cleaned OAuth parameters from URL')
|
|
62
115
|
}
|
|
63
116
|
}
|