@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.
@@ -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
  });
@@ -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 currentVersion } from '../../package.json'
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
- window.location.hostname === 'localhost' ||
8
- window.location.hostname === '127.0.0.1' ||
9
- window.location.hostname.includes('localhost') ||
10
- window.location.hostname.includes('127.0.0.1') ||
11
- window.location.hostname.includes('.local') ||
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 isBeta = currentVersion.includes('beta')
53
+ const currentVersion = normalizeVersion(pkgVersion)
54
+ if (!currentVersion) {
55
+ return { hasNewVersion: false, latestVersion: null, currentVersion: null }
56
+ }
24
57
 
25
- const response = await fetch(`https://registry.npmjs.org/@basictech/react/${isBeta ? 'beta' : 'latest'}`);
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 latestVersion = data.version;
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 (latestVersion !== currentVersion) {
86
+ if (!same) {
34
87
  console.warn('[basic] New version available:', latestVersion, `\nrun "npm install @basictech/react@${latestVersion}" to update`);
35
88
  }
36
- if (isBeta) {
89
+ if (usesBetaDistTag(currentVersion)) {
37
90
  log('thank you for being on basictech/react beta :)')
38
91
  }
39
92
 
40
93
  return {
41
- hasNewVersion: currentVersion !== latestVersion,
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.pushState({}, document.title, url.pathname + url.search)
113
+ window.history.replaceState({}, document.title, url.pathname + url.search)
61
114
  log('Cleaned OAuth parameters from URL')
62
115
  }
63
116
  }