@reverbia/sdk 1.0.0-next.20251218225706 → 1.0.0-next.20251219154503

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.
@@ -38,18 +38,28 @@ var __decorateClass = (decorators, target, key, kind) => {
38
38
  // src/react/index.ts
39
39
  var index_exports = {};
40
40
  __export(index_exports, {
41
+ BACKUP_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
42
+ BACKUP_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
43
+ BACKUP_ICLOUD_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
44
+ BackupAuthProvider: () => BackupAuthProvider,
41
45
  ChatConversation: () => Conversation,
42
46
  ChatMessage: () => Message,
43
47
  DEFAULT_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER,
44
48
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
45
49
  DEFAULT_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
50
+ DEFAULT_DROPBOX_FOLDER: () => DEFAULT_BACKUP_FOLDER,
51
+ DEFAULT_ICLOUD_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
46
52
  DEFAULT_TOOL_SELECTOR_MODEL: () => DEFAULT_TOOL_SELECTOR_MODEL,
47
53
  DropboxAuthProvider: () => DropboxAuthProvider,
54
+ GoogleDriveAuthProvider: () => GoogleDriveAuthProvider,
55
+ ICloudAuthProvider: () => ICloudAuthProvider,
48
56
  StoredMemoryModel: () => Memory,
49
57
  StoredModelPreferenceModel: () => ModelPreference,
50
58
  chatStorageMigrations: () => chatStorageMigrations,
51
59
  chatStorageSchema: () => chatStorageSchema,
52
60
  clearDropboxToken: () => clearToken,
61
+ clearGoogleDriveToken: () => clearGoogleDriveToken,
62
+ clearICloudAuth: () => clearICloudAuth,
53
63
  createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
54
64
  decryptData: () => decryptData,
55
65
  decryptDataBytes: () => decryptDataBytes,
@@ -60,8 +70,11 @@ __export(index_exports, {
60
70
  generateCompositeKey: () => generateCompositeKey,
61
71
  generateConversationId: () => generateConversationId,
62
72
  generateUniqueKey: () => generateUniqueKey,
63
- getDropboxToken: () => getStoredToken,
73
+ getGoogleDriveStoredToken: () => getGoogleDriveStoredToken,
74
+ hasDropboxCredentials: () => hasDropboxCredentials,
64
75
  hasEncryptionKey: () => hasEncryptionKey,
76
+ hasGoogleDriveCredentials: () => hasGoogleDriveCredentials,
77
+ hasICloudCredentials: () => hasICloudCredentials,
65
78
  memoryStorageSchema: () => memoryStorageSchema,
66
79
  requestEncryptionKey: () => requestEncryptionKey,
67
80
  sdkMigrations: () => sdkMigrations,
@@ -69,13 +82,17 @@ __export(index_exports, {
69
82
  sdkSchema: () => sdkSchema,
70
83
  selectTool: () => selectTool,
71
84
  settingsStorageSchema: () => settingsStorageSchema,
72
- storeDropboxToken: () => storeToken,
85
+ useBackup: () => useBackup,
86
+ useBackupAuth: () => useBackupAuth,
73
87
  useChat: () => useChat,
74
88
  useChatStorage: () => useChatStorage,
75
89
  useDropboxAuth: () => useDropboxAuth,
76
90
  useDropboxBackup: () => useDropboxBackup,
77
91
  useEncryption: () => useEncryption,
92
+ useGoogleDriveAuth: () => useGoogleDriveAuth,
78
93
  useGoogleDriveBackup: () => useGoogleDriveBackup,
94
+ useICloudAuth: () => useICloudAuth,
95
+ useICloudBackup: () => useICloudBackup,
79
96
  useImageGeneration: () => useImageGeneration,
80
97
  useMemoryStorage: () => useMemoryStorage,
81
98
  useModels: () => useModels,
@@ -3065,6 +3082,36 @@ var postApiV1Search = (options) => {
3065
3082
  }
3066
3083
  });
3067
3084
  };
3085
+ var postAuthOauthByProviderExchange = (options) => {
3086
+ return (options.client ?? client).post({
3087
+ url: "/auth/oauth/{provider}/exchange",
3088
+ ...options,
3089
+ headers: {
3090
+ "Content-Type": "application/json",
3091
+ ...options.headers
3092
+ }
3093
+ });
3094
+ };
3095
+ var postAuthOauthByProviderRefresh = (options) => {
3096
+ return (options.client ?? client).post({
3097
+ url: "/auth/oauth/{provider}/refresh",
3098
+ ...options,
3099
+ headers: {
3100
+ "Content-Type": "application/json",
3101
+ ...options.headers
3102
+ }
3103
+ });
3104
+ };
3105
+ var postAuthOauthByProviderRevoke = (options) => {
3106
+ return (options.client ?? client).post({
3107
+ url: "/auth/oauth/{provider}/revoke",
3108
+ ...options,
3109
+ headers: {
3110
+ "Content-Type": "application/json",
3111
+ ...options.headers
3112
+ }
3113
+ });
3114
+ };
3068
3115
 
3069
3116
  // src/lib/memory/constants.ts
3070
3117
  var DEFAULT_LOCAL_EMBEDDING_MODEL = "Snowflake/snowflake-arctic-embed-xs";
@@ -4608,116 +4655,196 @@ async function performDropboxImport(userAddress, token, deps, onProgress, backup
4608
4655
  // src/react/useDropboxAuth.ts
4609
4656
  var import_react10 = require("react");
4610
4657
 
4611
- // src/lib/backup/dropbox/auth.ts
4612
- var DROPBOX_AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
4613
- var DROPBOX_TOKEN_URL = "https://api.dropboxapi.com/oauth2/token";
4614
- var TOKEN_STORAGE_KEY = "dropbox_access_token";
4615
- var VERIFIER_STORAGE_KEY = "dropbox_code_verifier";
4616
- function generateCodeVerifier() {
4617
- const array = new Uint8Array(32);
4618
- crypto.getRandomValues(array);
4619
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
4620
- }
4621
- async function generateCodeChallenge(verifier) {
4622
- const encoder = new TextEncoder();
4623
- const data = encoder.encode(verifier);
4624
- const hash = await crypto.subtle.digest("SHA-256", data);
4625
- const base64 = btoa(String.fromCharCode(...new Uint8Array(hash)));
4626
- return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4658
+ // src/lib/backup/oauth/storage.ts
4659
+ var STORAGE_KEY_PREFIX = "oauth_token_";
4660
+ function getStorageKey2(provider) {
4661
+ return `${STORAGE_KEY_PREFIX}${provider}`;
4627
4662
  }
4628
- function getStoredToken() {
4663
+ function getStoredTokenData(provider) {
4629
4664
  if (typeof window === "undefined") return null;
4630
- return sessionStorage.getItem(TOKEN_STORAGE_KEY);
4665
+ try {
4666
+ const stored = localStorage.getItem(getStorageKey2(provider));
4667
+ if (!stored) return null;
4668
+ const data = JSON.parse(stored);
4669
+ if (!data.accessToken) return null;
4670
+ return data;
4671
+ } catch {
4672
+ return null;
4673
+ }
4631
4674
  }
4632
- function storeToken(token) {
4675
+ function storeTokenData(provider, data) {
4633
4676
  if (typeof window === "undefined") return;
4634
- sessionStorage.setItem(TOKEN_STORAGE_KEY, token);
4677
+ localStorage.setItem(getStorageKey2(provider), JSON.stringify(data));
4635
4678
  }
4636
- function clearToken() {
4679
+ function clearTokenData(provider) {
4637
4680
  if (typeof window === "undefined") return;
4638
- sessionStorage.removeItem(TOKEN_STORAGE_KEY);
4681
+ localStorage.removeItem(getStorageKey2(provider));
4639
4682
  }
4640
- function getStoredVerifier() {
4641
- if (typeof window === "undefined") return null;
4642
- return sessionStorage.getItem(VERIFIER_STORAGE_KEY);
4683
+ function isTokenExpired(data, bufferSeconds = 60) {
4684
+ if (!data) return true;
4685
+ if (!data.expiresAt) return false;
4686
+ const now = Date.now();
4687
+ const bufferMs = bufferSeconds * 1e3;
4688
+ return data.expiresAt - bufferMs <= now;
4643
4689
  }
4644
- function storeVerifier(verifier) {
4645
- if (typeof window === "undefined") return;
4646
- sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
4690
+ function getValidAccessToken(provider) {
4691
+ const data = getStoredTokenData(provider);
4692
+ if (!data) return null;
4693
+ if (data.expiresAt && isTokenExpired(data)) {
4694
+ return null;
4695
+ }
4696
+ return data.accessToken;
4647
4697
  }
4648
- function clearVerifier() {
4649
- if (typeof window === "undefined") return;
4650
- sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
4698
+ function getRefreshToken(provider) {
4699
+ const data = getStoredTokenData(provider);
4700
+ return data?.refreshToken ?? null;
4701
+ }
4702
+ function tokenResponseToStoredData(accessToken, expiresIn, refreshToken, scope) {
4703
+ const data = {
4704
+ accessToken,
4705
+ refreshToken,
4706
+ scope
4707
+ };
4708
+ if (expiresIn) {
4709
+ data.expiresAt = Date.now() + expiresIn * 1e3;
4710
+ }
4711
+ return data;
4651
4712
  }
4713
+
4714
+ // src/lib/backup/dropbox/auth.ts
4715
+ var PROVIDER = "dropbox";
4716
+ var STATE_STORAGE_KEY = "dropbox_oauth_state";
4717
+ var DROPBOX_AUTH_URL = "https://www.dropbox.com/oauth2/authorize";
4652
4718
  function getRedirectUri(callbackPath) {
4653
4719
  if (typeof window === "undefined") return "";
4654
4720
  return `${window.location.origin}${callbackPath}`;
4655
4721
  }
4656
- async function handleDropboxCallback(appKey, callbackPath) {
4722
+ function generateState() {
4723
+ const array = new Uint8Array(16);
4724
+ crypto.getRandomValues(array);
4725
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
4726
+ ""
4727
+ );
4728
+ }
4729
+ function storeOAuthState(state) {
4730
+ if (typeof window === "undefined") return;
4731
+ sessionStorage.setItem(STATE_STORAGE_KEY, state);
4732
+ }
4733
+ function getAndClearOAuthState() {
4734
+ if (typeof window === "undefined") return null;
4735
+ const state = sessionStorage.getItem(STATE_STORAGE_KEY);
4736
+ sessionStorage.removeItem(STATE_STORAGE_KEY);
4737
+ return state;
4738
+ }
4739
+ function isDropboxCallback() {
4740
+ if (typeof window === "undefined") return false;
4741
+ const url = new URL(window.location.href);
4742
+ const code = url.searchParams.get("code");
4743
+ const state = url.searchParams.get("state");
4744
+ const storedState = sessionStorage.getItem(STATE_STORAGE_KEY);
4745
+ return !!code && !!state && state === storedState;
4746
+ }
4747
+ async function handleDropboxCallback(callbackPath, apiClient) {
4657
4748
  if (typeof window === "undefined") return null;
4658
4749
  const url = new URL(window.location.href);
4659
4750
  const code = url.searchParams.get("code");
4660
4751
  const state = url.searchParams.get("state");
4661
- if (!code || state !== "dropbox_auth") return null;
4662
- const verifier = getStoredVerifier();
4663
- if (!verifier) return null;
4752
+ const storedState = getAndClearOAuthState();
4753
+ if (!code || !state || state !== storedState) {
4754
+ return null;
4755
+ }
4664
4756
  try {
4665
- const response = await fetch(DROPBOX_TOKEN_URL, {
4666
- method: "POST",
4667
- headers: {
4668
- "Content-Type": "application/x-www-form-urlencoded"
4669
- },
4670
- body: new URLSearchParams({
4757
+ const response = await postAuthOauthByProviderExchange({
4758
+ client: apiClient,
4759
+ path: { provider: PROVIDER },
4760
+ body: {
4671
4761
  code,
4672
- grant_type: "authorization_code",
4673
- client_id: appKey,
4674
- redirect_uri: getRedirectUri(callbackPath),
4675
- code_verifier: verifier
4676
- })
4762
+ redirect_uri: getRedirectUri(callbackPath)
4763
+ }
4677
4764
  });
4678
- if (!response.ok) {
4679
- throw new Error("Token exchange failed");
4765
+ if (!response.data?.access_token) {
4766
+ throw new Error("No access token in response");
4680
4767
  }
4681
- const data = await response.json();
4682
- const token = data.access_token;
4683
- if (typeof token !== "string" || token.trim() === "") {
4684
- throw new Error("Invalid token response: access_token is missing or empty");
4685
- }
4686
- storeToken(token);
4687
- clearVerifier();
4768
+ const tokenData = tokenResponseToStoredData(
4769
+ response.data.access_token,
4770
+ response.data.expires_in,
4771
+ response.data.refresh_token,
4772
+ response.data.scope
4773
+ );
4774
+ storeTokenData(PROVIDER, tokenData);
4688
4775
  window.history.replaceState({}, "", window.location.pathname);
4689
- return token;
4776
+ return response.data.access_token;
4777
+ } catch {
4778
+ return null;
4779
+ }
4780
+ }
4781
+ async function refreshDropboxToken(apiClient) {
4782
+ const refreshToken = getRefreshToken(PROVIDER);
4783
+ if (!refreshToken) return null;
4784
+ try {
4785
+ const response = await postAuthOauthByProviderRefresh({
4786
+ client: apiClient,
4787
+ path: { provider: PROVIDER },
4788
+ body: { refresh_token: refreshToken }
4789
+ });
4790
+ if (!response.data?.access_token) {
4791
+ throw new Error("No access token in refresh response");
4792
+ }
4793
+ const currentData = getStoredTokenData(PROVIDER);
4794
+ const tokenData = tokenResponseToStoredData(
4795
+ response.data.access_token,
4796
+ response.data.expires_in,
4797
+ response.data.refresh_token ?? currentData?.refreshToken,
4798
+ response.data.scope ?? currentData?.scope
4799
+ );
4800
+ storeTokenData(PROVIDER, tokenData);
4801
+ return response.data.access_token;
4690
4802
  } catch {
4691
- clearVerifier();
4803
+ clearTokenData(PROVIDER);
4692
4804
  return null;
4693
4805
  }
4694
4806
  }
4807
+ async function revokeDropboxToken(apiClient) {
4808
+ const tokenData = getStoredTokenData(PROVIDER);
4809
+ if (!tokenData) return;
4810
+ try {
4811
+ const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
4812
+ await postAuthOauthByProviderRevoke({
4813
+ client: apiClient,
4814
+ path: { provider: PROVIDER },
4815
+ body: { token: tokenToRevoke }
4816
+ });
4817
+ } catch {
4818
+ } finally {
4819
+ clearTokenData(PROVIDER);
4820
+ }
4821
+ }
4822
+ async function getDropboxAccessToken(apiClient) {
4823
+ const validToken = getValidAccessToken(PROVIDER);
4824
+ if (validToken) return validToken;
4825
+ return refreshDropboxToken(apiClient);
4826
+ }
4695
4827
  async function startDropboxAuth(appKey, callbackPath) {
4696
- const verifier = generateCodeVerifier();
4697
- const challenge = await generateCodeChallenge(verifier);
4698
- storeVerifier(verifier);
4828
+ const state = generateState();
4829
+ storeOAuthState(state);
4699
4830
  const params = new URLSearchParams({
4700
4831
  client_id: appKey,
4701
4832
  redirect_uri: getRedirectUri(callbackPath),
4702
4833
  response_type: "code",
4703
- code_challenge: challenge,
4704
- code_challenge_method: "S256",
4705
- state: "dropbox_auth",
4834
+ state,
4706
4835
  token_access_type: "offline"
4836
+ // Request refresh token
4707
4837
  });
4708
4838
  window.location.href = `${DROPBOX_AUTH_URL}?${params.toString()}`;
4709
4839
  return new Promise(() => {
4710
4840
  });
4711
4841
  }
4712
- async function requestDropboxAccess(appKey, callbackPath) {
4713
- if (!appKey) {
4714
- throw new Error("Dropbox is not configured");
4715
- }
4716
- const storedToken = getStoredToken();
4717
- if (storedToken) {
4718
- return storedToken;
4719
- }
4720
- return startDropboxAuth(appKey, callbackPath);
4842
+ function clearToken() {
4843
+ clearTokenData(PROVIDER);
4844
+ }
4845
+ function hasDropboxCredentials() {
4846
+ const data = getStoredTokenData(PROVIDER);
4847
+ return !!(data?.accessToken || data?.refreshToken);
4721
4848
  }
4722
4849
 
4723
4850
  // src/react/useDropboxAuth.ts
@@ -4725,26 +4852,41 @@ var DropboxAuthContext = (0, import_react10.createContext)(null);
4725
4852
  function DropboxAuthProvider({
4726
4853
  appKey,
4727
4854
  callbackPath = "/auth/dropbox/callback",
4855
+ apiClient,
4728
4856
  children
4729
4857
  }) {
4730
4858
  const [accessToken, setAccessToken] = (0, import_react10.useState)(null);
4731
4859
  const isConfigured = !!appKey;
4732
4860
  (0, import_react10.useEffect)(() => {
4733
- const storedToken = getStoredToken();
4734
- if (storedToken) {
4735
- setAccessToken(storedToken);
4736
- }
4737
- }, []);
4861
+ const checkStoredToken = async () => {
4862
+ if (hasDropboxCredentials()) {
4863
+ const token = await getDropboxAccessToken(apiClient);
4864
+ if (token) {
4865
+ setAccessToken(token);
4866
+ }
4867
+ }
4868
+ };
4869
+ checkStoredToken();
4870
+ }, [apiClient]);
4738
4871
  (0, import_react10.useEffect)(() => {
4739
- if (!isConfigured || !appKey) return;
4872
+ if (!isConfigured) return;
4740
4873
  const handleCallback = async () => {
4741
- const token = await handleDropboxCallback(appKey, callbackPath);
4742
- if (token) {
4743
- setAccessToken(token);
4874
+ if (isDropboxCallback()) {
4875
+ const token = await handleDropboxCallback(callbackPath, apiClient);
4876
+ if (token) {
4877
+ setAccessToken(token);
4878
+ }
4744
4879
  }
4745
4880
  };
4746
4881
  handleCallback();
4747
- }, [appKey, callbackPath, isConfigured]);
4882
+ }, [callbackPath, isConfigured, apiClient]);
4883
+ const refreshTokenFn = (0, import_react10.useCallback)(async () => {
4884
+ const token = await getDropboxAccessToken(apiClient);
4885
+ if (token) {
4886
+ setAccessToken(token);
4887
+ }
4888
+ return token;
4889
+ }, [apiClient]);
4748
4890
  const requestAccess = (0, import_react10.useCallback)(async () => {
4749
4891
  if (!isConfigured || !appKey) {
4750
4892
  throw new Error("Dropbox is not configured");
@@ -4752,17 +4894,17 @@ function DropboxAuthProvider({
4752
4894
  if (accessToken) {
4753
4895
  return accessToken;
4754
4896
  }
4755
- const storedToken = getStoredToken();
4897
+ const storedToken = await getDropboxAccessToken(apiClient);
4756
4898
  if (storedToken) {
4757
4899
  setAccessToken(storedToken);
4758
4900
  return storedToken;
4759
4901
  }
4760
- return requestDropboxAccess(appKey, callbackPath);
4761
- }, [accessToken, appKey, callbackPath, isConfigured]);
4762
- const logout = (0, import_react10.useCallback)(() => {
4763
- clearToken();
4902
+ return startDropboxAuth(appKey, callbackPath);
4903
+ }, [accessToken, appKey, callbackPath, isConfigured, apiClient]);
4904
+ const logout = (0, import_react10.useCallback)(async () => {
4905
+ await revokeDropboxToken(apiClient);
4764
4906
  setAccessToken(null);
4765
- }, []);
4907
+ }, [apiClient]);
4766
4908
  return (0, import_react10.createElement)(
4767
4909
  DropboxAuthContext.Provider,
4768
4910
  {
@@ -4771,7 +4913,8 @@ function DropboxAuthProvider({
4771
4913
  isAuthenticated: !!accessToken,
4772
4914
  isConfigured,
4773
4915
  requestAccess,
4774
- logout
4916
+ logout,
4917
+ refreshToken: refreshTokenFn
4775
4918
  }
4776
4919
  },
4777
4920
  children
@@ -4798,25 +4941,25 @@ function useDropboxBackup(options) {
4798
4941
  const {
4799
4942
  accessToken: dropboxToken,
4800
4943
  isConfigured: isDropboxConfigured,
4801
- requestAccess: requestDropboxAccess2
4944
+ requestAccess: requestDropboxAccess
4802
4945
  } = useDropboxAuth();
4803
4946
  const deps = (0, import_react11.useMemo)(
4804
4947
  () => ({
4805
- requestDropboxAccess: requestDropboxAccess2,
4948
+ requestDropboxAccess,
4806
4949
  requestEncryptionKey: requestEncryptionKey2,
4807
4950
  exportConversation,
4808
4951
  importConversation
4809
4952
  }),
4810
- [requestDropboxAccess2, requestEncryptionKey2, exportConversation, importConversation]
4953
+ [requestDropboxAccess, requestEncryptionKey2, exportConversation, importConversation]
4811
4954
  );
4812
4955
  const ensureToken = (0, import_react11.useCallback)(async () => {
4813
4956
  if (dropboxToken) return dropboxToken;
4814
4957
  try {
4815
- return await requestDropboxAccess2();
4958
+ return await requestDropboxAccess();
4816
4959
  } catch {
4817
4960
  return null;
4818
4961
  }
4819
- }, [dropboxToken, requestDropboxAccess2]);
4962
+ }, [dropboxToken, requestDropboxAccess]);
4820
4963
  const backup = (0, import_react11.useCallback)(
4821
4964
  async (backupOptions) => {
4822
4965
  if (!userAddress) {
@@ -4876,9 +5019,241 @@ function useDropboxBackup(options) {
4876
5019
  };
4877
5020
  }
4878
5021
 
4879
- // src/react/useGoogleDriveBackup.ts
5022
+ // src/react/useGoogleDriveAuth.ts
4880
5023
  var import_react12 = require("react");
4881
5024
 
5025
+ // src/lib/backup/google/auth.ts
5026
+ var PROVIDER2 = "google-drive";
5027
+ var CODE_STORAGE_KEY = "google_oauth_state";
5028
+ var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
5029
+ var DRIVE_SCOPES = [
5030
+ "https://www.googleapis.com/auth/drive.file"
5031
+ // Access to files created by the app
5032
+ ].join(" ");
5033
+ function getRedirectUri2(callbackPath) {
5034
+ if (typeof window === "undefined") return "";
5035
+ return `${window.location.origin}${callbackPath}`;
5036
+ }
5037
+ function generateState2() {
5038
+ const array = new Uint8Array(16);
5039
+ crypto.getRandomValues(array);
5040
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
5041
+ ""
5042
+ );
5043
+ }
5044
+ function storeOAuthState2(state) {
5045
+ if (typeof window === "undefined") return;
5046
+ sessionStorage.setItem(CODE_STORAGE_KEY, state);
5047
+ }
5048
+ function getAndClearOAuthState2() {
5049
+ if (typeof window === "undefined") return null;
5050
+ const state = sessionStorage.getItem(CODE_STORAGE_KEY);
5051
+ sessionStorage.removeItem(CODE_STORAGE_KEY);
5052
+ return state;
5053
+ }
5054
+ function isGoogleDriveCallback() {
5055
+ if (typeof window === "undefined") return false;
5056
+ const url = new URL(window.location.href);
5057
+ const code = url.searchParams.get("code");
5058
+ const state = url.searchParams.get("state");
5059
+ const storedState = sessionStorage.getItem(CODE_STORAGE_KEY);
5060
+ return !!code && !!state && state === storedState;
5061
+ }
5062
+ async function handleGoogleDriveCallback(callbackPath, apiClient) {
5063
+ if (typeof window === "undefined") return null;
5064
+ const url = new URL(window.location.href);
5065
+ const code = url.searchParams.get("code");
5066
+ const state = url.searchParams.get("state");
5067
+ const storedState = getAndClearOAuthState2();
5068
+ if (!code || !state || state !== storedState) {
5069
+ return null;
5070
+ }
5071
+ try {
5072
+ const response = await postAuthOauthByProviderExchange({
5073
+ client: apiClient,
5074
+ path: { provider: PROVIDER2 },
5075
+ body: {
5076
+ code,
5077
+ redirect_uri: getRedirectUri2(callbackPath)
5078
+ }
5079
+ });
5080
+ if (!response.data?.access_token) {
5081
+ throw new Error("No access token in response");
5082
+ }
5083
+ const tokenData = tokenResponseToStoredData(
5084
+ response.data.access_token,
5085
+ response.data.expires_in,
5086
+ response.data.refresh_token,
5087
+ response.data.scope
5088
+ );
5089
+ storeTokenData(PROVIDER2, tokenData);
5090
+ window.history.replaceState({}, "", window.location.pathname);
5091
+ return response.data.access_token;
5092
+ } catch {
5093
+ return null;
5094
+ }
5095
+ }
5096
+ async function refreshGoogleDriveToken(apiClient) {
5097
+ const refreshToken = getRefreshToken(PROVIDER2);
5098
+ if (!refreshToken) return null;
5099
+ try {
5100
+ const response = await postAuthOauthByProviderRefresh({
5101
+ client: apiClient,
5102
+ path: { provider: PROVIDER2 },
5103
+ body: { refresh_token: refreshToken }
5104
+ });
5105
+ if (!response.data?.access_token) {
5106
+ throw new Error("No access token in refresh response");
5107
+ }
5108
+ const currentData = getStoredTokenData(PROVIDER2);
5109
+ const tokenData = tokenResponseToStoredData(
5110
+ response.data.access_token,
5111
+ response.data.expires_in,
5112
+ response.data.refresh_token ?? currentData?.refreshToken,
5113
+ response.data.scope ?? currentData?.scope
5114
+ );
5115
+ storeTokenData(PROVIDER2, tokenData);
5116
+ return response.data.access_token;
5117
+ } catch {
5118
+ clearTokenData(PROVIDER2);
5119
+ return null;
5120
+ }
5121
+ }
5122
+ async function revokeGoogleDriveToken(apiClient) {
5123
+ const tokenData = getStoredTokenData(PROVIDER2);
5124
+ if (!tokenData) return;
5125
+ try {
5126
+ const tokenToRevoke = tokenData.refreshToken ?? tokenData.accessToken;
5127
+ await postAuthOauthByProviderRevoke({
5128
+ client: apiClient,
5129
+ path: { provider: PROVIDER2 },
5130
+ body: { token: tokenToRevoke }
5131
+ });
5132
+ } catch {
5133
+ } finally {
5134
+ clearTokenData(PROVIDER2);
5135
+ }
5136
+ }
5137
+ async function getGoogleDriveAccessToken(apiClient) {
5138
+ const validToken = getValidAccessToken(PROVIDER2);
5139
+ if (validToken) return validToken;
5140
+ return refreshGoogleDriveToken(apiClient);
5141
+ }
5142
+ async function startGoogleDriveAuth(clientId, callbackPath) {
5143
+ const state = generateState2();
5144
+ storeOAuthState2(state);
5145
+ const params = new URLSearchParams({
5146
+ client_id: clientId,
5147
+ redirect_uri: getRedirectUri2(callbackPath),
5148
+ response_type: "code",
5149
+ scope: DRIVE_SCOPES,
5150
+ state,
5151
+ access_type: "offline",
5152
+ // Request refresh token
5153
+ prompt: "consent"
5154
+ // Force consent to always get refresh token
5155
+ });
5156
+ window.location.href = `${GOOGLE_AUTH_URL}?${params.toString()}`;
5157
+ return new Promise(() => {
5158
+ });
5159
+ }
5160
+ function getGoogleDriveStoredToken() {
5161
+ return getValidAccessToken(PROVIDER2);
5162
+ }
5163
+ function clearGoogleDriveToken() {
5164
+ clearTokenData(PROVIDER2);
5165
+ }
5166
+ function hasGoogleDriveCredentials() {
5167
+ const data = getStoredTokenData(PROVIDER2);
5168
+ return !!(data?.accessToken || data?.refreshToken);
5169
+ }
5170
+
5171
+ // src/react/useGoogleDriveAuth.ts
5172
+ var GoogleDriveAuthContext = (0, import_react12.createContext)(null);
5173
+ function GoogleDriveAuthProvider({
5174
+ clientId,
5175
+ callbackPath = "/auth/google/callback",
5176
+ apiClient,
5177
+ children
5178
+ }) {
5179
+ const [accessToken, setAccessToken] = (0, import_react12.useState)(null);
5180
+ const isConfigured = !!clientId;
5181
+ (0, import_react12.useEffect)(() => {
5182
+ const checkStoredToken = async () => {
5183
+ if (hasGoogleDriveCredentials()) {
5184
+ const token = await getGoogleDriveAccessToken(apiClient);
5185
+ if (token) {
5186
+ setAccessToken(token);
5187
+ }
5188
+ }
5189
+ };
5190
+ checkStoredToken();
5191
+ }, [apiClient]);
5192
+ (0, import_react12.useEffect)(() => {
5193
+ if (!isConfigured) return;
5194
+ const handleCallback = async () => {
5195
+ if (isGoogleDriveCallback()) {
5196
+ const token = await handleGoogleDriveCallback(callbackPath, apiClient);
5197
+ if (token) {
5198
+ setAccessToken(token);
5199
+ }
5200
+ }
5201
+ };
5202
+ handleCallback();
5203
+ }, [callbackPath, isConfigured, apiClient]);
5204
+ const refreshTokenFn = (0, import_react12.useCallback)(async () => {
5205
+ const token = await getGoogleDriveAccessToken(apiClient);
5206
+ if (token) {
5207
+ setAccessToken(token);
5208
+ }
5209
+ return token;
5210
+ }, [apiClient]);
5211
+ const requestAccess = (0, import_react12.useCallback)(async () => {
5212
+ if (!isConfigured || !clientId) {
5213
+ throw new Error("Google Drive is not configured");
5214
+ }
5215
+ if (accessToken) {
5216
+ return accessToken;
5217
+ }
5218
+ const storedToken = await getGoogleDriveAccessToken(apiClient);
5219
+ if (storedToken) {
5220
+ setAccessToken(storedToken);
5221
+ return storedToken;
5222
+ }
5223
+ return startGoogleDriveAuth(clientId, callbackPath);
5224
+ }, [accessToken, clientId, callbackPath, isConfigured, apiClient]);
5225
+ const logout = (0, import_react12.useCallback)(async () => {
5226
+ await revokeGoogleDriveToken(apiClient);
5227
+ setAccessToken(null);
5228
+ }, [apiClient]);
5229
+ return (0, import_react12.createElement)(
5230
+ GoogleDriveAuthContext.Provider,
5231
+ {
5232
+ value: {
5233
+ accessToken,
5234
+ isAuthenticated: !!accessToken,
5235
+ isConfigured,
5236
+ requestAccess,
5237
+ logout,
5238
+ refreshToken: refreshTokenFn
5239
+ }
5240
+ },
5241
+ children
5242
+ );
5243
+ }
5244
+ function useGoogleDriveAuth() {
5245
+ const context = (0, import_react12.useContext)(GoogleDriveAuthContext);
5246
+ if (!context) {
5247
+ throw new Error(
5248
+ "useGoogleDriveAuth must be used within GoogleDriveAuthProvider"
5249
+ );
5250
+ }
5251
+ return context;
5252
+ }
5253
+
5254
+ // src/react/useGoogleDriveBackup.ts
5255
+ var import_react13 = require("react");
5256
+
4882
5257
  // src/lib/backup/google/api.ts
4883
5258
  var DRIVE_API_URL = "https://www.googleapis.com/drive/v3";
4884
5259
  var DRIVE_UPLOAD_URL = "https://www.googleapis.com/upload/drive/v3";
@@ -5184,37 +5559,35 @@ function useGoogleDriveBackup(options) {
5184
5559
  const {
5185
5560
  database,
5186
5561
  userAddress,
5187
- accessToken,
5188
- requestDriveAccess,
5189
5562
  requestEncryptionKey: requestEncryptionKey2,
5190
5563
  exportConversation,
5191
5564
  importConversation,
5192
5565
  rootFolder = DEFAULT_ROOT_FOLDER,
5193
5566
  conversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
5194
5567
  } = options;
5195
- const deps = (0, import_react12.useMemo)(
5568
+ const {
5569
+ accessToken: driveToken,
5570
+ isConfigured: isDriveConfigured,
5571
+ requestAccess: requestDriveAccess
5572
+ } = useGoogleDriveAuth();
5573
+ const deps = (0, import_react13.useMemo)(
5196
5574
  () => ({
5197
5575
  requestDriveAccess,
5198
5576
  requestEncryptionKey: requestEncryptionKey2,
5199
5577
  exportConversation,
5200
5578
  importConversation
5201
5579
  }),
5202
- [
5203
- requestDriveAccess,
5204
- requestEncryptionKey2,
5205
- exportConversation,
5206
- importConversation
5207
- ]
5580
+ [requestDriveAccess, requestEncryptionKey2, exportConversation, importConversation]
5208
5581
  );
5209
- const ensureToken = (0, import_react12.useCallback)(async () => {
5210
- if (accessToken) return accessToken;
5582
+ const ensureToken = (0, import_react13.useCallback)(async () => {
5583
+ if (driveToken) return driveToken;
5211
5584
  try {
5212
5585
  return await requestDriveAccess();
5213
5586
  } catch {
5214
5587
  return null;
5215
5588
  }
5216
- }, [accessToken, requestDriveAccess]);
5217
- const backup = (0, import_react12.useCallback)(
5589
+ }, [driveToken, requestDriveAccess]);
5590
+ const backup = (0, import_react13.useCallback)(
5218
5591
  async (backupOptions) => {
5219
5592
  if (!userAddress) {
5220
5593
  return { error: "Please sign in to backup to Google Drive" };
@@ -5241,7 +5614,7 @@ function useGoogleDriveBackup(options) {
5241
5614
  },
5242
5615
  [database, userAddress, ensureToken, deps, rootFolder, conversationsFolder]
5243
5616
  );
5244
- const restore = (0, import_react12.useCallback)(
5617
+ const restore = (0, import_react13.useCallback)(
5245
5618
  async (restoreOptions) => {
5246
5619
  if (!userAddress) {
5247
5620
  return { error: "Please sign in to restore from Google Drive" };
@@ -5270,23 +5643,1117 @@ function useGoogleDriveBackup(options) {
5270
5643
  return {
5271
5644
  backup,
5272
5645
  restore,
5273
- isAuthenticated: !!accessToken
5646
+ isConfigured: isDriveConfigured,
5647
+ isAuthenticated: !!driveToken
5648
+ };
5649
+ }
5650
+
5651
+ // src/react/useICloudAuth.ts
5652
+ var import_react14 = require("react");
5653
+
5654
+ // src/lib/backup/icloud/api.ts
5655
+ var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
5656
+ var DEFAULT_BACKUP_FOLDER2 = "conversations";
5657
+ var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
5658
+ var RECORD_TYPE = "ConversationBackup";
5659
+ var cloudKitLoadPromise = null;
5660
+ function isCloudKitAvailable() {
5661
+ return typeof window !== "undefined" && !!window.CloudKit;
5662
+ }
5663
+ async function loadCloudKit() {
5664
+ if (typeof window === "undefined") {
5665
+ throw new Error("CloudKit JS can only be loaded in browser environment");
5666
+ }
5667
+ if (window.CloudKit) {
5668
+ return;
5669
+ }
5670
+ if (cloudKitLoadPromise) {
5671
+ return cloudKitLoadPromise;
5672
+ }
5673
+ cloudKitLoadPromise = new Promise((resolve, reject) => {
5674
+ const script = document.createElement("script");
5675
+ script.src = CLOUDKIT_JS_URL;
5676
+ script.async = true;
5677
+ script.onload = () => {
5678
+ if (window.CloudKit) {
5679
+ resolve();
5680
+ } else {
5681
+ reject(new Error("CloudKit JS loaded but CloudKit object not found"));
5682
+ }
5683
+ };
5684
+ script.onerror = () => {
5685
+ cloudKitLoadPromise = null;
5686
+ reject(new Error("Failed to load CloudKit JS"));
5687
+ };
5688
+ document.head.appendChild(script);
5689
+ });
5690
+ return cloudKitLoadPromise;
5691
+ }
5692
+ async function ensureCloudKitLoaded() {
5693
+ if (!isCloudKitAvailable()) {
5694
+ await loadCloudKit();
5695
+ }
5696
+ }
5697
+ async function configureCloudKit(config) {
5698
+ await ensureCloudKitLoaded();
5699
+ ensureAuthElements();
5700
+ window.CloudKit.configure({
5701
+ containers: [
5702
+ {
5703
+ containerIdentifier: config.containerIdentifier,
5704
+ apiTokenAuth: {
5705
+ apiToken: config.apiToken,
5706
+ persist: true,
5707
+ signInButton: {
5708
+ id: "apple-sign-in-button",
5709
+ theme: "black"
5710
+ },
5711
+ signOutButton: {
5712
+ id: "apple-sign-out-button",
5713
+ theme: "black"
5714
+ }
5715
+ },
5716
+ environment: config.environment
5717
+ }
5718
+ ]
5719
+ });
5720
+ }
5721
+ async function getContainer() {
5722
+ await ensureCloudKitLoaded();
5723
+ return window.CloudKit.getDefaultContainer();
5724
+ }
5725
+ function ensureAuthElements() {
5726
+ let signInButton = document.getElementById("apple-sign-in-button");
5727
+ let signOutButton = document.getElementById("apple-sign-out-button");
5728
+ if (!signInButton) {
5729
+ signInButton = document.createElement("div");
5730
+ signInButton.id = "apple-sign-in-button";
5731
+ signInButton.style.position = "fixed";
5732
+ signInButton.style.top = "-9999px";
5733
+ signInButton.style.left = "-9999px";
5734
+ document.body.appendChild(signInButton);
5735
+ }
5736
+ if (!signOutButton) {
5737
+ signOutButton = document.createElement("div");
5738
+ signOutButton.id = "apple-sign-out-button";
5739
+ signOutButton.style.position = "fixed";
5740
+ signOutButton.style.top = "-9999px";
5741
+ signOutButton.style.left = "-9999px";
5742
+ document.body.appendChild(signOutButton);
5743
+ }
5744
+ return { signIn: signInButton, signOut: signOutButton };
5745
+ }
5746
+ async function authenticateICloud() {
5747
+ const container = await getContainer();
5748
+ ensureAuthElements();
5749
+ return container.setUpAuth();
5750
+ }
5751
+ async function requestICloudSignIn() {
5752
+ const container = await getContainer();
5753
+ const { signIn } = ensureAuthElements();
5754
+ const existingUser = await container.setUpAuth();
5755
+ if (existingUser) {
5756
+ return existingUser;
5757
+ }
5758
+ console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
5759
+ console.log("[CloudKit] Sign-in container children:", signIn.children.length);
5760
+ const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
5761
+ console.log("[CloudKit] Found button element:", appleButton);
5762
+ if (appleButton) {
5763
+ console.log("[CloudKit] Clicking button...");
5764
+ appleButton.click();
5765
+ } else {
5766
+ const anyClickable = signIn.firstElementChild;
5767
+ if (anyClickable) {
5768
+ console.log("[CloudKit] Clicking first child element:", anyClickable);
5769
+ anyClickable.click();
5770
+ }
5771
+ }
5772
+ return container.whenUserSignsIn();
5773
+ }
5774
+ async function uploadFileToICloud(filename, content) {
5775
+ const container = await getContainer();
5776
+ const database = container.privateCloudDatabase;
5777
+ const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
5778
+ const arrayBuffer = await content.arrayBuffer();
5779
+ const base64Data = btoa(
5780
+ String.fromCharCode(...new Uint8Array(arrayBuffer))
5781
+ );
5782
+ const record = {
5783
+ recordType: RECORD_TYPE,
5784
+ recordName,
5785
+ fields: {
5786
+ filename: { value: filename },
5787
+ data: { value: base64Data },
5788
+ size: { value: content.size },
5789
+ contentType: { value: content.type || "application/json" }
5790
+ }
5791
+ };
5792
+ const response = await database.saveRecords(record);
5793
+ if (!response.records || response.records.length === 0) {
5794
+ throw new Error("Failed to upload file to iCloud");
5795
+ }
5796
+ const savedRecord = response.records[0];
5797
+ return {
5798
+ recordName: savedRecord.recordName,
5799
+ filename,
5800
+ modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
5801
+ size: content.size
5802
+ };
5803
+ }
5804
+ async function listICloudFiles() {
5805
+ const container = await getContainer();
5806
+ const database = container.privateCloudDatabase;
5807
+ const query = {
5808
+ recordType: RECORD_TYPE
5809
+ // Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
5810
+ // For now, we skip sorting and sort client-side after fetching
5811
+ };
5812
+ const allRecords = [];
5813
+ let response = await database.performQuery(query);
5814
+ if (response.records) {
5815
+ allRecords.push(...response.records);
5816
+ }
5817
+ while (response.continuationMarker) {
5818
+ break;
5819
+ }
5820
+ const files = allRecords.map((record) => ({
5821
+ recordName: record.recordName,
5822
+ filename: record.fields.filename?.value ?? "",
5823
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5824
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5825
+ }));
5826
+ return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
5827
+ }
5828
+ async function downloadICloudFile(recordName) {
5829
+ const container = await getContainer();
5830
+ const database = container.privateCloudDatabase;
5831
+ const response = await database.fetchRecords([{ recordName }], {
5832
+ desiredKeys: ["filename", "data", "contentType"]
5833
+ });
5834
+ if (!response.records || response.records.length === 0) {
5835
+ throw new Error(`File not found: ${recordName}`);
5836
+ }
5837
+ const record = response.records[0];
5838
+ const dataField = record.fields.data?.value;
5839
+ if (!dataField) {
5840
+ throw new Error("No data in record");
5841
+ }
5842
+ if (typeof dataField === "string") {
5843
+ const binaryString = atob(dataField);
5844
+ const bytes = new Uint8Array(binaryString.length);
5845
+ for (let i = 0; i < binaryString.length; i++) {
5846
+ bytes[i] = binaryString.charCodeAt(i);
5847
+ }
5848
+ return new Blob([bytes], { type: "application/json" });
5849
+ }
5850
+ if (typeof dataField === "object" && "downloadURL" in dataField) {
5851
+ const fetchResponse = await fetch(
5852
+ dataField.downloadURL
5853
+ );
5854
+ if (!fetchResponse.ok) {
5855
+ throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
5856
+ }
5857
+ return fetchResponse.blob();
5858
+ }
5859
+ throw new Error("Unknown data format in iCloud record");
5860
+ }
5861
+ async function findICloudFile(filename) {
5862
+ const container = await getContainer();
5863
+ const database = container.privateCloudDatabase;
5864
+ const query = {
5865
+ recordType: RECORD_TYPE,
5866
+ filterBy: [
5867
+ {
5868
+ fieldName: "filename",
5869
+ comparator: "EQUALS",
5870
+ fieldValue: { value: filename }
5871
+ }
5872
+ ]
5873
+ };
5874
+ const response = await database.performQuery(query);
5875
+ if (!response.records || response.records.length === 0) {
5876
+ return null;
5877
+ }
5878
+ const record = response.records[0];
5879
+ return {
5880
+ recordName: record.recordName,
5881
+ filename: record.fields.filename?.value ?? "",
5882
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5883
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5884
+ };
5885
+ }
5886
+
5887
+ // src/react/useICloudAuth.ts
5888
+ var ICloudAuthContext = (0, import_react14.createContext)(null);
5889
+ function ICloudAuthProvider({
5890
+ apiToken,
5891
+ containerIdentifier = DEFAULT_CONTAINER_ID,
5892
+ environment = "production",
5893
+ children
5894
+ }) {
5895
+ const [isAuthenticated, setIsAuthenticated] = (0, import_react14.useState)(false);
5896
+ const [userRecordName, setUserRecordName] = (0, import_react14.useState)(null);
5897
+ const [isAvailable, setIsAvailable] = (0, import_react14.useState)(false);
5898
+ const [isConfigured, setIsConfigured] = (0, import_react14.useState)(false);
5899
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
5900
+ (0, import_react14.useEffect)(() => {
5901
+ if (!apiToken || typeof window === "undefined") {
5902
+ return;
5903
+ }
5904
+ const initCloudKit = async () => {
5905
+ setIsLoading(true);
5906
+ try {
5907
+ await loadCloudKit();
5908
+ setIsAvailable(true);
5909
+ const config = {
5910
+ containerIdentifier,
5911
+ apiToken,
5912
+ environment
5913
+ };
5914
+ await configureCloudKit(config);
5915
+ setIsConfigured(true);
5916
+ try {
5917
+ const userIdentity = await authenticateICloud();
5918
+ if (userIdentity) {
5919
+ setIsAuthenticated(true);
5920
+ setUserRecordName(userIdentity.userRecordName);
5921
+ }
5922
+ } catch {
5923
+ }
5924
+ } catch {
5925
+ setIsAvailable(false);
5926
+ setIsConfigured(false);
5927
+ } finally {
5928
+ setIsLoading(false);
5929
+ }
5930
+ };
5931
+ initCloudKit();
5932
+ }, [apiToken, containerIdentifier, environment]);
5933
+ const requestAccess = (0, import_react14.useCallback)(async () => {
5934
+ if (!isConfigured) {
5935
+ throw new Error("iCloud is not configured");
5936
+ }
5937
+ if (isAuthenticated) {
5938
+ return;
5939
+ }
5940
+ try {
5941
+ const userIdentity = await requestICloudSignIn();
5942
+ setIsAuthenticated(true);
5943
+ setUserRecordName(userIdentity.userRecordName);
5944
+ } catch (err) {
5945
+ throw new Error(
5946
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
5947
+ );
5948
+ }
5949
+ }, [isAuthenticated, isConfigured]);
5950
+ const logout = (0, import_react14.useCallback)(() => {
5951
+ setIsAuthenticated(false);
5952
+ setUserRecordName(null);
5953
+ }, []);
5954
+ return (0, import_react14.createElement)(
5955
+ ICloudAuthContext.Provider,
5956
+ {
5957
+ value: {
5958
+ isAuthenticated,
5959
+ isConfigured,
5960
+ isAvailable,
5961
+ userRecordName,
5962
+ requestAccess,
5963
+ logout
5964
+ }
5965
+ },
5966
+ children
5967
+ );
5968
+ }
5969
+ function useICloudAuth() {
5970
+ const context = (0, import_react14.useContext)(ICloudAuthContext);
5971
+ if (!context) {
5972
+ throw new Error("useICloudAuth must be used within ICloudAuthProvider");
5973
+ }
5974
+ return context;
5975
+ }
5976
+ function hasICloudCredentials() {
5977
+ return isCloudKitAvailable();
5978
+ }
5979
+ function clearICloudAuth() {
5980
+ }
5981
+
5982
+ // src/react/useICloudBackup.ts
5983
+ var import_react15 = require("react");
5984
+
5985
+ // src/lib/backup/icloud/backup.ts
5986
+ var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
5987
+ async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
5988
+ try {
5989
+ await deps.requestEncryptionKey(userAddress);
5990
+ const filename = `${conversationId}.json`;
5991
+ const existingFile = await findICloudFile(filename);
5992
+ if (existingFile) {
5993
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
5994
+ const conversationsCollection = database.get("conversations");
5995
+ const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
5996
+ if (records.length > 0) {
5997
+ const conversation = conversationToStored(records[0]);
5998
+ const localUpdated = conversation.updatedAt.getTime();
5999
+ const remoteModified = existingFile.modifiedAt.getTime();
6000
+ if (localUpdated <= remoteModified) {
6001
+ return "skipped";
6002
+ }
6003
+ }
6004
+ }
6005
+ const exportResult = await deps.exportConversation(
6006
+ conversationId,
6007
+ userAddress
6008
+ );
6009
+ if (!exportResult.success || !exportResult.blob) {
6010
+ return "failed";
6011
+ }
6012
+ await uploadFileToICloud(filename, exportResult.blob);
6013
+ return "uploaded";
6014
+ } catch (err) {
6015
+ if (isAuthError3(err) && !_retried) {
6016
+ try {
6017
+ await deps.requestICloudAccess();
6018
+ return pushConversationToICloud(
6019
+ database,
6020
+ conversationId,
6021
+ userAddress,
6022
+ deps,
6023
+ true
6024
+ );
6025
+ } catch {
6026
+ return "failed";
6027
+ }
6028
+ }
6029
+ return "failed";
6030
+ }
6031
+ }
6032
+ async function performICloudExport(database, userAddress, deps, onProgress) {
6033
+ await deps.requestEncryptionKey(userAddress);
6034
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
6035
+ const conversationsCollection = database.get("conversations");
6036
+ const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
6037
+ const conversations = records.map(conversationToStored);
6038
+ const total = conversations.length;
6039
+ if (total === 0) {
6040
+ return { success: true, uploaded: 0, skipped: 0, total: 0 };
6041
+ }
6042
+ let uploaded = 0;
6043
+ let skipped = 0;
6044
+ for (let i = 0; i < conversations.length; i++) {
6045
+ const conv = conversations[i];
6046
+ onProgress?.(i + 1, total);
6047
+ const result = await pushConversationToICloud(
6048
+ database,
6049
+ conv.conversationId,
6050
+ userAddress,
6051
+ deps
6052
+ );
6053
+ if (result === "uploaded") uploaded++;
6054
+ if (result === "skipped") skipped++;
6055
+ }
6056
+ return { success: true, uploaded, skipped, total };
6057
+ }
6058
+ async function performICloudImport(userAddress, deps, onProgress) {
6059
+ await deps.requestEncryptionKey(userAddress);
6060
+ const remoteFiles = await listICloudFiles();
6061
+ if (remoteFiles.length === 0) {
6062
+ return {
6063
+ success: false,
6064
+ restored: 0,
6065
+ failed: 0,
6066
+ total: 0,
6067
+ noBackupsFound: true
6068
+ };
6069
+ }
6070
+ const jsonFiles = remoteFiles.filter(
6071
+ (file) => file.filename.endsWith(".json")
6072
+ );
6073
+ const total = jsonFiles.length;
6074
+ let restored = 0;
6075
+ let failed = 0;
6076
+ for (let i = 0; i < jsonFiles.length; i++) {
6077
+ const file = jsonFiles[i];
6078
+ onProgress?.(i + 1, total);
6079
+ try {
6080
+ const blob = await downloadICloudFile(file.recordName);
6081
+ const result = await deps.importConversation(blob, userAddress);
6082
+ if (result.success) {
6083
+ restored++;
6084
+ } else {
6085
+ failed++;
6086
+ }
6087
+ } catch (err) {
6088
+ if (isAuthError3(err)) {
6089
+ try {
6090
+ await deps.requestICloudAccess();
6091
+ const blob = await downloadICloudFile(file.recordName);
6092
+ const result = await deps.importConversation(blob, userAddress);
6093
+ if (result.success) {
6094
+ restored++;
6095
+ } else {
6096
+ failed++;
6097
+ }
6098
+ } catch {
6099
+ failed++;
6100
+ }
6101
+ } else {
6102
+ failed++;
6103
+ }
6104
+ }
6105
+ }
6106
+ return { success: true, restored, failed, total };
6107
+ }
6108
+
6109
+ // src/react/useICloudBackup.ts
6110
+ function useICloudBackup(options) {
6111
+ const {
6112
+ database,
6113
+ userAddress,
6114
+ requestEncryptionKey: requestEncryptionKey2,
6115
+ exportConversation,
6116
+ importConversation
6117
+ } = options;
6118
+ const {
6119
+ isAuthenticated,
6120
+ isConfigured,
6121
+ isAvailable,
6122
+ requestAccess
6123
+ } = useICloudAuth();
6124
+ const deps = (0, import_react15.useMemo)(
6125
+ () => ({
6126
+ requestICloudAccess: requestAccess,
6127
+ requestEncryptionKey: requestEncryptionKey2,
6128
+ exportConversation,
6129
+ importConversation
6130
+ }),
6131
+ [requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6132
+ );
6133
+ const ensureAuthenticated = (0, import_react15.useCallback)(async () => {
6134
+ if (isAuthenticated) return true;
6135
+ try {
6136
+ await requestAccess();
6137
+ return true;
6138
+ } catch {
6139
+ return false;
6140
+ }
6141
+ }, [isAuthenticated, requestAccess]);
6142
+ const backup = (0, import_react15.useCallback)(
6143
+ async (backupOptions) => {
6144
+ if (!userAddress) {
6145
+ return { error: "Please sign in to backup to iCloud" };
6146
+ }
6147
+ if (!isAvailable) {
6148
+ return { error: "CloudKit JS is not loaded" };
6149
+ }
6150
+ if (!isConfigured) {
6151
+ return { error: "iCloud is not configured" };
6152
+ }
6153
+ const authenticated = await ensureAuthenticated();
6154
+ if (!authenticated) {
6155
+ return { error: "iCloud access denied" };
6156
+ }
6157
+ try {
6158
+ return await performICloudExport(
6159
+ database,
6160
+ userAddress,
6161
+ deps,
6162
+ backupOptions?.onProgress
6163
+ );
6164
+ } catch (err) {
6165
+ return {
6166
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6167
+ };
6168
+ }
6169
+ },
6170
+ [database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6171
+ );
6172
+ const restore = (0, import_react15.useCallback)(
6173
+ async (restoreOptions) => {
6174
+ if (!userAddress) {
6175
+ return { error: "Please sign in to restore from iCloud" };
6176
+ }
6177
+ if (!isAvailable) {
6178
+ return { error: "CloudKit JS is not loaded" };
6179
+ }
6180
+ if (!isConfigured) {
6181
+ return { error: "iCloud is not configured" };
6182
+ }
6183
+ const authenticated = await ensureAuthenticated();
6184
+ if (!authenticated) {
6185
+ return { error: "iCloud access denied" };
6186
+ }
6187
+ try {
6188
+ return await performICloudImport(
6189
+ userAddress,
6190
+ deps,
6191
+ restoreOptions?.onProgress
6192
+ );
6193
+ } catch (err) {
6194
+ return {
6195
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6196
+ };
6197
+ }
6198
+ },
6199
+ [userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6200
+ );
6201
+ return {
6202
+ backup,
6203
+ restore,
6204
+ isConfigured,
6205
+ isAuthenticated,
6206
+ isAvailable
6207
+ };
6208
+ }
6209
+
6210
+ // src/react/useBackupAuth.ts
6211
+ var import_react16 = require("react");
6212
+ var BackupAuthContext = (0, import_react16.createContext)(null);
6213
+ function BackupAuthProvider({
6214
+ dropboxAppKey,
6215
+ dropboxCallbackPath = "/auth/dropbox/callback",
6216
+ googleClientId,
6217
+ googleCallbackPath = "/auth/google/callback",
6218
+ icloudApiToken,
6219
+ icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
6220
+ icloudEnvironment = "production",
6221
+ apiClient,
6222
+ children
6223
+ }) {
6224
+ const [dropboxToken, setDropboxToken] = (0, import_react16.useState)(null);
6225
+ const isDropboxConfigured = !!dropboxAppKey;
6226
+ const [googleToken, setGoogleToken] = (0, import_react16.useState)(null);
6227
+ const isGoogleConfigured = !!googleClientId;
6228
+ const [icloudAuthenticated, setIcloudAuthenticated] = (0, import_react16.useState)(false);
6229
+ const [icloudUserRecordName, setIcloudUserRecordName] = (0, import_react16.useState)(null);
6230
+ const [isIcloudAvailable, setIsIcloudAvailable] = (0, import_react16.useState)(false);
6231
+ const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
6232
+ (0, import_react16.useEffect)(() => {
6233
+ const checkStoredTokens = async () => {
6234
+ if (hasDropboxCredentials()) {
6235
+ const token = await getDropboxAccessToken(apiClient);
6236
+ if (token) {
6237
+ setDropboxToken(token);
6238
+ }
6239
+ }
6240
+ if (hasGoogleDriveCredentials()) {
6241
+ const token = await getGoogleDriveAccessToken(apiClient);
6242
+ if (token) {
6243
+ setGoogleToken(token);
6244
+ }
6245
+ }
6246
+ };
6247
+ checkStoredTokens();
6248
+ }, [apiClient]);
6249
+ (0, import_react16.useEffect)(() => {
6250
+ if (!icloudApiToken || typeof window === "undefined") {
6251
+ return;
6252
+ }
6253
+ const initCloudKit = async () => {
6254
+ try {
6255
+ await loadCloudKit();
6256
+ setIsIcloudAvailable(true);
6257
+ const config = {
6258
+ containerIdentifier: icloudContainerIdentifier,
6259
+ apiToken: icloudApiToken,
6260
+ environment: icloudEnvironment
6261
+ };
6262
+ await configureCloudKit(config);
6263
+ try {
6264
+ const userIdentity = await authenticateICloud();
6265
+ if (userIdentity) {
6266
+ setIcloudAuthenticated(true);
6267
+ setIcloudUserRecordName(userIdentity.userRecordName);
6268
+ }
6269
+ } catch {
6270
+ }
6271
+ } catch {
6272
+ setIsIcloudAvailable(false);
6273
+ }
6274
+ };
6275
+ initCloudKit();
6276
+ }, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
6277
+ (0, import_react16.useEffect)(() => {
6278
+ if (!isDropboxConfigured) return;
6279
+ const handleCallback = async () => {
6280
+ if (isDropboxCallback()) {
6281
+ const token = await handleDropboxCallback(
6282
+ dropboxCallbackPath,
6283
+ apiClient
6284
+ );
6285
+ if (token) {
6286
+ setDropboxToken(token);
6287
+ }
6288
+ }
6289
+ };
6290
+ handleCallback();
6291
+ }, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
6292
+ (0, import_react16.useEffect)(() => {
6293
+ if (!isGoogleConfigured) return;
6294
+ const handleCallback = async () => {
6295
+ if (isGoogleDriveCallback()) {
6296
+ const token = await handleGoogleDriveCallback(
6297
+ googleCallbackPath,
6298
+ apiClient
6299
+ );
6300
+ if (token) {
6301
+ setGoogleToken(token);
6302
+ }
6303
+ }
6304
+ };
6305
+ handleCallback();
6306
+ }, [googleCallbackPath, isGoogleConfigured, apiClient]);
6307
+ const refreshDropboxTokenFn = (0, import_react16.useCallback)(async () => {
6308
+ const token = await getDropboxAccessToken(apiClient);
6309
+ if (token) {
6310
+ setDropboxToken(token);
6311
+ }
6312
+ return token;
6313
+ }, [apiClient]);
6314
+ const requestDropboxAccess = (0, import_react16.useCallback)(async () => {
6315
+ if (!isDropboxConfigured || !dropboxAppKey) {
6316
+ throw new Error("Dropbox is not configured");
6317
+ }
6318
+ if (dropboxToken) {
6319
+ return dropboxToken;
6320
+ }
6321
+ const storedToken = await getDropboxAccessToken(apiClient);
6322
+ if (storedToken) {
6323
+ setDropboxToken(storedToken);
6324
+ return storedToken;
6325
+ }
6326
+ return startDropboxAuth(dropboxAppKey, dropboxCallbackPath);
6327
+ }, [
6328
+ dropboxToken,
6329
+ dropboxAppKey,
6330
+ dropboxCallbackPath,
6331
+ isDropboxConfigured,
6332
+ apiClient
6333
+ ]);
6334
+ const logoutDropbox = (0, import_react16.useCallback)(async () => {
6335
+ await revokeDropboxToken(apiClient);
6336
+ setDropboxToken(null);
6337
+ }, [apiClient]);
6338
+ const refreshGoogleTokenFn = (0, import_react16.useCallback)(async () => {
6339
+ const token = await getGoogleDriveAccessToken(apiClient);
6340
+ if (token) {
6341
+ setGoogleToken(token);
6342
+ }
6343
+ return token;
6344
+ }, [apiClient]);
6345
+ const requestGoogleAccess = (0, import_react16.useCallback)(async () => {
6346
+ if (!isGoogleConfigured || !googleClientId) {
6347
+ throw new Error("Google Drive is not configured");
6348
+ }
6349
+ if (googleToken) {
6350
+ return googleToken;
6351
+ }
6352
+ const storedToken = await getGoogleDriveAccessToken(apiClient);
6353
+ if (storedToken) {
6354
+ setGoogleToken(storedToken);
6355
+ return storedToken;
6356
+ }
6357
+ return startGoogleDriveAuth(googleClientId, googleCallbackPath);
6358
+ }, [
6359
+ googleToken,
6360
+ googleClientId,
6361
+ googleCallbackPath,
6362
+ isGoogleConfigured,
6363
+ apiClient
6364
+ ]);
6365
+ const logoutGoogle = (0, import_react16.useCallback)(async () => {
6366
+ await revokeGoogleDriveToken(apiClient);
6367
+ setGoogleToken(null);
6368
+ }, [apiClient]);
6369
+ const refreshIcloudTokenFn = (0, import_react16.useCallback)(async () => {
6370
+ try {
6371
+ const userIdentity = await authenticateICloud();
6372
+ if (userIdentity) {
6373
+ setIcloudAuthenticated(true);
6374
+ setIcloudUserRecordName(userIdentity.userRecordName);
6375
+ return userIdentity.userRecordName;
6376
+ }
6377
+ } catch {
6378
+ }
6379
+ return null;
6380
+ }, []);
6381
+ const requestIcloudAccess = (0, import_react16.useCallback)(async () => {
6382
+ if (!isIcloudConfigured) {
6383
+ throw new Error("iCloud is not configured");
6384
+ }
6385
+ if (icloudAuthenticated && icloudUserRecordName) {
6386
+ return icloudUserRecordName;
6387
+ }
6388
+ try {
6389
+ const userIdentity = await requestICloudSignIn();
6390
+ setIcloudAuthenticated(true);
6391
+ setIcloudUserRecordName(userIdentity.userRecordName);
6392
+ return userIdentity.userRecordName;
6393
+ } catch (err) {
6394
+ throw new Error(
6395
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
6396
+ );
6397
+ }
6398
+ }, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
6399
+ const logoutIcloud = (0, import_react16.useCallback)(async () => {
6400
+ setIcloudAuthenticated(false);
6401
+ setIcloudUserRecordName(null);
6402
+ }, []);
6403
+ const logoutAll = (0, import_react16.useCallback)(async () => {
6404
+ await Promise.all([
6405
+ isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
6406
+ isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
6407
+ isIcloudConfigured ? logoutIcloud() : Promise.resolve()
6408
+ ]);
6409
+ }, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
6410
+ const dropboxState = {
6411
+ accessToken: dropboxToken,
6412
+ isAuthenticated: !!dropboxToken,
6413
+ isConfigured: isDropboxConfigured,
6414
+ requestAccess: requestDropboxAccess,
6415
+ logout: logoutDropbox,
6416
+ refreshToken: refreshDropboxTokenFn
6417
+ };
6418
+ const googleDriveState = {
6419
+ accessToken: googleToken,
6420
+ isAuthenticated: !!googleToken,
6421
+ isConfigured: isGoogleConfigured,
6422
+ requestAccess: requestGoogleAccess,
6423
+ logout: logoutGoogle,
6424
+ refreshToken: refreshGoogleTokenFn
6425
+ };
6426
+ const icloudState = {
6427
+ accessToken: icloudUserRecordName,
6428
+ // Use userRecordName as the "token" for iCloud
6429
+ isAuthenticated: icloudAuthenticated,
6430
+ isConfigured: isIcloudConfigured,
6431
+ requestAccess: requestIcloudAccess,
6432
+ logout: logoutIcloud,
6433
+ refreshToken: refreshIcloudTokenFn
6434
+ };
6435
+ return (0, import_react16.createElement)(
6436
+ BackupAuthContext.Provider,
6437
+ {
6438
+ value: {
6439
+ dropbox: dropboxState,
6440
+ googleDrive: googleDriveState,
6441
+ icloud: icloudState,
6442
+ hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
6443
+ hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
6444
+ logoutAll
6445
+ }
6446
+ },
6447
+ children
6448
+ );
6449
+ }
6450
+ function useBackupAuth() {
6451
+ const context = (0, import_react16.useContext)(BackupAuthContext);
6452
+ if (!context) {
6453
+ throw new Error("useBackupAuth must be used within BackupAuthProvider");
6454
+ }
6455
+ return context;
6456
+ }
6457
+
6458
+ // src/react/useBackup.ts
6459
+ var import_react17 = require("react");
6460
+ function useBackup(options) {
6461
+ const {
6462
+ database,
6463
+ userAddress,
6464
+ requestEncryptionKey: requestEncryptionKey2,
6465
+ exportConversation,
6466
+ importConversation,
6467
+ dropboxFolder = DEFAULT_BACKUP_FOLDER,
6468
+ googleRootFolder = DEFAULT_ROOT_FOLDER,
6469
+ googleConversationsFolder = DEFAULT_CONVERSATIONS_FOLDER
6470
+ } = options;
6471
+ const {
6472
+ dropbox: dropboxAuth,
6473
+ googleDrive: googleDriveAuth,
6474
+ icloud: icloudAuth,
6475
+ hasAnyProvider,
6476
+ hasAnyAuthentication,
6477
+ logoutAll
6478
+ } = useBackupAuth();
6479
+ const dropboxDeps = (0, import_react17.useMemo)(
6480
+ () => ({
6481
+ requestDropboxAccess: dropboxAuth.requestAccess,
6482
+ requestEncryptionKey: requestEncryptionKey2,
6483
+ exportConversation,
6484
+ importConversation
6485
+ }),
6486
+ [dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6487
+ );
6488
+ const googleDriveDeps = (0, import_react17.useMemo)(
6489
+ () => ({
6490
+ requestDriveAccess: googleDriveAuth.requestAccess,
6491
+ requestEncryptionKey: requestEncryptionKey2,
6492
+ exportConversation,
6493
+ importConversation
6494
+ }),
6495
+ [googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6496
+ );
6497
+ const icloudDeps = (0, import_react17.useMemo)(
6498
+ () => ({
6499
+ requestICloudAccess: async () => {
6500
+ await icloudAuth.requestAccess();
6501
+ },
6502
+ requestEncryptionKey: requestEncryptionKey2,
6503
+ exportConversation,
6504
+ importConversation
6505
+ }),
6506
+ [icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6507
+ );
6508
+ const dropboxBackup = (0, import_react17.useCallback)(
6509
+ async (backupOptions) => {
6510
+ if (!userAddress) {
6511
+ return { error: "Please sign in to backup to Dropbox" };
6512
+ }
6513
+ let token = dropboxAuth.accessToken;
6514
+ if (!token) {
6515
+ try {
6516
+ token = await dropboxAuth.requestAccess();
6517
+ } catch {
6518
+ return { error: "Dropbox access denied" };
6519
+ }
6520
+ }
6521
+ try {
6522
+ return await performDropboxExport(
6523
+ database,
6524
+ userAddress,
6525
+ token,
6526
+ dropboxDeps,
6527
+ backupOptions?.onProgress,
6528
+ dropboxFolder
6529
+ );
6530
+ } catch (err) {
6531
+ return {
6532
+ error: err instanceof Error ? err.message : "Failed to backup to Dropbox"
6533
+ };
6534
+ }
6535
+ },
6536
+ [database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
6537
+ );
6538
+ const dropboxRestore = (0, import_react17.useCallback)(
6539
+ async (restoreOptions) => {
6540
+ if (!userAddress) {
6541
+ return { error: "Please sign in to restore from Dropbox" };
6542
+ }
6543
+ let token = dropboxAuth.accessToken;
6544
+ if (!token) {
6545
+ try {
6546
+ token = await dropboxAuth.requestAccess();
6547
+ } catch {
6548
+ return { error: "Dropbox access denied" };
6549
+ }
6550
+ }
6551
+ try {
6552
+ return await performDropboxImport(
6553
+ userAddress,
6554
+ token,
6555
+ dropboxDeps,
6556
+ restoreOptions?.onProgress,
6557
+ dropboxFolder
6558
+ );
6559
+ } catch (err) {
6560
+ return {
6561
+ error: err instanceof Error ? err.message : "Failed to restore from Dropbox"
6562
+ };
6563
+ }
6564
+ },
6565
+ [userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
6566
+ );
6567
+ const googleDriveBackup = (0, import_react17.useCallback)(
6568
+ async (backupOptions) => {
6569
+ if (!userAddress) {
6570
+ return { error: "Please sign in to backup to Google Drive" };
6571
+ }
6572
+ let token = googleDriveAuth.accessToken;
6573
+ if (!token) {
6574
+ try {
6575
+ token = await googleDriveAuth.requestAccess();
6576
+ } catch {
6577
+ return { error: "Google Drive access denied" };
6578
+ }
6579
+ }
6580
+ try {
6581
+ return await performGoogleDriveExport(
6582
+ database,
6583
+ userAddress,
6584
+ token,
6585
+ googleDriveDeps,
6586
+ backupOptions?.onProgress,
6587
+ googleRootFolder,
6588
+ googleConversationsFolder
6589
+ );
6590
+ } catch (err) {
6591
+ return {
6592
+ error: err instanceof Error ? err.message : "Failed to backup to Google Drive"
6593
+ };
6594
+ }
6595
+ },
6596
+ [
6597
+ database,
6598
+ userAddress,
6599
+ googleDriveAuth,
6600
+ googleDriveDeps,
6601
+ googleRootFolder,
6602
+ googleConversationsFolder
6603
+ ]
6604
+ );
6605
+ const googleDriveRestore = (0, import_react17.useCallback)(
6606
+ async (restoreOptions) => {
6607
+ if (!userAddress) {
6608
+ return { error: "Please sign in to restore from Google Drive" };
6609
+ }
6610
+ let token = googleDriveAuth.accessToken;
6611
+ if (!token) {
6612
+ try {
6613
+ token = await googleDriveAuth.requestAccess();
6614
+ } catch {
6615
+ return { error: "Google Drive access denied" };
6616
+ }
6617
+ }
6618
+ try {
6619
+ return await performGoogleDriveImport(
6620
+ userAddress,
6621
+ token,
6622
+ googleDriveDeps,
6623
+ restoreOptions?.onProgress,
6624
+ googleRootFolder,
6625
+ googleConversationsFolder
6626
+ );
6627
+ } catch (err) {
6628
+ return {
6629
+ error: err instanceof Error ? err.message : "Failed to restore from Google Drive"
6630
+ };
6631
+ }
6632
+ },
6633
+ [
6634
+ userAddress,
6635
+ googleDriveAuth,
6636
+ googleDriveDeps,
6637
+ googleRootFolder,
6638
+ googleConversationsFolder
6639
+ ]
6640
+ );
6641
+ const dropboxState = {
6642
+ isConfigured: dropboxAuth.isConfigured,
6643
+ isAuthenticated: dropboxAuth.isAuthenticated,
6644
+ backup: dropboxBackup,
6645
+ restore: dropboxRestore,
6646
+ connect: dropboxAuth.requestAccess,
6647
+ disconnect: dropboxAuth.logout
6648
+ };
6649
+ const googleDriveState = {
6650
+ isConfigured: googleDriveAuth.isConfigured,
6651
+ isAuthenticated: googleDriveAuth.isAuthenticated,
6652
+ backup: googleDriveBackup,
6653
+ restore: googleDriveRestore,
6654
+ connect: googleDriveAuth.requestAccess,
6655
+ disconnect: googleDriveAuth.logout
6656
+ };
6657
+ const icloudBackup = (0, import_react17.useCallback)(
6658
+ async (backupOptions) => {
6659
+ if (!userAddress) {
6660
+ return { error: "Please sign in to backup to iCloud" };
6661
+ }
6662
+ if (!icloudAuth.isConfigured) {
6663
+ return { error: "iCloud is not configured" };
6664
+ }
6665
+ if (!icloudAuth.isAuthenticated) {
6666
+ try {
6667
+ await icloudAuth.requestAccess();
6668
+ } catch {
6669
+ return { error: "iCloud access denied" };
6670
+ }
6671
+ }
6672
+ try {
6673
+ return await performICloudExport(
6674
+ database,
6675
+ userAddress,
6676
+ icloudDeps,
6677
+ backupOptions?.onProgress
6678
+ );
6679
+ } catch (err) {
6680
+ return {
6681
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6682
+ };
6683
+ }
6684
+ },
6685
+ [database, userAddress, icloudAuth, icloudDeps]
6686
+ );
6687
+ const icloudRestore = (0, import_react17.useCallback)(
6688
+ async (restoreOptions) => {
6689
+ if (!userAddress) {
6690
+ return { error: "Please sign in to restore from iCloud" };
6691
+ }
6692
+ if (!icloudAuth.isConfigured) {
6693
+ return { error: "iCloud is not configured" };
6694
+ }
6695
+ if (!icloudAuth.isAuthenticated) {
6696
+ try {
6697
+ await icloudAuth.requestAccess();
6698
+ } catch {
6699
+ return { error: "iCloud access denied" };
6700
+ }
6701
+ }
6702
+ try {
6703
+ return await performICloudImport(
6704
+ userAddress,
6705
+ icloudDeps,
6706
+ restoreOptions?.onProgress
6707
+ );
6708
+ } catch (err) {
6709
+ return {
6710
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6711
+ };
6712
+ }
6713
+ },
6714
+ [userAddress, icloudAuth, icloudDeps]
6715
+ );
6716
+ const icloudState = {
6717
+ isConfigured: icloudAuth.isConfigured,
6718
+ isAuthenticated: icloudAuth.isAuthenticated,
6719
+ backup: icloudBackup,
6720
+ restore: icloudRestore,
6721
+ connect: icloudAuth.requestAccess,
6722
+ disconnect: icloudAuth.logout
6723
+ };
6724
+ return {
6725
+ dropbox: dropboxState,
6726
+ googleDrive: googleDriveState,
6727
+ icloud: icloudState,
6728
+ hasAnyProvider,
6729
+ hasAnyAuthentication,
6730
+ disconnectAll: logoutAll
5274
6731
  };
5275
6732
  }
5276
6733
  // Annotate the CommonJS export names for ESM import in node:
5277
6734
  0 && (module.exports = {
6735
+ BACKUP_DRIVE_CONVERSATIONS_FOLDER,
6736
+ BACKUP_DRIVE_ROOT_FOLDER,
6737
+ BACKUP_ICLOUD_FOLDER,
6738
+ BackupAuthProvider,
5278
6739
  ChatConversation,
5279
6740
  ChatMessage,
5280
6741
  DEFAULT_BACKUP_FOLDER,
5281
6742
  DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
5282
6743
  DEFAULT_DRIVE_ROOT_FOLDER,
6744
+ DEFAULT_DROPBOX_FOLDER,
6745
+ DEFAULT_ICLOUD_BACKUP_FOLDER,
5283
6746
  DEFAULT_TOOL_SELECTOR_MODEL,
5284
6747
  DropboxAuthProvider,
6748
+ GoogleDriveAuthProvider,
6749
+ ICloudAuthProvider,
5285
6750
  StoredMemoryModel,
5286
6751
  StoredModelPreferenceModel,
5287
6752
  chatStorageMigrations,
5288
6753
  chatStorageSchema,
5289
6754
  clearDropboxToken,
6755
+ clearGoogleDriveToken,
6756
+ clearICloudAuth,
5290
6757
  createMemoryContextSystemMessage,
5291
6758
  decryptData,
5292
6759
  decryptDataBytes,
@@ -5297,8 +6764,11 @@ function useGoogleDriveBackup(options) {
5297
6764
  generateCompositeKey,
5298
6765
  generateConversationId,
5299
6766
  generateUniqueKey,
5300
- getDropboxToken,
6767
+ getGoogleDriveStoredToken,
6768
+ hasDropboxCredentials,
5301
6769
  hasEncryptionKey,
6770
+ hasGoogleDriveCredentials,
6771
+ hasICloudCredentials,
5302
6772
  memoryStorageSchema,
5303
6773
  requestEncryptionKey,
5304
6774
  sdkMigrations,
@@ -5306,13 +6776,17 @@ function useGoogleDriveBackup(options) {
5306
6776
  sdkSchema,
5307
6777
  selectTool,
5308
6778
  settingsStorageSchema,
5309
- storeDropboxToken,
6779
+ useBackup,
6780
+ useBackupAuth,
5310
6781
  useChat,
5311
6782
  useChatStorage,
5312
6783
  useDropboxAuth,
5313
6784
  useDropboxBackup,
5314
6785
  useEncryption,
6786
+ useGoogleDriveAuth,
5315
6787
  useGoogleDriveBackup,
6788
+ useICloudAuth,
6789
+ useICloudBackup,
5316
6790
  useImageGeneration,
5317
6791
  useMemoryStorage,
5318
6792
  useModels,