@reverbia/sdk 1.0.0-next.20251219092050 → 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.
@@ -5575,7 +5575,7 @@ function useGoogleDriveBackup(options) {
5575
5575
  };
5576
5576
  }
5577
5577
 
5578
- // src/react/useBackupAuth.ts
5578
+ // src/react/useICloudAuth.ts
5579
5579
  import {
5580
5580
  createContext as createContext3,
5581
5581
  createElement as createElement3,
@@ -5584,20 +5584,593 @@ import {
5584
5584
  useEffect as useEffect8,
5585
5585
  useState as useState12
5586
5586
  } from "react";
5587
- var BackupAuthContext = createContext3(null);
5587
+
5588
+ // src/lib/backup/icloud/api.ts
5589
+ var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
5590
+ var DEFAULT_BACKUP_FOLDER2 = "conversations";
5591
+ var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
5592
+ var RECORD_TYPE = "ConversationBackup";
5593
+ var cloudKitLoadPromise = null;
5594
+ function isCloudKitAvailable() {
5595
+ return typeof window !== "undefined" && !!window.CloudKit;
5596
+ }
5597
+ async function loadCloudKit() {
5598
+ if (typeof window === "undefined") {
5599
+ throw new Error("CloudKit JS can only be loaded in browser environment");
5600
+ }
5601
+ if (window.CloudKit) {
5602
+ return;
5603
+ }
5604
+ if (cloudKitLoadPromise) {
5605
+ return cloudKitLoadPromise;
5606
+ }
5607
+ cloudKitLoadPromise = new Promise((resolve, reject) => {
5608
+ const script = document.createElement("script");
5609
+ script.src = CLOUDKIT_JS_URL;
5610
+ script.async = true;
5611
+ script.onload = () => {
5612
+ if (window.CloudKit) {
5613
+ resolve();
5614
+ } else {
5615
+ reject(new Error("CloudKit JS loaded but CloudKit object not found"));
5616
+ }
5617
+ };
5618
+ script.onerror = () => {
5619
+ cloudKitLoadPromise = null;
5620
+ reject(new Error("Failed to load CloudKit JS"));
5621
+ };
5622
+ document.head.appendChild(script);
5623
+ });
5624
+ return cloudKitLoadPromise;
5625
+ }
5626
+ async function ensureCloudKitLoaded() {
5627
+ if (!isCloudKitAvailable()) {
5628
+ await loadCloudKit();
5629
+ }
5630
+ }
5631
+ async function configureCloudKit(config) {
5632
+ await ensureCloudKitLoaded();
5633
+ ensureAuthElements();
5634
+ window.CloudKit.configure({
5635
+ containers: [
5636
+ {
5637
+ containerIdentifier: config.containerIdentifier,
5638
+ apiTokenAuth: {
5639
+ apiToken: config.apiToken,
5640
+ persist: true,
5641
+ signInButton: {
5642
+ id: "apple-sign-in-button",
5643
+ theme: "black"
5644
+ },
5645
+ signOutButton: {
5646
+ id: "apple-sign-out-button",
5647
+ theme: "black"
5648
+ }
5649
+ },
5650
+ environment: config.environment
5651
+ }
5652
+ ]
5653
+ });
5654
+ }
5655
+ async function getContainer() {
5656
+ await ensureCloudKitLoaded();
5657
+ return window.CloudKit.getDefaultContainer();
5658
+ }
5659
+ function ensureAuthElements() {
5660
+ let signInButton = document.getElementById("apple-sign-in-button");
5661
+ let signOutButton = document.getElementById("apple-sign-out-button");
5662
+ if (!signInButton) {
5663
+ signInButton = document.createElement("div");
5664
+ signInButton.id = "apple-sign-in-button";
5665
+ signInButton.style.position = "fixed";
5666
+ signInButton.style.top = "-9999px";
5667
+ signInButton.style.left = "-9999px";
5668
+ document.body.appendChild(signInButton);
5669
+ }
5670
+ if (!signOutButton) {
5671
+ signOutButton = document.createElement("div");
5672
+ signOutButton.id = "apple-sign-out-button";
5673
+ signOutButton.style.position = "fixed";
5674
+ signOutButton.style.top = "-9999px";
5675
+ signOutButton.style.left = "-9999px";
5676
+ document.body.appendChild(signOutButton);
5677
+ }
5678
+ return { signIn: signInButton, signOut: signOutButton };
5679
+ }
5680
+ async function authenticateICloud() {
5681
+ const container = await getContainer();
5682
+ ensureAuthElements();
5683
+ return container.setUpAuth();
5684
+ }
5685
+ async function requestICloudSignIn() {
5686
+ const container = await getContainer();
5687
+ const { signIn } = ensureAuthElements();
5688
+ const existingUser = await container.setUpAuth();
5689
+ if (existingUser) {
5690
+ return existingUser;
5691
+ }
5692
+ console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
5693
+ console.log("[CloudKit] Sign-in container children:", signIn.children.length);
5694
+ const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
5695
+ console.log("[CloudKit] Found button element:", appleButton);
5696
+ if (appleButton) {
5697
+ console.log("[CloudKit] Clicking button...");
5698
+ appleButton.click();
5699
+ } else {
5700
+ const anyClickable = signIn.firstElementChild;
5701
+ if (anyClickable) {
5702
+ console.log("[CloudKit] Clicking first child element:", anyClickable);
5703
+ anyClickable.click();
5704
+ }
5705
+ }
5706
+ return container.whenUserSignsIn();
5707
+ }
5708
+ async function uploadFileToICloud(filename, content) {
5709
+ const container = await getContainer();
5710
+ const database = container.privateCloudDatabase;
5711
+ const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
5712
+ const arrayBuffer = await content.arrayBuffer();
5713
+ const base64Data = btoa(
5714
+ String.fromCharCode(...new Uint8Array(arrayBuffer))
5715
+ );
5716
+ const record = {
5717
+ recordType: RECORD_TYPE,
5718
+ recordName,
5719
+ fields: {
5720
+ filename: { value: filename },
5721
+ data: { value: base64Data },
5722
+ size: { value: content.size },
5723
+ contentType: { value: content.type || "application/json" }
5724
+ }
5725
+ };
5726
+ const response = await database.saveRecords(record);
5727
+ if (!response.records || response.records.length === 0) {
5728
+ throw new Error("Failed to upload file to iCloud");
5729
+ }
5730
+ const savedRecord = response.records[0];
5731
+ return {
5732
+ recordName: savedRecord.recordName,
5733
+ filename,
5734
+ modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
5735
+ size: content.size
5736
+ };
5737
+ }
5738
+ async function listICloudFiles() {
5739
+ const container = await getContainer();
5740
+ const database = container.privateCloudDatabase;
5741
+ const query = {
5742
+ recordType: RECORD_TYPE
5743
+ // Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
5744
+ // For now, we skip sorting and sort client-side after fetching
5745
+ };
5746
+ const allRecords = [];
5747
+ let response = await database.performQuery(query);
5748
+ if (response.records) {
5749
+ allRecords.push(...response.records);
5750
+ }
5751
+ while (response.continuationMarker) {
5752
+ break;
5753
+ }
5754
+ const files = allRecords.map((record) => ({
5755
+ recordName: record.recordName,
5756
+ filename: record.fields.filename?.value ?? "",
5757
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5758
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5759
+ }));
5760
+ return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
5761
+ }
5762
+ async function downloadICloudFile(recordName) {
5763
+ const container = await getContainer();
5764
+ const database = container.privateCloudDatabase;
5765
+ const response = await database.fetchRecords([{ recordName }], {
5766
+ desiredKeys: ["filename", "data", "contentType"]
5767
+ });
5768
+ if (!response.records || response.records.length === 0) {
5769
+ throw new Error(`File not found: ${recordName}`);
5770
+ }
5771
+ const record = response.records[0];
5772
+ const dataField = record.fields.data?.value;
5773
+ if (!dataField) {
5774
+ throw new Error("No data in record");
5775
+ }
5776
+ if (typeof dataField === "string") {
5777
+ const binaryString = atob(dataField);
5778
+ const bytes = new Uint8Array(binaryString.length);
5779
+ for (let i = 0; i < binaryString.length; i++) {
5780
+ bytes[i] = binaryString.charCodeAt(i);
5781
+ }
5782
+ return new Blob([bytes], { type: "application/json" });
5783
+ }
5784
+ if (typeof dataField === "object" && "downloadURL" in dataField) {
5785
+ const fetchResponse = await fetch(
5786
+ dataField.downloadURL
5787
+ );
5788
+ if (!fetchResponse.ok) {
5789
+ throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
5790
+ }
5791
+ return fetchResponse.blob();
5792
+ }
5793
+ throw new Error("Unknown data format in iCloud record");
5794
+ }
5795
+ async function findICloudFile(filename) {
5796
+ const container = await getContainer();
5797
+ const database = container.privateCloudDatabase;
5798
+ const query = {
5799
+ recordType: RECORD_TYPE,
5800
+ filterBy: [
5801
+ {
5802
+ fieldName: "filename",
5803
+ comparator: "EQUALS",
5804
+ fieldValue: { value: filename }
5805
+ }
5806
+ ]
5807
+ };
5808
+ const response = await database.performQuery(query);
5809
+ if (!response.records || response.records.length === 0) {
5810
+ return null;
5811
+ }
5812
+ const record = response.records[0];
5813
+ return {
5814
+ recordName: record.recordName,
5815
+ filename: record.fields.filename?.value ?? "",
5816
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5817
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5818
+ };
5819
+ }
5820
+
5821
+ // src/react/useICloudAuth.ts
5822
+ var ICloudAuthContext = createContext3(null);
5823
+ function ICloudAuthProvider({
5824
+ apiToken,
5825
+ containerIdentifier = DEFAULT_CONTAINER_ID,
5826
+ environment = "production",
5827
+ children
5828
+ }) {
5829
+ const [isAuthenticated, setIsAuthenticated] = useState12(false);
5830
+ const [userRecordName, setUserRecordName] = useState12(null);
5831
+ const [isAvailable, setIsAvailable] = useState12(false);
5832
+ const [isConfigured, setIsConfigured] = useState12(false);
5833
+ const [isLoading, setIsLoading] = useState12(false);
5834
+ useEffect8(() => {
5835
+ if (!apiToken || typeof window === "undefined") {
5836
+ return;
5837
+ }
5838
+ const initCloudKit = async () => {
5839
+ setIsLoading(true);
5840
+ try {
5841
+ await loadCloudKit();
5842
+ setIsAvailable(true);
5843
+ const config = {
5844
+ containerIdentifier,
5845
+ apiToken,
5846
+ environment
5847
+ };
5848
+ await configureCloudKit(config);
5849
+ setIsConfigured(true);
5850
+ try {
5851
+ const userIdentity = await authenticateICloud();
5852
+ if (userIdentity) {
5853
+ setIsAuthenticated(true);
5854
+ setUserRecordName(userIdentity.userRecordName);
5855
+ }
5856
+ } catch {
5857
+ }
5858
+ } catch {
5859
+ setIsAvailable(false);
5860
+ setIsConfigured(false);
5861
+ } finally {
5862
+ setIsLoading(false);
5863
+ }
5864
+ };
5865
+ initCloudKit();
5866
+ }, [apiToken, containerIdentifier, environment]);
5867
+ const requestAccess = useCallback14(async () => {
5868
+ if (!isConfigured) {
5869
+ throw new Error("iCloud is not configured");
5870
+ }
5871
+ if (isAuthenticated) {
5872
+ return;
5873
+ }
5874
+ try {
5875
+ const userIdentity = await requestICloudSignIn();
5876
+ setIsAuthenticated(true);
5877
+ setUserRecordName(userIdentity.userRecordName);
5878
+ } catch (err) {
5879
+ throw new Error(
5880
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
5881
+ );
5882
+ }
5883
+ }, [isAuthenticated, isConfigured]);
5884
+ const logout = useCallback14(() => {
5885
+ setIsAuthenticated(false);
5886
+ setUserRecordName(null);
5887
+ }, []);
5888
+ return createElement3(
5889
+ ICloudAuthContext.Provider,
5890
+ {
5891
+ value: {
5892
+ isAuthenticated,
5893
+ isConfigured,
5894
+ isAvailable,
5895
+ userRecordName,
5896
+ requestAccess,
5897
+ logout
5898
+ }
5899
+ },
5900
+ children
5901
+ );
5902
+ }
5903
+ function useICloudAuth() {
5904
+ const context = useContext3(ICloudAuthContext);
5905
+ if (!context) {
5906
+ throw new Error("useICloudAuth must be used within ICloudAuthProvider");
5907
+ }
5908
+ return context;
5909
+ }
5910
+ function hasICloudCredentials() {
5911
+ return isCloudKitAvailable();
5912
+ }
5913
+ function clearICloudAuth() {
5914
+ }
5915
+
5916
+ // src/react/useICloudBackup.ts
5917
+ import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
5918
+
5919
+ // src/lib/backup/icloud/backup.ts
5920
+ var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
5921
+ async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
5922
+ try {
5923
+ await deps.requestEncryptionKey(userAddress);
5924
+ const filename = `${conversationId}.json`;
5925
+ const existingFile = await findICloudFile(filename);
5926
+ if (existingFile) {
5927
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
5928
+ const conversationsCollection = database.get("conversations");
5929
+ const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
5930
+ if (records.length > 0) {
5931
+ const conversation = conversationToStored(records[0]);
5932
+ const localUpdated = conversation.updatedAt.getTime();
5933
+ const remoteModified = existingFile.modifiedAt.getTime();
5934
+ if (localUpdated <= remoteModified) {
5935
+ return "skipped";
5936
+ }
5937
+ }
5938
+ }
5939
+ const exportResult = await deps.exportConversation(
5940
+ conversationId,
5941
+ userAddress
5942
+ );
5943
+ if (!exportResult.success || !exportResult.blob) {
5944
+ return "failed";
5945
+ }
5946
+ await uploadFileToICloud(filename, exportResult.blob);
5947
+ return "uploaded";
5948
+ } catch (err) {
5949
+ if (isAuthError3(err) && !_retried) {
5950
+ try {
5951
+ await deps.requestICloudAccess();
5952
+ return pushConversationToICloud(
5953
+ database,
5954
+ conversationId,
5955
+ userAddress,
5956
+ deps,
5957
+ true
5958
+ );
5959
+ } catch {
5960
+ return "failed";
5961
+ }
5962
+ }
5963
+ return "failed";
5964
+ }
5965
+ }
5966
+ async function performICloudExport(database, userAddress, deps, onProgress) {
5967
+ await deps.requestEncryptionKey(userAddress);
5968
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
5969
+ const conversationsCollection = database.get("conversations");
5970
+ const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
5971
+ const conversations = records.map(conversationToStored);
5972
+ const total = conversations.length;
5973
+ if (total === 0) {
5974
+ return { success: true, uploaded: 0, skipped: 0, total: 0 };
5975
+ }
5976
+ let uploaded = 0;
5977
+ let skipped = 0;
5978
+ for (let i = 0; i < conversations.length; i++) {
5979
+ const conv = conversations[i];
5980
+ onProgress?.(i + 1, total);
5981
+ const result = await pushConversationToICloud(
5982
+ database,
5983
+ conv.conversationId,
5984
+ userAddress,
5985
+ deps
5986
+ );
5987
+ if (result === "uploaded") uploaded++;
5988
+ if (result === "skipped") skipped++;
5989
+ }
5990
+ return { success: true, uploaded, skipped, total };
5991
+ }
5992
+ async function performICloudImport(userAddress, deps, onProgress) {
5993
+ await deps.requestEncryptionKey(userAddress);
5994
+ const remoteFiles = await listICloudFiles();
5995
+ if (remoteFiles.length === 0) {
5996
+ return {
5997
+ success: false,
5998
+ restored: 0,
5999
+ failed: 0,
6000
+ total: 0,
6001
+ noBackupsFound: true
6002
+ };
6003
+ }
6004
+ const jsonFiles = remoteFiles.filter(
6005
+ (file) => file.filename.endsWith(".json")
6006
+ );
6007
+ const total = jsonFiles.length;
6008
+ let restored = 0;
6009
+ let failed = 0;
6010
+ for (let i = 0; i < jsonFiles.length; i++) {
6011
+ const file = jsonFiles[i];
6012
+ onProgress?.(i + 1, total);
6013
+ try {
6014
+ const blob = await downloadICloudFile(file.recordName);
6015
+ const result = await deps.importConversation(blob, userAddress);
6016
+ if (result.success) {
6017
+ restored++;
6018
+ } else {
6019
+ failed++;
6020
+ }
6021
+ } catch (err) {
6022
+ if (isAuthError3(err)) {
6023
+ try {
6024
+ await deps.requestICloudAccess();
6025
+ const blob = await downloadICloudFile(file.recordName);
6026
+ const result = await deps.importConversation(blob, userAddress);
6027
+ if (result.success) {
6028
+ restored++;
6029
+ } else {
6030
+ failed++;
6031
+ }
6032
+ } catch {
6033
+ failed++;
6034
+ }
6035
+ } else {
6036
+ failed++;
6037
+ }
6038
+ }
6039
+ }
6040
+ return { success: true, restored, failed, total };
6041
+ }
6042
+
6043
+ // src/react/useICloudBackup.ts
6044
+ function useICloudBackup(options) {
6045
+ const {
6046
+ database,
6047
+ userAddress,
6048
+ requestEncryptionKey: requestEncryptionKey2,
6049
+ exportConversation,
6050
+ importConversation
6051
+ } = options;
6052
+ const {
6053
+ isAuthenticated,
6054
+ isConfigured,
6055
+ isAvailable,
6056
+ requestAccess
6057
+ } = useICloudAuth();
6058
+ const deps = useMemo6(
6059
+ () => ({
6060
+ requestICloudAccess: requestAccess,
6061
+ requestEncryptionKey: requestEncryptionKey2,
6062
+ exportConversation,
6063
+ importConversation
6064
+ }),
6065
+ [requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6066
+ );
6067
+ const ensureAuthenticated = useCallback15(async () => {
6068
+ if (isAuthenticated) return true;
6069
+ try {
6070
+ await requestAccess();
6071
+ return true;
6072
+ } catch {
6073
+ return false;
6074
+ }
6075
+ }, [isAuthenticated, requestAccess]);
6076
+ const backup = useCallback15(
6077
+ async (backupOptions) => {
6078
+ if (!userAddress) {
6079
+ return { error: "Please sign in to backup to iCloud" };
6080
+ }
6081
+ if (!isAvailable) {
6082
+ return { error: "CloudKit JS is not loaded" };
6083
+ }
6084
+ if (!isConfigured) {
6085
+ return { error: "iCloud is not configured" };
6086
+ }
6087
+ const authenticated = await ensureAuthenticated();
6088
+ if (!authenticated) {
6089
+ return { error: "iCloud access denied" };
6090
+ }
6091
+ try {
6092
+ return await performICloudExport(
6093
+ database,
6094
+ userAddress,
6095
+ deps,
6096
+ backupOptions?.onProgress
6097
+ );
6098
+ } catch (err) {
6099
+ return {
6100
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6101
+ };
6102
+ }
6103
+ },
6104
+ [database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6105
+ );
6106
+ const restore = useCallback15(
6107
+ async (restoreOptions) => {
6108
+ if (!userAddress) {
6109
+ return { error: "Please sign in to restore from iCloud" };
6110
+ }
6111
+ if (!isAvailable) {
6112
+ return { error: "CloudKit JS is not loaded" };
6113
+ }
6114
+ if (!isConfigured) {
6115
+ return { error: "iCloud is not configured" };
6116
+ }
6117
+ const authenticated = await ensureAuthenticated();
6118
+ if (!authenticated) {
6119
+ return { error: "iCloud access denied" };
6120
+ }
6121
+ try {
6122
+ return await performICloudImport(
6123
+ userAddress,
6124
+ deps,
6125
+ restoreOptions?.onProgress
6126
+ );
6127
+ } catch (err) {
6128
+ return {
6129
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6130
+ };
6131
+ }
6132
+ },
6133
+ [userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6134
+ );
6135
+ return {
6136
+ backup,
6137
+ restore,
6138
+ isConfigured,
6139
+ isAuthenticated,
6140
+ isAvailable
6141
+ };
6142
+ }
6143
+
6144
+ // src/react/useBackupAuth.ts
6145
+ import {
6146
+ createContext as createContext4,
6147
+ createElement as createElement4,
6148
+ useCallback as useCallback16,
6149
+ useContext as useContext4,
6150
+ useEffect as useEffect9,
6151
+ useState as useState13
6152
+ } from "react";
6153
+ var BackupAuthContext = createContext4(null);
5588
6154
  function BackupAuthProvider({
5589
6155
  dropboxAppKey,
5590
6156
  dropboxCallbackPath = "/auth/dropbox/callback",
5591
6157
  googleClientId,
5592
6158
  googleCallbackPath = "/auth/google/callback",
6159
+ icloudApiToken,
6160
+ icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
6161
+ icloudEnvironment = "production",
5593
6162
  apiClient,
5594
6163
  children
5595
6164
  }) {
5596
- const [dropboxToken, setDropboxToken] = useState12(null);
6165
+ const [dropboxToken, setDropboxToken] = useState13(null);
5597
6166
  const isDropboxConfigured = !!dropboxAppKey;
5598
- const [googleToken, setGoogleToken] = useState12(null);
6167
+ const [googleToken, setGoogleToken] = useState13(null);
5599
6168
  const isGoogleConfigured = !!googleClientId;
5600
- useEffect8(() => {
6169
+ const [icloudAuthenticated, setIcloudAuthenticated] = useState13(false);
6170
+ const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
6171
+ const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
6172
+ const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
6173
+ useEffect9(() => {
5601
6174
  const checkStoredTokens = async () => {
5602
6175
  if (hasDropboxCredentials()) {
5603
6176
  const token = await getDropboxAccessToken(apiClient);
@@ -5614,7 +6187,35 @@ function BackupAuthProvider({
5614
6187
  };
5615
6188
  checkStoredTokens();
5616
6189
  }, [apiClient]);
5617
- useEffect8(() => {
6190
+ useEffect9(() => {
6191
+ if (!icloudApiToken || typeof window === "undefined") {
6192
+ return;
6193
+ }
6194
+ const initCloudKit = async () => {
6195
+ try {
6196
+ await loadCloudKit();
6197
+ setIsIcloudAvailable(true);
6198
+ const config = {
6199
+ containerIdentifier: icloudContainerIdentifier,
6200
+ apiToken: icloudApiToken,
6201
+ environment: icloudEnvironment
6202
+ };
6203
+ await configureCloudKit(config);
6204
+ try {
6205
+ const userIdentity = await authenticateICloud();
6206
+ if (userIdentity) {
6207
+ setIcloudAuthenticated(true);
6208
+ setIcloudUserRecordName(userIdentity.userRecordName);
6209
+ }
6210
+ } catch {
6211
+ }
6212
+ } catch {
6213
+ setIsIcloudAvailable(false);
6214
+ }
6215
+ };
6216
+ initCloudKit();
6217
+ }, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
6218
+ useEffect9(() => {
5618
6219
  if (!isDropboxConfigured) return;
5619
6220
  const handleCallback = async () => {
5620
6221
  if (isDropboxCallback()) {
@@ -5629,7 +6230,7 @@ function BackupAuthProvider({
5629
6230
  };
5630
6231
  handleCallback();
5631
6232
  }, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
5632
- useEffect8(() => {
6233
+ useEffect9(() => {
5633
6234
  if (!isGoogleConfigured) return;
5634
6235
  const handleCallback = async () => {
5635
6236
  if (isGoogleDriveCallback()) {
@@ -5644,14 +6245,14 @@ function BackupAuthProvider({
5644
6245
  };
5645
6246
  handleCallback();
5646
6247
  }, [googleCallbackPath, isGoogleConfigured, apiClient]);
5647
- const refreshDropboxTokenFn = useCallback14(async () => {
6248
+ const refreshDropboxTokenFn = useCallback16(async () => {
5648
6249
  const token = await getDropboxAccessToken(apiClient);
5649
6250
  if (token) {
5650
6251
  setDropboxToken(token);
5651
6252
  }
5652
6253
  return token;
5653
6254
  }, [apiClient]);
5654
- const requestDropboxAccess = useCallback14(async () => {
6255
+ const requestDropboxAccess = useCallback16(async () => {
5655
6256
  if (!isDropboxConfigured || !dropboxAppKey) {
5656
6257
  throw new Error("Dropbox is not configured");
5657
6258
  }
@@ -5671,18 +6272,18 @@ function BackupAuthProvider({
5671
6272
  isDropboxConfigured,
5672
6273
  apiClient
5673
6274
  ]);
5674
- const logoutDropbox = useCallback14(async () => {
6275
+ const logoutDropbox = useCallback16(async () => {
5675
6276
  await revokeDropboxToken(apiClient);
5676
6277
  setDropboxToken(null);
5677
6278
  }, [apiClient]);
5678
- const refreshGoogleTokenFn = useCallback14(async () => {
6279
+ const refreshGoogleTokenFn = useCallback16(async () => {
5679
6280
  const token = await getGoogleDriveAccessToken(apiClient);
5680
6281
  if (token) {
5681
6282
  setGoogleToken(token);
5682
6283
  }
5683
6284
  return token;
5684
6285
  }, [apiClient]);
5685
- const requestGoogleAccess = useCallback14(async () => {
6286
+ const requestGoogleAccess = useCallback16(async () => {
5686
6287
  if (!isGoogleConfigured || !googleClientId) {
5687
6288
  throw new Error("Google Drive is not configured");
5688
6289
  }
@@ -5702,16 +6303,51 @@ function BackupAuthProvider({
5702
6303
  isGoogleConfigured,
5703
6304
  apiClient
5704
6305
  ]);
5705
- const logoutGoogle = useCallback14(async () => {
6306
+ const logoutGoogle = useCallback16(async () => {
5706
6307
  await revokeGoogleDriveToken(apiClient);
5707
6308
  setGoogleToken(null);
5708
6309
  }, [apiClient]);
5709
- const logoutAll = useCallback14(async () => {
6310
+ const refreshIcloudTokenFn = useCallback16(async () => {
6311
+ try {
6312
+ const userIdentity = await authenticateICloud();
6313
+ if (userIdentity) {
6314
+ setIcloudAuthenticated(true);
6315
+ setIcloudUserRecordName(userIdentity.userRecordName);
6316
+ return userIdentity.userRecordName;
6317
+ }
6318
+ } catch {
6319
+ }
6320
+ return null;
6321
+ }, []);
6322
+ const requestIcloudAccess = useCallback16(async () => {
6323
+ if (!isIcloudConfigured) {
6324
+ throw new Error("iCloud is not configured");
6325
+ }
6326
+ if (icloudAuthenticated && icloudUserRecordName) {
6327
+ return icloudUserRecordName;
6328
+ }
6329
+ try {
6330
+ const userIdentity = await requestICloudSignIn();
6331
+ setIcloudAuthenticated(true);
6332
+ setIcloudUserRecordName(userIdentity.userRecordName);
6333
+ return userIdentity.userRecordName;
6334
+ } catch (err) {
6335
+ throw new Error(
6336
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
6337
+ );
6338
+ }
6339
+ }, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
6340
+ const logoutIcloud = useCallback16(async () => {
6341
+ setIcloudAuthenticated(false);
6342
+ setIcloudUserRecordName(null);
6343
+ }, []);
6344
+ const logoutAll = useCallback16(async () => {
5710
6345
  await Promise.all([
5711
6346
  isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
5712
- isGoogleConfigured ? logoutGoogle() : Promise.resolve()
6347
+ isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
6348
+ isIcloudConfigured ? logoutIcloud() : Promise.resolve()
5713
6349
  ]);
5714
- }, [isDropboxConfigured, isGoogleConfigured, logoutDropbox, logoutGoogle]);
6350
+ }, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
5715
6351
  const dropboxState = {
5716
6352
  accessToken: dropboxToken,
5717
6353
  isAuthenticated: !!dropboxToken,
@@ -5728,14 +6364,24 @@ function BackupAuthProvider({
5728
6364
  logout: logoutGoogle,
5729
6365
  refreshToken: refreshGoogleTokenFn
5730
6366
  };
5731
- return createElement3(
6367
+ const icloudState = {
6368
+ accessToken: icloudUserRecordName,
6369
+ // Use userRecordName as the "token" for iCloud
6370
+ isAuthenticated: icloudAuthenticated,
6371
+ isConfigured: isIcloudConfigured,
6372
+ requestAccess: requestIcloudAccess,
6373
+ logout: logoutIcloud,
6374
+ refreshToken: refreshIcloudTokenFn
6375
+ };
6376
+ return createElement4(
5732
6377
  BackupAuthContext.Provider,
5733
6378
  {
5734
6379
  value: {
5735
6380
  dropbox: dropboxState,
5736
6381
  googleDrive: googleDriveState,
5737
- hasAnyProvider: isDropboxConfigured || isGoogleConfigured,
5738
- hasAnyAuthentication: !!dropboxToken || !!googleToken,
6382
+ icloud: icloudState,
6383
+ hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
6384
+ hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
5739
6385
  logoutAll
5740
6386
  }
5741
6387
  },
@@ -5743,7 +6389,7 @@ function BackupAuthProvider({
5743
6389
  );
5744
6390
  }
5745
6391
  function useBackupAuth() {
5746
- const context = useContext3(BackupAuthContext);
6392
+ const context = useContext4(BackupAuthContext);
5747
6393
  if (!context) {
5748
6394
  throw new Error("useBackupAuth must be used within BackupAuthProvider");
5749
6395
  }
@@ -5751,7 +6397,7 @@ function useBackupAuth() {
5751
6397
  }
5752
6398
 
5753
6399
  // src/react/useBackup.ts
5754
- import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
6400
+ import { useCallback as useCallback17, useMemo as useMemo7 } from "react";
5755
6401
  function useBackup(options) {
5756
6402
  const {
5757
6403
  database,
@@ -5766,11 +6412,12 @@ function useBackup(options) {
5766
6412
  const {
5767
6413
  dropbox: dropboxAuth,
5768
6414
  googleDrive: googleDriveAuth,
6415
+ icloud: icloudAuth,
5769
6416
  hasAnyProvider,
5770
6417
  hasAnyAuthentication,
5771
6418
  logoutAll
5772
6419
  } = useBackupAuth();
5773
- const dropboxDeps = useMemo6(
6420
+ const dropboxDeps = useMemo7(
5774
6421
  () => ({
5775
6422
  requestDropboxAccess: dropboxAuth.requestAccess,
5776
6423
  requestEncryptionKey: requestEncryptionKey2,
@@ -5779,7 +6426,7 @@ function useBackup(options) {
5779
6426
  }),
5780
6427
  [dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
5781
6428
  );
5782
- const googleDriveDeps = useMemo6(
6429
+ const googleDriveDeps = useMemo7(
5783
6430
  () => ({
5784
6431
  requestDriveAccess: googleDriveAuth.requestAccess,
5785
6432
  requestEncryptionKey: requestEncryptionKey2,
@@ -5788,7 +6435,18 @@ function useBackup(options) {
5788
6435
  }),
5789
6436
  [googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
5790
6437
  );
5791
- const dropboxBackup = useCallback15(
6438
+ const icloudDeps = useMemo7(
6439
+ () => ({
6440
+ requestICloudAccess: async () => {
6441
+ await icloudAuth.requestAccess();
6442
+ },
6443
+ requestEncryptionKey: requestEncryptionKey2,
6444
+ exportConversation,
6445
+ importConversation
6446
+ }),
6447
+ [icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6448
+ );
6449
+ const dropboxBackup = useCallback17(
5792
6450
  async (backupOptions) => {
5793
6451
  if (!userAddress) {
5794
6452
  return { error: "Please sign in to backup to Dropbox" };
@@ -5818,7 +6476,7 @@ function useBackup(options) {
5818
6476
  },
5819
6477
  [database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
5820
6478
  );
5821
- const dropboxRestore = useCallback15(
6479
+ const dropboxRestore = useCallback17(
5822
6480
  async (restoreOptions) => {
5823
6481
  if (!userAddress) {
5824
6482
  return { error: "Please sign in to restore from Dropbox" };
@@ -5847,7 +6505,7 @@ function useBackup(options) {
5847
6505
  },
5848
6506
  [userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
5849
6507
  );
5850
- const googleDriveBackup = useCallback15(
6508
+ const googleDriveBackup = useCallback17(
5851
6509
  async (backupOptions) => {
5852
6510
  if (!userAddress) {
5853
6511
  return { error: "Please sign in to backup to Google Drive" };
@@ -5885,7 +6543,7 @@ function useBackup(options) {
5885
6543
  googleConversationsFolder
5886
6544
  ]
5887
6545
  );
5888
- const googleDriveRestore = useCallback15(
6546
+ const googleDriveRestore = useCallback17(
5889
6547
  async (restoreOptions) => {
5890
6548
  if (!userAddress) {
5891
6549
  return { error: "Please sign in to restore from Google Drive" };
@@ -5937,9 +6595,77 @@ function useBackup(options) {
5937
6595
  connect: googleDriveAuth.requestAccess,
5938
6596
  disconnect: googleDriveAuth.logout
5939
6597
  };
6598
+ const icloudBackup = useCallback17(
6599
+ async (backupOptions) => {
6600
+ if (!userAddress) {
6601
+ return { error: "Please sign in to backup to iCloud" };
6602
+ }
6603
+ if (!icloudAuth.isConfigured) {
6604
+ return { error: "iCloud is not configured" };
6605
+ }
6606
+ if (!icloudAuth.isAuthenticated) {
6607
+ try {
6608
+ await icloudAuth.requestAccess();
6609
+ } catch {
6610
+ return { error: "iCloud access denied" };
6611
+ }
6612
+ }
6613
+ try {
6614
+ return await performICloudExport(
6615
+ database,
6616
+ userAddress,
6617
+ icloudDeps,
6618
+ backupOptions?.onProgress
6619
+ );
6620
+ } catch (err) {
6621
+ return {
6622
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6623
+ };
6624
+ }
6625
+ },
6626
+ [database, userAddress, icloudAuth, icloudDeps]
6627
+ );
6628
+ const icloudRestore = useCallback17(
6629
+ async (restoreOptions) => {
6630
+ if (!userAddress) {
6631
+ return { error: "Please sign in to restore from iCloud" };
6632
+ }
6633
+ if (!icloudAuth.isConfigured) {
6634
+ return { error: "iCloud is not configured" };
6635
+ }
6636
+ if (!icloudAuth.isAuthenticated) {
6637
+ try {
6638
+ await icloudAuth.requestAccess();
6639
+ } catch {
6640
+ return { error: "iCloud access denied" };
6641
+ }
6642
+ }
6643
+ try {
6644
+ return await performICloudImport(
6645
+ userAddress,
6646
+ icloudDeps,
6647
+ restoreOptions?.onProgress
6648
+ );
6649
+ } catch (err) {
6650
+ return {
6651
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6652
+ };
6653
+ }
6654
+ },
6655
+ [userAddress, icloudAuth, icloudDeps]
6656
+ );
6657
+ const icloudState = {
6658
+ isConfigured: icloudAuth.isConfigured,
6659
+ isAuthenticated: icloudAuth.isAuthenticated,
6660
+ backup: icloudBackup,
6661
+ restore: icloudRestore,
6662
+ connect: icloudAuth.requestAccess,
6663
+ disconnect: icloudAuth.logout
6664
+ };
5940
6665
  return {
5941
6666
  dropbox: dropboxState,
5942
6667
  googleDrive: googleDriveState,
6668
+ icloud: icloudState,
5943
6669
  hasAnyProvider,
5944
6670
  hasAnyAuthentication,
5945
6671
  disconnectAll: logoutAll
@@ -5948,6 +6674,7 @@ function useBackup(options) {
5948
6674
  export {
5949
6675
  DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER,
5950
6676
  DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
6677
+ DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
5951
6678
  BackupAuthProvider,
5952
6679
  Conversation as ChatConversation,
5953
6680
  Message as ChatMessage,
@@ -5955,15 +6682,18 @@ export {
5955
6682
  DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
5956
6683
  DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER,
5957
6684
  DEFAULT_BACKUP_FOLDER as DEFAULT_DROPBOX_FOLDER,
6685
+ DEFAULT_BACKUP_FOLDER2 as DEFAULT_ICLOUD_BACKUP_FOLDER,
5958
6686
  DEFAULT_TOOL_SELECTOR_MODEL,
5959
6687
  DropboxAuthProvider,
5960
6688
  GoogleDriveAuthProvider,
6689
+ ICloudAuthProvider,
5961
6690
  Memory as StoredMemoryModel,
5962
6691
  ModelPreference as StoredModelPreferenceModel,
5963
6692
  chatStorageMigrations,
5964
6693
  chatStorageSchema,
5965
6694
  clearToken as clearDropboxToken,
5966
6695
  clearGoogleDriveToken,
6696
+ clearICloudAuth,
5967
6697
  createMemoryContextSystemMessage,
5968
6698
  decryptData,
5969
6699
  decryptDataBytes,
@@ -5978,6 +6708,7 @@ export {
5978
6708
  hasDropboxCredentials,
5979
6709
  hasEncryptionKey,
5980
6710
  hasGoogleDriveCredentials,
6711
+ hasICloudCredentials,
5981
6712
  memoryStorageSchema,
5982
6713
  requestEncryptionKey,
5983
6714
  sdkMigrations,
@@ -5994,6 +6725,8 @@ export {
5994
6725
  useEncryption,
5995
6726
  useGoogleDriveAuth,
5996
6727
  useGoogleDriveBackup,
6728
+ useICloudAuth,
6729
+ useICloudBackup,
5997
6730
  useImageGeneration,
5998
6731
  useMemoryStorage,
5999
6732
  useModels,