@chaosity/location-client-react 0.1.3 → 0.1.5

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.
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import type { ReactNode } from 'react';
2
2
  import { GeoPlacesClient, ClientConfig } from '@chaosity/location-client';
3
3
  interface LocationClientContextValue {
4
4
  config: ClientConfig | null;
@@ -7,9 +7,12 @@ interface LocationClientContextValue {
7
7
  error: string | null;
8
8
  }
9
9
  export interface LocationClientProviderProps {
10
- children: React.ReactNode;
11
- getConfig: () => Promise<ClientConfig>;
10
+ children: ReactNode;
11
+ getConfig: () => Promise<ClientConfig & {
12
+ expiresAt?: number;
13
+ }>;
14
+ refreshBuffer?: number;
12
15
  }
13
- export declare function LocationClientProvider({ children, getConfig }: LocationClientProviderProps): React.JSX.Element;
16
+ export declare function LocationClientProvider({ children, getConfig, refreshBuffer }: LocationClientProviderProps): import("react/jsx-runtime").JSX.Element;
14
17
  export declare function useLocationClient(): LocationClientContextValue;
15
18
  export {};
@@ -1,24 +1,78 @@
1
- import React, { createContext, useContext, useEffect, useState } from 'react';
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext, useEffect, useState, useRef, useCallback } from 'react';
2
4
  import { GeoPlacesClient } from '@chaosity/location-client';
5
+ import debug from 'debug';
6
+ const log = debug('location-client-react:provider');
3
7
  const LocationClientContext = createContext(undefined);
4
- export function LocationClientProvider({ children, getConfig }) {
8
+ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60 }) {
5
9
  const [config, setConfig] = useState(null);
6
10
  const [client, setClient] = useState(null);
7
11
  const [loading, setLoading] = useState(true);
8
12
  const [error, setError] = useState(null);
13
+ const expiresAtRef = useRef(null);
14
+ const getConfigRef = useRef(getConfig);
15
+ // Update ref when getConfig changes
9
16
  useEffect(() => {
17
+ getConfigRef.current = getConfig;
18
+ }, [getConfig]);
19
+ // Check if token is expired or about to expire
20
+ const isTokenExpired = useCallback(() => {
21
+ if (!expiresAtRef.current)
22
+ return false;
23
+ return Date.now() >= (expiresAtRef.current - refreshBuffer * 1000);
24
+ }, [refreshBuffer]);
25
+ // Refresh token if needed
26
+ const ensureValidToken = useCallback(async () => {
27
+ if (!isTokenExpired())
28
+ return;
29
+ const timeUntilExpiry = expiresAtRef.current ? Math.floor((expiresAtRef.current - Date.now()) / 1000) : 0;
30
+ log('Token expired or expiring soon (expires in %ds), refreshing...', timeUntilExpiry);
31
+ try {
32
+ const cfg = await getConfigRef.current();
33
+ setConfig(cfg);
34
+ setClient(new GeoPlacesClient(cfg));
35
+ if (cfg.expiresAt) {
36
+ expiresAtRef.current = cfg.expiresAt;
37
+ const newExpiry = Math.floor((cfg.expiresAt - Date.now()) / 1000);
38
+ log('Token refreshed successfully (new token expires in %ds)', newExpiry);
39
+ }
40
+ }
41
+ catch (err) {
42
+ log('Token refresh failed: %s', err instanceof Error ? err.message : 'Unknown error');
43
+ setError(err instanceof Error ? err.message : 'Failed to refresh token');
44
+ }
45
+ }, [isTokenExpired]);
46
+ // Initial load
47
+ useEffect(() => {
48
+ log('Initializing LocationClientProvider');
10
49
  getConfig()
11
50
  .then((cfg) => {
12
51
  setConfig(cfg);
13
52
  setClient(new GeoPlacesClient(cfg));
53
+ if (cfg.expiresAt) {
54
+ expiresAtRef.current = cfg.expiresAt;
55
+ const expiry = Math.floor((cfg.expiresAt - Date.now()) / 1000);
56
+ log('Client initialized (token expires in %ds)', expiry);
57
+ }
58
+ else {
59
+ log('Client initialized (no expiry info)');
60
+ }
14
61
  setLoading(false);
15
62
  })
16
63
  .catch((err) => {
64
+ log('Initialization failed: %s', err instanceof Error ? err.message : 'Unknown error');
17
65
  setError(err instanceof Error ? err.message : 'Failed to initialize client');
18
66
  setLoading(false);
19
67
  });
20
- }, []);
21
- return (React.createElement(LocationClientContext.Provider, { value: { config, client, loading, error } }, children));
68
+ }, [getConfig]);
69
+ // Check token validity on every state change
70
+ useEffect(() => {
71
+ if (!loading && config) {
72
+ ensureValidToken();
73
+ }
74
+ }, [config, loading, ensureValidToken]);
75
+ return (_jsx(LocationClientContext.Provider, { value: { config, client, loading, error }, children: children }));
22
76
  }
23
77
  export function useLocationClient() {
24
78
  const context = useContext(LocationClientContext);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaosity/location-client-react",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "React bindings for Chaosity Location Service client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,15 +27,17 @@
27
27
  "url": "https://github.com/chaosity-io/location-service-client-react/issues"
28
28
  },
29
29
  "dependencies": {
30
- "@chaosity/location-client": "^0.1.3"
30
+ "@chaosity/location-client": "^0.1.3",
31
+ "debug": "^4.4.3"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "react": "^18.0.0 || ^19.0.0"
34
35
  },
35
36
  "devDependencies": {
37
+ "@types/debug": "^4.1.12",
36
38
  "@types/react": "^18.0.0",
37
- "typescript": "^5.0.0",
38
- "react": "^18.0.0"
39
+ "react": "^18.0.0",
40
+ "typescript": "^5.0.0"
39
41
  },
40
42
  "files": [
41
43
  "dist",