@chaosity/location-client-react 0.1.12 → 0.1.18

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/README.md CHANGED
@@ -33,7 +33,11 @@ export async function getLocationConfig() {
33
33
  import { LocationClientProvider } from '@chaosity/location-client-react'
34
34
  import { getLocationConfig } from './actions/location'
35
35
 
36
- export default function RootLayout({ children }: { children: React.ReactNode }) {
36
+ export default function RootLayout({
37
+ children,
38
+ }: {
39
+ children: React.ReactNode
40
+ }) {
37
41
  return (
38
42
  <LocationClientProvider getConfig={getLocationConfig}>
39
43
  {children}
@@ -54,7 +58,7 @@ function SearchComponent() {
54
58
  const searchPlaces = async (query: string) => {
55
59
  if (!client) return
56
60
  const response = await client.send(
57
- new SuggestCommand({ QueryText: query, MaxResults: 5 })
61
+ new SuggestCommand({ QueryText: query, MaxResults: 5 }),
58
62
  )
59
63
  return response.ResultItems
60
64
  }
@@ -83,7 +87,9 @@ function MapComponent() {
83
87
  useMapLanguage(mapInstance, language)
84
88
 
85
89
  useEffect(() => {
86
- const map = new maplibregl.Map({ /* ... */ })
90
+ const map = new maplibregl.Map({
91
+ /* ... */
92
+ })
87
93
  map.once('load', () => setMapInstance(map))
88
94
  return () => map.remove()
89
95
  }, [])
@@ -109,15 +115,13 @@ function MapComponent() {
109
115
  Provides the location client and automatic token refresh to all child components.
110
116
 
111
117
  ```tsx
112
- <LocationClientProvider
113
- getConfig={getLocationConfig}
114
- refreshBuffer={60}
115
- >
118
+ <LocationClientProvider getConfig={getLocationConfig} refreshBuffer={60}>
116
119
  {children}
117
120
  </LocationClientProvider>
118
121
  ```
119
122
 
120
123
  **Props:**
124
+
121
125
  - `getConfig` — Async function that returns `{ apiUrl: string, token: string, expiresAt?: number }`. Called on init and whenever the token needs refreshing.
122
126
  - `refreshBuffer` (optional, default: `60`) — Seconds before token expiry to proactively refresh. Prevents mid-request expiration.
123
127
  - `children` — Child components.
@@ -131,6 +135,7 @@ const { client, getToken, loading, error } = useLocationClient()
131
135
  ```
132
136
 
133
137
  **Returns:**
138
+
134
139
  - `client` (`GeoPlacesClient | null`) — The location client instance. Automatically refreshes the token before each `send()` call if needed.
135
140
  - `getToken` (`() => string | undefined`) — Returns the current token. Useful for direct API calls (e.g., map style fetch).
136
141
  - `loading` (`boolean`) — Whether the client is initializing.
@@ -155,7 +160,10 @@ No manual token management needed. The `client` always uses a valid token.
155
160
  'use client'
156
161
 
157
162
  import { useEffect, useRef, useState } from 'react'
158
- import { useLocationClient, useMapLanguage } from '@chaosity/location-client-react'
163
+ import {
164
+ useLocationClient,
165
+ useMapLanguage,
166
+ } from '@chaosity/location-client-react'
159
167
  import {
160
168
  GeoPlaces,
161
169
  fetchMapStyle,
@@ -178,7 +186,6 @@ export default function MapComponent() {
178
186
 
179
187
  useEffect(() => {
180
188
  if (!mapContainer.current || map.current || loading || !client) return
181
-
182
189
  ;(async () => {
183
190
  // Fetch style with terrain, 3D buildings, and language baked into the descriptor
184
191
  const style = await fetchMapStyle(API_URL, 'Standard', getToken, {
@@ -197,8 +204,14 @@ export default function MapComponent() {
197
204
  transformRequest: createTransformRequest(API_URL, getToken),
198
205
  })
199
206
 
200
- instance.addControl(new maplibregl.NavigationControl({ visualizePitch: true }), 'top-right')
201
- instance.addControl(new maplibregl.TerrainControl({ source: 'amazon' }), 'top-right')
207
+ instance.addControl(
208
+ new maplibregl.NavigationControl({ visualizePitch: true }),
209
+ 'top-right',
210
+ )
211
+ instance.addControl(
212
+ new maplibregl.TerrainControl({ source: 'amazon' }),
213
+ 'top-right',
214
+ )
202
215
 
203
216
  const geoPlaces = new GeoPlaces(client, instance)
204
217
  const geocoder = new MaplibreGeocoder(geoPlaces, {
@@ -237,6 +250,7 @@ useMapLanguage(map: MapLike | null, language: string): void
237
250
  ```
238
251
 
239
252
  **Parameters:**
253
+
240
254
  - `map` — MapLibre Map instance, or `null` while the map is initializing.
241
255
  - `language` — ISO 639-1 language code (e.g. `'en'`, `'fr'`, `'de'`, `'ja'`, `'zh'`, `'ar'`).
242
256
 
@@ -259,7 +273,7 @@ function MyComponent() {
259
273
 
260
274
  const search = async () => {
261
275
  const response = await client!.send(
262
- new SuggestCommand({ QueryText: 'Vancouver', MaxResults: 5 })
276
+ new SuggestCommand({ QueryText: 'Vancouver', MaxResults: 5 }),
263
277
  )
264
278
  return response.ResultItems
265
279
  }
@@ -283,7 +297,7 @@ import type { SuggestCommandOutput } from '@aws-sdk/client-geo-places'
283
297
 
284
298
  const { client } = useLocationClient()
285
299
  const response: SuggestCommandOutput = await client!.send(
286
- new SuggestCommand({ QueryText: 'Vancouver' })
300
+ new SuggestCommand({ QueryText: 'Vancouver' }),
287
301
  )
288
302
  ```
289
303
 
@@ -1,12 +1,4 @@
1
- /**
2
- * Minimal interface describing the MapLibre Map methods used by this hook.
3
- * Using a structural type avoids maplibre-gl version conflicts between packages.
4
- */
5
- interface MapLike {
6
- isStyleLoaded(): boolean | void;
7
- on(event: string, listener: (...args: unknown[]) => void): void;
8
- off(event: string, listener: (...args: unknown[]) => void): void;
9
- }
1
+ import type { MapLike } from '@chaosity/location-client';
10
2
  /**
11
3
  * React hook that keeps map label language in sync with the `language` prop.
12
4
  *
@@ -22,4 +14,3 @@ interface MapLike {
22
14
  * useMapLanguage(mapInstance, language)
23
15
  */
24
16
  export declare function useMapLanguage(map: MapLike | null, language: string): void;
25
- export {};
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- import { useEffect } from 'react';
3
2
  import { applyMapLanguage } from '@chaosity/location-client';
3
+ import { useEffect } from 'react';
4
4
  /**
5
5
  * React hook that keeps map label language in sync with the `language` prop.
6
6
  *
@@ -19,7 +19,6 @@ export function useMapLanguage(map, language) {
19
19
  useEffect(() => {
20
20
  if (!map)
21
21
  return;
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
22
  const applyFn = () => applyMapLanguage(map, language);
24
23
  if (map.isStyleLoaded()) {
25
24
  applyFn();
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { LocationClientProvider, useLocationClient } from './provider/LocationClientProvider';
1
+ export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider';
2
2
  export type { LocationClientProviderProps } from './provider/LocationClientProvider';
3
3
  export { useMapLanguage } from './hooks/useMapLanguage';
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { LocationClientProvider, useLocationClient } from './provider/LocationClientProvider';
1
+ export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider';
2
2
  export { useMapLanguage } from './hooks/useMapLanguage';
@@ -1,5 +1,6 @@
1
+ import type { ClientConfig } from '@chaosity/location-client';
2
+ import { GeoPlacesClient } from '@chaosity/location-client';
1
3
  import type { ReactNode } from 'react';
2
- import { GeoPlacesClient, ClientConfig } from '@chaosity/location-client';
3
4
  interface LocationClientContextValue {
4
5
  client: GeoPlacesClient | null;
5
6
  getToken: () => string | undefined;
@@ -14,6 +15,6 @@ export interface LocationClientProviderProps {
14
15
  /** Seconds before expiry to proactively refresh (default: 60) */
15
16
  refreshBuffer?: number;
16
17
  }
17
- export declare function LocationClientProvider({ children, getConfig, refreshBuffer }: LocationClientProviderProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function LocationClientProvider({ children, getConfig, refreshBuffer, }: LocationClientProviderProps): import("react/jsx-runtime").JSX.Element;
18
19
  export declare function useLocationClient(): LocationClientContextValue;
19
20
  export {};
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext, useEffect, useState, useRef, useCallback } from 'react';
4
3
  import { GeoPlacesClient } from '@chaosity/location-client';
5
4
  import debug from 'debug';
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
- export function LocationClientProvider({ children, getConfig, refreshBuffer = 60 }) {
8
+ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60, }) {
9
9
  const [client, setClient] = useState(null);
10
10
  const [loading, setLoading] = useState(true);
11
11
  const [error, setError] = useState(null);
@@ -26,7 +26,7 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
26
26
  const isTokenExpired = useCallback(() => {
27
27
  if (!expiresAtRef.current)
28
28
  return true;
29
- return Date.now() >= (expiresAtRef.current - refreshBuffer * 1000);
29
+ return Date.now() >= expiresAtRef.current - refreshBuffer * 1000;
30
30
  }, [refreshBuffer]);
31
31
  // Refreshes the token exactly once even when called concurrently.
32
32
  // All concurrent callers await the same in-flight promise — same pattern as
@@ -46,7 +46,7 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
46
46
  refreshPromiseRef.current = (async () => {
47
47
  const cfg = await getConfigRef.current();
48
48
  tokenRef.current = cfg.token;
49
- expiresAtRef.current = cfg.expiresAt ?? (Date.now() + 900000);
49
+ expiresAtRef.current = cfg.expiresAt ?? Date.now() + 900000;
50
50
  const newExpiry = Math.floor((expiresAtRef.current - Date.now()) / 1000);
51
51
  log('Token refreshed (expires in %ds)', newExpiry);
52
52
  })();
@@ -71,16 +71,21 @@ export function LocationClientProvider({ children, getConfig, refreshBuffer = 60
71
71
  // transparently without recreating the client or causing re-renders.
72
72
  useEffect(() => {
73
73
  log('Initializing LocationClientProvider');
74
- getConfigRef.current()
74
+ getConfigRef
75
+ .current()
75
76
  .then((cfg) => {
76
77
  tokenRef.current = cfg.token;
77
- expiresAtRef.current = cfg.expiresAt ?? (Date.now() + 900000);
78
+ expiresAtRef.current = cfg.expiresAt ?? Date.now() + 900000;
78
79
  // GeoPlacesClient.send() calls getToken() synchronously for the token value,
79
80
  // but token refresh is async. We create a thin send wrapper that:
80
81
  // 1. awaits ensureValidToken (via ref — always latest, race-safe)
81
82
  // 2. delegates to baseClient which reads the now-fresh token from getToken()
82
83
  // The client instance is created once and never recreated on token refresh.
83
- const baseClient = new GeoPlacesClient({ apiUrl: cfg.apiUrl, token: cfg.token, getToken });
84
+ const baseClient = new GeoPlacesClient({
85
+ apiUrl: cfg.apiUrl,
86
+ token: cfg.token,
87
+ getToken,
88
+ });
84
89
  const wrappingClient = Object.create(baseClient);
85
90
  wrappingClient.send = async (command) => {
86
91
  await ensureValidTokenRef.current();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaosity/location-client-react",
3
- "version": "0.1.12",
3
+ "version": "0.1.18",
4
4
  "description": "React bindings for Chaosity Location Service client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,6 +8,9 @@
8
8
  "scripts": {
9
9
  "build": "tsc",
10
10
  "dev": "tsc --watch",
11
+ "lint": "eslint . && prettier --check .",
12
+ "lint:fix": "eslint --fix . && prettier --write .",
13
+ "format": "prettier --write .",
11
14
  "test": "vitest run --passWithNoTests",
12
15
  "test:watch": "vitest",
13
16
  "prepublishOnly": "npm run build",
@@ -44,16 +47,21 @@
44
47
  }
45
48
  },
46
49
  "devDependencies": {
50
+ "@eslint/js": "^10.0.1",
47
51
  "@testing-library/react": "^16.0.0",
48
52
  "@testing-library/user-event": "^14.0.0",
49
53
  "@types/debug": "^4.1.12",
50
54
  "@types/react": "^19.0.0",
51
55
  "@vitest/coverage-v8": "^3.0.0",
56
+ "eslint": "^10.1.0",
52
57
  "happy-dom": "^17.0.0",
53
58
  "husky": "^9.0.0",
54
59
  "maplibre-gl": "^5.0.0",
60
+ "prettier": "^3.8.1",
61
+ "prettier-plugin-organize-imports": "^4.3.0",
55
62
  "react": "^19.0.0",
56
63
  "typescript": "^5.0.0",
64
+ "typescript-eslint": "^8.57.1",
57
65
  "vitest": "^3.0.0"
58
66
  },
59
67
  "publishConfig": {