@volr/react 0.1.20 → 0.1.21

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.js CHANGED
@@ -9306,6 +9306,21 @@ var APIClient = class {
9306
9306
  }
9307
9307
  };
9308
9308
 
9309
+ // src/config/backend.ts
9310
+ var DEFAULT_API_BASE_URL = "https://api.volr.io";
9311
+ function resolveApiBaseUrl(config) {
9312
+ const anyConfig = config;
9313
+ const devOverride = anyConfig.__devApiBaseUrl;
9314
+ if (devOverride && typeof devOverride === "string") {
9315
+ return devOverride.replace(/\/+$/, "");
9316
+ }
9317
+ const override = anyConfig.apiBaseUrl;
9318
+ if (override && typeof override === "string") {
9319
+ return override.replace(/\/+$/, "");
9320
+ }
9321
+ return DEFAULT_API_BASE_URL;
9322
+ }
9323
+
9309
9324
  // src/headless/auth-sync.ts
9310
9325
  var SessionSync = class {
9311
9326
  constructor() {
@@ -9319,11 +9334,12 @@ var SessionSync = class {
9319
9334
  }
9320
9335
  if (typeof window !== "undefined") {
9321
9336
  window.addEventListener("storage", (e) => {
9322
- if (e.key === STORAGE_KEYS.accessToken || e.key === STORAGE_KEYS.user) {
9337
+ if (e.key === STORAGE_KEYS.accessToken || e.key === STORAGE_KEYS.refreshToken || e.key === STORAGE_KEYS.user) {
9323
9338
  this.notifyListeners({
9324
9339
  type: "REFRESH",
9325
9340
  payload: {
9326
- accessToken: safeStorage.getItem(STORAGE_KEYS.accessToken) || ""
9341
+ accessToken: safeStorage.getItem(STORAGE_KEYS.accessToken) || "",
9342
+ refreshToken: safeStorage.getItem(STORAGE_KEYS.refreshToken)
9327
9343
  }
9328
9344
  });
9329
9345
  }
@@ -9377,19 +9393,63 @@ var SessionSync = class {
9377
9393
  }
9378
9394
  };
9379
9395
 
9380
- // src/config/backend.ts
9381
- var DEFAULT_API_BASE_URL = "https://api.volr.io";
9382
- function resolveApiBaseUrl(config) {
9383
- const anyConfig = config;
9384
- const devOverride = anyConfig.__devApiBaseUrl;
9385
- if (devOverride && typeof devOverride === "string") {
9386
- return devOverride.replace(/\/+$/, "");
9387
- }
9388
- const override = anyConfig.apiBaseUrl;
9389
- if (override && typeof override === "string") {
9390
- return override.replace(/\/+$/, "");
9391
- }
9392
- return DEFAULT_API_BASE_URL;
9396
+ // src/react/hooks/useSessionSync.ts
9397
+ function useSessionSync({
9398
+ client,
9399
+ config,
9400
+ setAccessTokenState,
9401
+ setRefreshTokenState,
9402
+ setUser,
9403
+ setProviderState
9404
+ }) {
9405
+ const syncRef = useRef(null);
9406
+ useEffect(() => {
9407
+ syncRef.current = new SessionSync();
9408
+ const unsubscribe = syncRef.current.subscribe((event) => {
9409
+ if (event.type === "LOGIN") {
9410
+ client.setAccessToken(event.payload.accessToken);
9411
+ setAccessTokenState(event.payload.accessToken);
9412
+ client.setRefreshToken(event.payload.refreshToken);
9413
+ setRefreshTokenState(event.payload.refreshToken);
9414
+ setUser(event.payload.user);
9415
+ safeStorage.setItem(
9416
+ STORAGE_KEYS.user,
9417
+ JSON.stringify(event.payload.user)
9418
+ );
9419
+ client.setApiKey(config.projectApiKey);
9420
+ } else if (event.type === "LOGOUT") {
9421
+ client.setAccessToken(null);
9422
+ client.setRefreshToken(null);
9423
+ setAccessTokenState(null);
9424
+ setRefreshTokenState(null);
9425
+ setUser(null);
9426
+ setProviderState(null);
9427
+ safeStorage.removeItem(STORAGE_KEYS.user);
9428
+ safeStorage.removeItem(STORAGE_KEYS.provider);
9429
+ } else if (event.type === "REFRESH") {
9430
+ client.setAccessToken(event.payload.accessToken);
9431
+ setAccessTokenState(event.payload.accessToken);
9432
+ if (event.payload.refreshToken) {
9433
+ client.setRefreshToken(event.payload.refreshToken);
9434
+ setRefreshTokenState(event.payload.refreshToken);
9435
+ }
9436
+ } else if (event.type === "PROVIDER_SET") {
9437
+ setUser(
9438
+ (prev) => prev ? {
9439
+ ...prev,
9440
+ keyStorageType: event.payload.keyStorageType,
9441
+ address: event.payload.address
9442
+ } : null
9443
+ );
9444
+ safeStorage.setItem(STORAGE_KEYS.provider, event.payload.keyStorageType);
9445
+ }
9446
+ });
9447
+ return () => {
9448
+ unsubscribe();
9449
+ syncRef.current?.destroy();
9450
+ };
9451
+ }, [client, config.projectApiKey, setAccessTokenState, setRefreshTokenState, setUser, setProviderState]);
9452
+ return syncRef;
9393
9453
  }
9394
9454
 
9395
9455
  // src/config/webauthn.ts
@@ -9652,6 +9712,120 @@ async function restorePasskey(params) {
9652
9712
  provider
9653
9713
  };
9654
9714
  }
9715
+
9716
+ // src/react/hooks/useAutoRecover.ts
9717
+ function useAutoRecover({
9718
+ client,
9719
+ config,
9720
+ setAccessTokenState,
9721
+ setRefreshTokenState,
9722
+ setUser,
9723
+ setProviderState,
9724
+ setProvider,
9725
+ setIsLoading
9726
+ }) {
9727
+ const REQUIRE_USER_GESTURE_TO_RESTORE = true;
9728
+ const hasRecoveredRef = useRef(false);
9729
+ const restorationAttemptedRef = useRef(false);
9730
+ useEffect(() => {
9731
+ if (config.autoRecoverOnLogin !== false && !hasRecoveredRef.current) {
9732
+ hasRecoveredRef.current = true;
9733
+ const recover = async () => {
9734
+ try {
9735
+ setIsLoading(true);
9736
+ if (!client.getRefreshToken()) {
9737
+ console.log("[Provider] No refresh token found, skipping auto-recover");
9738
+ setIsLoading(false);
9739
+ return;
9740
+ }
9741
+ const refreshedUser = await client.refreshSession();
9742
+ console.log("[Provider] Session refreshed, user:", refreshedUser);
9743
+ setAccessTokenState(client.getAccessToken());
9744
+ setRefreshTokenState(client.getRefreshToken());
9745
+ if (refreshedUser) {
9746
+ setUser(refreshedUser);
9747
+ if (!REQUIRE_USER_GESTURE_TO_RESTORE) ; else if (refreshedUser.keyStorageType === "passkey") {
9748
+ console.log("[Provider] TTL=0 mode: Provider restoration deferred");
9749
+ if (!refreshedUser.blobUrl || !refreshedUser.prfInput) {
9750
+ console.warn("[Provider] Passkey user missing blobUrl or prfInput");
9751
+ }
9752
+ }
9753
+ } else {
9754
+ const userStr = safeStorage.getItem(STORAGE_KEYS.user);
9755
+ if (userStr) {
9756
+ try {
9757
+ setUser(JSON.parse(userStr));
9758
+ } catch {
9759
+ safeStorage.removeItem(STORAGE_KEYS.user);
9760
+ }
9761
+ }
9762
+ }
9763
+ } catch {
9764
+ client.setAccessToken(null);
9765
+ client.setRefreshToken(null);
9766
+ setAccessTokenState(null);
9767
+ setRefreshTokenState(null);
9768
+ setUser(null);
9769
+ setProviderState(null);
9770
+ safeStorage.removeItem(STORAGE_KEYS.user);
9771
+ safeStorage.removeItem(STORAGE_KEYS.provider);
9772
+ } finally {
9773
+ setIsLoading(false);
9774
+ }
9775
+ };
9776
+ recover();
9777
+ } else {
9778
+ setIsLoading(false);
9779
+ }
9780
+ }, [client, config.autoRecoverOnLogin, setAccessTokenState, setRefreshTokenState, setUser, setProviderState, setProvider, setIsLoading]);
9781
+ }
9782
+ function useWalletEvents({
9783
+ user,
9784
+ logout,
9785
+ setUser,
9786
+ setProviderState
9787
+ }) {
9788
+ useEffect(() => {
9789
+ if (!user) return;
9790
+ if (typeof window === "undefined" || !window.ethereum) {
9791
+ return;
9792
+ }
9793
+ const ethereum = window.ethereum;
9794
+ const handleAccountsChanged = (accounts) => {
9795
+ console.log("[Provider] accountsChanged event:", accounts);
9796
+ if (accounts.length === 0) {
9797
+ console.warn("[Provider] Wallet disconnected, logging out...");
9798
+ setTimeout(() => logout(), 3e3);
9799
+ } else if (user.evmAddress && accounts[0].toLowerCase() !== user.evmAddress.toLowerCase()) {
9800
+ console.warn("[Provider] Account changed, logging out in 3 seconds...");
9801
+ setTimeout(() => logout(), 3e3);
9802
+ }
9803
+ };
9804
+ const handleChainChanged = (chainIdHex) => {
9805
+ const newChainId = parseInt(chainIdHex, 16);
9806
+ console.log("[Provider] chainChanged event:", newChainId);
9807
+ setUser(
9808
+ (prev) => prev ? { ...prev, lastWalletChainId: newChainId } : null
9809
+ );
9810
+ window.location.reload();
9811
+ };
9812
+ const handleDisconnect = () => {
9813
+ console.log("[Provider] disconnect event");
9814
+ setProviderState(null);
9815
+ safeStorage.removeItem(STORAGE_KEYS.provider);
9816
+ };
9817
+ ethereum.on("accountsChanged", handleAccountsChanged);
9818
+ ethereum.on("chainChanged", handleChainChanged);
9819
+ ethereum.on("disconnect", handleDisconnect);
9820
+ return () => {
9821
+ ethereum.removeListener("accountsChanged", handleAccountsChanged);
9822
+ ethereum.removeListener("chainChanged", handleChainChanged);
9823
+ ethereum.removeListener("disconnect", handleDisconnect);
9824
+ };
9825
+ }, [user, logout, setUser, setProviderState]);
9826
+ }
9827
+
9828
+ // src/utils/json.ts
9655
9829
  function serializeBigIntDeep(obj) {
9656
9830
  if (obj === null || obj === void 0) {
9657
9831
  return obj;
@@ -9673,8 +9847,62 @@ function serializeBigIntDeep(obj) {
9673
9847
  }
9674
9848
  return obj;
9675
9849
  }
9850
+
9851
+ // src/react/hooks/useVolrActions.ts
9852
+ function useVolrActions({
9853
+ client,
9854
+ setError
9855
+ }) {
9856
+ const precheck = useCallback(
9857
+ async (input) => {
9858
+ try {
9859
+ setError(null);
9860
+ await client.ensureAccessToken();
9861
+ const response = await client.post(
9862
+ "/wallet/precheck",
9863
+ input
9864
+ );
9865
+ return response.quote;
9866
+ } catch (err) {
9867
+ const error = err instanceof Error ? err : new Error("Precheck failed");
9868
+ const diag = err?.response?.data?.error?.developerDiagnostics;
9869
+ if (diag) {
9870
+ console.error("[volr][precheck] developerDiagnostics:", diag);
9871
+ }
9872
+ setError(error);
9873
+ throw error;
9874
+ }
9875
+ },
9876
+ [client, setError]
9877
+ );
9878
+ const relay = useCallback(
9879
+ async (input, opts) => {
9880
+ try {
9881
+ setError(null);
9882
+ await client.ensureAccessToken();
9883
+ const idempotencyKey = opts?.idempotencyKey ?? (typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`);
9884
+ const serializedInput = serializeBigIntDeep(input);
9885
+ const response = await client.post(
9886
+ "/wallet/relay",
9887
+ serializedInput,
9888
+ idempotencyKey
9889
+ );
9890
+ return response;
9891
+ } catch (err) {
9892
+ const error = err instanceof Error ? err : new Error("Relay failed");
9893
+ const diag = err?.response?.data?.error?.developerDiagnostics;
9894
+ if (diag) {
9895
+ console.error("[volr][relay] developerDiagnostics:", diag);
9896
+ }
9897
+ setError(error);
9898
+ throw error;
9899
+ }
9900
+ },
9901
+ [client, setError]
9902
+ );
9903
+ return { precheck, relay };
9904
+ }
9676
9905
  function VolrProvider({ config, children }) {
9677
- const REQUIRE_USER_GESTURE_TO_RESTORE = true;
9678
9906
  const providerCountRef = useRef(0);
9679
9907
  useEffect(() => {
9680
9908
  providerCountRef.current++;
@@ -9723,45 +9951,14 @@ function VolrProvider({ config, children }) {
9723
9951
  const [refreshToken, setRefreshTokenState] = useState(() => {
9724
9952
  return client.getRefreshToken();
9725
9953
  });
9726
- const syncRef = useRef(null);
9727
- useEffect(() => {
9728
- syncRef.current = new SessionSync();
9729
- const unsubscribe = syncRef.current.subscribe((event) => {
9730
- if (event.type === "LOGIN") {
9731
- client.setAccessToken(event.payload.accessToken);
9732
- setAccessTokenState(event.payload.accessToken);
9733
- setUser(event.payload.user);
9734
- safeStorage.setItem(
9735
- STORAGE_KEYS.user,
9736
- JSON.stringify(event.payload.user)
9737
- );
9738
- client.setApiKey(config.projectApiKey);
9739
- } else if (event.type === "LOGOUT") {
9740
- client.setAccessToken(null);
9741
- client.setRefreshToken(null);
9742
- setAccessTokenState(null);
9743
- setRefreshTokenState(null);
9744
- setUser(null);
9745
- setProviderState(null);
9746
- safeStorage.removeItem(STORAGE_KEYS.user);
9747
- safeStorage.removeItem(STORAGE_KEYS.provider);
9748
- } else if (event.type === "REFRESH") {
9749
- client.setAccessToken(event.payload.accessToken);
9750
- setAccessTokenState(event.payload.accessToken);
9751
- } else if (event.type === "PROVIDER_SET") {
9752
- setUser((prev) => ({
9753
- ...prev,
9754
- keyStorageType: event.payload.keyStorageType,
9755
- address: event.payload.address
9756
- }));
9757
- safeStorage.setItem(STORAGE_KEYS.provider, event.payload.keyStorageType);
9758
- }
9759
- });
9760
- return () => {
9761
- unsubscribe();
9762
- syncRef.current?.destroy();
9763
- };
9764
- }, [client, config.projectApiKey]);
9954
+ const syncRef = useSessionSync({
9955
+ client,
9956
+ config,
9957
+ setAccessTokenState,
9958
+ setRefreshTokenState,
9959
+ setUser,
9960
+ setProviderState
9961
+ });
9765
9962
  const setProvider = useCallback(
9766
9963
  async (newProvider) => {
9767
9964
  try {
@@ -9793,108 +9990,22 @@ function VolrProvider({ config, children }) {
9793
9990
  throw error2;
9794
9991
  }
9795
9992
  },
9796
- [client, user]
9797
- );
9798
- const hasRecoveredRef = useRef(false);
9799
- const restorationAttemptedRef = useRef(false);
9800
- useEffect(() => {
9801
- if (config.autoRecoverOnLogin !== false && !hasRecoveredRef.current) {
9802
- hasRecoveredRef.current = true;
9803
- const recover = async () => {
9804
- try {
9805
- setIsLoading(true);
9806
- if (!client.getRefreshToken()) {
9807
- console.log("[Provider] No refresh token found, skipping auto-recover");
9808
- setIsLoading(false);
9809
- return;
9810
- }
9811
- const refreshedUser = await client.refreshSession();
9812
- console.log("[Provider] Session refreshed, user:", refreshedUser);
9813
- setAccessTokenState(client.getAccessToken());
9814
- setRefreshTokenState(client.getRefreshToken());
9815
- if (refreshedUser) {
9816
- setUser(refreshedUser);
9817
- if (!REQUIRE_USER_GESTURE_TO_RESTORE) ; else if (refreshedUser.keyStorageType === "passkey") {
9818
- console.log("[Provider] TTL=0 mode: Provider restoration deferred");
9819
- if (!refreshedUser.blobUrl || !refreshedUser.prfInput) {
9820
- console.warn("[Provider] Passkey user missing blobUrl or prfInput");
9821
- }
9822
- }
9823
- } else {
9824
- const userStr = safeStorage.getItem(STORAGE_KEYS.user);
9825
- if (userStr) {
9826
- try {
9827
- setUser(JSON.parse(userStr));
9828
- } catch {
9829
- safeStorage.removeItem(STORAGE_KEYS.user);
9830
- }
9831
- }
9832
- }
9833
- } catch {
9834
- client.setAccessToken(null);
9835
- client.setRefreshToken(null);
9836
- setAccessTokenState(null);
9837
- setRefreshTokenState(null);
9838
- setUser(null);
9839
- setProviderState(null);
9840
- safeStorage.removeItem(STORAGE_KEYS.user);
9841
- safeStorage.removeItem(STORAGE_KEYS.provider);
9842
- } finally {
9843
- setIsLoading(false);
9844
- }
9845
- };
9846
- recover();
9847
- } else {
9848
- setIsLoading(false);
9849
- }
9850
- }, [client, config.autoRecoverOnLogin]);
9851
- const precheck = useCallback(
9852
- async (input) => {
9853
- try {
9854
- setError(null);
9855
- await client.ensureAccessToken();
9856
- const response = await client.post(
9857
- "/wallet/precheck",
9858
- input
9859
- );
9860
- return response.quote;
9861
- } catch (err) {
9862
- const error2 = err instanceof Error ? err : new Error("Precheck failed");
9863
- const diag = err?.response?.data?.error?.developerDiagnostics;
9864
- if (diag) {
9865
- console.error("[volr][precheck] developerDiagnostics:", diag);
9866
- }
9867
- setError(error2);
9868
- throw error2;
9869
- }
9870
- },
9871
- [client]
9872
- );
9873
- const relay = useCallback(
9874
- async (input, opts) => {
9875
- try {
9876
- setError(null);
9877
- await client.ensureAccessToken();
9878
- const idempotencyKey = opts?.idempotencyKey ?? (typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`);
9879
- const serializedInput = serializeBigIntDeep(input);
9880
- const response = await client.post(
9881
- "/wallet/relay",
9882
- serializedInput,
9883
- idempotencyKey
9884
- );
9885
- return response;
9886
- } catch (err) {
9887
- const error2 = err instanceof Error ? err : new Error("Relay failed");
9888
- const diag = err?.response?.data?.error?.developerDiagnostics;
9889
- if (diag) {
9890
- console.error("[volr][relay] developerDiagnostics:", diag);
9891
- }
9892
- setError(error2);
9893
- throw error2;
9894
- }
9895
- },
9896
- [client]
9993
+ [client, user, syncRef]
9897
9994
  );
9995
+ useAutoRecover({
9996
+ client,
9997
+ config,
9998
+ setAccessTokenState,
9999
+ setRefreshTokenState,
10000
+ setUser,
10001
+ setProviderState,
10002
+ setProvider,
10003
+ setIsLoading
10004
+ });
10005
+ const { precheck, relay } = useVolrActions({
10006
+ client,
10007
+ setError
10008
+ });
9898
10009
  const logout = useCallback(async () => {
9899
10010
  try {
9900
10011
  setError(null);
@@ -9914,45 +10025,13 @@ function VolrProvider({ config, children }) {
9914
10025
  setError(error2);
9915
10026
  throw error2;
9916
10027
  }
9917
- }, [client]);
9918
- useEffect(() => {
9919
- if (!user) return;
9920
- if (typeof window === "undefined" || !window.ethereum) {
9921
- return;
9922
- }
9923
- const ethereum = window.ethereum;
9924
- const handleAccountsChanged = (accounts) => {
9925
- console.log("[Provider] accountsChanged event:", accounts);
9926
- if (accounts.length === 0) {
9927
- console.warn("[Provider] Wallet disconnected, logging out...");
9928
- setTimeout(() => logout(), 3e3);
9929
- } else if (user.evmAddress && accounts[0].toLowerCase() !== user.evmAddress.toLowerCase()) {
9930
- console.warn("[Provider] Account changed, logging out in 3 seconds...");
9931
- setTimeout(() => logout(), 3e3);
9932
- }
9933
- };
9934
- const handleChainChanged = (chainIdHex) => {
9935
- const newChainId = parseInt(chainIdHex, 16);
9936
- console.log("[Provider] chainChanged event:", newChainId);
9937
- setUser(
9938
- (prev) => prev ? { ...prev, lastWalletChainId: newChainId } : null
9939
- );
9940
- window.location.reload();
9941
- };
9942
- const handleDisconnect = () => {
9943
- console.log("[Provider] disconnect event");
9944
- setProviderState(null);
9945
- safeStorage.removeItem(STORAGE_KEYS.provider);
9946
- };
9947
- ethereum.on("accountsChanged", handleAccountsChanged);
9948
- ethereum.on("chainChanged", handleChainChanged);
9949
- ethereum.on("disconnect", handleDisconnect);
9950
- return () => {
9951
- ethereum.removeListener("accountsChanged", handleAccountsChanged);
9952
- ethereum.removeListener("chainChanged", handleChainChanged);
9953
- ethereum.removeListener("disconnect", handleDisconnect);
9954
- };
9955
- }, [user, logout, setUser]);
10028
+ }, [client, syncRef]);
10029
+ useWalletEvents({
10030
+ user,
10031
+ logout,
10032
+ setUser,
10033
+ setProviderState
10034
+ });
9956
10035
  const publicValue = useMemo(
9957
10036
  () => ({
9958
10037
  config,
@@ -18538,6 +18617,10 @@ function useVolrLogin() {
18538
18617
  setAccessToken(accessToken);
18539
18618
  if (refreshToken) {
18540
18619
  setRefreshToken(refreshToken);
18620
+ } else {
18621
+ console.warn(
18622
+ "[useVolrLogin] Access token received but refresh token is missing. Check if backend is updated to return refresh token in body."
18623
+ );
18541
18624
  }
18542
18625
  if (userFromServer) {
18543
18626
  setUser(toVolrUser(userFromServer));