@chaosity/location-client-react 0.1.5 → 0.1.6
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.
|
@@ -3,6 +3,8 @@ import { GeoPlacesClient, ClientConfig } from '@chaosity/location-client';
|
|
|
3
3
|
interface LocationClientContextValue {
|
|
4
4
|
config: ClientConfig | null;
|
|
5
5
|
client: GeoPlacesClient | null;
|
|
6
|
+
getToken: () => string | undefined;
|
|
7
|
+
ensureValidToken: () => Promise<void>;
|
|
6
8
|
loading: boolean;
|
|
7
9
|
error: string | null;
|
|
8
10
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useContext, useEffect, useState, useRef, useCallback } from 'react';
|
|
4
4
|
import { GeoPlacesClient } from '@chaosity/location-client';
|
|
5
5
|
import debug from 'debug';
|
|
6
|
+
import { jwtDecode } from 'jwt-decode';
|
|
6
7
|
const log = debug('location-client-react:provider');
|
|
7
8
|
const LocationClientContext = createContext(undefined);
|
|
8
9
|
export function LocationClientProvider({ children, getConfig, refreshBuffer = 60 }) {
|
|
@@ -10,12 +11,16 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
|
|
|
10
11
|
const [client, setClient] = useState(null);
|
|
11
12
|
const [loading, setLoading] = useState(true);
|
|
12
13
|
const [error, setError] = useState(null);
|
|
14
|
+
const tokenRef = useRef(undefined);
|
|
13
15
|
const expiresAtRef = useRef(null);
|
|
14
16
|
const getConfigRef = useRef(getConfig);
|
|
17
|
+
const tokenGenerationCount = useRef(0);
|
|
15
18
|
// Update ref when getConfig changes
|
|
16
19
|
useEffect(() => {
|
|
17
20
|
getConfigRef.current = getConfig;
|
|
18
21
|
}, [getConfig]);
|
|
22
|
+
// Token getter function (stable reference)
|
|
23
|
+
const getToken = useCallback(() => tokenRef.current, []);
|
|
19
24
|
// Check if token is expired or about to expire
|
|
20
25
|
const isTokenExpired = useCallback(() => {
|
|
21
26
|
if (!expiresAtRef.current)
|
|
@@ -28,15 +33,29 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
|
|
|
28
33
|
return;
|
|
29
34
|
const timeUntilExpiry = expiresAtRef.current ? Math.floor((expiresAtRef.current - Date.now()) / 1000) : 0;
|
|
30
35
|
log('Token expired or expiring soon (expires in %ds), refreshing...', timeUntilExpiry);
|
|
36
|
+
tokenGenerationCount.current++;
|
|
31
37
|
try {
|
|
32
38
|
const cfg = await getConfigRef.current();
|
|
33
39
|
setConfig(cfg);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
tokenRef.current = cfg.token;
|
|
41
|
+
expiresAtRef.current = null;
|
|
42
|
+
const baseClient = new GeoPlacesClient(cfg);
|
|
43
|
+
const wrappedClient = new Proxy(baseClient, {
|
|
44
|
+
get(target, prop) {
|
|
45
|
+
if (prop === 'send') {
|
|
46
|
+
return async (command) => {
|
|
47
|
+
await ensureValidToken();
|
|
48
|
+
return target.send(command);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return target[prop];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
setClient(wrappedClient);
|
|
55
|
+
const decoded = jwtDecode(cfg.token);
|
|
56
|
+
expiresAtRef.current = decoded.exp * 1000;
|
|
57
|
+
const newExpiry = Math.floor((decoded.exp * 1000 - Date.now()) / 1000);
|
|
58
|
+
log('Token refreshed successfully (new token expires in %ds)', newExpiry);
|
|
40
59
|
}
|
|
41
60
|
catch (err) {
|
|
42
61
|
log('Token refresh failed: %s', err instanceof Error ? err.message : 'Unknown error');
|
|
@@ -46,17 +65,32 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
|
|
|
46
65
|
// Initial load
|
|
47
66
|
useEffect(() => {
|
|
48
67
|
log('Initializing LocationClientProvider');
|
|
68
|
+
tokenGenerationCount.current++;
|
|
49
69
|
getConfig()
|
|
50
70
|
.then((cfg) => {
|
|
51
71
|
setConfig(cfg);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
tokenRef.current = cfg.token;
|
|
73
|
+
const baseClient = new GeoPlacesClient(cfg);
|
|
74
|
+
const wrappedClient = new Proxy(baseClient, {
|
|
75
|
+
get(target, prop) {
|
|
76
|
+
if (prop === 'send') {
|
|
77
|
+
return async (command) => {
|
|
78
|
+
await ensureValidToken();
|
|
79
|
+
return target.send(command);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return target[prop];
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
setClient(wrappedClient);
|
|
86
|
+
try {
|
|
87
|
+
const decoded = jwtDecode(cfg.token);
|
|
88
|
+
expiresAtRef.current = decoded.exp * 1000;
|
|
89
|
+
const expiry = Math.floor((decoded.exp * 1000 - Date.now()) / 1000);
|
|
56
90
|
log('Client initialized (token expires in %ds)', expiry);
|
|
57
91
|
}
|
|
58
|
-
|
|
59
|
-
log('
|
|
92
|
+
catch (err) {
|
|
93
|
+
log('Failed to decode token, no expiry info');
|
|
60
94
|
}
|
|
61
95
|
setLoading(false);
|
|
62
96
|
})
|
|
@@ -65,14 +99,14 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
|
|
|
65
99
|
setError(err instanceof Error ? err.message : 'Failed to initialize client');
|
|
66
100
|
setLoading(false);
|
|
67
101
|
});
|
|
68
|
-
}, [
|
|
102
|
+
}, [ensureValidToken]);
|
|
69
103
|
// Check token validity on every state change
|
|
70
104
|
useEffect(() => {
|
|
71
105
|
if (!loading && config) {
|
|
72
106
|
ensureValidToken();
|
|
73
107
|
}
|
|
74
108
|
}, [config, loading, ensureValidToken]);
|
|
75
|
-
return (_jsx(LocationClientContext.Provider, { value: { config, client, loading, error }, children: children }));
|
|
109
|
+
return (_jsx(LocationClientContext.Provider, { value: { config, client, getToken, ensureValidToken, loading, error }, children: children }));
|
|
76
110
|
}
|
|
77
111
|
export function useLocationClient() {
|
|
78
112
|
const context = useContext(LocationClientContext);
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaosity/location-client-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "React bindings for Chaosity Location Service client",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"scripts": {
|
|
@@ -27,16 +28,17 @@
|
|
|
27
28
|
"url": "https://github.com/chaosity-io/location-service-client-react/issues"
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
|
-
"@chaosity/location-client": "^0.1.
|
|
31
|
-
"debug": "^4.4.3"
|
|
31
|
+
"@chaosity/location-client": "^0.1.5",
|
|
32
|
+
"debug": "^4.4.3",
|
|
33
|
+
"jwt-decode": "^4.0.0"
|
|
32
34
|
},
|
|
33
35
|
"peerDependencies": {
|
|
34
36
|
"react": "^18.0.0 || ^19.0.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@types/debug": "^4.1.12",
|
|
38
|
-
"@types/react": "^
|
|
39
|
-
"react": "^
|
|
40
|
+
"@types/react": "^19.0.0",
|
|
41
|
+
"react": "^19.0.0",
|
|
40
42
|
"typescript": "^5.0.0"
|
|
41
43
|
},
|
|
42
44
|
"files": [
|