@wherabouts/react 0.1.1 → 0.2.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/index.cjs CHANGED
@@ -1,66 +1,361 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ var sdk = require('@wherabouts/sdk');
5
+
6
+ // src/combobox.ts
7
+ var INITIAL_COMBOBOX_STATE = {
8
+ isOpen: false,
9
+ activeIndex: -1
10
+ };
11
+ function comboboxReducer(state, action) {
12
+ switch (action.type) {
13
+ case "next": {
14
+ if (action.count === 0) {
15
+ return state;
16
+ }
17
+ const activeIndex = state.activeIndex >= action.count - 1 ? 0 : state.activeIndex + 1;
18
+ return { isOpen: true, activeIndex };
19
+ }
20
+ case "prev": {
21
+ if (action.count === 0) {
22
+ return state;
23
+ }
24
+ const activeIndex = state.activeIndex <= 0 ? action.count - 1 : state.activeIndex - 1;
25
+ return { isOpen: true, activeIndex };
26
+ }
27
+ case "first":
28
+ return action.count === 0 ? state : { isOpen: true, activeIndex: 0 };
29
+ case "last":
30
+ return action.count === 0 ? state : { isOpen: true, activeIndex: action.count - 1 };
31
+ case "set":
32
+ return { isOpen: true, activeIndex: action.index };
33
+ case "open":
34
+ return { ...state, isOpen: true };
35
+ case "close":
36
+ case "select":
37
+ return INITIAL_COMBOBOX_STATE;
38
+ default:
39
+ return INITIAL_COMBOBOX_STATE;
40
+ }
41
+ }
42
+ function keyToAction(key, count) {
43
+ switch (key) {
44
+ case "ArrowDown":
45
+ return { type: "next", count };
46
+ case "ArrowUp":
47
+ return { type: "prev", count };
48
+ case "Home":
49
+ return { type: "first", count };
50
+ case "End":
51
+ return { type: "last", count };
52
+ case "Escape":
53
+ return { type: "close" };
54
+ case "Enter":
55
+ return { type: "select" };
56
+ default:
57
+ return null;
58
+ }
59
+ }
60
+ var listboxId = (id) => `${id}-listbox`;
61
+ var optionId = (id, index) => `${id}-option-${index}`;
62
+ function buildInputProps(id, state) {
63
+ return {
64
+ role: "combobox",
65
+ "aria-autocomplete": "list",
66
+ "aria-expanded": state.isOpen,
67
+ "aria-controls": listboxId(id),
68
+ "aria-activedescendant": state.isOpen && state.activeIndex >= 0 ? optionId(id, state.activeIndex) : void 0
69
+ };
70
+ }
71
+ function buildListboxProps(id) {
72
+ return { id: listboxId(id), role: "listbox" };
73
+ }
74
+ function buildItemProps(id, index, state) {
75
+ return {
76
+ role: "option",
77
+ id: optionId(id, index),
78
+ "aria-selected": state.activeIndex === index
79
+ };
80
+ }
81
+ function useCombobox(options) {
82
+ const { id, count, onSelect } = options;
83
+ const [state, dispatch] = react.useReducer(comboboxReducer, INITIAL_COMBOBOX_STATE);
84
+ const open = react.useCallback(() => dispatch({ type: "open" }), []);
85
+ const close = react.useCallback(() => dispatch({ type: "close" }), []);
86
+ const reset = react.useCallback(() => dispatch({ type: "reset" }), []);
87
+ const setActiveIndex = react.useCallback(
88
+ (index) => dispatch({ type: "set", index }),
89
+ []
90
+ );
91
+ const getInputProps = react.useCallback(
92
+ () => ({
93
+ ...buildInputProps(id, state),
94
+ onFocus: () => dispatch({ type: "open" }),
95
+ onBlur: () => dispatch({ type: "close" }),
96
+ onKeyDown: (event) => {
97
+ const action = keyToAction(event.key, count);
98
+ if (!action) {
99
+ return;
100
+ }
101
+ if (action.type === "select") {
102
+ if (state.activeIndex >= 0) {
103
+ event.preventDefault();
104
+ onSelect?.(state.activeIndex);
105
+ }
106
+ dispatch({ type: "select" });
107
+ return;
108
+ }
109
+ event.preventDefault();
110
+ dispatch(action);
111
+ }
112
+ }),
113
+ [id, state, count, onSelect]
114
+ );
115
+ const getListboxProps = react.useCallback(() => buildListboxProps(id), [id]);
116
+ const getItemProps = react.useCallback(
117
+ (index) => ({
118
+ ...buildItemProps(id, index, state),
119
+ onMouseEnter: () => dispatch({ type: "set", index }),
120
+ onMouseDown: (event) => {
121
+ event.preventDefault();
122
+ onSelect?.(index);
123
+ dispatch({ type: "select" });
124
+ }
125
+ }),
126
+ [id, state, onSelect]
127
+ );
128
+ return {
129
+ isOpen: state.isOpen,
130
+ activeIndex: state.activeIndex,
131
+ open,
132
+ close,
133
+ reset,
134
+ setActiveIndex,
135
+ getInputProps,
136
+ getListboxProps,
137
+ getItemProps
138
+ };
139
+ }
140
+
141
+ // src/autocomplete-cache.ts
142
+ var KEY_PREFIX = "wh:ac:";
143
+ function buildCacheKey(parts) {
144
+ const q = parts.q.trim().toLowerCase();
145
+ return JSON.stringify([
146
+ q,
147
+ parts.country ?? "",
148
+ parts.state ?? "",
149
+ parts.limit ?? "",
150
+ parts.lat ?? "",
151
+ parts.lng ?? ""
152
+ ]);
153
+ }
154
+ function readCache(storage, key, ttlMs, now) {
155
+ const raw = storage.getItem(KEY_PREFIX + key);
156
+ if (raw === null) {
157
+ return null;
158
+ }
159
+ let entry;
160
+ try {
161
+ entry = JSON.parse(raw);
162
+ } catch {
163
+ storage.removeItem(KEY_PREFIX + key);
164
+ return null;
165
+ }
166
+ if (typeof entry?.t !== "number" || !Array.isArray(entry.results) || now - entry.t > ttlMs) {
167
+ storage.removeItem(KEY_PREFIX + key);
168
+ return null;
169
+ }
170
+ return entry.results;
171
+ }
172
+ function writeCache(storage, key, results, now) {
173
+ const entry = { results, t: now };
174
+ try {
175
+ storage.setItem(KEY_PREFIX + key, JSON.stringify(entry));
176
+ } catch {
177
+ }
178
+ }
4
179
 
5
180
  // src/use-autocomplete.ts
181
+ var DEFAULT_DEBOUNCE_MS = 300;
182
+ var DEFAULT_MIN_LENGTH = 2;
183
+ var DEFAULT_CACHE_TTL_MS = 6e4;
184
+ function deriveStatus(s) {
185
+ if (s.query.trim().length < s.minLength) {
186
+ return "idle";
187
+ }
188
+ if (s.loading) {
189
+ return "loading";
190
+ }
191
+ if (s.error) {
192
+ return "error";
193
+ }
194
+ return s.results.length === 0 ? "empty" : "success";
195
+ }
196
+ function planSearch(args) {
197
+ if (args.trimmed.length < args.minLength) {
198
+ return { kind: "idle" };
199
+ }
200
+ if (args.cacheStorage) {
201
+ const hit = readCache(
202
+ args.cacheStorage,
203
+ buildCacheKey(args.keyParts),
204
+ args.cacheTtl,
205
+ Date.now()
206
+ );
207
+ if (hit) {
208
+ return { kind: "cache", results: hit };
209
+ }
210
+ }
211
+ return { kind: "fetch" };
212
+ }
213
+ async function executeAutocomplete(client, params, controller, actions) {
214
+ try {
215
+ const res = await client.addresses.autocomplete(params, {
216
+ signal: controller.signal
217
+ });
218
+ if (!controller.signal.aborted) {
219
+ actions.onSuccess(res.results);
220
+ }
221
+ } catch (e) {
222
+ if (!controller.signal.aborted) {
223
+ const err = e instanceof Error ? e : new Error(String(e));
224
+ actions.onError(err, sdk.isRateLimitError(e));
225
+ }
226
+ } finally {
227
+ if (!controller.signal.aborted) {
228
+ actions.onSettled();
229
+ }
230
+ }
231
+ }
232
+ function cancelPending(timer, controller) {
233
+ if (timer) {
234
+ clearTimeout(timer);
235
+ }
236
+ controller?.abort();
237
+ }
6
238
  function useAutocomplete(client, options = {}) {
7
- const { debounceMs = 300, limit, country, state } = options;
239
+ const {
240
+ debounceMs = DEFAULT_DEBOUNCE_MS,
241
+ minLength = DEFAULT_MIN_LENGTH,
242
+ limit,
243
+ country,
244
+ state,
245
+ lat,
246
+ lng,
247
+ sessionToken,
248
+ keepPreviousData = false,
249
+ cache
250
+ } = options;
8
251
  const [query, setQuery] = react.useState("");
9
252
  const [results, setResults] = react.useState([]);
10
253
  const [loading, setLoading] = react.useState(false);
11
254
  const [error, setError] = react.useState(null);
255
+ const [rateLimited, setRateLimited] = react.useState(false);
12
256
  const abortRef = react.useRef(null);
13
257
  const timerRef = react.useRef(null);
14
258
  const handleSetQuery = react.useCallback((q) => {
15
259
  setQuery(q);
16
260
  }, []);
261
+ const reset = react.useCallback(() => {
262
+ cancelPending(timerRef.current, abortRef.current);
263
+ setQuery("");
264
+ setResults([]);
265
+ setLoading(false);
266
+ setError(null);
267
+ setRateLimited(false);
268
+ }, []);
269
+ const cacheStorage = cache?.storage;
270
+ const cacheTtl = cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
17
271
  react.useEffect(() => {
18
- if (timerRef.current) {
19
- clearTimeout(timerRef.current);
20
- }
21
- if (abortRef.current) {
22
- abortRef.current.abort();
272
+ cancelPending(timerRef.current, abortRef.current);
273
+ const trimmed = query.trim();
274
+ const keyParts = {
275
+ q: trimmed,
276
+ country,
277
+ state,
278
+ limit,
279
+ lat,
280
+ lng
281
+ };
282
+ const plan = planSearch({
283
+ trimmed,
284
+ minLength,
285
+ cacheStorage,
286
+ cacheTtl,
287
+ keyParts
288
+ });
289
+ setError(null);
290
+ setRateLimited(false);
291
+ if (plan.kind === "idle") {
292
+ if (!keepPreviousData) {
293
+ setResults([]);
294
+ }
295
+ setLoading(false);
296
+ return;
23
297
  }
24
- if (!query.trim()) {
25
- setResults([]);
298
+ if (plan.kind === "cache") {
299
+ setResults(plan.results);
26
300
  setLoading(false);
27
- setError(null);
28
301
  return;
29
302
  }
303
+ if (!keepPreviousData) {
304
+ setResults([]);
305
+ }
30
306
  setLoading(true);
31
307
  timerRef.current = setTimeout(async () => {
32
308
  const controller = new AbortController();
33
309
  abortRef.current = controller;
34
- setError(null);
35
- try {
36
- const res = await client.addresses.autocomplete(
37
- { q: query, limit, country, state },
38
- { signal: controller.signal }
39
- );
40
- if (!controller.signal.aborted) {
41
- setResults(res.results);
310
+ await executeAutocomplete(
311
+ client,
312
+ { q: trimmed, limit, country, state, lat, lng, sessionToken },
313
+ controller,
314
+ {
315
+ onSuccess: (r) => {
316
+ setResults(r);
317
+ if (cacheStorage) {
318
+ writeCache(cacheStorage, buildCacheKey(keyParts), r, Date.now());
319
+ }
320
+ },
321
+ onError: (err, limited) => {
322
+ setError(err);
323
+ setRateLimited(limited);
324
+ if (!keepPreviousData) {
325
+ setResults([]);
326
+ }
327
+ },
328
+ onSettled: () => setLoading(false)
42
329
  }
43
- } catch (e) {
44
- if (!controller.signal.aborted) {
45
- setError(e instanceof Error ? e : new Error(String(e)));
46
- setResults([]);
47
- }
48
- } finally {
49
- if (!controller.signal.aborted) {
50
- setLoading(false);
51
- }
52
- }
330
+ );
53
331
  }, debounceMs);
54
- return () => {
55
- if (timerRef.current) {
56
- clearTimeout(timerRef.current);
57
- }
58
- if (abortRef.current) {
59
- abortRef.current.abort();
60
- }
61
- };
62
- }, [query, debounceMs, limit, country, state, client]);
63
- return { query, setQuery: handleSetQuery, results, loading, error };
332
+ return () => cancelPending(timerRef.current, abortRef.current);
333
+ }, [
334
+ query,
335
+ debounceMs,
336
+ minLength,
337
+ limit,
338
+ country,
339
+ state,
340
+ lat,
341
+ lng,
342
+ sessionToken,
343
+ keepPreviousData,
344
+ cacheStorage,
345
+ cacheTtl,
346
+ client
347
+ ]);
348
+ const status = deriveStatus({ query, minLength, loading, error, results });
349
+ return {
350
+ query,
351
+ setQuery: handleSetQuery,
352
+ results,
353
+ loading,
354
+ error,
355
+ rateLimited,
356
+ reset,
357
+ status
358
+ };
64
359
  }
65
360
  function useReverseGeocode(client, coords) {
66
361
  const [address, setAddress] = react.useState(null);
@@ -103,6 +398,71 @@ function useReverseGeocode(client, coords) {
103
398
  }, [coords?.lat, coords?.lng, client]);
104
399
  return { address, distance, loading, error };
105
400
  }
401
+
402
+ // src/async-resource.ts
403
+ async function runResource(promise, signal, actions) {
404
+ try {
405
+ const data = await promise;
406
+ if (!signal.aborted) {
407
+ actions.onSuccess(data);
408
+ }
409
+ } catch (e) {
410
+ if (!signal.aborted) {
411
+ actions.onError(e instanceof Error ? e : new Error(String(e)));
412
+ }
413
+ } finally {
414
+ if (!signal.aborted) {
415
+ actions.onSettled();
416
+ }
417
+ }
418
+ }
419
+
420
+ // src/use-routing.ts
421
+ function useRoutingResource(client, params, call) {
422
+ const [data, setData] = react.useState(null);
423
+ const [loading, setLoading] = react.useState(false);
424
+ const [error, setError] = react.useState(null);
425
+ const abortRef = react.useRef(null);
426
+ const key = params ? JSON.stringify(params) : null;
427
+ react.useEffect(() => {
428
+ if (abortRef.current) {
429
+ abortRef.current.abort();
430
+ }
431
+ if (!params) {
432
+ setData(null);
433
+ setLoading(false);
434
+ setError(null);
435
+ return;
436
+ }
437
+ const controller = new AbortController();
438
+ abortRef.current = controller;
439
+ setLoading(true);
440
+ setError(null);
441
+ runResource(
442
+ call(client, params, { signal: controller.signal }),
443
+ controller.signal,
444
+ {
445
+ onSuccess: setData,
446
+ onError: setError,
447
+ onSettled: () => setLoading(false)
448
+ }
449
+ );
450
+ return () => controller.abort();
451
+ }, [key, client, call]);
452
+ return { data, loading, error };
453
+ }
454
+ var directionsCall = (client, params, options) => client.routing.directions(params, options);
455
+ var matrixCall = (client, params, options) => client.routing.matrix(params, options);
456
+ var isochroneCall = (client, params, options) => client.routing.isochrone(params, options);
457
+ function useDirections(client, params) {
458
+ return useRoutingResource(client, params, directionsCall);
459
+ }
460
+ function useMatrix(client, params) {
461
+ return useRoutingResource(client, params, matrixCall);
462
+ }
463
+ function useIsochrone(client, params) {
464
+ return useRoutingResource(client, params, isochroneCall);
465
+ }
106
466
  function useZoneContains(client, coords) {
107
467
  const [zones, setZones] = react.useState([]);
108
468
  const [loading, setLoading] = react.useState(false);
@@ -142,7 +502,18 @@ function useZoneContains(client, coords) {
142
502
  return { zones, loading, error };
143
503
  }
144
504
 
505
+ exports.INITIAL_COMBOBOX_STATE = INITIAL_COMBOBOX_STATE;
506
+ exports.buildInputProps = buildInputProps;
507
+ exports.buildItemProps = buildItemProps;
508
+ exports.buildListboxProps = buildListboxProps;
509
+ exports.comboboxReducer = comboboxReducer;
510
+ exports.deriveStatus = deriveStatus;
511
+ exports.keyToAction = keyToAction;
145
512
  exports.useAutocomplete = useAutocomplete;
513
+ exports.useCombobox = useCombobox;
514
+ exports.useDirections = useDirections;
515
+ exports.useIsochrone = useIsochrone;
516
+ exports.useMatrix = useMatrix;
146
517
  exports.useReverseGeocode = useReverseGeocode;
147
518
  exports.useZoneContains = useZoneContains;
148
519
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-autocomplete.ts","../src/use-reverse-geocode.ts","../src/use-zone-contains.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AddressSuggestion, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAutocompleteOptions {\n\tcountry?: string;\n\tdebounceMs?: number;\n\tlimit?: number;\n\tstate?: string;\n}\n\nexport interface UseAutocompleteResult {\n\terror: Error | null;\n\tloading: boolean;\n\tquery: string;\n\tresults: AddressSuggestion[];\n\tsetQuery: (q: string) => void;\n}\n\nexport function useAutocomplete(\n\tclient: WheraboutsClient,\n\toptions: UseAutocompleteOptions = {}\n): UseAutocompleteResult {\n\tconst { debounceMs = 300, limit, country, state } = options;\n\n\tconst [query, setQuery] = useState(\"\");\n\tconst [results, setResults] = useState<AddressSuggestion[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst abortRef = useRef<AbortController | null>(null);\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t// Stable setter — consumers can safely pass this to input onChange\n\tconst handleSetQuery = useCallback((q: string) => {\n\t\tsetQuery(q);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (timerRef.current) {\n\t\t\tclearTimeout(timerRef.current);\n\t\t}\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!query.trim()) {\n\t\t\tsetResults([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tsetLoading(true);\n\n\t\ttimerRef.current = setTimeout(async () => {\n\t\t\tconst controller = new AbortController();\n\t\t\tabortRef.current = controller;\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tconst res = await client.addresses.autocomplete(\n\t\t\t\t\t{ q: query, limit, country, state },\n\t\t\t\t\t{ signal: controller.signal }\n\t\t\t\t);\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetResults(res.results);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t\tsetResults([]);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}, debounceMs);\n\n\t\treturn () => {\n\t\t\tif (timerRef.current) {\n\t\t\t\tclearTimeout(timerRef.current);\n\t\t\t}\n\t\t\tif (abortRef.current) {\n\t\t\t\tabortRef.current.abort();\n\t\t\t}\n\t\t};\n\t}, [query, debounceMs, limit, country, state, client]);\n\n\treturn { query, setQuery: handleSetQuery, results, loading, error };\n}\n","import type { ReverseGeocodeAddress, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface LatLng {\n\tlat: number;\n\tlng: number;\n}\n\nexport interface UseReverseGeocodeResult {\n\taddress: ReverseGeocodeAddress | null;\n\tdistance: number | null;\n\terror: Error | null;\n\tloading: boolean;\n}\n\nexport function useReverseGeocode(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseReverseGeocodeResult {\n\tconst [address, setAddress] = useState<ReverseGeocodeAddress | null>(null);\n\tconst [distance, setDistance] = useState<number | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetAddress(null);\n\t\t\tsetDistance(null);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.addresses\n\t\t\t.reverse(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetAddress(res.address);\n\t\t\t\t\tsetDistance(res.distance);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { address, distance, loading, error };\n}\n","import type { WheraboutsClient, ZoneRecord } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport type { LatLng } from \"./use-reverse-geocode.ts\";\n\nexport interface UseZoneContainsResult {\n\terror: Error | null;\n\tloading: boolean;\n\tzones: ZoneRecord[];\n}\n\nexport function useZoneContains(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseZoneContainsResult {\n\tconst [zones, setZones] = useState<ZoneRecord[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetZones([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.zones\n\t\t\t.contains(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetZones(res.zones);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { zones, loading, error };\n}\n"]}
1
+ {"version":3,"sources":["../src/combobox.ts","../src/autocomplete-cache.ts","../src/use-autocomplete.ts","../src/use-reverse-geocode.ts","../src/async-resource.ts","../src/use-routing.ts","../src/use-zone-contains.ts"],"names":["useReducer","useCallback","isRateLimitError","useState","useRef","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n\ttype KeyboardEvent,\n\ttype MouseEvent,\n\tuseCallback,\n\tuseReducer,\n} from \"react\";\n\n/**\n * Headless WAI-ARIA combobox helper for autocomplete dropdowns. Provides the\n * keyboard state machine (↑/↓/Home/End/Enter/Esc, wrapping) and ARIA prop\n * getters so an input + listbox is accessible by default. Bring your own\n * markup and data; this hook owns only the open/active-option state.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/\n */\n\nexport interface ComboboxState {\n\t/** Index of the visually-active option, or -1 when none is active. */\n\tactiveIndex: number;\n\tisOpen: boolean;\n}\n\nexport type ComboboxAction =\n\t| { type: \"next\"; count: number }\n\t| { type: \"prev\"; count: number }\n\t| { type: \"first\"; count: number }\n\t| { type: \"last\"; count: number }\n\t| { type: \"set\"; index: number }\n\t| { type: \"open\" }\n\t| { type: \"close\" }\n\t| { type: \"select\" }\n\t| { type: \"reset\" };\n\nexport const INITIAL_COMBOBOX_STATE: ComboboxState = {\n\tisOpen: false,\n\tactiveIndex: -1,\n};\n\n/** Pure transition function for the combobox keyboard state machine. */\nexport function comboboxReducer(\n\tstate: ComboboxState,\n\taction: ComboboxAction\n): ComboboxState {\n\tswitch (action.type) {\n\t\tcase \"next\": {\n\t\t\tif (action.count === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst activeIndex =\n\t\t\t\tstate.activeIndex >= action.count - 1 ? 0 : state.activeIndex + 1;\n\t\t\treturn { isOpen: true, activeIndex };\n\t\t}\n\t\tcase \"prev\": {\n\t\t\tif (action.count === 0) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst activeIndex =\n\t\t\t\tstate.activeIndex <= 0 ? action.count - 1 : state.activeIndex - 1;\n\t\t\treturn { isOpen: true, activeIndex };\n\t\t}\n\t\tcase \"first\":\n\t\t\treturn action.count === 0 ? state : { isOpen: true, activeIndex: 0 };\n\t\tcase \"last\":\n\t\t\treturn action.count === 0\n\t\t\t\t? state\n\t\t\t\t: { isOpen: true, activeIndex: action.count - 1 };\n\t\tcase \"set\":\n\t\t\treturn { isOpen: true, activeIndex: action.index };\n\t\tcase \"open\":\n\t\t\treturn { ...state, isOpen: true };\n\t\tcase \"close\":\n\t\tcase \"select\":\n\t\t\treturn INITIAL_COMBOBOX_STATE;\n\t\tdefault:\n\t\t\treturn INITIAL_COMBOBOX_STATE;\n\t}\n}\n\n/** Map a keyboard key to a combobox action, or null when it is irrelevant. */\nexport function keyToAction(key: string, count: number): ComboboxAction | null {\n\tswitch (key) {\n\t\tcase \"ArrowDown\":\n\t\t\treturn { type: \"next\", count };\n\t\tcase \"ArrowUp\":\n\t\t\treturn { type: \"prev\", count };\n\t\tcase \"Home\":\n\t\t\treturn { type: \"first\", count };\n\t\tcase \"End\":\n\t\t\treturn { type: \"last\", count };\n\t\tcase \"Escape\":\n\t\t\treturn { type: \"close\" };\n\t\tcase \"Enter\":\n\t\t\treturn { type: \"select\" };\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nexport interface ComboboxInputProps {\n\t\"aria-activedescendant\": string | undefined;\n\t\"aria-autocomplete\": \"list\";\n\t\"aria-controls\": string;\n\t\"aria-expanded\": boolean;\n\trole: \"combobox\";\n}\n\nexport interface ComboboxListboxProps {\n\tid: string;\n\trole: \"listbox\";\n}\n\nexport interface ComboboxItemProps {\n\t\"aria-selected\": boolean;\n\tid: string;\n\trole: \"option\";\n}\n\nconst listboxId = (id: string) => `${id}-listbox`;\nconst optionId = (id: string, index: number) => `${id}-option-${index}`;\n\n/** Build the ARIA attributes for the combobox input — pure. */\nexport function buildInputProps(\n\tid: string,\n\tstate: ComboboxState\n): ComboboxInputProps {\n\treturn {\n\t\trole: \"combobox\",\n\t\t\"aria-autocomplete\": \"list\",\n\t\t\"aria-expanded\": state.isOpen,\n\t\t\"aria-controls\": listboxId(id),\n\t\t\"aria-activedescendant\":\n\t\t\tstate.isOpen && state.activeIndex >= 0\n\t\t\t\t? optionId(id, state.activeIndex)\n\t\t\t\t: undefined,\n\t};\n}\n\n/** Build the ARIA attributes for the listbox container — pure. */\nexport function buildListboxProps(id: string): ComboboxListboxProps {\n\treturn { id: listboxId(id), role: \"listbox\" };\n}\n\n/** Build the ARIA attributes for an option at `index` — pure. */\nexport function buildItemProps(\n\tid: string,\n\tindex: number,\n\tstate: ComboboxState\n): ComboboxItemProps {\n\treturn {\n\t\trole: \"option\",\n\t\tid: optionId(id, index),\n\t\t\"aria-selected\": state.activeIndex === index,\n\t};\n}\n\nexport interface UseComboboxOptions {\n\t/** Number of options currently rendered (drives wrapping/bounds). */\n\tcount: number;\n\t/** Stable id root; option/listbox ids derive from it. */\n\tid: string;\n\t/** Called when an option is chosen via Enter or pointer. */\n\tonSelect?: (index: number) => void;\n}\n\nexport interface UseComboboxResult {\n\tactiveIndex: number;\n\tclose: () => void;\n\tgetInputProps: () => ComboboxInputProps & {\n\t\tonBlur: () => void;\n\t\tonFocus: () => void;\n\t\tonKeyDown: (event: KeyboardEvent) => void;\n\t};\n\tgetItemProps: (index: number) => ComboboxItemProps & {\n\t\tonMouseDown: (event: MouseEvent) => void;\n\t\tonMouseEnter: () => void;\n\t};\n\tgetListboxProps: () => ComboboxListboxProps;\n\tisOpen: boolean;\n\topen: () => void;\n\treset: () => void;\n\tsetActiveIndex: (index: number) => void;\n}\n\nexport function useCombobox(options: UseComboboxOptions): UseComboboxResult {\n\tconst { id, count, onSelect } = options;\n\tconst [state, dispatch] = useReducer(comboboxReducer, INITIAL_COMBOBOX_STATE);\n\n\tconst open = useCallback(() => dispatch({ type: \"open\" }), []);\n\tconst close = useCallback(() => dispatch({ type: \"close\" }), []);\n\tconst reset = useCallback(() => dispatch({ type: \"reset\" }), []);\n\tconst setActiveIndex = useCallback(\n\t\t(index: number) => dispatch({ type: \"set\", index }),\n\t\t[]\n\t);\n\n\tconst getInputProps = useCallback(\n\t\t() => ({\n\t\t\t...buildInputProps(id, state),\n\t\t\tonFocus: () => dispatch({ type: \"open\" }),\n\t\t\tonBlur: () => dispatch({ type: \"close\" }),\n\t\t\tonKeyDown: (event: KeyboardEvent) => {\n\t\t\t\tconst action = keyToAction(event.key, count);\n\t\t\t\tif (!action) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (action.type === \"select\") {\n\t\t\t\t\tif (state.activeIndex >= 0) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tonSelect?.(state.activeIndex);\n\t\t\t\t\t}\n\t\t\t\t\tdispatch({ type: \"select\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tevent.preventDefault();\n\t\t\t\tdispatch(action);\n\t\t\t},\n\t\t}),\n\t\t[id, state, count, onSelect]\n\t);\n\n\tconst getListboxProps = useCallback(() => buildListboxProps(id), [id]);\n\n\tconst getItemProps = useCallback(\n\t\t(index: number) => ({\n\t\t\t...buildItemProps(id, index, state),\n\t\t\tonMouseEnter: () => dispatch({ type: \"set\", index }),\n\t\t\tonMouseDown: (event: MouseEvent) => {\n\t\t\t\t// Keep input focus so selection isn't cancelled by blur.\n\t\t\t\tevent.preventDefault();\n\t\t\t\tonSelect?.(index);\n\t\t\t\tdispatch({ type: \"select\" });\n\t\t\t},\n\t\t}),\n\t\t[id, state, onSelect]\n\t);\n\n\treturn {\n\t\tisOpen: state.isOpen,\n\t\tactiveIndex: state.activeIndex,\n\t\topen,\n\t\tclose,\n\t\treset,\n\t\tsetActiveIndex,\n\t\tgetInputProps,\n\t\tgetListboxProps,\n\t\tgetItemProps,\n\t};\n}\n","import type { AddressSuggestion } from \"@wherabouts/sdk\";\n\n/**\n * Minimal structural view of the Web Storage API (sessionStorage/localStorage).\n * Declared locally so the package type-checks with no DOM lib.\n */\nexport interface StorageLike {\n\tgetItem(key: string): string | null;\n\tremoveItem(key: string): void;\n\tsetItem(key: string, value: string): void;\n}\n\nexport interface AutocompleteCacheKeyParts {\n\tcountry?: string;\n\tlat?: number;\n\tlimit?: number;\n\tlng?: number;\n\tq: string;\n\tstate?: string;\n}\n\nconst KEY_PREFIX = \"wh:ac:\";\n\n/**\n * Build a stable cache key from the query parameters. The query is trimmed and\n * lower-cased so cosmetic differences (\"George St\" vs \"george st\") share a\n * cache entry; coordinates and filters are included so proximity-boosted or\n * filtered searches never collide with unfiltered ones.\n */\nexport function buildCacheKey(parts: AutocompleteCacheKeyParts): string {\n\tconst q = parts.q.trim().toLowerCase();\n\treturn JSON.stringify([\n\t\tq,\n\t\tparts.country ?? \"\",\n\t\tparts.state ?? \"\",\n\t\tparts.limit ?? \"\",\n\t\tparts.lat ?? \"\",\n\t\tparts.lng ?? \"\",\n\t]);\n}\n\ninterface CacheEntry {\n\tresults: AddressSuggestion[];\n\tt: number;\n}\n\n/**\n * Read cached suggestions for `key`. Returns null on a miss, on corrupt data,\n * or when the entry is older than `ttlMs`; expired/corrupt entries are removed.\n */\nexport function readCache(\n\tstorage: StorageLike,\n\tkey: string,\n\tttlMs: number,\n\tnow: number\n): AddressSuggestion[] | null {\n\tconst raw = storage.getItem(KEY_PREFIX + key);\n\tif (raw === null) {\n\t\treturn null;\n\t}\n\tlet entry: CacheEntry;\n\ttry {\n\t\tentry = JSON.parse(raw) as CacheEntry;\n\t} catch {\n\t\tstorage.removeItem(KEY_PREFIX + key);\n\t\treturn null;\n\t}\n\tif (\n\t\ttypeof entry?.t !== \"number\" ||\n\t\t!Array.isArray(entry.results) ||\n\t\tnow - entry.t > ttlMs\n\t) {\n\t\tstorage.removeItem(KEY_PREFIX + key);\n\t\treturn null;\n\t}\n\treturn entry.results;\n}\n\n/** Store suggestions for `key`, stamped with `now` for TTL eviction on read. */\nexport function writeCache(\n\tstorage: StorageLike,\n\tkey: string,\n\tresults: AddressSuggestion[],\n\tnow: number\n): void {\n\tconst entry: CacheEntry = { results, t: now };\n\ttry {\n\t\tstorage.setItem(KEY_PREFIX + key, JSON.stringify(entry));\n\t} catch {\n\t\t// Storage full or unavailable (private mode/quota) — caching is best-effort.\n\t}\n}\n","import type {\n\tAddressSuggestion,\n\tAutocompleteParams,\n\tWheraboutsClient,\n} from \"@wherabouts/sdk\";\nimport { isRateLimitError } from \"@wherabouts/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n\ttype AutocompleteCacheKeyParts,\n\tbuildCacheKey,\n\treadCache,\n\ttype StorageLike,\n\twriteCache,\n} from \"./autocomplete-cache.ts\";\n\nconst DEFAULT_DEBOUNCE_MS = 300;\n/** API rejects `q` shorter than 2 chars; gate locally to avoid wasted 400s. */\nconst DEFAULT_MIN_LENGTH = 2;\nconst DEFAULT_CACHE_TTL_MS = 60_000;\n\nexport type AutocompleteStatus =\n\t| \"idle\"\n\t| \"loading\"\n\t| \"success\"\n\t| \"empty\"\n\t| \"error\";\n\n/** Opt-in client-side cache for repeated/back-and-forth queries. */\nexport interface AutocompleteCacheConfig {\n\t/** A `sessionStorage`-like store. Entries are namespaced under `wh:ac:`. */\n\tstorage: StorageLike;\n\t/** Entry lifetime in ms (default 60_000). */\n\tttlMs?: number;\n}\n\nexport interface UseAutocompleteOptions {\n\tcache?: AutocompleteCacheConfig;\n\tcountry?: string;\n\tdebounceMs?: number;\n\t/** Keep the previous results visible while a new search runs / on error. */\n\tkeepPreviousData?: boolean;\n\t/** Latitude for proximity boosting (pair with `lng`). */\n\tlat?: number;\n\tlimit?: number;\n\t/** Longitude for proximity boosting (pair with `lat`). */\n\tlng?: number;\n\t/** Minimum trimmed query length before a request fires (default 2). */\n\tminLength?: number;\n\t/** Groups keystrokes into one billable session; see `newSessionToken()`. */\n\tsessionToken?: string;\n\tstate?: string;\n}\n\nexport interface UseAutocompleteResult {\n\terror: Error | null;\n\tloading: boolean;\n\tquery: string;\n\t/** True when the last request was rejected with HTTP 429. */\n\trateLimited: boolean;\n\t/** Clear the query, results, and any in-flight request. */\n\treset: () => void;\n\tresults: AddressSuggestion[];\n\tsetQuery: (q: string) => void;\n\tstatus: AutocompleteStatus;\n}\n\n/**\n * Derive a single coarse status from the hook's raw flags. Exported for\n * unit-testing and for consumers who prefer a state machine to booleans.\n */\nexport function deriveStatus(s: {\n\terror: Error | null;\n\tloading: boolean;\n\tminLength: number;\n\tquery: string;\n\tresults: AddressSuggestion[];\n}): AutocompleteStatus {\n\tif (s.query.trim().length < s.minLength) {\n\t\treturn \"idle\";\n\t}\n\tif (s.loading) {\n\t\treturn \"loading\";\n\t}\n\tif (s.error) {\n\t\treturn \"error\";\n\t}\n\treturn s.results.length === 0 ? \"empty\" : \"success\";\n}\n\ntype SearchPlan =\n\t| { kind: \"idle\" }\n\t| { kind: \"cache\"; results: AddressSuggestion[] }\n\t| { kind: \"fetch\" };\n\n/** Decide whether to skip, serve from cache, or hit the network — pure. */\nfunction planSearch(args: {\n\tcacheStorage: StorageLike | undefined;\n\tcacheTtl: number;\n\tkeyParts: AutocompleteCacheKeyParts;\n\tminLength: number;\n\ttrimmed: string;\n}): SearchPlan {\n\tif (args.trimmed.length < args.minLength) {\n\t\treturn { kind: \"idle\" };\n\t}\n\tif (args.cacheStorage) {\n\t\tconst hit = readCache(\n\t\t\targs.cacheStorage,\n\t\t\tbuildCacheKey(args.keyParts),\n\t\t\targs.cacheTtl,\n\t\t\tDate.now()\n\t\t);\n\t\tif (hit) {\n\t\t\treturn { kind: \"cache\", results: hit };\n\t\t}\n\t}\n\treturn { kind: \"fetch\" };\n}\n\ninterface SearchActions {\n\tonError: (error: Error, rateLimited: boolean) => void;\n\tonSettled: () => void;\n\tonSuccess: (results: AddressSuggestion[]) => void;\n}\n\n/** Run one autocomplete request, routing the outcome through `actions`. */\nasync function executeAutocomplete(\n\tclient: WheraboutsClient,\n\tparams: AutocompleteParams,\n\tcontroller: AbortController,\n\tactions: SearchActions\n): Promise<void> {\n\ttry {\n\t\tconst res = await client.addresses.autocomplete(params, {\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!controller.signal.aborted) {\n\t\t\tactions.onSuccess(res.results);\n\t\t}\n\t} catch (e) {\n\t\tif (!controller.signal.aborted) {\n\t\t\tconst err = e instanceof Error ? e : new Error(String(e));\n\t\t\tactions.onError(err, isRateLimitError(e));\n\t\t}\n\t} finally {\n\t\tif (!controller.signal.aborted) {\n\t\t\tactions.onSettled();\n\t\t}\n\t}\n}\n\nfunction cancelPending(\n\ttimer: ReturnType<typeof setTimeout> | null,\n\tcontroller: AbortController | null\n): void {\n\tif (timer) {\n\t\tclearTimeout(timer);\n\t}\n\tcontroller?.abort();\n}\n\nexport function useAutocomplete(\n\tclient: WheraboutsClient,\n\toptions: UseAutocompleteOptions = {}\n): UseAutocompleteResult {\n\tconst {\n\t\tdebounceMs = DEFAULT_DEBOUNCE_MS,\n\t\tminLength = DEFAULT_MIN_LENGTH,\n\t\tlimit,\n\t\tcountry,\n\t\tstate,\n\t\tlat,\n\t\tlng,\n\t\tsessionToken,\n\t\tkeepPreviousData = false,\n\t\tcache,\n\t} = options;\n\n\tconst [query, setQuery] = useState(\"\");\n\tconst [results, setResults] = useState<AddressSuggestion[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [rateLimited, setRateLimited] = useState(false);\n\n\tconst abortRef = useRef<AbortController | null>(null);\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t// Stable setter — consumers can safely pass this to input onChange.\n\tconst handleSetQuery = useCallback((q: string) => {\n\t\tsetQuery(q);\n\t}, []);\n\n\tconst reset = useCallback(() => {\n\t\tcancelPending(timerRef.current, abortRef.current);\n\t\tsetQuery(\"\");\n\t\tsetResults([]);\n\t\tsetLoading(false);\n\t\tsetError(null);\n\t\tsetRateLimited(false);\n\t}, []);\n\n\tconst cacheStorage = cache?.storage;\n\tconst cacheTtl = cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;\n\n\tuseEffect(() => {\n\t\tcancelPending(timerRef.current, abortRef.current);\n\n\t\tconst trimmed = query.trim();\n\t\tconst keyParts: AutocompleteCacheKeyParts = {\n\t\t\tq: trimmed,\n\t\t\tcountry,\n\t\t\tstate,\n\t\t\tlimit,\n\t\t\tlat,\n\t\t\tlng,\n\t\t};\n\t\tconst plan = planSearch({\n\t\t\ttrimmed,\n\t\t\tminLength,\n\t\t\tcacheStorage,\n\t\t\tcacheTtl,\n\t\t\tkeyParts,\n\t\t});\n\n\t\tsetError(null);\n\t\tsetRateLimited(false);\n\n\t\tif (plan.kind === \"idle\") {\n\t\t\tif (!keepPreviousData) {\n\t\t\t\tsetResults([]);\n\t\t\t}\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\t\tif (plan.kind === \"cache\") {\n\t\t\tsetResults(plan.results);\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear stale results as the new search starts unless asked to keep them\n\t\t// visible (keepPreviousData avoids dropdown flicker between keystrokes).\n\t\tif (!keepPreviousData) {\n\t\t\tsetResults([]);\n\t\t}\n\t\tsetLoading(true);\n\t\ttimerRef.current = setTimeout(async () => {\n\t\t\tconst controller = new AbortController();\n\t\t\tabortRef.current = controller;\n\t\t\tawait executeAutocomplete(\n\t\t\t\tclient,\n\t\t\t\t{ q: trimmed, limit, country, state, lat, lng, sessionToken },\n\t\t\t\tcontroller,\n\t\t\t\t{\n\t\t\t\t\tonSuccess: (r) => {\n\t\t\t\t\t\tsetResults(r);\n\t\t\t\t\t\tif (cacheStorage) {\n\t\t\t\t\t\t\twriteCache(cacheStorage, buildCacheKey(keyParts), r, Date.now());\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonError: (err, limited) => {\n\t\t\t\t\t\tsetError(err);\n\t\t\t\t\t\tsetRateLimited(limited);\n\t\t\t\t\t\tif (!keepPreviousData) {\n\t\t\t\t\t\t\tsetResults([]);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonSettled: () => setLoading(false),\n\t\t\t\t}\n\t\t\t);\n\t\t}, debounceMs);\n\n\t\treturn () => cancelPending(timerRef.current, abortRef.current);\n\t}, [\n\t\tquery,\n\t\tdebounceMs,\n\t\tminLength,\n\t\tlimit,\n\t\tcountry,\n\t\tstate,\n\t\tlat,\n\t\tlng,\n\t\tsessionToken,\n\t\tkeepPreviousData,\n\t\tcacheStorage,\n\t\tcacheTtl,\n\t\tclient,\n\t]);\n\n\tconst status = deriveStatus({ query, minLength, loading, error, results });\n\n\treturn {\n\t\tquery,\n\t\tsetQuery: handleSetQuery,\n\t\tresults,\n\t\tloading,\n\t\terror,\n\t\trateLimited,\n\t\treset,\n\t\tstatus,\n\t};\n}\n","import type { ReverseGeocodeAddress, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface LatLng {\n\tlat: number;\n\tlng: number;\n}\n\nexport interface UseReverseGeocodeResult {\n\taddress: ReverseGeocodeAddress | null;\n\tdistance: number | null;\n\terror: Error | null;\n\tloading: boolean;\n}\n\nexport function useReverseGeocode(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseReverseGeocodeResult {\n\tconst [address, setAddress] = useState<ReverseGeocodeAddress | null>(null);\n\tconst [distance, setDistance] = useState<number | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetAddress(null);\n\t\t\tsetDistance(null);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.addresses\n\t\t\t.reverse(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetAddress(res.address);\n\t\t\t\t\tsetDistance(res.distance);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { address, distance, loading, error };\n}\n","/** Minimal view of AbortSignal — only the `aborted` flag is needed here. */\nexport interface AbortLike {\n\taborted: boolean;\n}\n\nexport interface ResourceActions<T> {\n\tonError: (error: Error) => void;\n\tonSettled: () => void;\n\tonSuccess: (data: T) => void;\n}\n\n/**\n * Await `promise` and route the outcome through `actions`, skipping every\n * callback once `signal` is aborted (so a stale request can't clobber the\n * state of a newer one). Shared by the data-fetching hooks.\n */\nexport async function runResource<T>(\n\tpromise: Promise<T>,\n\tsignal: AbortLike,\n\tactions: ResourceActions<T>\n): Promise<void> {\n\ttry {\n\t\tconst data = await promise;\n\t\tif (!signal.aborted) {\n\t\t\tactions.onSuccess(data);\n\t\t}\n\t} catch (e) {\n\t\tif (!signal.aborted) {\n\t\t\tactions.onError(e instanceof Error ? e : new Error(String(e)));\n\t\t}\n\t} finally {\n\t\tif (!signal.aborted) {\n\t\t\tactions.onSettled();\n\t\t}\n\t}\n}\n","import type {\n\tCallOptions,\n\tDirectionsParams,\n\tDirectionsResponse,\n\tIsochroneParams,\n\tIsochroneResponse,\n\tMatrixParams,\n\tMatrixResponse,\n\tWheraboutsClient,\n} from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { runResource } from \"./async-resource.ts\";\n\nexport interface RoutingResourceResult<T> {\n\tdata: T | null;\n\terror: Error | null;\n\tloading: boolean;\n}\n\ntype RoutingCall<P, R> = (\n\tclient: WheraboutsClient,\n\tparams: P,\n\toptions: CallOptions\n) => Promise<R>;\n\n/**\n * Shared effect skeleton for the routing hooks: fires `call` whenever the\n * (serialized) params change, aborts the previous request, and exposes\n * `{ data, loading, error }`. Pass `null` params to stay idle.\n */\nfunction useRoutingResource<P, R>(\n\tclient: WheraboutsClient,\n\tparams: P | null,\n\tcall: RoutingCall<P, R>\n): RoutingResourceResult<R> {\n\tconst [data, setData] = useState<R | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\t// Serialize params so stable values don't refire on every render.\n\tconst key = params ? JSON.stringify(params) : null;\n\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: `params` is tracked via its serialized `key`\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!params) {\n\t\t\tsetData(null);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\trunResource(\n\t\t\tcall(client, params, { signal: controller.signal }),\n\t\t\tcontroller.signal,\n\t\t\t{\n\t\t\t\tonSuccess: setData,\n\t\t\t\tonError: setError,\n\t\t\t\tonSettled: () => setLoading(false),\n\t\t\t}\n\t\t);\n\n\t\treturn () => controller.abort();\n\t}, [key, client, call]);\n\n\treturn { data, loading, error };\n}\n\nconst directionsCall: RoutingCall<DirectionsParams, DirectionsResponse> = (\n\tclient,\n\tparams,\n\toptions\n) => client.routing.directions(params, options);\n\nconst matrixCall: RoutingCall<MatrixParams, MatrixResponse> = (\n\tclient,\n\tparams,\n\toptions\n) => client.routing.matrix(params, options);\n\nconst isochroneCall: RoutingCall<IsochroneParams, IsochroneResponse> = (\n\tclient,\n\tparams,\n\toptions\n) => client.routing.isochrone(params, options);\n\n/** Fetch turn-by-turn directions; pass `null` to stay idle. */\nexport function useDirections(\n\tclient: WheraboutsClient,\n\tparams: DirectionsParams | null\n): RoutingResourceResult<DirectionsResponse> {\n\treturn useRoutingResource(client, params, directionsCall);\n}\n\n/** Fetch a duration/distance matrix; pass `null` to stay idle. */\nexport function useMatrix(\n\tclient: WheraboutsClient,\n\tparams: MatrixParams | null\n): RoutingResourceResult<MatrixResponse> {\n\treturn useRoutingResource(client, params, matrixCall);\n}\n\n/** Fetch an isochrone polygon; pass `null` to stay idle. */\nexport function useIsochrone(\n\tclient: WheraboutsClient,\n\tparams: IsochroneParams | null\n): RoutingResourceResult<IsochroneResponse> {\n\treturn useRoutingResource(client, params, isochroneCall);\n}\n","import type { WheraboutsClient, ZoneRecord } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport type { LatLng } from \"./use-reverse-geocode.ts\";\n\nexport interface UseZoneContainsResult {\n\terror: Error | null;\n\tloading: boolean;\n\tzones: ZoneRecord[];\n}\n\nexport function useZoneContains(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseZoneContainsResult {\n\tconst [zones, setZones] = useState<ZoneRecord[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetZones([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.zones\n\t\t\t.contains(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetZones(res.zones);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { zones, loading, error };\n}\n"]}