@volr/react 0.1.69 → 0.1.71

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
@@ -526,15 +526,52 @@ interface UseVolrLoginReturn {
526
526
  declare function useVolrLogin(): UseVolrLoginReturn;
527
527
 
528
528
  interface UseVolrAuthCallbackOptions {
529
+ /**
530
+ * Called when OAuth authentication is successful
531
+ */
529
532
  onSuccess?: (user: VolrUser) => void;
533
+ /**
534
+ * Called when OAuth authentication fails
535
+ */
530
536
  onError?: (error: string) => void;
537
+ /**
538
+ * If true, automatically clears the URL query params after processing
539
+ * @default true
540
+ */
541
+ clearUrlParams?: boolean;
531
542
  }
532
543
  interface UseVolrAuthCallbackReturn {
544
+ /** Whether the callback is being processed */
533
545
  isLoading: boolean;
546
+ /** Error message if authentication failed */
534
547
  error: string | null;
548
+ /** Whether this is a new user (first time sign in) */
535
549
  isNewUser: boolean;
550
+ /** The authenticated user object */
536
551
  user: VolrUser | null;
537
552
  }
553
+ /**
554
+ * Hook to handle OAuth callback after social login redirect.
555
+ *
556
+ * Usage:
557
+ * ```tsx
558
+ * // In your /auth/callback page
559
+ * function AuthCallbackPage() {
560
+ * const { isLoading, error, user, isNewUser } = useVolrAuthCallback({
561
+ * onSuccess: (user) => {
562
+ * navigate('/dashboard');
563
+ * },
564
+ * onError: (error) => {
565
+ * navigate('/login?error=' + encodeURIComponent(error));
566
+ * },
567
+ * });
568
+ *
569
+ * if (isLoading) return <LoadingSpinner />;
570
+ * if (error) return <ErrorMessage message={error} />;
571
+ * return null;
572
+ * }
573
+ * ```
574
+ */
538
575
  declare function useVolrAuthCallback(options?: UseVolrAuthCallbackOptions): UseVolrAuthCallbackReturn;
539
576
 
540
577
  type Erc20Token = {
package/dist/index.d.ts CHANGED
@@ -526,15 +526,52 @@ interface UseVolrLoginReturn {
526
526
  declare function useVolrLogin(): UseVolrLoginReturn;
527
527
 
528
528
  interface UseVolrAuthCallbackOptions {
529
+ /**
530
+ * Called when OAuth authentication is successful
531
+ */
529
532
  onSuccess?: (user: VolrUser) => void;
533
+ /**
534
+ * Called when OAuth authentication fails
535
+ */
530
536
  onError?: (error: string) => void;
537
+ /**
538
+ * If true, automatically clears the URL query params after processing
539
+ * @default true
540
+ */
541
+ clearUrlParams?: boolean;
531
542
  }
532
543
  interface UseVolrAuthCallbackReturn {
544
+ /** Whether the callback is being processed */
533
545
  isLoading: boolean;
546
+ /** Error message if authentication failed */
534
547
  error: string | null;
548
+ /** Whether this is a new user (first time sign in) */
535
549
  isNewUser: boolean;
550
+ /** The authenticated user object */
536
551
  user: VolrUser | null;
537
552
  }
553
+ /**
554
+ * Hook to handle OAuth callback after social login redirect.
555
+ *
556
+ * Usage:
557
+ * ```tsx
558
+ * // In your /auth/callback page
559
+ * function AuthCallbackPage() {
560
+ * const { isLoading, error, user, isNewUser } = useVolrAuthCallback({
561
+ * onSuccess: (user) => {
562
+ * navigate('/dashboard');
563
+ * },
564
+ * onError: (error) => {
565
+ * navigate('/login?error=' + encodeURIComponent(error));
566
+ * },
567
+ * });
568
+ *
569
+ * if (isLoading) return <LoadingSpinner />;
570
+ * if (error) return <ErrorMessage message={error} />;
571
+ * return null;
572
+ * }
573
+ * ```
574
+ */
538
575
  declare function useVolrAuthCallback(options?: UseVolrAuthCallbackOptions): UseVolrAuthCallbackReturn;
539
576
 
540
577
  type Erc20Token = {
package/dist/index.js CHANGED
@@ -18667,45 +18667,15 @@ function useVolrLogin() {
18667
18667
  handlePasskeyComplete
18668
18668
  };
18669
18669
  }
18670
- function createAxiosInstance(baseUrl, apiKey) {
18671
- const instance = axios.create({
18672
- baseURL: baseUrl.replace(/\/+$/, ""),
18673
- // Remove trailing slashes
18674
- withCredentials: true,
18675
- // Include cookies
18676
- headers: {
18677
- "Content-Type": "application/json"
18678
- }
18679
- });
18680
- instance.interceptors.request.use((config) => {
18681
- if (apiKey) {
18682
- config.headers["X-API-Key"] = apiKey;
18683
- }
18684
- return config;
18685
- });
18686
- instance.interceptors.response.use(
18687
- (response) => response,
18688
- (error) => {
18689
- if (error.response?.data) {
18690
- const errorData = error.response.data;
18691
- if (errorData.error?.message) {
18692
- error.message = errorData.error.message;
18693
- }
18694
- }
18695
- return Promise.reject(error);
18696
- }
18697
- );
18698
- return instance;
18699
- }
18700
-
18701
- // src/hooks/useVolrAuthCallback.ts
18702
18670
  function useVolrAuthCallback(options = {}) {
18703
- const { config, setUser } = useVolrContext();
18704
- const { refreshAccessToken, setAccessToken, setRefreshToken, client } = useInternalAuth();
18671
+ const { clearUrlParams = true } = options;
18672
+ const { setUser } = useVolrContext();
18673
+ const { setAccessToken, setRefreshToken, client } = useInternalAuth();
18705
18674
  const [isLoading, setIsLoading] = useState(true);
18706
18675
  const [error, setError] = useState(null);
18707
18676
  const [isNewUser, setIsNewUser] = useState(false);
18708
18677
  const [user, setLocalUser] = useState(null);
18678
+ const hasProcessed = useRef(false);
18709
18679
  const toVolrUser = useCallback((u) => {
18710
18680
  return {
18711
18681
  id: u.id,
@@ -18721,24 +18691,31 @@ function useVolrAuthCallback(options = {}) {
18721
18691
  credentialId: u.credentialId ?? void 0
18722
18692
  };
18723
18693
  }, []);
18724
- const apiBaseUrl = resolveApiBaseUrl(config);
18725
18694
  useEffect(() => {
18726
18695
  const handleCallback = async () => {
18727
18696
  if (typeof window === "undefined") return;
18697
+ if (hasProcessed.current) return;
18698
+ hasProcessed.current = true;
18728
18699
  const searchParams = new URLSearchParams(window.location.search);
18729
- const success = searchParams.get("success");
18700
+ const authCode = searchParams.get("code");
18730
18701
  const errorParam = searchParams.get("error");
18731
- const userId = searchParams.get("userId");
18732
18702
  const isNew = searchParams.get("isNewUser") === "true";
18733
18703
  if (errorParam) {
18734
- const errorMsg = `Authentication failed: ${errorParam}`;
18704
+ const errorMsg = `Authentication failed: ${errorParam.replace(/_/g, " ")}`;
18705
+ setError(errorMsg);
18706
+ setIsLoading(false);
18707
+ options.onError?.(errorMsg);
18708
+ return;
18709
+ }
18710
+ if (!authCode) {
18711
+ const errorMsg = "No authorization code found. Please try logging in again.";
18735
18712
  setError(errorMsg);
18736
18713
  setIsLoading(false);
18737
18714
  options.onError?.(errorMsg);
18738
18715
  return;
18739
18716
  }
18740
- if (success !== "true" || !userId) {
18741
- const errorMsg = "Invalid callback parameters";
18717
+ if (!authCode.startsWith("volr_auth_")) {
18718
+ const errorMsg = "Invalid authorization code format";
18742
18719
  setError(errorMsg);
18743
18720
  setIsLoading(false);
18744
18721
  options.onError?.(errorMsg);
@@ -18746,45 +18723,44 @@ function useVolrAuthCallback(options = {}) {
18746
18723
  }
18747
18724
  try {
18748
18725
  setIsNewUser(isNew);
18749
- await refreshAccessToken();
18750
- const api = createAxiosInstance(
18751
- apiBaseUrl,
18752
- config.projectApiKey
18753
- );
18754
- const userRes = await api.get(`/auth/onboarding/${userId}`);
18755
- if (!userRes.data?.ok) {
18756
- throw new Error("Failed to fetch user details");
18757
- }
18758
- const currentRefreshToken = client.getRefreshToken();
18759
- if (!currentRefreshToken) {
18760
- throw new Error("No refresh token available. Please log in again.");
18761
- }
18762
- const refreshRes = await api.post("/auth/refresh", { refreshToken: currentRefreshToken });
18763
- if (!refreshRes.data?.ok) {
18764
- throw new Error("Failed to refresh session");
18726
+ const response = await client.post("/auth/token/exchange", { code: authCode });
18727
+ const { user: userData, accessToken, refreshToken } = response;
18728
+ if (!accessToken) {
18729
+ throw new Error("No access token received from server");
18765
18730
  }
18766
- const userData = refreshRes.data.data.user;
18767
- const accessToken = refreshRes.data.data.accessToken;
18768
- const newRefreshToken = refreshRes.data.data.refreshToken;
18769
18731
  setAccessToken(accessToken);
18770
- if (newRefreshToken) {
18771
- setRefreshToken(newRefreshToken);
18732
+ if (refreshToken) {
18733
+ setRefreshToken(refreshToken);
18772
18734
  }
18773
18735
  const volrUser = toVolrUser(userData);
18774
18736
  setUser(volrUser);
18775
18737
  setLocalUser(volrUser);
18738
+ if (clearUrlParams && typeof window !== "undefined") {
18739
+ const url = new URL(window.location.href);
18740
+ url.search = "";
18741
+ window.history.replaceState({}, "", url.toString());
18742
+ }
18776
18743
  setIsLoading(false);
18777
18744
  options.onSuccess?.(volrUser);
18778
18745
  } catch (err) {
18779
- console.error("Callback error:", err);
18780
- const errorMsg = err.message || "Failed to complete authentication";
18746
+ console.error("[useVolrAuthCallback] Token exchange error:", err);
18747
+ let errorMsg = "Failed to complete authentication";
18748
+ if (err instanceof Error) {
18749
+ if (err.message.includes("AUTH_CODE_EXPIRED")) {
18750
+ errorMsg = "Login link has expired. Please try again.";
18751
+ } else if (err.message.includes("AUTH_INVALID_CODE")) {
18752
+ errorMsg = "Invalid login link. Please try again.";
18753
+ } else {
18754
+ errorMsg = err.message;
18755
+ }
18756
+ }
18781
18757
  setError(errorMsg);
18782
18758
  setIsLoading(false);
18783
18759
  options.onError?.(errorMsg);
18784
18760
  }
18785
18761
  };
18786
18762
  handleCallback();
18787
- }, [apiBaseUrl, config.projectApiKey, refreshAccessToken, setAccessToken, setRefreshToken, setUser, toVolrUser, client]);
18763
+ }, []);
18788
18764
  return {
18789
18765
  isLoading,
18790
18766
  error,
@@ -18866,10 +18842,8 @@ function useDepositListener(input) {
18866
18842
  try {
18867
18843
  if (!rpcUrlRef.current) {
18868
18844
  rpcUrlRef.current = await getRpcUrl(input.chainId);
18869
- console.log("[DepositListener] RPC URL:", rpcUrlRef.current);
18870
18845
  }
18871
18846
  const initial = await fetchBalance();
18872
- console.log("[DepositListener] Initial balance:", initial.toString());
18873
18847
  if (cancelled) return;
18874
18848
  setStatus({ state: "listening", balance: initial });
18875
18849
  } catch (e) {
@@ -18879,16 +18853,13 @@ function useDepositListener(input) {
18879
18853
  return;
18880
18854
  }
18881
18855
  const intervalMs = input.pollIntervalMs ?? config.deposit?.pollIntervalMs ?? 6e3;
18882
- console.log("[DepositListener] Polling interval:", intervalMs, "ms");
18883
18856
  intervalRef.current = window.setInterval(async () => {
18884
18857
  try {
18885
18858
  const current = await fetchBalance();
18886
- console.log("[DepositListener] Current balance:", current.toString());
18887
18859
  if (cancelled) return;
18888
18860
  setStatus((prev) => {
18889
18861
  if (prev.state === "listening") {
18890
18862
  if (current > prev.balance) {
18891
- console.log("[DepositListener] \u2705 DEPOSIT DETECTED! Previous:", prev.balance.toString(), "New:", current.toString());
18892
18863
  return {
18893
18864
  state: "detected",
18894
18865
  previousBalance: prev.balance,
@@ -18897,7 +18868,6 @@ function useDepositListener(input) {
18897
18868
  };
18898
18869
  }
18899
18870
  if (current !== prev.balance) {
18900
- console.log("[DepositListener] \u26A0\uFE0F Balance changed (decreased or other). Previous:", prev.balance.toString(), "New:", current.toString());
18901
18871
  return {
18902
18872
  state: "detected",
18903
18873
  previousBalance: prev.balance,
@@ -18908,7 +18878,6 @@ function useDepositListener(input) {
18908
18878
  return prev;
18909
18879
  }
18910
18880
  if (prev.state === "detected") {
18911
- console.log("[DepositListener] Resuming listening from new balance:", current.toString());
18912
18881
  return { state: "listening", balance: current };
18913
18882
  }
18914
18883
  return prev;
@@ -19341,6 +19310,36 @@ function useMpcConnection() {
19341
19310
  error
19342
19311
  };
19343
19312
  }
19313
+ function createAxiosInstance(baseUrl, apiKey) {
19314
+ const instance = axios.create({
19315
+ baseURL: baseUrl.replace(/\/+$/, ""),
19316
+ // Remove trailing slashes
19317
+ withCredentials: true,
19318
+ // Include cookies
19319
+ headers: {
19320
+ "Content-Type": "application/json"
19321
+ }
19322
+ });
19323
+ instance.interceptors.request.use((config) => {
19324
+ if (apiKey) {
19325
+ config.headers["X-API-Key"] = apiKey;
19326
+ }
19327
+ return config;
19328
+ });
19329
+ instance.interceptors.response.use(
19330
+ (response) => response,
19331
+ (error) => {
19332
+ if (error.response?.data) {
19333
+ const errorData = error.response.data;
19334
+ if (errorData.error?.message) {
19335
+ error.message = errorData.error.message;
19336
+ }
19337
+ }
19338
+ return Promise.reject(error);
19339
+ }
19340
+ );
19341
+ return instance;
19342
+ }
19344
19343
 
19345
19344
  // src/headless/blobs.ts
19346
19345
  async function uploadBlobViaPresign(params) {