@usebridge/sdk-react 0.7.3 → 0.8.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.d.cts CHANGED
@@ -43,6 +43,10 @@ declare function usePayerAutocomplete(query: string, { limit, }?: {
43
43
  * The results to display currently
44
44
  */
45
45
  results: Payer[];
46
+ /**
47
+ * Irrecoverable error from the latest search, if any
48
+ */
49
+ error?: Error;
46
50
  };
47
51
 
48
52
  /**
package/dist/index.d.ts CHANGED
@@ -43,6 +43,10 @@ declare function usePayerAutocomplete(query: string, { limit, }?: {
43
43
  * The results to display currently
44
44
  */
45
45
  results: Payer[];
46
+ /**
47
+ * Irrecoverable error from the latest search, if any
48
+ */
49
+ error?: Error;
46
50
  };
47
51
 
48
52
  /**
package/dist/index.js CHANGED
@@ -520,6 +520,9 @@ function useIsMounted() {
520
520
  return useCallback(() => isMounted.current, []);
521
521
  }
522
522
 
523
+ // src/payer-search/use-payer-autocomplete.ts
524
+ import { BridgeApiError } from "@usebridge/api";
525
+
523
526
  // src/lib/retry-loop.ts
524
527
  var RetryLoopCancelledError = class extends Error {
525
528
  constructor() {
@@ -527,20 +530,22 @@ var RetryLoopCancelledError = class extends Error {
527
530
  this.name = "RetryLoopCancelledError";
528
531
  }
529
532
  };
530
- async function retryLoop(fn, shouldRetry, delayMs = 500) {
531
- let cancelled = false;
532
- while (!cancelled) {
533
+ async function retryLoop(fn, getRetryDecision, delayMs = 500) {
534
+ let decision = "retry";
535
+ let lastError;
536
+ while (decision === "retry") {
533
537
  try {
534
538
  return await fn();
535
- } catch (_) {
536
- if (!shouldRetry()) {
537
- cancelled = true;
538
- } else {
539
+ } catch (err) {
540
+ lastError = err;
541
+ decision = getRetryDecision(err);
542
+ if (decision === "retry") {
539
543
  await new Promise((r) => setTimeout(r, delayMs));
540
544
  }
541
545
  }
542
546
  }
543
- throw new RetryLoopCancelledError();
547
+ if (decision === "cancel") throw new RetryLoopCancelledError();
548
+ throw lastError;
544
549
  }
545
550
 
546
551
  // src/payer-search/use-payer-search.ts
@@ -2099,45 +2104,71 @@ var omit_default = omit;
2099
2104
 
2100
2105
  // src/payer-search/use-payer-autocomplete.ts
2101
2106
  var resultCache = /* @__PURE__ */ new Map();
2107
+ function toCacheKey(normalizedQuery, limit) {
2108
+ return `${limit}:${normalizedQuery}`;
2109
+ }
2110
+ function toError(err) {
2111
+ if (err instanceof Error) return err;
2112
+ return new Error(String(err));
2113
+ }
2114
+ function isRetryablePayerSearchError(err) {
2115
+ if (err instanceof BridgeApiError && err.statusCode != null) {
2116
+ if (err.statusCode === 429 || err.statusCode === 408) return true;
2117
+ if (err.statusCode >= 400 && err.statusCode < 500) return false;
2118
+ }
2119
+ return true;
2120
+ }
2102
2121
  function usePayerAutocomplete(query, {
2103
2122
  limit = 10
2104
2123
  } = {}) {
2105
2124
  const payerSearch = usePayerSearch();
2106
2125
  const [isLoading, setIsLoading] = useState2(false);
2107
2126
  const [results, setResults] = useState2([]);
2127
+ const [error, setError] = useState2();
2108
2128
  const normalizedQuery = query.trim().toLowerCase();
2109
2129
  const reqId = useRef4(0);
2110
2130
  const isMounted = useIsMounted();
2111
2131
  useEffect4(() => {
2112
2132
  const thisId = ++reqId.current;
2113
- const cachedResults = resultCache.get(normalizedQuery);
2133
+ const cacheKey = toCacheKey(normalizedQuery, limit);
2134
+ const cachedResults = resultCache.get(cacheKey);
2114
2135
  if (cachedResults) {
2115
2136
  setIsLoading(false);
2116
2137
  setResults(cachedResults);
2138
+ setError(void 0);
2117
2139
  return;
2118
2140
  }
2119
2141
  async function run() {
2120
2142
  setIsLoading(true);
2143
+ setError(void 0);
2121
2144
  try {
2122
2145
  const response = await retryLoop(
2123
2146
  () => payerSearch({ query: normalizedQuery, limit }),
2124
- () => isMounted() && thisId === reqId.current
2147
+ (err) => {
2148
+ if (!isMounted() || thisId !== reqId.current) return "cancel";
2149
+ if (!isRetryablePayerSearchError(err)) return "fail";
2150
+ return "retry";
2151
+ }
2125
2152
  );
2126
2153
  if (!isMounted()) return;
2127
- resultCache.set(normalizedQuery, response.items);
2154
+ resultCache.set(cacheKey, response.items);
2128
2155
  if (thisId !== reqId.current) return;
2129
2156
  setIsLoading(false);
2130
2157
  setResults(response.items);
2158
+ setError(void 0);
2131
2159
  } catch (err) {
2132
2160
  if (err instanceof RetryLoopCancelledError) return;
2133
- throw err;
2161
+ if (!isMounted() || thisId !== reqId.current) return;
2162
+ setResults([]);
2163
+ setIsLoading(false);
2164
+ setError(toError(err));
2134
2165
  }
2135
2166
  }
2136
2167
  void run();
2137
2168
  }, [payerSearch, normalizedQuery, limit]);
2138
2169
  return useMemo2(
2139
- () => ({ isLoading, results: results.map((r) => omit_default(r, "code")) }),
2140
- [isLoading, results]
2170
+ () => ({ isLoading, results: results.map((r) => omit_default(r, "code")), error }),
2171
+ [isLoading, results, error]
2141
2172
  );
2142
2173
  }
2143
2174