@chaosity/location-client-react 0.2.0 → 0.3.0

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,9 +38,7 @@ export interface LocationClientProviderProps {
38
38
  getConfig: () => Promise<ClientConfig & {
39
39
  expiresAt?: number;
40
40
  }>;
41
- /** Seconds before expiry to proactively refresh (default: 60) */
42
- refreshBuffer?: number;
43
41
  }
44
- export declare function LocationClientProvider({ children, getConfig, refreshBuffer, }: LocationClientProviderProps): import("react").JSX.Element;
42
+ export declare function LocationClientProvider({ children, getConfig, }: LocationClientProviderProps): import("react").JSX.Element;
45
43
  export declare function useLocationClient(): LocationClientContextValue;
46
44
  export {};
@@ -1,12 +1,25 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { GeoPlacesClient } from '@chaosity/location-client';
3
+ import { GeoPlacesClient, TOKEN_REFRESH_BUFFER_SECONDS, readTokenExpiry, } from '@chaosity/location-client';
4
4
  import debug from 'debug';
5
5
  import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
6
6
  const log = debug('location-client-react:provider');
7
7
  const LocationClientContext = createContext(undefined);
8
8
  const DEFAULT_LIFETIME_MS = 900000;
9
- export function LocationClientProvider({ children, getConfig, refreshBuffer = 60, }) {
9
+ /**
10
+ * When this token needs replacing.
11
+ *
12
+ * The `exp` claim first — it is the only value that cannot disagree with what
13
+ * the API will accept, and the server-side TokenProvider reads the same one.
14
+ * `expiresAt` is whatever `getConfig` chose to report, and the final fallback
15
+ * is a guess used only when the token cannot be parsed at all.
16
+ */
17
+ function expiryOf(cfg) {
18
+ return (readTokenExpiry(cfg.token) ??
19
+ cfg.expiresAt ??
20
+ Date.now() + DEFAULT_LIFETIME_MS);
21
+ }
22
+ export function LocationClientProvider({ children, getConfig, }) {
10
23
  const [client, setClient] = useState(null);
11
24
  const [loading, setLoading] = useState(true);
12
25
  const [error, setError] = useState(null);
@@ -22,8 +35,8 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
22
35
  const isTokenExpired = useCallback(() => {
23
36
  if (!expiresAtRef.current)
24
37
  return true;
25
- return Date.now() >= expiresAtRef.current - refreshBuffer * 1000;
26
- }, [refreshBuffer]);
38
+ return (Date.now() >= expiresAtRef.current - TOKEN_REFRESH_BUFFER_SECONDS * 1000);
39
+ }, []);
27
40
  /**
28
41
  * Refresh once, however many callers ask at the same moment.
29
42
  *
@@ -38,7 +51,7 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
38
51
  refreshPromiseRef.current = (async () => {
39
52
  const cfg = await getConfigRef.current();
40
53
  tokenRef.current = cfg.token;
41
- expiresAtRef.current = cfg.expiresAt ?? Date.now() + DEFAULT_LIFETIME_MS;
54
+ expiresAtRef.current = expiryOf(cfg);
42
55
  log('Token refreshed (expires in %ds)', Math.floor((expiresAtRef.current - Date.now()) / 1000));
43
56
  if (mountedRef.current)
44
57
  setError(null);
@@ -73,14 +86,14 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
73
86
  clearTimeout(timerRef.current);
74
87
  if (!expiresAtRef.current)
75
88
  return;
76
- const delay = Math.max(0, expiresAtRef.current - refreshBuffer * 1000 - Date.now());
89
+ const delay = Math.max(0, expiresAtRef.current - TOKEN_REFRESH_BUFFER_SECONDS * 1000 - Date.now());
77
90
  log('Next refresh in %ds', Math.floor(delay / 1000));
78
91
  timerRef.current = setTimeout(() => {
79
92
  // Errors are already surfaced onto state by refreshToken; swallow here so
80
93
  // a failed background refresh cannot become an unhandled rejection.
81
94
  void refreshToken().catch(() => { });
82
95
  }, delay);
83
- }, [refreshBuffer, refreshToken]);
96
+ }, [refreshToken]);
84
97
  // refreshToken and scheduleRefresh reference each other; a ref breaks the cycle
85
98
  // without recreating either callback on every render.
86
99
  const scheduleRefreshRef = useRef(() => { });
@@ -141,7 +154,7 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
141
154
  if (!mountedRef.current)
142
155
  return;
143
156
  tokenRef.current = cfg.token;
144
- expiresAtRef.current = cfg.expiresAt ?? Date.now() + DEFAULT_LIFETIME_MS;
157
+ expiresAtRef.current = expiryOf(cfg);
145
158
  const baseClient = new GeoPlacesClient({
146
159
  apiUrl: cfg.apiUrl,
147
160
  token: cfg.token,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaosity/location-client-react",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "React bindings for Chaosity Location Service client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "url": "https://github.com/chaosity-io/location-service-client-react/issues"
35
35
  },
36
36
  "dependencies": {
37
- "@chaosity/location-client": "^0.2.0",
37
+ "@chaosity/location-client": "^0.2.1",
38
38
  "debug": "^4.4.3"
39
39
  },
40
40
  "peerDependencies": {
@@ -54,7 +54,7 @@
54
54
  "@types/react": "^19.0.0",
55
55
  "@vitest/coverage-v8": "^3.0.0",
56
56
  "eslint": "^10.1.0",
57
- "happy-dom": "^17.0.0",
57
+ "happy-dom": "^20.11.6",
58
58
  "husky": "^9.0.0",
59
59
  "maplibre-gl": "^5.0.0",
60
60
  "prettier": "^3.8.1",