|  |  | @ -26,11 +26,12 @@ import "dexie-export-import"; | 
			
		
	
		
			
				
					|  |  |  | import { PlatformServiceFactory } from "./PlatformServiceFactory"; | 
			
		
	
		
			
				
					|  |  |  | import { db, accountsDBPromise } from "../db/index"; | 
			
		
	
		
			
				
					|  |  |  | import { Contact, ContactMethod } from "../db/tables/contacts"; | 
			
		
	
		
			
				
					|  |  |  | import { Settings, MASTER_SETTINGS_KEY } from "../db/tables/settings"; | 
			
		
	
		
			
				
					|  |  |  | import { Account } from "../db/tables/accounts"; | 
			
		
	
		
			
				
					|  |  |  | import { Settings, MASTER_SETTINGS_KEY, SettingsWithJsonStrings, BoundingBox } from "../db/tables/settings"; | 
			
		
	
		
			
				
					|  |  |  | import { Account, AccountEncrypted } from "../db/tables/accounts"; | 
			
		
	
		
			
				
					|  |  |  | import { logger } from "../utils/logger"; | 
			
		
	
		
			
				
					|  |  |  | import { parseJsonField } from "../db/databaseUtil"; | 
			
		
	
		
			
				
					|  |  |  | import { mapColumnsToValues, parseJsonField } from "../db/databaseUtil"; | 
			
		
	
		
			
				
					|  |  |  | import { importFromMnemonic } from "../libs/util"; | 
			
		
	
		
			
				
					|  |  |  | import { IIdentifier } from "@veramo/core"; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  |  * Interface for data comparison results between Dexie and SQLite databases | 
			
		
	
	
		
			
				
					|  |  | @ -66,22 +67,24 @@ export interface DataComparison { | 
			
		
	
		
			
				
					|  |  |  |   dexieSettings: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |   sqliteSettings: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |   dexieAccounts: Account[]; | 
			
		
	
		
			
				
					|  |  |  |   sqliteAccounts: Account[]; | 
			
		
	
		
			
				
					|  |  |  |   sqliteAccounts: string[]; | 
			
		
	
		
			
				
					|  |  |  |   differences: { | 
			
		
	
		
			
				
					|  |  |  |     contacts: { | 
			
		
	
		
			
				
					|  |  |  |       added: Contact[]; | 
			
		
	
		
			
				
					|  |  |  |       modified: Contact[]; | 
			
		
	
		
			
				
					|  |  |  |       unmodified: Contact[]; | 
			
		
	
		
			
				
					|  |  |  |       missing: Contact[]; | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |     settings: { | 
			
		
	
		
			
				
					|  |  |  |       added: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |       modified: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |       unmodified: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |       missing: Settings[]; | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |     accounts: { | 
			
		
	
		
			
				
					|  |  |  |       added: Account[]; | 
			
		
	
		
			
				
					|  |  |  |       modified: Account[]; | 
			
		
	
		
			
				
					|  |  |  |       missing: Account[]; | 
			
		
	
		
			
				
					|  |  |  |       unmodified: Account[]; | 
			
		
	
		
			
				
					|  |  |  |       missing: string[]; | 
			
		
	
		
			
				
					|  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | @ -184,22 +187,14 @@ export async function getSqliteContacts(): Promise<Contact[]> { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let contacts: Contact[] = []; | 
			
		
	
		
			
				
					|  |  |  |     if (result?.values?.length) { | 
			
		
	
		
			
				
					|  |  |  |       contacts = result.values.map((row) => { | 
			
		
	
		
			
				
					|  |  |  |         const contact = parseJsonField(row, {}) as Contact; | 
			
		
	
		
			
				
					|  |  |  |         return { | 
			
		
	
		
			
				
					|  |  |  |           did: contact.did || "", | 
			
		
	
		
			
				
					|  |  |  |           name: contact.name || "", | 
			
		
	
		
			
				
					|  |  |  |           contactMethods: parseJsonField( | 
			
		
	
		
			
				
					|  |  |  |             contact.contactMethods, | 
			
		
	
		
			
				
					|  |  |  |             [], | 
			
		
	
		
			
				
					|  |  |  |           ) as ContactMethod[], | 
			
		
	
		
			
				
					|  |  |  |           nextPubKeyHashB64: contact.nextPubKeyHashB64 || "", | 
			
		
	
		
			
				
					|  |  |  |           notes: contact.notes || "", | 
			
		
	
		
			
				
					|  |  |  |           profileImageUrl: contact.profileImageUrl || "", | 
			
		
	
		
			
				
					|  |  |  |           publicKeyBase64: contact.publicKeyBase64 || "", | 
			
		
	
		
			
				
					|  |  |  |           seesMe: contact.seesMe || false, | 
			
		
	
		
			
				
					|  |  |  |           registered: contact.registered || false, | 
			
		
	
		
			
				
					|  |  |  |         } as Contact; | 
			
		
	
		
			
				
					|  |  |  |       const preContacts = mapColumnsToValues(result.columns, result.values) as unknown as Contact[]; | 
			
		
	
		
			
				
					|  |  |  |       // This is redundant since absurd-sql auto-parses JSON strings to objects.
 | 
			
		
	
		
			
				
					|  |  |  |       // But we started it, and it should be known everywhere, so we're keeping it.
 | 
			
		
	
		
			
				
					|  |  |  |       contacts = preContacts.map((contact) => { | 
			
		
	
		
			
				
					|  |  |  |         if (contact.contactMethods) { | 
			
		
	
		
			
				
					|  |  |  |           contact.contactMethods = parseJsonField(contact.contactMethods, []) as ContactMethod[]; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         return contact; | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -281,37 +276,16 @@ export async function getSqliteSettings(): Promise<Settings[]> { | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let settings: Settings[] = []; | 
			
		
	
		
			
				
					|  |  |  |     if (result?.values?.length) { | 
			
		
	
		
			
				
					|  |  |  |       settings = result.values.map((row) => { | 
			
		
	
		
			
				
					|  |  |  |         const setting = parseJsonField(row, {}) as Settings; | 
			
		
	
		
			
				
					|  |  |  |         return { | 
			
		
	
		
			
				
					|  |  |  |           id: setting.id, | 
			
		
	
		
			
				
					|  |  |  |           accountDid: setting.accountDid || "", | 
			
		
	
		
			
				
					|  |  |  |           activeDid: setting.activeDid || "", | 
			
		
	
		
			
				
					|  |  |  |           apiServer: setting.apiServer || "", | 
			
		
	
		
			
				
					|  |  |  |           filterFeedByNearby: setting.filterFeedByNearby || false, | 
			
		
	
		
			
				
					|  |  |  |           filterFeedByVisible: setting.filterFeedByVisible || false, | 
			
		
	
		
			
				
					|  |  |  |           finishedOnboarding: setting.finishedOnboarding || false, | 
			
		
	
		
			
				
					|  |  |  |           firstName: setting.firstName || "", | 
			
		
	
		
			
				
					|  |  |  |           hideRegisterPromptOnNewContact: setting.hideRegisterPromptOnNewContact || false, | 
			
		
	
		
			
				
					|  |  |  |           isRegistered: setting.isRegistered || false, | 
			
		
	
		
			
				
					|  |  |  |           lastName: setting.lastName || "", | 
			
		
	
		
			
				
					|  |  |  |           lastAckedOfferToUserJwtId: setting.lastAckedOfferToUserJwtId || "", | 
			
		
	
		
			
				
					|  |  |  |           lastAckedOfferToUserProjectsJwtId: setting.lastAckedOfferToUserProjectsJwtId || "", | 
			
		
	
		
			
				
					|  |  |  |           lastNotifiedClaimId: setting.lastNotifiedClaimId || "", | 
			
		
	
		
			
				
					|  |  |  |           lastViewedClaimId: setting.lastViewedClaimId || "", | 
			
		
	
		
			
				
					|  |  |  |           notifyingNewActivityTime: setting.notifyingNewActivityTime || "", | 
			
		
	
		
			
				
					|  |  |  |           notifyingReminderMessage: setting.notifyingReminderMessage || "", | 
			
		
	
		
			
				
					|  |  |  |           notifyingReminderTime: setting.notifyingReminderTime || "", | 
			
		
	
		
			
				
					|  |  |  |           partnerApiServer: setting.partnerApiServer || "", | 
			
		
	
		
			
				
					|  |  |  |           passkeyExpirationMinutes: setting.passkeyExpirationMinutes, | 
			
		
	
		
			
				
					|  |  |  |           profileImageUrl: setting.profileImageUrl || "", | 
			
		
	
		
			
				
					|  |  |  |           searchBoxes: parseJsonField(setting.searchBoxes, []), | 
			
		
	
		
			
				
					|  |  |  |           showContactGivesInline: setting.showContactGivesInline || false, | 
			
		
	
		
			
				
					|  |  |  |           showGeneralAdvanced: setting.showGeneralAdvanced || false, | 
			
		
	
		
			
				
					|  |  |  |           showShortcutBvc: setting.showShortcutBvc || false, | 
			
		
	
		
			
				
					|  |  |  |           vapid: setting.vapid || "", | 
			
		
	
		
			
				
					|  |  |  |         } as Settings; | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |       const presettings = | 
			
		
	
		
			
				
					|  |  |  |         mapColumnsToValues(result.columns, result.values) as Settings[]; | 
			
		
	
		
			
				
					|  |  |  |       // This is redundant since absurd-sql auto-parses JSON strings to objects.
 | 
			
		
	
		
			
				
					|  |  |  |       // But we started it, and it should be known everywhere, so we're keeping it.
 | 
			
		
	
		
			
				
					|  |  |  |       settings = presettings.map((setting) => { | 
			
		
	
		
			
				
					|  |  |  |         if (setting.searchBoxes) { | 
			
		
	
		
			
				
					|  |  |  |           setting.searchBoxes = parseJsonField(setting.searchBoxes, []) as Array<{ name: string, bbox: BoundingBox }>; | 
			
		
	
		
			
				
					|  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |         return setting; | 
			
		
	
		
			
				
					|  |  |  |       }) | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     logger.info( | 
			
		
	
	
		
			
				
					|  |  | @ -336,7 +310,7 @@ export async function getSqliteSettings(): Promise<Settings[]> { | 
			
		
	
		
			
				
					|  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  * @async | 
			
		
	
		
			
				
					|  |  |  |  * @function getSqliteAccounts | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Promise<Account[]>} Array of all accounts from SQLite database | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Promise<string[]>} Array of all accounts from SQLite database | 
			
		
	
		
			
				
					|  |  |  |  * @throws {Error} If database query fails or data conversion fails | 
			
		
	
		
			
				
					|  |  |  |  * @example | 
			
		
	
		
			
				
					|  |  |  |  * ```typescript
 | 
			
		
	
	
		
			
				
					|  |  | @ -348,32 +322,20 @@ export async function getSqliteSettings(): Promise<Settings[]> { | 
			
		
	
		
			
				
					|  |  |  |  * } | 
			
		
	
		
			
				
					|  |  |  |  * ``` | 
			
		
	
		
			
				
					|  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  | export async function getSqliteAccounts(): Promise<Account[]> { | 
			
		
	
		
			
				
					|  |  |  | export async function getSqliteAccounts(): Promise<string[]> { | 
			
		
	
		
			
				
					|  |  |  |   try { | 
			
		
	
		
			
				
					|  |  |  |     const platformService = PlatformServiceFactory.getInstance(); | 
			
		
	
		
			
				
					|  |  |  |     const result = await platformService.dbQuery("SELECT * FROM accounts"); | 
			
		
	
		
			
				
					|  |  |  |     const result = await platformService.dbQuery("SELECT did FROM accounts"); | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     let accounts: Account[] = []; | 
			
		
	
		
			
				
					|  |  |  |     let dids: string[] = []; | 
			
		
	
		
			
				
					|  |  |  |     if (result?.values?.length) { | 
			
		
	
		
			
				
					|  |  |  |       accounts = result.values.map((row) => { | 
			
		
	
		
			
				
					|  |  |  |         const account = parseJsonField(row, {}) as Account; | 
			
		
	
		
			
				
					|  |  |  |         return { | 
			
		
	
		
			
				
					|  |  |  |           id: account.id, | 
			
		
	
		
			
				
					|  |  |  |           dateCreated: account.dateCreated || "", | 
			
		
	
		
			
				
					|  |  |  |           derivationPath: account.derivationPath || "", | 
			
		
	
		
			
				
					|  |  |  |           did: account.did || "", | 
			
		
	
		
			
				
					|  |  |  |           identity: account.identity || "", | 
			
		
	
		
			
				
					|  |  |  |           mnemonic: account.mnemonic || "", | 
			
		
	
		
			
				
					|  |  |  |           passkeyCredIdHex: account.passkeyCredIdHex || "", | 
			
		
	
		
			
				
					|  |  |  |           publicKeyHex: account.publicKeyHex || "", | 
			
		
	
		
			
				
					|  |  |  |         } as Account; | 
			
		
	
		
			
				
					|  |  |  |       }); | 
			
		
	
		
			
				
					|  |  |  |       dids = result.values.map((row) => row[0] as string); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |     logger.info( | 
			
		
	
		
			
				
					|  |  |  |       `[MigrationService] Retrieved ${accounts.length} accounts from SQLite`, | 
			
		
	
		
			
				
					|  |  |  |       `[MigrationService] Retrieved ${dids.length} accounts from SQLite`, | 
			
		
	
		
			
				
					|  |  |  |     ); | 
			
		
	
		
			
				
					|  |  |  |     return accounts; | 
			
		
	
		
			
				
					|  |  |  |     return dids; | 
			
		
	
		
			
				
					|  |  |  |   } catch (error) { | 
			
		
	
		
			
				
					|  |  |  |     logger.error("[MigrationService] Error retrieving SQLite accounts:", error); | 
			
		
	
		
			
				
					|  |  |  |     throw new Error(`Failed to retrieve SQLite accounts: ${error}`); | 
			
		
	
	
		
			
				
					|  |  | @ -532,6 +494,7 @@ export async function compareDatabases(): Promise<DataComparison> { | 
			
		
	
		
			
				
					|  |  |  | function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) { | 
			
		
	
		
			
				
					|  |  |  |   const added: Contact[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const modified: Contact[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const unmodified: Contact[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const missing: Contact[] = []; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   // Find contacts that exist in Dexie but not in SQLite
 | 
			
		
	
	
		
			
				
					|  |  | @ -543,6 +506,8 @@ function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) { | 
			
		
	
		
			
				
					|  |  |  |       added.push(dexieContact); | 
			
		
	
		
			
				
					|  |  |  |     } else if (!contactsEqual(dexieContact, sqliteContact)) { | 
			
		
	
		
			
				
					|  |  |  |       modified.push(dexieContact); | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       unmodified.push(dexieContact); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -554,7 +519,7 @@ function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) { | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   return { added, modified, missing }; | 
			
		
	
		
			
				
					|  |  |  |   return { added, modified, unmodified, missing }; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -588,27 +553,30 @@ function compareSettings( | 
			
		
	
		
			
				
					|  |  |  | ) { | 
			
		
	
		
			
				
					|  |  |  |   const added: Settings[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const modified: Settings[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const unmodified: Settings[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const missing: Settings[] = []; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   // Find settings that exist in Dexie but not in SQLite
 | 
			
		
	
		
			
				
					|  |  |  |   for (const dexieSetting of dexieSettings) { | 
			
		
	
		
			
				
					|  |  |  |     const sqliteSetting = sqliteSettings.find((s) => s.id === dexieSetting.id); | 
			
		
	
		
			
				
					|  |  |  |     const sqliteSetting = sqliteSettings.find((s) => s.accountDid === dexieSetting.accountDid); | 
			
		
	
		
			
				
					|  |  |  |     if (!sqliteSetting) { | 
			
		
	
		
			
				
					|  |  |  |       added.push(dexieSetting); | 
			
		
	
		
			
				
					|  |  |  |     } else if (!settingsEqual(dexieSetting, sqliteSetting)) { | 
			
		
	
		
			
				
					|  |  |  |       modified.push(dexieSetting); | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       unmodified.push(dexieSetting); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   // Find settings that exist in SQLite but not in Dexie
 | 
			
		
	
		
			
				
					|  |  |  |   for (const sqliteSetting of sqliteSettings) { | 
			
		
	
		
			
				
					|  |  |  |     const dexieSetting = dexieSettings.find((s) => s.id === sqliteSetting.id); | 
			
		
	
		
			
				
					|  |  |  |     const dexieSetting = dexieSettings.find((s) => s.accountDid === sqliteSetting.accountDid); | 
			
		
	
		
			
				
					|  |  |  |     if (!dexieSetting) { | 
			
		
	
		
			
				
					|  |  |  |       missing.push(sqliteSetting); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   return { added, modified, missing }; | 
			
		
	
		
			
				
					|  |  |  |   return { added, modified, unmodified, missing }; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -623,11 +591,11 @@ function compareSettings( | 
			
		
	
		
			
				
					|  |  |  |  * | 
			
		
	
		
			
				
					|  |  |  |  * @function compareAccounts | 
			
		
	
		
			
				
					|  |  |  |  * @param {Account[]} dexieAccounts - Accounts from Dexie database | 
			
		
	
		
			
				
					|  |  |  |  * @param {Account[]} sqliteAccounts - Accounts from SQLite database | 
			
		
	
		
			
				
					|  |  |  |  * @param {Account[]} sqliteDids - Accounts from SQLite database | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Object} Object containing added, modified, and missing accounts | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Account[]} returns.added - Accounts in Dexie but not SQLite | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Account[]} returns.modified - Accounts that differ between databases | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Account[]} returns.missing - Accounts in SQLite but not Dexie | 
			
		
	
		
			
				
					|  |  |  |  * @returns {Account[]} returns.modified - always 0 because we don't check | 
			
		
	
		
			
				
					|  |  |  |  * @returns {string[]} returns.missing - Accounts in SQLite but not Dexie | 
			
		
	
		
			
				
					|  |  |  |  * @example | 
			
		
	
		
			
				
					|  |  |  |  * ```typescript
 | 
			
		
	
		
			
				
					|  |  |  |  * const differences = compareAccounts(dexieAccounts, sqliteAccounts); | 
			
		
	
	
		
			
				
					|  |  | @ -636,30 +604,30 @@ function compareSettings( | 
			
		
	
		
			
				
					|  |  |  |  * console.log(`Missing: ${differences.missing.length}`); | 
			
		
	
		
			
				
					|  |  |  |  * ``` | 
			
		
	
		
			
				
					|  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  | function compareAccounts(dexieAccounts: Account[], sqliteAccounts: Account[]) { | 
			
		
	
		
			
				
					|  |  |  | function compareAccounts(dexieAccounts: Account[], sqliteDids: string[]) { | 
			
		
	
		
			
				
					|  |  |  |   const added: Account[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const modified: Account[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const missing: Account[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const unmodified: Account[] = []; | 
			
		
	
		
			
				
					|  |  |  |   const missing: string[] = []; | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   // Find accounts that exist in Dexie but not in SQLite
 | 
			
		
	
		
			
				
					|  |  |  |   for (const dexieAccount of dexieAccounts) { | 
			
		
	
		
			
				
					|  |  |  |     const sqliteAccount = sqliteAccounts.find((a) => a.id === dexieAccount.id); | 
			
		
	
		
			
				
					|  |  |  |     if (!sqliteAccount) { | 
			
		
	
		
			
				
					|  |  |  |     const sqliteDid = sqliteDids.find((a) => a === dexieAccount.did); | 
			
		
	
		
			
				
					|  |  |  |     if (!sqliteDid) { | 
			
		
	
		
			
				
					|  |  |  |       added.push(dexieAccount); | 
			
		
	
		
			
				
					|  |  |  |     } else if (!accountsEqual(dexieAccount, sqliteAccount)) { | 
			
		
	
		
			
				
					|  |  |  |       modified.push(dexieAccount); | 
			
		
	
		
			
				
					|  |  |  |     } else { | 
			
		
	
		
			
				
					|  |  |  |       unmodified.push(dexieAccount); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   // Find accounts that exist in SQLite but not in Dexie
 | 
			
		
	
		
			
				
					|  |  |  |   for (const sqliteAccount of sqliteAccounts) { | 
			
		
	
		
			
				
					|  |  |  |     const dexieAccount = dexieAccounts.find((a) => a.id === sqliteAccount.id); | 
			
		
	
		
			
				
					|  |  |  |   for (const sqliteDid of sqliteDids) { | 
			
		
	
		
			
				
					|  |  |  |     const dexieAccount = dexieAccounts.find((a) => a.did === sqliteDid); | 
			
		
	
		
			
				
					|  |  |  |     if (!dexieAccount) { | 
			
		
	
		
			
				
					|  |  |  |       missing.push(sqliteAccount); | 
			
		
	
		
			
				
					|  |  |  |       missing.push(sqliteDid); | 
			
		
	
		
			
				
					|  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |   return { added, modified, missing }; | 
			
		
	
		
			
				
					|  |  |  |   return { added, unmodified, missing }; | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | /** | 
			
		
	
	
		
			
				
					|  |  | @ -688,15 +656,15 @@ function compareAccounts(dexieAccounts: Account[], sqliteAccounts: Account[]) { | 
			
		
	
		
			
				
					|  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  | function contactsEqual(contact1: Contact, contact2: Contact): boolean { | 
			
		
	
		
			
				
					|  |  |  |   return ( | 
			
		
	
		
			
				
					|  |  |  |     contact1.did === contact2.did && | 
			
		
	
		
			
				
					|  |  |  |     contact1.name === contact2.name && | 
			
		
	
		
			
				
					|  |  |  |     contact1.notes === contact2.notes && | 
			
		
	
		
			
				
					|  |  |  |     contact1.profileImageUrl === contact2.profileImageUrl && | 
			
		
	
		
			
				
					|  |  |  |     contact1.publicKeyBase64 === contact2.publicKeyBase64 && | 
			
		
	
		
			
				
					|  |  |  |     contact1.nextPubKeyHashB64 === contact2.nextPubKeyHashB64 && | 
			
		
	
		
			
				
					|  |  |  |     contact1.seesMe === contact2.seesMe && | 
			
		
	
		
			
				
					|  |  |  |     contact1.registered === contact2.registered && | 
			
		
	
		
			
				
					|  |  |  |     JSON.stringify(contact1.contactMethods) === | 
			
		
	
		
			
				
					|  |  |  |     contact1.did == contact2.did && | 
			
		
	
		
			
				
					|  |  |  |     contact1.name == contact2.name && | 
			
		
	
		
			
				
					|  |  |  |     contact1.notes == contact2.notes && | 
			
		
	
		
			
				
					|  |  |  |     contact1.profileImageUrl == contact2.profileImageUrl && | 
			
		
	
		
			
				
					|  |  |  |     contact1.publicKeyBase64 == contact2.publicKeyBase64 && | 
			
		
	
		
			
				
					|  |  |  |     contact1.nextPubKeyHashB64 == contact2.nextPubKeyHashB64 && | 
			
		
	
		
			
				
					|  |  |  |     contact1.seesMe == contact2.seesMe && | 
			
		
	
		
			
				
					|  |  |  |     contact1.registered == contact2.registered && | 
			
		
	
		
			
				
					|  |  |  |     JSON.stringify(contact1.contactMethods) == | 
			
		
	
		
			
				
					|  |  |  |       JSON.stringify(contact2.contactMethods) | 
			
		
	
		
			
				
					|  |  |  |   ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | @ -727,38 +695,38 @@ function contactsEqual(contact1: Contact, contact2: Contact): boolean { | 
			
		
	
		
			
				
					|  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  | function settingsEqual(settings1: Settings, settings2: Settings): boolean { | 
			
		
	
		
			
				
					|  |  |  |   return ( | 
			
		
	
		
			
				
					|  |  |  |     settings1.id === settings2.id && | 
			
		
	
		
			
				
					|  |  |  |     settings1.accountDid === settings2.accountDid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.activeDid === settings2.activeDid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.apiServer === settings2.apiServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.filterFeedByNearby === settings2.filterFeedByNearby && | 
			
		
	
		
			
				
					|  |  |  |     settings1.filterFeedByVisible === settings2.filterFeedByVisible && | 
			
		
	
		
			
				
					|  |  |  |     settings1.finishedOnboarding === settings2.finishedOnboarding && | 
			
		
	
		
			
				
					|  |  |  |     settings1.firstName === settings2.firstName && | 
			
		
	
		
			
				
					|  |  |  |     settings1.hideRegisterPromptOnNewContact === | 
			
		
	
		
			
				
					|  |  |  |     settings1.id == settings2.id && | 
			
		
	
		
			
				
					|  |  |  |     settings1.accountDid == settings2.accountDid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.activeDid == settings2.activeDid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.apiServer == settings2.apiServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.filterFeedByNearby == settings2.filterFeedByNearby && | 
			
		
	
		
			
				
					|  |  |  |     settings1.filterFeedByVisible == settings2.filterFeedByVisible && | 
			
		
	
		
			
				
					|  |  |  |     settings1.finishedOnboarding == settings2.finishedOnboarding && | 
			
		
	
		
			
				
					|  |  |  |     settings1.firstName == settings2.firstName && | 
			
		
	
		
			
				
					|  |  |  |     settings1.hideRegisterPromptOnNewContact == | 
			
		
	
		
			
				
					|  |  |  |       settings2.hideRegisterPromptOnNewContact && | 
			
		
	
		
			
				
					|  |  |  |     settings1.isRegistered === settings2.isRegistered && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastName === settings2.lastName && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastAckedOfferToUserJwtId === | 
			
		
	
		
			
				
					|  |  |  |     settings1.isRegistered == settings2.isRegistered && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastName == settings2.lastName && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastAckedOfferToUserJwtId == | 
			
		
	
		
			
				
					|  |  |  |       settings2.lastAckedOfferToUserJwtId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastAckedOfferToUserProjectsJwtId === | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastAckedOfferToUserProjectsJwtId == | 
			
		
	
		
			
				
					|  |  |  |       settings2.lastAckedOfferToUserProjectsJwtId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastNotifiedClaimId === settings2.lastNotifiedClaimId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastViewedClaimId === settings2.lastViewedClaimId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingNewActivityTime === settings2.notifyingNewActivityTime && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingReminderMessage === settings2.notifyingReminderMessage && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingReminderTime === settings2.notifyingReminderTime && | 
			
		
	
		
			
				
					|  |  |  |     settings1.partnerApiServer === settings2.partnerApiServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.passkeyExpirationMinutes === settings2.passkeyExpirationMinutes && | 
			
		
	
		
			
				
					|  |  |  |     settings1.profileImageUrl === settings2.profileImageUrl && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showContactGivesInline === settings2.showContactGivesInline && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showGeneralAdvanced === settings2.showGeneralAdvanced && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showShortcutBvc === settings2.showShortcutBvc && | 
			
		
	
		
			
				
					|  |  |  |     settings1.vapid === settings2.vapid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.warnIfProdServer === settings2.warnIfProdServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.warnIfTestServer === settings2.warnIfTestServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.webPushServer === settings2.webPushServer && | 
			
		
	
		
			
				
					|  |  |  |     JSON.stringify(settings1.searchBoxes) === | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastNotifiedClaimId == settings2.lastNotifiedClaimId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.lastViewedClaimId == settings2.lastViewedClaimId && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingNewActivityTime == settings2.notifyingNewActivityTime && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingReminderMessage == settings2.notifyingReminderMessage && | 
			
		
	
		
			
				
					|  |  |  |     settings1.notifyingReminderTime == settings2.notifyingReminderTime && | 
			
		
	
		
			
				
					|  |  |  |     settings1.partnerApiServer == settings2.partnerApiServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.passkeyExpirationMinutes == settings2.passkeyExpirationMinutes && | 
			
		
	
		
			
				
					|  |  |  |     settings1.profileImageUrl == settings2.profileImageUrl && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showContactGivesInline == settings2.showContactGivesInline && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showGeneralAdvanced == settings2.showGeneralAdvanced && | 
			
		
	
		
			
				
					|  |  |  |     settings1.showShortcutBvc == settings2.showShortcutBvc && | 
			
		
	
		
			
				
					|  |  |  |     settings1.vapid == settings2.vapid && | 
			
		
	
		
			
				
					|  |  |  |     settings1.warnIfProdServer == settings2.warnIfProdServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.warnIfTestServer == settings2.warnIfTestServer && | 
			
		
	
		
			
				
					|  |  |  |     settings1.webPushServer == settings2.webPushServer && | 
			
		
	
		
			
				
					|  |  |  |     JSON.stringify(settings1.searchBoxes) == | 
			
		
	
		
			
				
					|  |  |  |       JSON.stringify(settings2.searchBoxes) | 
			
		
	
		
			
				
					|  |  |  |   ); | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
	
		
			
				
					|  |  | @ -830,23 +798,25 @@ export function generateComparisonYaml(comparison: DataComparison): string { | 
			
		
	
		
			
				
					|  |  |  |       dexieSettings: comparison.dexieSettings.length, | 
			
		
	
		
			
				
					|  |  |  |       sqliteSettings: comparison.sqliteSettings.filter(s => s.accountDid || s.activeDid).length, | 
			
		
	
		
			
				
					|  |  |  |       dexieAccounts: comparison.dexieAccounts.length, | 
			
		
	
		
			
				
					|  |  |  |       sqliteAccounts: comparison.sqliteAccounts.filter(a => a.did).length, | 
			
		
	
		
			
				
					|  |  |  |       sqliteAccounts: comparison.sqliteAccounts.filter(a => a).length, | 
			
		
	
		
			
				
					|  |  |  |     }, | 
			
		
	
		
			
				
					|  |  |  |     differences: { | 
			
		
	
		
			
				
					|  |  |  |       contacts: { | 
			
		
	
		
			
				
					|  |  |  |         added: comparison.differences.contacts.added.length, | 
			
		
	
		
			
				
					|  |  |  |         modified: comparison.differences.contacts.modified.length, | 
			
		
	
		
			
				
					|  |  |  |         unmodified: comparison.differences.contacts.unmodified.length, | 
			
		
	
		
			
				
					|  |  |  |         missing: comparison.differences.contacts.missing.filter(c => c.did).length, | 
			
		
	
		
			
				
					|  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |       settings: { | 
			
		
	
		
			
				
					|  |  |  |         added: comparison.differences.settings.added.length, | 
			
		
	
		
			
				
					|  |  |  |         modified: comparison.differences.settings.modified.length, | 
			
		
	
		
			
				
					|  |  |  |         unmodified: comparison.differences.settings.unmodified.length, | 
			
		
	
		
			
				
					|  |  |  |         missing: comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length, | 
			
		
	
		
			
				
					|  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |       accounts: { | 
			
		
	
		
			
				
					|  |  |  |         added: comparison.differences.accounts.added.length, | 
			
		
	
		
			
				
					|  |  |  |         modified: comparison.differences.accounts.modified.length, | 
			
		
	
		
			
				
					|  |  |  |         missing: comparison.differences.accounts.missing.filter(a => a.did).length, | 
			
		
	
		
			
				
					|  |  |  |         unmodified: comparison.differences.accounts.unmodified.length, | 
			
		
	
		
			
				
					|  |  |  |         missing: comparison.differences.accounts.missing.filter(a => a).length, | 
			
		
	
		
			
				
					|  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |     }, | 
			
		
	
		
			
				
					|  |  |  |     details: { | 
			
		
	
	
		
			
				
					|  |  | @ -888,15 +858,9 @@ export function generateComparisonYaml(comparison: DataComparison): string { | 
			
		
	
		
			
				
					|  |  |  |           hasIdentity: !!a.identity, | 
			
		
	
		
			
				
					|  |  |  |           hasMnemonic: !!a.mnemonic, | 
			
		
	
		
			
				
					|  |  |  |         })), | 
			
		
	
		
			
				
					|  |  |  |         sqlite: comparison.sqliteAccounts | 
			
		
	
		
			
				
					|  |  |  |           .filter(a => a.did) | 
			
		
	
		
			
				
					|  |  |  |           .map((a) => ({ | 
			
		
	
		
			
				
					|  |  |  |             id: a.id, | 
			
		
	
		
			
				
					|  |  |  |             did: a.did, | 
			
		
	
		
			
				
					|  |  |  |             dateCreated: a.dateCreated, | 
			
		
	
		
			
				
					|  |  |  |             hasIdentity: !!a.identity, | 
			
		
	
		
			
				
					|  |  |  |             hasMnemonic: !!a.mnemonic, | 
			
		
	
		
			
				
					|  |  |  |           })), | 
			
		
	
		
			
				
					|  |  |  |         sqlite: comparison.sqliteAccounts.map((a) => ({ | 
			
		
	
		
			
				
					|  |  |  |           did: a, | 
			
		
	
		
			
				
					|  |  |  |         })), | 
			
		
	
		
			
				
					|  |  |  |       }, | 
			
		
	
		
			
				
					|  |  |  |     }, | 
			
		
	
		
			
				
					|  |  |  |   }; | 
			
		
	
	
		
			
				
					|  |  | 
 |