@chaosity/location-client-react 0.4.0 → 0.5.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.
- package/dist/cjs/hooks/useMapLanguage.d.ts +16 -0
- package/dist/cjs/hooks/useMapLanguage.js +34 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/provider/LocationClientProvider.d.ts +63 -0
- package/dist/cjs/provider/LocationClientProvider.js +215 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/package.json +25 -7
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MapLike } from '@chaosity/location-client';
|
|
2
|
+
/**
|
|
3
|
+
* React hook that keeps map label language in sync with the `language` prop.
|
|
4
|
+
*
|
|
5
|
+
* Registers a persistent `style.load` listener so language is automatically
|
|
6
|
+
* reapplied whenever `map.setStyle()` is called (e.g. style or color scheme change).
|
|
7
|
+
* Also applies immediately if the style is already loaded.
|
|
8
|
+
*
|
|
9
|
+
* @param map - MapLibre Map instance, or null while the map is initializing
|
|
10
|
+
* @param language - ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja')
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const [mapInstance, setMapInstance] = useState<maplibregl.Map | null>(null)
|
|
14
|
+
* useMapLanguage(mapInstance, language)
|
|
15
|
+
*/
|
|
16
|
+
export declare function useMapLanguage(map: MapLike | null, language: string): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.useMapLanguage = useMapLanguage;
|
|
5
|
+
const location_client_1 = require("@chaosity/location-client");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
/**
|
|
8
|
+
* React hook that keeps map label language in sync with the `language` prop.
|
|
9
|
+
*
|
|
10
|
+
* Registers a persistent `style.load` listener so language is automatically
|
|
11
|
+
* reapplied whenever `map.setStyle()` is called (e.g. style or color scheme change).
|
|
12
|
+
* Also applies immediately if the style is already loaded.
|
|
13
|
+
*
|
|
14
|
+
* @param map - MapLibre Map instance, or null while the map is initializing
|
|
15
|
+
* @param language - ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja')
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const [mapInstance, setMapInstance] = useState<maplibregl.Map | null>(null)
|
|
19
|
+
* useMapLanguage(mapInstance, language)
|
|
20
|
+
*/
|
|
21
|
+
function useMapLanguage(map, language) {
|
|
22
|
+
(0, react_1.useEffect)(() => {
|
|
23
|
+
if (!map)
|
|
24
|
+
return;
|
|
25
|
+
const applyFn = () => (0, location_client_1.applyMapLanguage)(map, language);
|
|
26
|
+
if (map.isStyleLoaded()) {
|
|
27
|
+
applyFn();
|
|
28
|
+
}
|
|
29
|
+
map.on('style.load', applyFn);
|
|
30
|
+
return () => {
|
|
31
|
+
map.off('style.load', applyFn);
|
|
32
|
+
};
|
|
33
|
+
}, [map, language]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider.js';
|
|
2
|
+
export type { LocationClient, LocationClientProviderProps, SendOptions, } from './provider/LocationClientProvider.js';
|
|
3
|
+
export { useMapLanguage } from './hooks/useMapLanguage.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMapLanguage = exports.useLocationClient = exports.LocationClientProvider = void 0;
|
|
4
|
+
var LocationClientProvider_js_1 = require("./provider/LocationClientProvider.js");
|
|
5
|
+
Object.defineProperty(exports, "LocationClientProvider", { enumerable: true, get: function () { return LocationClientProvider_js_1.LocationClientProvider; } });
|
|
6
|
+
Object.defineProperty(exports, "useLocationClient", { enumerable: true, get: function () { return LocationClientProvider_js_1.useLocationClient; } });
|
|
7
|
+
var useMapLanguage_js_1 = require("./hooks/useMapLanguage.js");
|
|
8
|
+
Object.defineProperty(exports, "useMapLanguage", { enumerable: true, get: function () { return useMapLanguage_js_1.useMapLanguage; } });
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ClientConfig } from '@chaosity/location-client';
|
|
2
|
+
import { type AppConfigClaims } from '@chaosity/location-client';
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Per-request transport options.
|
|
6
|
+
*
|
|
7
|
+
* Declared structurally rather than imported so this package builds against the
|
|
8
|
+
* currently published client; it matches `SendOptions` there exactly.
|
|
9
|
+
*/
|
|
10
|
+
export interface SendOptions {
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
retry?: false | {
|
|
14
|
+
maxAttempts?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* What the provider hands out.
|
|
19
|
+
*
|
|
20
|
+
* An interface rather than `GeoPlacesClient` because the provider wraps the
|
|
21
|
+
* real client to refresh tokens first, and a class with private fields is not
|
|
22
|
+
* structurally assignable — which is why this used to be an `Object.create`
|
|
23
|
+
* prototype hack.
|
|
24
|
+
*/
|
|
25
|
+
export interface LocationClient {
|
|
26
|
+
readonly config: {
|
|
27
|
+
serviceId: string;
|
|
28
|
+
};
|
|
29
|
+
send<TInput, TOutput>(command: TInput, options?: SendOptions): Promise<TOutput>;
|
|
30
|
+
/**
|
|
31
|
+
* This application's own configuration, read from the access token —
|
|
32
|
+
* bias precision, and the countries it is scoped to (api#65).
|
|
33
|
+
*
|
|
34
|
+
* Here so a React app can SHOW its own settings: populate a country
|
|
35
|
+
* selector with the markets it serves, label a settings screen. Being a
|
|
36
|
+
* few minutes stale is cosmetic for that.
|
|
37
|
+
*
|
|
38
|
+
* It is not an entitlement check, and the `countries` value must not be
|
|
39
|
+
* used to shape requests. The token is a snapshot; the API reads the scope
|
|
40
|
+
* fresh from the application row on every call. Injecting a stale scope
|
|
41
|
+
* turns a request that would have succeeded into a 400. See
|
|
42
|
+
* `AppConfigClaims` in @chaosity/location-client for the measurement.
|
|
43
|
+
*
|
|
44
|
+
* Returns `{}` when the token carries no application config, which is the
|
|
45
|
+
* case until one is set in the portal.
|
|
46
|
+
*/
|
|
47
|
+
getAppConfig(): AppConfigClaims;
|
|
48
|
+
}
|
|
49
|
+
interface LocationClientContextValue {
|
|
50
|
+
client: LocationClient | null;
|
|
51
|
+
getToken: () => string | undefined;
|
|
52
|
+
loading: boolean;
|
|
53
|
+
error: string | null;
|
|
54
|
+
}
|
|
55
|
+
export interface LocationClientProviderProps {
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
getConfig: () => Promise<ClientConfig & {
|
|
58
|
+
expiresAt?: number;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export declare function LocationClientProvider({ children, getConfig, }: LocationClientProviderProps): import("react").JSX.Element;
|
|
62
|
+
export declare function useLocationClient(): LocationClientContextValue;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.LocationClientProvider = LocationClientProvider;
|
|
8
|
+
exports.useLocationClient = useLocationClient;
|
|
9
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
10
|
+
const location_client_1 = require("@chaosity/location-client");
|
|
11
|
+
const debug_1 = __importDefault(require("debug"));
|
|
12
|
+
const react_1 = require("react");
|
|
13
|
+
const log = (0, debug_1.default)('location-client-react:provider');
|
|
14
|
+
const LocationClientContext = (0, react_1.createContext)(undefined);
|
|
15
|
+
const DEFAULT_LIFETIME_MS = 900000;
|
|
16
|
+
/**
|
|
17
|
+
* When this token needs replacing.
|
|
18
|
+
*
|
|
19
|
+
* The `exp` claim first — it is the only value that cannot disagree with what
|
|
20
|
+
* the API will accept, and the server-side TokenProvider reads the same one.
|
|
21
|
+
* `expiresAt` is whatever `getConfig` chose to report, and the final fallback
|
|
22
|
+
* is a guess used only when the token cannot be parsed at all.
|
|
23
|
+
*/
|
|
24
|
+
function expiryOf(cfg) {
|
|
25
|
+
return ((0, location_client_1.readTokenExpiry)(cfg.token) ??
|
|
26
|
+
cfg.expiresAt ??
|
|
27
|
+
Date.now() + DEFAULT_LIFETIME_MS);
|
|
28
|
+
}
|
|
29
|
+
function LocationClientProvider({ children, getConfig, }) {
|
|
30
|
+
const [client, setClient] = (0, react_1.useState)(null);
|
|
31
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
32
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
33
|
+
const tokenRef = (0, react_1.useRef)(undefined);
|
|
34
|
+
const expiresAtRef = (0, react_1.useRef)(null);
|
|
35
|
+
const getConfigRef = (0, react_1.useRef)(getConfig);
|
|
36
|
+
const refreshPromiseRef = (0, react_1.useRef)(null);
|
|
37
|
+
const timerRef = (0, react_1.useRef)(null);
|
|
38
|
+
const mountedRef = (0, react_1.useRef)(true);
|
|
39
|
+
(0, react_1.useEffect)(() => {
|
|
40
|
+
getConfigRef.current = getConfig;
|
|
41
|
+
}, [getConfig]);
|
|
42
|
+
const isTokenExpired = (0, react_1.useCallback)(() => {
|
|
43
|
+
if (!expiresAtRef.current)
|
|
44
|
+
return true;
|
|
45
|
+
return (Date.now() >= expiresAtRef.current - location_client_1.TOKEN_REFRESH_BUFFER_SECONDS * 1000);
|
|
46
|
+
}, []);
|
|
47
|
+
/**
|
|
48
|
+
* Refresh once, however many callers ask at the same moment.
|
|
49
|
+
*
|
|
50
|
+
* REJECTS on failure. It used to swallow the error into state and resolve,
|
|
51
|
+
* so `send` carried on with the token it already had — guaranteeing a 401 on
|
|
52
|
+
* the very next call and reporting it as an API error rather than a refresh
|
|
53
|
+
* failure.
|
|
54
|
+
*/
|
|
55
|
+
const refreshToken = (0, react_1.useCallback)(async () => {
|
|
56
|
+
if (refreshPromiseRef.current)
|
|
57
|
+
return refreshPromiseRef.current;
|
|
58
|
+
refreshPromiseRef.current = (async () => {
|
|
59
|
+
const cfg = await getConfigRef.current();
|
|
60
|
+
tokenRef.current = cfg.token;
|
|
61
|
+
expiresAtRef.current = expiryOf(cfg);
|
|
62
|
+
log('Token refreshed (expires in %ds)', Math.floor((expiresAtRef.current - Date.now()) / 1000));
|
|
63
|
+
if (mountedRef.current)
|
|
64
|
+
setError(null);
|
|
65
|
+
})();
|
|
66
|
+
try {
|
|
67
|
+
await refreshPromiseRef.current;
|
|
68
|
+
scheduleRefreshRef.current();
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const message = err instanceof Error ? err.message : 'Failed to refresh token';
|
|
72
|
+
log('Token refresh failed: %s', message);
|
|
73
|
+
if (mountedRef.current)
|
|
74
|
+
setError(message);
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
refreshPromiseRef.current = null;
|
|
79
|
+
}
|
|
80
|
+
}, []);
|
|
81
|
+
/**
|
|
82
|
+
* Refresh AHEAD of expiry, on a timer.
|
|
83
|
+
*
|
|
84
|
+
* This is the whole fix for the map path. MapLibre's `transformRequest` is
|
|
85
|
+
* synchronous by contract, so `getToken` cannot await anything — the token it
|
|
86
|
+
* reads has to be valid already. Refresh used to happen only inside the `send`
|
|
87
|
+
* wrapper, which the map never calls: it requests tiles, glyphs and sprites
|
|
88
|
+
* directly. So after 15 minutes every map request failed, for as long as the
|
|
89
|
+
* page stayed open, and no amount of panning recovered it.
|
|
90
|
+
*/
|
|
91
|
+
const scheduleRefresh = (0, react_1.useCallback)(() => {
|
|
92
|
+
if (timerRef.current)
|
|
93
|
+
clearTimeout(timerRef.current);
|
|
94
|
+
if (!expiresAtRef.current)
|
|
95
|
+
return;
|
|
96
|
+
const delay = Math.max(0, expiresAtRef.current - location_client_1.TOKEN_REFRESH_BUFFER_SECONDS * 1000 - Date.now());
|
|
97
|
+
log('Next refresh in %ds', Math.floor(delay / 1000));
|
|
98
|
+
timerRef.current = setTimeout(() => {
|
|
99
|
+
// Errors are already surfaced onto state by refreshToken; swallow here so
|
|
100
|
+
// a failed background refresh cannot become an unhandled rejection.
|
|
101
|
+
void refreshToken().catch(() => { });
|
|
102
|
+
}, delay);
|
|
103
|
+
}, [refreshToken]);
|
|
104
|
+
// refreshToken and scheduleRefresh reference each other; a ref breaks the cycle
|
|
105
|
+
// without recreating either callback on every render.
|
|
106
|
+
const scheduleRefreshRef = (0, react_1.useRef)(() => { });
|
|
107
|
+
(0, react_1.useEffect)(() => {
|
|
108
|
+
scheduleRefreshRef.current = scheduleRefresh;
|
|
109
|
+
}, [scheduleRefresh]);
|
|
110
|
+
/**
|
|
111
|
+
* Synchronous read for the map path.
|
|
112
|
+
*
|
|
113
|
+
* If the token is already stale — a timer that never fired because the tab was
|
|
114
|
+
* backgrounded and throttled — this kicks off a refresh but cannot wait for it.
|
|
115
|
+
* The current read still returns the stale value; the point is that the NEXT
|
|
116
|
+
* one will not.
|
|
117
|
+
*/
|
|
118
|
+
const getToken = (0, react_1.useCallback)(() => {
|
|
119
|
+
// `tokenRef.current` guards the pre-initialisation window: until the first
|
|
120
|
+
// config load lands there is no expiry to judge, and firing here would race
|
|
121
|
+
// the initial fetch and request a second token nobody asked for.
|
|
122
|
+
if (tokenRef.current && isTokenExpired() && !refreshPromiseRef.current) {
|
|
123
|
+
log('Stale token read — refreshing in the background');
|
|
124
|
+
void refreshToken().catch(() => { });
|
|
125
|
+
}
|
|
126
|
+
return tokenRef.current;
|
|
127
|
+
}, [isTokenExpired, refreshToken]);
|
|
128
|
+
const ensureValidToken = (0, react_1.useCallback)(async () => {
|
|
129
|
+
if (!isTokenExpired())
|
|
130
|
+
return;
|
|
131
|
+
await refreshToken();
|
|
132
|
+
}, [isTokenExpired, refreshToken]);
|
|
133
|
+
const ensureValidTokenRef = (0, react_1.useRef)(ensureValidToken);
|
|
134
|
+
(0, react_1.useEffect)(() => {
|
|
135
|
+
ensureValidTokenRef.current = ensureValidToken;
|
|
136
|
+
}, [ensureValidToken]);
|
|
137
|
+
/**
|
|
138
|
+
* A backgrounded tab has its timers throttled, so the scheduled refresh can be
|
|
139
|
+
* arbitrarily late. Refresh on the way back in, before the user touches the map.
|
|
140
|
+
*/
|
|
141
|
+
(0, react_1.useEffect)(() => {
|
|
142
|
+
if (typeof document === 'undefined')
|
|
143
|
+
return;
|
|
144
|
+
const onVisible = () => {
|
|
145
|
+
if (document.visibilityState === 'visible' &&
|
|
146
|
+
tokenRef.current &&
|
|
147
|
+
isTokenExpired()) {
|
|
148
|
+
log('Tab visible again with a stale token — refreshing');
|
|
149
|
+
void refreshToken().catch(() => { });
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
153
|
+
return () => document.removeEventListener('visibilitychange', onVisible);
|
|
154
|
+
}, [isTokenExpired, refreshToken]);
|
|
155
|
+
(0, react_1.useEffect)(() => {
|
|
156
|
+
mountedRef.current = true;
|
|
157
|
+
log('Initializing LocationClientProvider');
|
|
158
|
+
getConfigRef
|
|
159
|
+
.current()
|
|
160
|
+
.then((cfg) => {
|
|
161
|
+
if (!mountedRef.current)
|
|
162
|
+
return;
|
|
163
|
+
tokenRef.current = cfg.token;
|
|
164
|
+
expiresAtRef.current = expiryOf(cfg);
|
|
165
|
+
const baseClient = new location_client_1.GeoPlacesClient({
|
|
166
|
+
apiUrl: cfg.apiUrl,
|
|
167
|
+
token: cfg.token,
|
|
168
|
+
getToken,
|
|
169
|
+
});
|
|
170
|
+
// A plain object, not Object.create(baseClient): the prototype hack was
|
|
171
|
+
// opaque, and its `send` dropped the second argument entirely — so once
|
|
172
|
+
// the client gained `signal`/`timeoutMs`, every option passed through
|
|
173
|
+
// this provider would have been silently discarded.
|
|
174
|
+
const refreshing = {
|
|
175
|
+
config: baseClient.config,
|
|
176
|
+
async send(command, options) {
|
|
177
|
+
await ensureValidTokenRef.current();
|
|
178
|
+
return baseClient.send(command, options);
|
|
179
|
+
},
|
|
180
|
+
// Reads whatever token the client currently holds. Deliberately not
|
|
181
|
+
// awaiting a refresh: this is display data, callers expect it to be
|
|
182
|
+
// synchronous, and a token that is minutes from expiry carries the
|
|
183
|
+
// same application config as its replacement will.
|
|
184
|
+
getAppConfig() {
|
|
185
|
+
return baseClient.getAppConfig();
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
setClient(refreshing);
|
|
189
|
+
scheduleRefreshRef.current();
|
|
190
|
+
log('Client initialized (token expires in %ds)', Math.floor((expiresAtRef.current - Date.now()) / 1000));
|
|
191
|
+
setLoading(false);
|
|
192
|
+
})
|
|
193
|
+
.catch((err) => {
|
|
194
|
+
if (!mountedRef.current)
|
|
195
|
+
return;
|
|
196
|
+
const message = err instanceof Error ? err.message : 'Failed to initialize client';
|
|
197
|
+
log('Initialization failed: %s', message);
|
|
198
|
+
setError(message);
|
|
199
|
+
setLoading(false);
|
|
200
|
+
});
|
|
201
|
+
return () => {
|
|
202
|
+
mountedRef.current = false;
|
|
203
|
+
if (timerRef.current)
|
|
204
|
+
clearTimeout(timerRef.current);
|
|
205
|
+
};
|
|
206
|
+
}, [getToken]);
|
|
207
|
+
return ((0, jsx_runtime_1.jsx)(LocationClientContext.Provider, { value: { client, getToken, loading, error }, children: children }));
|
|
208
|
+
}
|
|
209
|
+
function useLocationClient() {
|
|
210
|
+
const context = (0, react_1.useContext)(LocationClientContext);
|
|
211
|
+
if (context === undefined) {
|
|
212
|
+
throw new Error('useLocationClient must be used within LocationClientProvider');
|
|
213
|
+
}
|
|
214
|
+
return context;
|
|
215
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider';
|
|
2
|
-
export type { LocationClient, LocationClientProviderProps, SendOptions, } from './provider/LocationClientProvider';
|
|
3
|
-
export { useMapLanguage } from './hooks/useMapLanguage';
|
|
1
|
+
export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider.js';
|
|
2
|
+
export type { LocationClient, LocationClientProviderProps, SendOptions, } from './provider/LocationClientProvider.js';
|
|
3
|
+
export { useMapLanguage } from './hooks/useMapLanguage.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider';
|
|
2
|
-
export { useMapLanguage } from './hooks/useMapLanguage';
|
|
1
|
+
export { LocationClientProvider, useLocationClient, } from './provider/LocationClientProvider.js';
|
|
2
|
+
export { useMapLanguage } from './hooks/useMapLanguage.js';
|
package/package.json
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaosity/location-client-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "React bindings for Chaosity Location Service client",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
6
|
+
"main": "dist/cjs/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
9
|
+
"build": "tsc && tsc -p tsconfig.cjs.json && node scripts/finish-cjs.mjs",
|
|
10
10
|
"dev": "tsc --watch",
|
|
11
11
|
"lint": "eslint . && prettier --check .",
|
|
12
12
|
"lint:fix": "eslint --fix . && prettier --write .",
|
|
13
13
|
"format": "prettier --write .",
|
|
14
14
|
"test": "vitest run",
|
|
15
15
|
"test:watch": "vitest",
|
|
16
|
-
"prepublishOnly": "npm run build",
|
|
17
|
-
"prepare": "husky"
|
|
16
|
+
"prepublishOnly": "npm run build && npm run smoke",
|
|
17
|
+
"prepare": "husky",
|
|
18
|
+
"smoke": "node scripts/smoke.mjs"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [
|
|
20
21
|
"react",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@chaosity/location-client": "^0.
|
|
51
|
+
"@chaosity/location-client": "^0.6.0",
|
|
51
52
|
"@eslint/js": "^10.0.1",
|
|
52
53
|
"@testing-library/react": "^16.0.0",
|
|
53
54
|
"@testing-library/user-event": "^14.0.0",
|
|
@@ -71,5 +72,22 @@
|
|
|
71
72
|
"files": [
|
|
72
73
|
"dist",
|
|
73
74
|
"README.md"
|
|
74
|
-
]
|
|
75
|
+
],
|
|
76
|
+
"exports": {
|
|
77
|
+
".": {
|
|
78
|
+
"import": {
|
|
79
|
+
"types": "./dist/index.d.ts",
|
|
80
|
+
"default": "./dist/index.js"
|
|
81
|
+
},
|
|
82
|
+
"require": {
|
|
83
|
+
"types": "./dist/cjs/index.d.ts",
|
|
84
|
+
"default": "./dist/cjs/index.js"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"module": "dist/index.js",
|
|
89
|
+
"sideEffects": false,
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=20"
|
|
92
|
+
}
|
|
75
93
|
}
|