@easypayment/medusa-paypal-ui 1.0.56 → 1.1.1

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.mjs CHANGED
@@ -1,6 +1,33 @@
1
1
  "use client";
2
2
 
3
3
  // src/client/http.ts
4
+ var HttpError = class extends Error {
5
+ constructor(message, status) {
6
+ super(message);
7
+ this.name = "HttpError";
8
+ this.status = status;
9
+ }
10
+ };
11
+ var RETRY_BASE_DELAY_MS = 400;
12
+ function isRetryableStatus(status) {
13
+ return status === 429 || status >= 500;
14
+ }
15
+ function isRetryableError(err) {
16
+ return err instanceof Error && !(err instanceof HttpError);
17
+ }
18
+ var wait = (ms) => new Promise((r) => setTimeout(r, ms));
19
+ function extractJsonMessage(text) {
20
+ if (!text) return null;
21
+ try {
22
+ const parsed = JSON.parse(text);
23
+ const message = parsed?.message;
24
+ if (typeof message === "string" && message.trim()) {
25
+ return message.trim().slice(0, 500);
26
+ }
27
+ } catch {
28
+ }
29
+ return null;
30
+ }
4
31
  function toHeaderRecord(headers) {
5
32
  if (!headers) {
6
33
  return {};
@@ -15,7 +42,7 @@ function toHeaderRecord(headers) {
15
42
  }
16
43
  function createHttpClient(opts) {
17
44
  const base = opts.baseUrl.replace(/\/+$/, "");
18
- async function request(path, init) {
45
+ async function attempt(path, init) {
19
46
  const url = `${base}${path.startsWith("/") ? "" : "/"}${path}`;
20
47
  const headers = {
21
48
  Accept: "application/json",
@@ -25,41 +52,54 @@ function createHttpClient(opts) {
25
52
  headers["x-publishable-api-key"] = opts.publishableApiKey;
26
53
  }
27
54
  const timeoutMs = 3e4;
28
- let timeoutId;
29
55
  const controller = new AbortController();
30
- if (!init?.signal) {
31
- timeoutId = setTimeout(() => controller.abort(), timeoutMs);
56
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
57
+ const callerSignal = init?.signal ?? null;
58
+ const onCallerAbort = () => controller.abort();
59
+ if (callerSignal) {
60
+ if (callerSignal.aborted) controller.abort();
61
+ else callerSignal.addEventListener("abort", onCallerAbort);
32
62
  }
33
- const effectiveSignal = init?.signal || controller.signal;
34
63
  let res;
64
+ let text;
35
65
  try {
36
- res = await fetch(url, { ...init, headers, credentials: "include", signal: effectiveSignal });
66
+ res = await fetch(url, {
67
+ ...init,
68
+ headers,
69
+ credentials: "include",
70
+ signal: controller.signal
71
+ });
72
+ text = await res.text().catch(() => "");
37
73
  } catch (err) {
38
- if (err instanceof Error && err.name === "AbortError" && !init?.signal) {
74
+ if (err instanceof Error && err.name === "AbortError" && !(callerSignal && callerSignal.aborted)) {
39
75
  throw new Error(`[PayPal] Request to ${path} timed out after ${timeoutMs / 1e3}s`);
40
76
  }
41
77
  throw err;
42
78
  } finally {
43
- if (timeoutId !== void 0) clearTimeout(timeoutId);
79
+ clearTimeout(timeoutId);
80
+ if (callerSignal) callerSignal.removeEventListener("abort", onCallerAbort);
44
81
  }
45
- const text = await res.text().catch(() => "");
46
82
  if (!res.ok) {
83
+ const parsedMessage = extractJsonMessage(text);
47
84
  if (res.status === 401) {
48
- throw new Error(
49
- "[PayPal] Unauthorized (401) \u2014 check that your publishable API key is correct and set in NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY"
85
+ throw new HttpError(
86
+ parsedMessage || "[PayPal] Unauthorized (401) \u2014 check that your publishable API key is correct and set in NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY",
87
+ 401
50
88
  );
51
89
  }
52
90
  if (res.status === 403) {
53
- throw new Error(
54
- "[PayPal] Forbidden (403) \u2014 this request is not allowed. Check your CORS and API key settings."
91
+ throw new HttpError(
92
+ parsedMessage || "[PayPal] Forbidden (403) \u2014 this request is not allowed. Check your CORS and API key settings.",
93
+ 403
55
94
  );
56
95
  }
57
- throw new Error(
58
- text.slice(0, 500).replace(/<[^>]*>/g, "") || `Request failed (${res.status})`
96
+ throw new HttpError(
97
+ parsedMessage || text.slice(0, 500).replace(/<[^>]*>/g, "") || `Request failed (${res.status})`,
98
+ res.status
59
99
  );
60
100
  }
61
101
  if (!text) {
62
- throw new Error(`[PayPal] Empty response body from ${path} (${res.status})`);
102
+ throw new HttpError(`[PayPal] Empty response body from ${path} (${res.status})`, res.status);
63
103
  }
64
104
  const contentType = res.headers.get("content-type") || "";
65
105
  if (!contentType.includes("application/json")) {
@@ -73,17 +113,39 @@ function createHttpClient(opts) {
73
113
  throw new Error(`[PayPal] Failed to parse JSON response from ${path}`);
74
114
  }
75
115
  }
116
+ async function request(path, init, reqOpts) {
117
+ const retries = Math.max(0, reqOpts?.retries ?? 0);
118
+ let lastError;
119
+ for (let i = 0; i <= retries; i++) {
120
+ try {
121
+ return await attempt(path, init);
122
+ } catch (err) {
123
+ lastError = err;
124
+ const transient = err instanceof HttpError && isRetryableStatus(err.status) || isRetryableError(err);
125
+ const aborted = err instanceof Error && err.name === "AbortError" && !!init?.signal;
126
+ if (i >= retries || !transient || aborted) {
127
+ throw err;
128
+ }
129
+ await wait(RETRY_BASE_DELAY_MS * Math.pow(2, i) + Math.random() * RETRY_BASE_DELAY_MS);
130
+ }
131
+ }
132
+ throw lastError;
133
+ }
76
134
  return { request };
77
135
  }
78
136
 
79
137
  // src/client/paypal.ts
80
138
  async function markPaymentComplete(baseUrl, cartId, publishableApiKey) {
81
139
  const http = createHttpClient({ baseUrl, publishableApiKey });
82
- return http.request(`/store/paypal-complete`, {
83
- method: "POST",
84
- headers: { "Content-Type": "application/json" },
85
- body: JSON.stringify({ cart_id: cartId })
86
- });
140
+ return http.request(
141
+ `/store/paypal-complete`,
142
+ {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({ cart_id: cartId })
146
+ },
147
+ { retries: 3 }
148
+ );
87
149
  }
88
150
  function createPayPalStoreApi(opts) {
89
151
  const http = createHttpClient(opts);
@@ -103,11 +165,15 @@ function createPayPalStoreApi(opts) {
103
165
  });
104
166
  },
105
167
  captureOrder(cartId, orderId) {
106
- return http.request(`/store/paypal/capture-order`, {
107
- method: "POST",
108
- headers: { "Content-Type": "application/json" },
109
- body: JSON.stringify({ cart_id: cartId, order_id: orderId })
110
- });
168
+ return http.request(
169
+ `/store/paypal/capture-order`,
170
+ {
171
+ method: "POST",
172
+ headers: { "Content-Type": "application/json" },
173
+ body: JSON.stringify({ cart_id: cartId, order_id: orderId })
174
+ },
175
+ { retries: 2 }
176
+ );
111
177
  }
112
178
  };
113
179
  }
@@ -224,14 +290,16 @@ function PayPalCurrencyNotice({ config }) {
224
290
  }
225
291
 
226
292
  // src/components/PayPalSmartButtons.tsx
227
- import { useCallback, useMemo as useMemo3, useState as useState2 } from "react";
293
+ import { useCallback, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
228
294
  import { PayPalButtons, usePayPalScriptReducer } from "@paypal/react-paypal-js";
229
295
 
230
296
  // src/utils/next-errors.ts
231
297
  function isNextRouterError(e) {
232
298
  if (typeof e !== "object" || e === null || !("digest" in e)) return false;
233
299
  const digest = e.digest;
234
- return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND"));
300
+ return typeof digest === "string" && (digest.startsWith("NEXT_REDIRECT") || digest.startsWith("NEXT_NOT_FOUND") || // Next.js 15: notFound()/forbidden()/unauthorized() throw
301
+ // HTTPAccessFallbackError with digest "NEXT_HTTP_ERROR_FALLBACK;<status>".
302
+ digest.startsWith("NEXT_HTTP_ERROR_FALLBACK"));
235
303
  }
236
304
 
237
305
  // src/utils/processing-overlay.ts
@@ -295,8 +363,13 @@ function showProcessingOverlay() {
295
363
  origReplace(...args);
296
364
  onNav();
297
365
  };
366
+ const onPageShow = (e) => {
367
+ if (e.persisted) hideProcessingOverlay();
368
+ };
369
+ window.addEventListener("pageshow", onPageShow);
298
370
  cleanupNavListener = () => {
299
371
  window.removeEventListener("popstate", onNav);
372
+ window.removeEventListener("pageshow", onPageShow);
300
373
  history.pushState = origPush;
301
374
  history.replaceState = origReplace;
302
375
  };
@@ -329,11 +402,48 @@ function PayPalSmartButtons(props) {
329
402
  const [error, setError] = useState2(null);
330
403
  const [processing, setProcessing] = useState2(false);
331
404
  const [buttonsReady, setButtonsReady] = useState2(false);
405
+ const [loadTimedOut, setLoadTimedOut] = useState2(false);
406
+ const capturedRef = useRef2(null);
407
+ const [completionPending, setCompletionPending] = useState2(false);
332
408
  const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer();
333
409
  const handleInit = useCallback(() => {
334
410
  setButtonsReady(true);
335
411
  }, []);
336
- const showSpinner = isPending || isResolved && !buttonsReady;
412
+ useEffect2(() => {
413
+ const onPageShow = (e) => {
414
+ if (e.persisted) setProcessing(false);
415
+ };
416
+ window.addEventListener("pageshow", onPageShow);
417
+ return () => window.removeEventListener("pageshow", onPageShow);
418
+ }, []);
419
+ useEffect2(() => {
420
+ if (!isResolved || buttonsReady) {
421
+ return;
422
+ }
423
+ const t = setTimeout(() => setLoadTimedOut(true), 1e4);
424
+ return () => clearTimeout(t);
425
+ }, [isResolved, buttonsReady]);
426
+ const finalizeCapturedPayment = useCallback(async () => {
427
+ const captured = capturedRef.current;
428
+ if (!captured) return;
429
+ setProcessing(true);
430
+ showProcessingOverlay();
431
+ setError(null);
432
+ try {
433
+ const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
434
+ await onPaid?.({ ...captured, ...completeResult });
435
+ setCompletionPending(false);
436
+ } catch (e) {
437
+ if (isNextRouterError(e)) return;
438
+ hideProcessingOverlay();
439
+ setProcessing(false);
440
+ setCompletionPending(true);
441
+ const msg = "Your payment was received, but finalizing your order failed. Please use the Retry button below \u2014 do not pay again.";
442
+ setError(msg);
443
+ onError?.(msg);
444
+ }
445
+ }, [baseUrl, cartId, publishableApiKey, onPaid, onError]);
446
+ const showSpinner = (isPending || isResolved && !buttonsReady) && !loadTimedOut;
337
447
  if (!config.currency_supported) return null;
338
448
  const containerWidth = BUTTON_WIDTH_MAP[config.button_width ?? "responsive"] ?? "100%";
339
449
  return /* @__PURE__ */ jsxs2("div", { style: { width: containerWidth, position: "relative" }, children: [
@@ -472,6 +582,28 @@ function PayPalSmartButtons(props) {
472
582
  ]
473
583
  }
474
584
  ),
585
+ loadTimedOut && !buttonsReady && !isRejected && /* @__PURE__ */ jsxs2(
586
+ "div",
587
+ {
588
+ role: "alert",
589
+ style: {
590
+ display: "flex",
591
+ alignItems: "flex-start",
592
+ gap: 8,
593
+ padding: "10px 14px",
594
+ background: "#fef2f2",
595
+ border: "1px solid #fecaca",
596
+ borderRadius: 8,
597
+ fontSize: 13,
598
+ color: "#b91c1c",
599
+ lineHeight: 1.5
600
+ },
601
+ children: [
602
+ /* @__PURE__ */ jsx3("span", { style: { flexShrink: 0, fontSize: 15 }, children: "\u26A0\uFE0F" }),
603
+ /* @__PURE__ */ jsx3("span", { children: "PayPal is not available for this purchase. Please try a different payment method." })
604
+ ]
605
+ }
606
+ ),
475
607
  isRejected && /* @__PURE__ */ jsxs2(
476
608
  "div",
477
609
  {
@@ -532,7 +664,7 @@ function PayPalSmartButtons(props) {
532
664
  ]
533
665
  }
534
666
  ),
535
- isResolved && /* @__PURE__ */ jsx3("div", { style: buttonsReady ? void 0 : { position: "absolute", left: -9999, opacity: 0, pointerEvents: "none" }, children: /* @__PURE__ */ jsx3(
667
+ isResolved && !completionPending && /* @__PURE__ */ jsx3("div", { style: buttonsReady ? void 0 : { position: "absolute", left: -9999, opacity: 0, pointerEvents: "none" }, children: /* @__PURE__ */ jsx3(
536
668
  PayPalButtons,
537
669
  {
538
670
  forceReRender: [config.currency, config.intent, cartId],
@@ -546,6 +678,10 @@ function PayPalSmartButtons(props) {
546
678
  },
547
679
  createOrder: async () => {
548
680
  if (processing) throw new Error("Payment already processing");
681
+ if (capturedRef.current) {
682
+ void finalizeCapturedPayment();
683
+ throw new Error("Your payment was already received \u2014 finalizing your order.");
684
+ }
549
685
  setError(null);
550
686
  setProcessing(true);
551
687
  try {
@@ -564,8 +700,7 @@ function PayPalSmartButtons(props) {
564
700
  const orderId = String(data?.orderID || "");
565
701
  if (!orderId) throw new Error("PayPal order ID is missing from approval response");
566
702
  const result = await api.captureOrder(cartId, orderId);
567
- const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
568
- await onPaid?.({ ...result, ...completeResult });
703
+ capturedRef.current = result || {};
569
704
  } catch (e) {
570
705
  if (isNextRouterError(e)) return;
571
706
  hideProcessingOverlay();
@@ -573,7 +708,9 @@ function PayPalSmartButtons(props) {
573
708
  const msg = e instanceof Error ? e.message : "Payment capture failed";
574
709
  setError(msg);
575
710
  onError?.(msg);
711
+ return;
576
712
  }
713
+ await finalizeCapturedPayment();
577
714
  },
578
715
  onCancel: () => {
579
716
  hideProcessingOverlay();
@@ -588,6 +725,29 @@ function PayPalSmartButtons(props) {
588
725
  }
589
726
  }
590
727
  ) }),
728
+ completionPending && /* @__PURE__ */ jsx3(
729
+ "button",
730
+ {
731
+ type: "button",
732
+ onClick: () => void finalizeCapturedPayment(),
733
+ disabled: processing,
734
+ "aria-busy": processing,
735
+ style: {
736
+ width: "100%",
737
+ padding: "12px 16px",
738
+ marginTop: 4,
739
+ background: "#0070ba",
740
+ color: "#ffffff",
741
+ border: "none",
742
+ borderRadius: 8,
743
+ fontSize: 14,
744
+ fontWeight: 600,
745
+ cursor: processing ? "not-allowed" : "pointer",
746
+ opacity: processing ? 0.7 : 1
747
+ },
748
+ children: "Retry \u2014 finish placing my order"
749
+ }
750
+ ),
591
751
  error ? /* @__PURE__ */ jsxs2(
592
752
  "div",
593
753
  {
@@ -615,7 +775,7 @@ function PayPalSmartButtons(props) {
615
775
  }
616
776
 
617
777
  // src/components/PayPalAdvancedCard.tsx
618
- import { useMemo as useMemo4, useRef as useRef2, useState as useState3 } from "react";
778
+ import React3, { useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
619
779
  import {
620
780
  PayPalCardFieldsProvider,
621
781
  PayPalNumberField,
@@ -674,7 +834,8 @@ var labelStyle = {
674
834
  function SubmitButton({
675
835
  disabled,
676
836
  label,
677
- onSubmit
837
+ onSubmit,
838
+ onSubmitError
678
839
  }) {
679
840
  const { cardFieldsForm } = usePayPalCardFields();
680
841
  const isDisabled = disabled || !cardFieldsForm;
@@ -686,10 +847,10 @@ function SubmitButton({
686
847
  "aria-busy": disabled,
687
848
  onClick: () => {
688
849
  onSubmit();
689
- try {
690
- cardFieldsForm?.submit();
691
- } catch {
692
- }
850
+ Promise.resolve(cardFieldsForm?.submit()).catch((e) => {
851
+ const msg = e instanceof Error && e.message ? e.message : "Card payment could not be submitted. Please check your card details.";
852
+ onSubmitError(msg);
853
+ });
693
854
  },
694
855
  style: {
695
856
  width: "100%",
@@ -733,9 +894,47 @@ function PayPalAdvancedCard(props) {
733
894
  );
734
895
  const [error, setError] = useState3(null);
735
896
  const [submitting, setSubmitting] = useState3(false);
736
- const submittingRef = useRef2(false);
737
- const creatingOrderRef = useRef2(false);
897
+ const submittingRef = useRef3(false);
898
+ const creatingOrderRef = useRef3(false);
899
+ const capturedRef = useRef3(null);
900
+ const [completionPending, setCompletionPending] = useState3(false);
738
901
  const [{ isPending, isResolved, isRejected }] = usePayPalScriptReducer2();
902
+ const resetSubmitState = React3.useCallback(() => {
903
+ hideProcessingOverlay();
904
+ creatingOrderRef.current = false;
905
+ submittingRef.current = false;
906
+ setSubmitting(false);
907
+ }, []);
908
+ React3.useEffect(() => {
909
+ const onPageShow = (e) => {
910
+ if (e.persisted) {
911
+ creatingOrderRef.current = false;
912
+ submittingRef.current = false;
913
+ setSubmitting(false);
914
+ }
915
+ };
916
+ window.addEventListener("pageshow", onPageShow);
917
+ return () => window.removeEventListener("pageshow", onPageShow);
918
+ }, []);
919
+ const finalizeCapturedPayment = React3.useCallback(async () => {
920
+ const captured = capturedRef.current;
921
+ if (!captured) return;
922
+ setSubmitting(true);
923
+ showProcessingOverlay();
924
+ setError(null);
925
+ try {
926
+ const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
927
+ await onPaid?.({ ...captured, ...completeResult });
928
+ setCompletionPending(false);
929
+ } catch (e) {
930
+ if (isNextRouterError(e)) return;
931
+ resetSubmitState();
932
+ setCompletionPending(true);
933
+ const msg = "Your payment was received, but finalizing your order failed. Please use the Retry button below \u2014 do not pay again.";
934
+ setError(msg);
935
+ onError?.(msg);
936
+ }
937
+ }, [baseUrl, cartId, publishableApiKey, onPaid, onError, resetSubmitState]);
739
938
  if (!config.currency_supported) return null;
740
939
  if (isRejected) {
741
940
  return /* @__PURE__ */ jsxs3(
@@ -924,6 +1123,10 @@ function PayPalAdvancedCard(props) {
924
1123
  style: cardStyle,
925
1124
  createOrder: async () => {
926
1125
  if (creatingOrderRef.current) throw new Error("Payment already processing");
1126
+ if (capturedRef.current) {
1127
+ void finalizeCapturedPayment();
1128
+ throw new Error("Your payment was already received \u2014 finalizing your order.");
1129
+ }
927
1130
  creatingOrderRef.current = true;
928
1131
  try {
929
1132
  setError(null);
@@ -939,19 +1142,18 @@ function PayPalAdvancedCard(props) {
939
1142
  setError(null);
940
1143
  showProcessingOverlay();
941
1144
  const orderId = String(data?.orderID || "");
1145
+ if (!orderId) throw new Error("PayPal order ID is missing from approval response");
942
1146
  const result = await api.captureOrder(cartId, orderId);
943
- const completeResult = await markPaymentComplete(baseUrl, cartId, publishableApiKey);
944
- await onPaid?.({ ...result, ...completeResult });
1147
+ capturedRef.current = result || {};
945
1148
  } catch (e) {
946
1149
  if (isNextRouterError(e)) return;
947
- hideProcessingOverlay();
948
- creatingOrderRef.current = false;
949
- submittingRef.current = false;
950
- setSubmitting(false);
1150
+ resetSubmitState();
951
1151
  const msg = e instanceof Error ? e.message : "Card payment failed";
952
1152
  setError(msg);
953
1153
  onError?.(msg);
1154
+ return;
954
1155
  }
1156
+ await finalizeCapturedPayment();
955
1157
  },
956
1158
  onCancel: () => {
957
1159
  hideProcessingOverlay();
@@ -1058,7 +1260,29 @@ function PayPalAdvancedCard(props) {
1058
1260
  ]
1059
1261
  }
1060
1262
  ),
1061
- /* @__PURE__ */ jsx4(
1263
+ completionPending ? /* @__PURE__ */ jsx4(
1264
+ "button",
1265
+ {
1266
+ type: "button",
1267
+ onClick: () => void finalizeCapturedPayment(),
1268
+ disabled: submitting,
1269
+ "aria-busy": submitting,
1270
+ style: {
1271
+ width: "100%",
1272
+ height: 48,
1273
+ padding: "0 20px",
1274
+ borderRadius: 8,
1275
+ border: "none",
1276
+ background: "#0070ba",
1277
+ color: "#ffffff",
1278
+ fontSize: 15,
1279
+ fontWeight: 600,
1280
+ cursor: submitting ? "not-allowed" : "pointer",
1281
+ opacity: submitting ? 0.7 : 1
1282
+ },
1283
+ children: "Retry \u2014 finish placing my order"
1284
+ }
1285
+ ) : /* @__PURE__ */ jsx4(
1062
1286
  SubmitButton,
1063
1287
  {
1064
1288
  disabled: submitting,
@@ -1069,6 +1293,11 @@ function PayPalAdvancedCard(props) {
1069
1293
  setError(null);
1070
1294
  setSubmitting(true);
1071
1295
  showProcessingOverlay();
1296
+ },
1297
+ onSubmitError: (msg) => {
1298
+ resetSubmitState();
1299
+ setError(msg);
1300
+ onError?.(msg);
1072
1301
  }
1073
1302
  }
1074
1303
  ),
@@ -1410,7 +1639,7 @@ function PayPalPaymentSection({
1410
1639
  }
1411
1640
 
1412
1641
  // src/hooks/usePayPalPaymentMethods.ts
1413
- import { useEffect as useEffect2, useMemo as useMemo5, useRef as useRef3, useState as useState4 } from "react";
1642
+ import { useEffect as useEffect3, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1414
1643
  var MAX_CACHE_ENTRIES2 = 50;
1415
1644
  var _cache2 = /* @__PURE__ */ new Map();
1416
1645
  var CACHE_TTL2 = 5 * 60 * 1e3;
@@ -1445,8 +1674,8 @@ function usePayPalPaymentMethods({
1445
1674
  const hit = _cache2.get(key);
1446
1675
  const seed = hit && Date.now() - hit.at < CACHE_TTL2 ? hit.result : null;
1447
1676
  const [result, setResult] = useState4(seed ?? { ...DEFAULT_RESULT, loading: enabled });
1448
- const fetchIdRef = useRef3(0);
1449
- useEffect2(() => {
1677
+ const fetchIdRef = useRef4(0);
1678
+ useEffect3(() => {
1450
1679
  if (!enabled) {
1451
1680
  setResult((prev) => ({ ...prev, loading: false }));
1452
1681
  return;