@metamask-previews/account-tree-controller 5.0.1-preview-2d89a26 → 5.0.1-preview-afe010990

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.
Files changed (41) hide show
  1. package/dist/backup-and-sync/service/index.cjs +2 -2
  2. package/dist/backup-and-sync/service/index.cjs.map +1 -1
  3. package/dist/backup-and-sync/service/index.d.cts.map +1 -1
  4. package/dist/backup-and-sync/service/index.d.mts.map +1 -1
  5. package/dist/backup-and-sync/service/index.mjs +3 -3
  6. package/dist/backup-and-sync/service/index.mjs.map +1 -1
  7. package/dist/backup-and-sync/syncing/group.cjs +2 -1
  8. package/dist/backup-and-sync/syncing/group.cjs.map +1 -1
  9. package/dist/backup-and-sync/syncing/group.d.cts.map +1 -1
  10. package/dist/backup-and-sync/syncing/group.d.mts.map +1 -1
  11. package/dist/backup-and-sync/syncing/group.mjs +2 -1
  12. package/dist/backup-and-sync/syncing/group.mjs.map +1 -1
  13. package/dist/backup-and-sync/user-storage/format-utils.cjs +21 -7
  14. package/dist/backup-and-sync/user-storage/format-utils.cjs.map +1 -1
  15. package/dist/backup-and-sync/user-storage/format-utils.d.cts.map +1 -1
  16. package/dist/backup-and-sync/user-storage/format-utils.d.mts.map +1 -1
  17. package/dist/backup-and-sync/user-storage/format-utils.mjs +21 -7
  18. package/dist/backup-and-sync/user-storage/format-utils.mjs.map +1 -1
  19. package/dist/backup-and-sync/user-storage/network-operations.cjs +5 -4
  20. package/dist/backup-and-sync/user-storage/network-operations.cjs.map +1 -1
  21. package/dist/backup-and-sync/user-storage/network-operations.d.cts.map +1 -1
  22. package/dist/backup-and-sync/user-storage/network-operations.d.mts.map +1 -1
  23. package/dist/backup-and-sync/user-storage/network-operations.mjs +5 -4
  24. package/dist/backup-and-sync/user-storage/network-operations.mjs.map +1 -1
  25. package/dist/backup-and-sync/utils/errors.cjs +14 -0
  26. package/dist/backup-and-sync/utils/errors.cjs.map +1 -0
  27. package/dist/backup-and-sync/utils/errors.d.cts +8 -0
  28. package/dist/backup-and-sync/utils/errors.d.cts.map +1 -0
  29. package/dist/backup-and-sync/utils/errors.d.mts +8 -0
  30. package/dist/backup-and-sync/utils/errors.d.mts.map +1 -0
  31. package/dist/backup-and-sync/utils/errors.mjs +10 -0
  32. package/dist/backup-and-sync/utils/errors.mjs.map +1 -0
  33. package/dist/backup-and-sync/utils/index.cjs +3 -0
  34. package/dist/backup-and-sync/utils/index.cjs.map +1 -1
  35. package/dist/backup-and-sync/utils/index.d.cts +1 -0
  36. package/dist/backup-and-sync/utils/index.d.cts.map +1 -1
  37. package/dist/backup-and-sync/utils/index.d.mts +1 -0
  38. package/dist/backup-and-sync/utils/index.d.mts.map +1 -1
  39. package/dist/backup-and-sync/utils/index.mjs +1 -0
  40. package/dist/backup-and-sync/utils/index.mjs.map +1 -1
  41. package/package.json +1 -1
@@ -261,7 +261,7 @@ async function _BackupAndSyncService_performFullSyncInner() {
261
261
  }
262
262
  }
263
263
  catch (error) {
264
- const errorMessage = error instanceof Error ? error.message : String(error);
264
+ const errorMessage = (0, utils_1.toErrorMessage)(error);
265
265
  const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;
266
266
  (0, logger_1.backupAndSyncLogger)(errorString);
267
267
  throw new Error(errorString);
@@ -288,7 +288,7 @@ async function _BackupAndSyncService_performFullSyncInner() {
288
288
  await (0, syncing_1.syncGroupsMetadata)(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), wallet, groupsFromUserStorage, entropySourceId, walletProfileId);
289
289
  }
290
290
  catch (error) {
291
- const errorMessage = error instanceof Error ? error.message : String(error);
291
+ const errorMessage = (0, utils_1.toErrorMessage)(error);
292
292
  const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;
293
293
  (0, logger_1.backupAndSyncLogger)(errorString);
294
294
  // Attempt to rollback state changes for this wallet
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletType } from '@metamask/account-api';\nimport type { UserStorageController } from '@metamask/profile-sync-controller';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountTreeControllerState } from '../../types';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport { TraceName } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n getAllGroupsFromUserStorage,\n getGroupFromUserStorage,\n getWalletFromUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage';\nimport {\n createStateSnapshot,\n restoreStateFromSnapshot,\n getLocalEntropyWallets,\n getLocalGroupsForEntropyWallet,\n} from '../utils';\nimport type { StateSnapshot } from '../utils';\n\n/**\n * Service responsible for managing all backup and sync operations.\n *\n * This service handles:\n * - Full sync operations\n * - Single item sync operations\n * - Sync queue management\n * - Sync state management\n */\nexport class BackupAndSyncService {\n readonly #context: BackupAndSyncContext;\n\n /**\n * Queue manager for atomic sync operations.\n */\n readonly #atomicSyncQueue: AtomicSyncQueue;\n\n /**\n * Cached promise for ongoing full sync operations.\n * Ensures multiple callers await the same sync operation.\n */\n #ongoingFullSyncPromise: Promise<void> | null = null;\n\n /**\n * Cached promise for the first ongoing full sync operation.\n * Ensures multiple callers await the same sync operation.\n */\n #firstOngoingFullSyncPromise: Promise<void> | null = null;\n\n constructor(context: BackupAndSyncContext) {\n this.#context = context;\n this.#atomicSyncQueue = new AtomicSyncQueue();\n }\n\n /**\n * Checks if syncing is currently in progress.\n *\n * @returns True if syncing is in progress.\n */\n get isInProgress(): boolean {\n return this.#context.controller.state.isAccountTreeSyncingInProgress;\n }\n\n /**\n * Checks if the account tree has been synced at least once.\n *\n * @returns True if the account tree has been synced at least once.\n */\n get hasSyncedAtLeastOnce(): boolean {\n return this.#context.controller.state\n .hasAccountTreeSyncingSyncedAtLeastOnce;\n }\n\n /**\n * Checks if backup and sync is enabled by checking UserStorageController state.\n *\n * @returns True if backup and sync + account syncing is enabled.\n */\n get isBackupAndSyncEnabled(): boolean {\n const userStorageControllerState = this.#context.messenger.call(\n 'UserStorageController:getState',\n );\n const { isAccountSyncingEnabled, isBackupAndSyncEnabled } =\n userStorageControllerState;\n\n return isBackupAndSyncEnabled && isAccountSyncingEnabled;\n }\n\n /**\n * Clears the atomic queue and resets ongoing operations.\n */\n clearState(): void {\n this.#atomicSyncQueue.clear();\n this.#ongoingFullSyncPromise = null;\n this.#firstOngoingFullSyncPromise = null;\n }\n\n /**\n * Handles changes to the user storage state.\n * Used to clear the backup and sync service state.\n *\n * @param state - The new user storage state.\n */\n handleUserStorageStateChange(\n state: UserStorageController.UserStorageControllerState,\n ): void {\n if (!state.isAccountSyncingEnabled || !state.isBackupAndSyncEnabled) {\n // If either syncing is disabled, clear the account tree state\n this.clearState();\n }\n }\n\n /**\n * Gets the entropy wallet associated with the given wallet ID.\n *\n * @param walletId - The wallet ID to look up.\n * @returns The associated entropy wallet, or undefined if not found.\n */\n #getEntropyWallet(\n walletId: AccountWalletId,\n ): AccountWalletEntropyObject | undefined {\n const wallet = this.#context.controller.state.accountTree.wallets[walletId];\n return wallet?.type === AccountWalletType.Entropy ? wallet : undefined;\n }\n\n /**\n * Sets up cleanup for ongoing sync promise tracking without affecting error propagation.\n *\n * @param promise - The promise to track and clean up\n * @returns The same promise (for chaining)\n */\n #setupOngoingPromiseCleanup(promise: Promise<void>): Promise<void> {\n this.#ongoingFullSyncPromise = promise;\n // Set up cleanup without affecting the returned promise\n promise\n .finally(() => {\n this.#ongoingFullSyncPromise = null;\n })\n .catch(() => {\n // Only ignore errors from the cleanup operation itself\n // The original promise errors are still propagated to callers\n });\n return promise;\n }\n\n /**\n * Enqueues a single wallet sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (!this.isBackupAndSyncEnabled || !this.hasSyncedAtLeastOnce) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleWalletSyncInner(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.isBackupAndSyncEnabled ||\n !this.hasSyncedAtLeastOnce ||\n // This prevents rate limiting scenarios where full syncs trigger group creations\n // that in turn enqueue the same single group syncs that the full sync just did.\n // This can very rarely lead to inconsistencies, but will be fixed on the next full sync.\n // TODO: let's improve this in the future by tracking the updates done in the full sync and\n // comparing against that.\n this.isInProgress\n ) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleGroupSyncInner(groupId),\n );\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n * If a full sync is already in progress, it will return the ongoing promise.\n * This clears the atomic sync queue before starting the full sync.\n *\n * NOTE: in some very edge cases, this can be ran concurrently if triggered quickly after\n * toggling back and forth the backup and sync feature from the UI.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSync(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n // If there's an ongoing sync (including first sync), return it\n if (this.#ongoingFullSyncPromise) {\n return this.#ongoingFullSyncPromise;\n }\n\n // Create a new ongoing sync (sequential calls after previous completed)\n const newSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(() =>\n this.#performFullSyncInner(),\n );\n\n // First sync setup - create and cache the first sync promise\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = newSyncPromise;\n }\n\n return this.#setupOngoingPromiseCleanup(newSyncPromise);\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync has already completed, it will resolve and NOT start a new sync.\n *\n * This clears the atomic sync queue before starting the full sync.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSyncAtLeastOnce(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(\n () => this.#performFullSyncInner(),\n );\n // eslint-disable-next-line no-void\n void this.#setupOngoingPromiseCleanup(this.#firstOngoingFullSyncPromise);\n }\n\n return this.#firstOngoingFullSyncPromise;\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * This method performs a comprehensive sync operation that:\n * 1. Identifies all local entropy wallets that can be synchronized\n * 2. Performs legacy account syncing if needed (for backwards compatibility)\n * - Disables subsequent legacy syncing by setting a flag in user storage\n * - Exits early if multichain account syncing is disabled after legacy sync\n * 3. Executes multichain account syncing for each wallet:\n * - Syncs wallet metadata bidirectionally\n * - Creates missing local groups from user storage data (or pushes local groups if none exist remotely)\n * - Refreshes local state to reflect newly created groups\n * - Syncs group metadata bidirectionally\n *\n * The sync is atomic per wallet with rollback on errors, but continues processing other wallets\n * if individual wallet sync fails. A global lock prevents concurrent sync operations.\n *\n * During this process, all other atomic multichain related user storage updates are blocked.\n *\n * @throws Will throw if the sync operation encounters unrecoverable errors\n */\n async #performFullSyncInner(): Promise<void> {\n // Prevent multiple syncs from running at the same time.\n // Also prevents atomic updates from being applied while syncing is in progress.\n if (this.isInProgress) {\n return;\n }\n\n // Set isAccountTreeSyncingInProgress immediately to prevent race conditions\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n // 1. Identifies all local entropy wallets that can be synchronized\n const localSyncableWallets = getLocalEntropyWallets(this.#context);\n\n if (!localSyncableWallets.length) {\n // No wallets to sync, just return. This shouldn't happen.\n return;\n }\n\n // 2. Iterate over each local wallet\n for (const wallet of localSyncableWallets) {\n const entropySourceId = wallet.metadata.entropy.id;\n\n let walletProfileId: ProfileId;\n let walletFromUserStorage: UserStorageSyncedWallet | null;\n let groupsFromUserStorage: UserStorageSyncedWalletGroup[];\n\n try {\n walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n [walletFromUserStorage, groupsFromUserStorage] = await Promise.all([\n getWalletFromUserStorage(this.#context, entropySourceId),\n getAllGroupsFromUserStorage(this.#context, entropySourceId),\n ]);\n\n // 2.1 Decide if we need to perform legacy account syncing\n if (\n !walletFromUserStorage ||\n !walletFromUserStorage.isLegacyAccountSyncingDisabled\n ) {\n // 2.2 Perform legacy account syncing\n // This will migrate legacy account data to the new structure.\n // This operation will only be performed once.\n await performLegacyAccountSyncing(\n this.#context,\n entropySourceId,\n walletProfileId,\n );\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n throw new Error(errorString);\n }\n\n // 3. Execute multichain account syncing\n let stateSnapshot: StateSnapshot | undefined;\n\n try {\n // 3.1 Wallet syncing\n // Create a state snapshot before processing each wallet for potential rollback\n stateSnapshot = createStateSnapshot(this.#context);\n\n // Sync wallet metadata bidirectionally\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n\n // 3.2 Groups syncing\n // If groups data does not exist in user storage yet, create it\n if (!groupsFromUserStorage.length) {\n // If no groups exist in user storage, we can push all groups from the wallet to the user storage and exit\n await pushGroupToUserStorageBatch(\n this.#context,\n getLocalGroupsForEntropyWallet(this.#context, wallet.id),\n entropySourceId,\n );\n\n continue; // No need to proceed with metadata comparison if groups are new\n }\n\n // Create local groups for each group from user storage if they do not exist\n // This will ensure that we have all groups available locally before syncing metadata\n await createLocalGroupsFromUserStorage(\n this.#context,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n\n // Sync group metadata bidirectionally\n await syncGroupsMetadata(\n this.#context,\n wallet,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n\n // Attempt to rollback state changes for this wallet\n try {\n if (!stateSnapshot) {\n throw new Error(\n `State snapshot is missing for wallet ${wallet.id}`,\n );\n }\n restoreStateFromSnapshot(this.#context, stateSnapshot);\n backupAndSyncLogger(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n } catch (rollbackError) {\n backupAndSyncLogger(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n backupAndSyncLogger('Error during multichain account syncing:', error);\n throw error;\n }\n\n this.#context.controllerStateUpdateFn((state) => {\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n });\n };\n\n // Execute the big sync function with tracing and ensure state cleanup\n try {\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\n );\n } finally {\n // Always reset state, regardless of success or failure\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n },\n );\n }\n }\n\n /**\n * Performs a single wallet's bidirectional metadata sync with user storage.\n *\n * @param walletId - The wallet ID to sync.\n */\n async #performSingleWalletSyncInner(\n walletId: AccountWalletId,\n ): Promise<void> {\n try {\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n const walletFromUserStorage = await getWalletFromUserStorage(\n this.#context,\n entropySourceId,\n );\n\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Performs a single group's bidirectional metadata sync with user storage.\n *\n * @param groupId - The group ID to sync.\n */\n async #performSingleGroupSyncInner(groupId: AccountGroupId): Promise<void> {\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const group = wallet.groups[groupId];\n if (!group) {\n return;\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n // Get the specific group from user storage\n const groupFromUserStorage = await getGroupFromUserStorage(\n this.#context,\n entropySourceId,\n group.metadata.entropy.groupIndex,\n );\n\n await syncGroupMetadata(\n this.#context,\n group,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(`Error in single group sync for ${groupId}:`, error);\n throw error;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletType } from '@metamask/account-api';\nimport type { UserStorageController } from '@metamask/profile-sync-controller';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountTreeControllerState } from '../../types';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport { TraceName } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n getAllGroupsFromUserStorage,\n getGroupFromUserStorage,\n getWalletFromUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage';\nimport {\n createStateSnapshot,\n restoreStateFromSnapshot,\n getLocalEntropyWallets,\n getLocalGroupsForEntropyWallet,\n toErrorMessage,\n} from '../utils';\nimport type { StateSnapshot } from '../utils';\n\n/**\n * Service responsible for managing all backup and sync operations.\n *\n * This service handles:\n * - Full sync operations\n * - Single item sync operations\n * - Sync queue management\n * - Sync state management\n */\nexport class BackupAndSyncService {\n readonly #context: BackupAndSyncContext;\n\n /**\n * Queue manager for atomic sync operations.\n */\n readonly #atomicSyncQueue: AtomicSyncQueue;\n\n /**\n * Cached promise for ongoing full sync operations.\n * Ensures multiple callers await the same sync operation.\n */\n #ongoingFullSyncPromise: Promise<void> | null = null;\n\n /**\n * Cached promise for the first ongoing full sync operation.\n * Ensures multiple callers await the same sync operation.\n */\n #firstOngoingFullSyncPromise: Promise<void> | null = null;\n\n constructor(context: BackupAndSyncContext) {\n this.#context = context;\n this.#atomicSyncQueue = new AtomicSyncQueue();\n }\n\n /**\n * Checks if syncing is currently in progress.\n *\n * @returns True if syncing is in progress.\n */\n get isInProgress(): boolean {\n return this.#context.controller.state.isAccountTreeSyncingInProgress;\n }\n\n /**\n * Checks if the account tree has been synced at least once.\n *\n * @returns True if the account tree has been synced at least once.\n */\n get hasSyncedAtLeastOnce(): boolean {\n return this.#context.controller.state\n .hasAccountTreeSyncingSyncedAtLeastOnce;\n }\n\n /**\n * Checks if backup and sync is enabled by checking UserStorageController state.\n *\n * @returns True if backup and sync + account syncing is enabled.\n */\n get isBackupAndSyncEnabled(): boolean {\n const userStorageControllerState = this.#context.messenger.call(\n 'UserStorageController:getState',\n );\n const { isAccountSyncingEnabled, isBackupAndSyncEnabled } =\n userStorageControllerState;\n\n return isBackupAndSyncEnabled && isAccountSyncingEnabled;\n }\n\n /**\n * Clears the atomic queue and resets ongoing operations.\n */\n clearState(): void {\n this.#atomicSyncQueue.clear();\n this.#ongoingFullSyncPromise = null;\n this.#firstOngoingFullSyncPromise = null;\n }\n\n /**\n * Handles changes to the user storage state.\n * Used to clear the backup and sync service state.\n *\n * @param state - The new user storage state.\n */\n handleUserStorageStateChange(\n state: UserStorageController.UserStorageControllerState,\n ): void {\n if (!state.isAccountSyncingEnabled || !state.isBackupAndSyncEnabled) {\n // If either syncing is disabled, clear the account tree state\n this.clearState();\n }\n }\n\n /**\n * Gets the entropy wallet associated with the given wallet ID.\n *\n * @param walletId - The wallet ID to look up.\n * @returns The associated entropy wallet, or undefined if not found.\n */\n #getEntropyWallet(\n walletId: AccountWalletId,\n ): AccountWalletEntropyObject | undefined {\n const wallet = this.#context.controller.state.accountTree.wallets[walletId];\n return wallet?.type === AccountWalletType.Entropy ? wallet : undefined;\n }\n\n /**\n * Sets up cleanup for ongoing sync promise tracking without affecting error propagation.\n *\n * @param promise - The promise to track and clean up\n * @returns The same promise (for chaining)\n */\n #setupOngoingPromiseCleanup(promise: Promise<void>): Promise<void> {\n this.#ongoingFullSyncPromise = promise;\n // Set up cleanup without affecting the returned promise\n promise\n .finally(() => {\n this.#ongoingFullSyncPromise = null;\n })\n .catch(() => {\n // Only ignore errors from the cleanup operation itself\n // The original promise errors are still propagated to callers\n });\n return promise;\n }\n\n /**\n * Enqueues a single wallet sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (!this.isBackupAndSyncEnabled || !this.hasSyncedAtLeastOnce) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleWalletSyncInner(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.isBackupAndSyncEnabled ||\n !this.hasSyncedAtLeastOnce ||\n // This prevents rate limiting scenarios where full syncs trigger group creations\n // that in turn enqueue the same single group syncs that the full sync just did.\n // This can very rarely lead to inconsistencies, but will be fixed on the next full sync.\n // TODO: let's improve this in the future by tracking the updates done in the full sync and\n // comparing against that.\n this.isInProgress\n ) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleGroupSyncInner(groupId),\n );\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n * If a full sync is already in progress, it will return the ongoing promise.\n * This clears the atomic sync queue before starting the full sync.\n *\n * NOTE: in some very edge cases, this can be ran concurrently if triggered quickly after\n * toggling back and forth the backup and sync feature from the UI.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSync(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n // If there's an ongoing sync (including first sync), return it\n if (this.#ongoingFullSyncPromise) {\n return this.#ongoingFullSyncPromise;\n }\n\n // Create a new ongoing sync (sequential calls after previous completed)\n const newSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(() =>\n this.#performFullSyncInner(),\n );\n\n // First sync setup - create and cache the first sync promise\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = newSyncPromise;\n }\n\n return this.#setupOngoingPromiseCleanup(newSyncPromise);\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync has already completed, it will resolve and NOT start a new sync.\n *\n * This clears the atomic sync queue before starting the full sync.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSyncAtLeastOnce(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(\n () => this.#performFullSyncInner(),\n );\n // eslint-disable-next-line no-void\n void this.#setupOngoingPromiseCleanup(this.#firstOngoingFullSyncPromise);\n }\n\n return this.#firstOngoingFullSyncPromise;\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * This method performs a comprehensive sync operation that:\n * 1. Identifies all local entropy wallets that can be synchronized\n * 2. Performs legacy account syncing if needed (for backwards compatibility)\n * - Disables subsequent legacy syncing by setting a flag in user storage\n * - Exits early if multichain account syncing is disabled after legacy sync\n * 3. Executes multichain account syncing for each wallet:\n * - Syncs wallet metadata bidirectionally\n * - Creates missing local groups from user storage data (or pushes local groups if none exist remotely)\n * - Refreshes local state to reflect newly created groups\n * - Syncs group metadata bidirectionally\n *\n * The sync is atomic per wallet with rollback on errors, but continues processing other wallets\n * if individual wallet sync fails. A global lock prevents concurrent sync operations.\n *\n * During this process, all other atomic multichain related user storage updates are blocked.\n *\n * @throws Will throw if the sync operation encounters unrecoverable errors\n */\n async #performFullSyncInner(): Promise<void> {\n // Prevent multiple syncs from running at the same time.\n // Also prevents atomic updates from being applied while syncing is in progress.\n if (this.isInProgress) {\n return;\n }\n\n // Set isAccountTreeSyncingInProgress immediately to prevent race conditions\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n // 1. Identifies all local entropy wallets that can be synchronized\n const localSyncableWallets = getLocalEntropyWallets(this.#context);\n\n if (!localSyncableWallets.length) {\n // No wallets to sync, just return. This shouldn't happen.\n return;\n }\n\n // 2. Iterate over each local wallet\n for (const wallet of localSyncableWallets) {\n const entropySourceId = wallet.metadata.entropy.id;\n\n let walletProfileId: ProfileId;\n let walletFromUserStorage: UserStorageSyncedWallet | null;\n let groupsFromUserStorage: UserStorageSyncedWalletGroup[];\n\n try {\n walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n [walletFromUserStorage, groupsFromUserStorage] = await Promise.all([\n getWalletFromUserStorage(this.#context, entropySourceId),\n getAllGroupsFromUserStorage(this.#context, entropySourceId),\n ]);\n\n // 2.1 Decide if we need to perform legacy account syncing\n if (\n !walletFromUserStorage ||\n !walletFromUserStorage.isLegacyAccountSyncingDisabled\n ) {\n // 2.2 Perform legacy account syncing\n // This will migrate legacy account data to the new structure.\n // This operation will only be performed once.\n await performLegacyAccountSyncing(\n this.#context,\n entropySourceId,\n walletProfileId,\n );\n }\n } catch (error) {\n const errorMessage = toErrorMessage(error);\n const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n throw new Error(errorString);\n }\n\n // 3. Execute multichain account syncing\n let stateSnapshot: StateSnapshot | undefined;\n\n try {\n // 3.1 Wallet syncing\n // Create a state snapshot before processing each wallet for potential rollback\n stateSnapshot = createStateSnapshot(this.#context);\n\n // Sync wallet metadata bidirectionally\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n\n // 3.2 Groups syncing\n // If groups data does not exist in user storage yet, create it\n if (!groupsFromUserStorage.length) {\n // If no groups exist in user storage, we can push all groups from the wallet to the user storage and exit\n await pushGroupToUserStorageBatch(\n this.#context,\n getLocalGroupsForEntropyWallet(this.#context, wallet.id),\n entropySourceId,\n );\n\n continue; // No need to proceed with metadata comparison if groups are new\n }\n\n // Create local groups for each group from user storage if they do not exist\n // This will ensure that we have all groups available locally before syncing metadata\n await createLocalGroupsFromUserStorage(\n this.#context,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n\n // Sync group metadata bidirectionally\n await syncGroupsMetadata(\n this.#context,\n wallet,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n const errorMessage = toErrorMessage(error);\n const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n\n // Attempt to rollback state changes for this wallet\n try {\n if (!stateSnapshot) {\n throw new Error(\n `State snapshot is missing for wallet ${wallet.id}`,\n );\n }\n restoreStateFromSnapshot(this.#context, stateSnapshot);\n backupAndSyncLogger(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n } catch (rollbackError) {\n backupAndSyncLogger(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n backupAndSyncLogger('Error during multichain account syncing:', error);\n throw error;\n }\n\n this.#context.controllerStateUpdateFn((state) => {\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n });\n };\n\n // Execute the big sync function with tracing and ensure state cleanup\n try {\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\n );\n } finally {\n // Always reset state, regardless of success or failure\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n },\n );\n }\n }\n\n /**\n * Performs a single wallet's bidirectional metadata sync with user storage.\n *\n * @param walletId - The wallet ID to sync.\n */\n async #performSingleWalletSyncInner(\n walletId: AccountWalletId,\n ): Promise<void> {\n try {\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n const walletFromUserStorage = await getWalletFromUserStorage(\n this.#context,\n entropySourceId,\n );\n\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Performs a single group's bidirectional metadata sync with user storage.\n *\n * @param groupId - The group ID to sync.\n */\n async #performSingleGroupSyncInner(groupId: AccountGroupId): Promise<void> {\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const group = wallet.groups[groupId];\n if (!group) {\n return;\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n // Get the specific group from user storage\n const groupFromUserStorage = await getGroupFromUserStorage(\n this.#context,\n entropySourceId,\n group.metadata.entropy.groupIndex,\n );\n\n await syncGroupMetadata(\n this.#context,\n group,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(`Error in single group sync for ${groupId}:`, error);\n throw error;\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,0CAA0C;AAgB/E,OAAO,KAAK,EACV,oBAAoB,EAGrB,qBAAiB;AAelB;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAoBnB,OAAO,EAAE,oBAAoB;IAKzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAGlC;IAED;;;;OAIG;IACH,IAAI,sBAAsB,IAAI,OAAO,CAQpC;IAED;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;;;;OAKG;IACH,4BAA4B,CAC1B,KAAK,EAAE,qBAAqB,CAAC,0BAA0B,GACtD,IAAI;IAwCP;;;;;OAKG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAWxD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAoBrD;;;;;;;;;;OAUG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtC;;;;;;;;;;OAUG;IACG,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CAwSlD"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,0CAA0C;AAgB/E,OAAO,KAAK,EACV,oBAAoB,EAGrB,qBAAiB;AAgBlB;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAoBnB,OAAO,EAAE,oBAAoB;IAKzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAGlC;IAED;;;;OAIG;IACH,IAAI,sBAAsB,IAAI,OAAO,CAQpC;IAED;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;;;;OAKG;IACH,4BAA4B,CAC1B,KAAK,EAAE,qBAAqB,CAAC,0BAA0B,GACtD,IAAI;IAwCP;;;;;OAKG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAWxD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAoBrD;;;;;;;;;;OAUG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtC;;;;;;;;;;OAUG;IACG,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CAsSlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,0CAA0C;AAgB/E,OAAO,KAAK,EACV,oBAAoB,EAGrB,qBAAiB;AAelB;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAoBnB,OAAO,EAAE,oBAAoB;IAKzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAGlC;IAED;;;;OAIG;IACH,IAAI,sBAAsB,IAAI,OAAO,CAQpC;IAED;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;;;;OAKG;IACH,4BAA4B,CAC1B,KAAK,EAAE,qBAAqB,CAAC,0BAA0B,GACtD,IAAI;IAwCP;;;;;OAKG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAWxD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAoBrD;;;;;;;;;;OAUG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtC;;;;;;;;;;OAUG;IACG,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CAwSlD"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAE7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,0CAA0C;AAgB/E,OAAO,KAAK,EACV,oBAAoB,EAGrB,qBAAiB;AAgBlB;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAoBnB,OAAO,EAAE,oBAAoB;IAKzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAGlC;IAED;;;;OAIG;IACH,IAAI,sBAAsB,IAAI,OAAO,CAQpC;IAED;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;;;;OAKG;IACH,4BAA4B,CAC1B,KAAK,EAAE,qBAAqB,CAAC,0BAA0B,GACtD,IAAI;IAwCP;;;;;OAKG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAWxD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAoBrD;;;;;;;;;;OAUG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtC;;;;;;;;;;OAUG;IACG,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CAsSlD"}
@@ -17,7 +17,7 @@ import { TraceName } from "../analytics/index.mjs";
17
17
  import { getProfileId } from "../authentication/index.mjs";
18
18
  import { createLocalGroupsFromUserStorage, performLegacyAccountSyncing, syncGroupsMetadata, syncGroupMetadata, syncWalletMetadata } from "../syncing/index.mjs";
19
19
  import { getAllGroupsFromUserStorage, getGroupFromUserStorage, getWalletFromUserStorage, pushGroupToUserStorageBatch } from "../user-storage/index.mjs";
20
- import { createStateSnapshot, restoreStateFromSnapshot, getLocalEntropyWallets, getLocalGroupsForEntropyWallet } from "../utils/index.mjs";
20
+ import { createStateSnapshot, restoreStateFromSnapshot, getLocalEntropyWallets, getLocalGroupsForEntropyWallet, toErrorMessage } from "../utils/index.mjs";
21
21
  /**
22
22
  * Service responsible for managing all backup and sync operations.
23
23
  *
@@ -257,7 +257,7 @@ async function _BackupAndSyncService_performFullSyncInner() {
257
257
  }
258
258
  }
259
259
  catch (error) {
260
- const errorMessage = error instanceof Error ? error.message : String(error);
260
+ const errorMessage = toErrorMessage(error);
261
261
  const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;
262
262
  backupAndSyncLogger(errorString);
263
263
  throw new Error(errorString);
@@ -284,7 +284,7 @@ async function _BackupAndSyncService_performFullSyncInner() {
284
284
  await syncGroupsMetadata(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), wallet, groupsFromUserStorage, entropySourceId, walletProfileId);
285
285
  }
286
286
  catch (error) {
287
- const errorMessage = error instanceof Error ? error.message : String(error);
287
+ const errorMessage = toErrorMessage(error);
288
288
  const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;
289
289
  backupAndSyncLogger(errorString);
290
290
  // Attempt to rollback state changes for this wallet
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletType } from '@metamask/account-api';\nimport type { UserStorageController } from '@metamask/profile-sync-controller';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountTreeControllerState } from '../../types';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport { TraceName } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n getAllGroupsFromUserStorage,\n getGroupFromUserStorage,\n getWalletFromUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage';\nimport {\n createStateSnapshot,\n restoreStateFromSnapshot,\n getLocalEntropyWallets,\n getLocalGroupsForEntropyWallet,\n} from '../utils';\nimport type { StateSnapshot } from '../utils';\n\n/**\n * Service responsible for managing all backup and sync operations.\n *\n * This service handles:\n * - Full sync operations\n * - Single item sync operations\n * - Sync queue management\n * - Sync state management\n */\nexport class BackupAndSyncService {\n readonly #context: BackupAndSyncContext;\n\n /**\n * Queue manager for atomic sync operations.\n */\n readonly #atomicSyncQueue: AtomicSyncQueue;\n\n /**\n * Cached promise for ongoing full sync operations.\n * Ensures multiple callers await the same sync operation.\n */\n #ongoingFullSyncPromise: Promise<void> | null = null;\n\n /**\n * Cached promise for the first ongoing full sync operation.\n * Ensures multiple callers await the same sync operation.\n */\n #firstOngoingFullSyncPromise: Promise<void> | null = null;\n\n constructor(context: BackupAndSyncContext) {\n this.#context = context;\n this.#atomicSyncQueue = new AtomicSyncQueue();\n }\n\n /**\n * Checks if syncing is currently in progress.\n *\n * @returns True if syncing is in progress.\n */\n get isInProgress(): boolean {\n return this.#context.controller.state.isAccountTreeSyncingInProgress;\n }\n\n /**\n * Checks if the account tree has been synced at least once.\n *\n * @returns True if the account tree has been synced at least once.\n */\n get hasSyncedAtLeastOnce(): boolean {\n return this.#context.controller.state\n .hasAccountTreeSyncingSyncedAtLeastOnce;\n }\n\n /**\n * Checks if backup and sync is enabled by checking UserStorageController state.\n *\n * @returns True if backup and sync + account syncing is enabled.\n */\n get isBackupAndSyncEnabled(): boolean {\n const userStorageControllerState = this.#context.messenger.call(\n 'UserStorageController:getState',\n );\n const { isAccountSyncingEnabled, isBackupAndSyncEnabled } =\n userStorageControllerState;\n\n return isBackupAndSyncEnabled && isAccountSyncingEnabled;\n }\n\n /**\n * Clears the atomic queue and resets ongoing operations.\n */\n clearState(): void {\n this.#atomicSyncQueue.clear();\n this.#ongoingFullSyncPromise = null;\n this.#firstOngoingFullSyncPromise = null;\n }\n\n /**\n * Handles changes to the user storage state.\n * Used to clear the backup and sync service state.\n *\n * @param state - The new user storage state.\n */\n handleUserStorageStateChange(\n state: UserStorageController.UserStorageControllerState,\n ): void {\n if (!state.isAccountSyncingEnabled || !state.isBackupAndSyncEnabled) {\n // If either syncing is disabled, clear the account tree state\n this.clearState();\n }\n }\n\n /**\n * Gets the entropy wallet associated with the given wallet ID.\n *\n * @param walletId - The wallet ID to look up.\n * @returns The associated entropy wallet, or undefined if not found.\n */\n #getEntropyWallet(\n walletId: AccountWalletId,\n ): AccountWalletEntropyObject | undefined {\n const wallet = this.#context.controller.state.accountTree.wallets[walletId];\n return wallet?.type === AccountWalletType.Entropy ? wallet : undefined;\n }\n\n /**\n * Sets up cleanup for ongoing sync promise tracking without affecting error propagation.\n *\n * @param promise - The promise to track and clean up\n * @returns The same promise (for chaining)\n */\n #setupOngoingPromiseCleanup(promise: Promise<void>): Promise<void> {\n this.#ongoingFullSyncPromise = promise;\n // Set up cleanup without affecting the returned promise\n promise\n .finally(() => {\n this.#ongoingFullSyncPromise = null;\n })\n .catch(() => {\n // Only ignore errors from the cleanup operation itself\n // The original promise errors are still propagated to callers\n });\n return promise;\n }\n\n /**\n * Enqueues a single wallet sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (!this.isBackupAndSyncEnabled || !this.hasSyncedAtLeastOnce) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleWalletSyncInner(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.isBackupAndSyncEnabled ||\n !this.hasSyncedAtLeastOnce ||\n // This prevents rate limiting scenarios where full syncs trigger group creations\n // that in turn enqueue the same single group syncs that the full sync just did.\n // This can very rarely lead to inconsistencies, but will be fixed on the next full sync.\n // TODO: let's improve this in the future by tracking the updates done in the full sync and\n // comparing against that.\n this.isInProgress\n ) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleGroupSyncInner(groupId),\n );\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n * If a full sync is already in progress, it will return the ongoing promise.\n * This clears the atomic sync queue before starting the full sync.\n *\n * NOTE: in some very edge cases, this can be ran concurrently if triggered quickly after\n * toggling back and forth the backup and sync feature from the UI.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSync(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n // If there's an ongoing sync (including first sync), return it\n if (this.#ongoingFullSyncPromise) {\n return this.#ongoingFullSyncPromise;\n }\n\n // Create a new ongoing sync (sequential calls after previous completed)\n const newSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(() =>\n this.#performFullSyncInner(),\n );\n\n // First sync setup - create and cache the first sync promise\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = newSyncPromise;\n }\n\n return this.#setupOngoingPromiseCleanup(newSyncPromise);\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync has already completed, it will resolve and NOT start a new sync.\n *\n * This clears the atomic sync queue before starting the full sync.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSyncAtLeastOnce(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(\n () => this.#performFullSyncInner(),\n );\n // eslint-disable-next-line no-void\n void this.#setupOngoingPromiseCleanup(this.#firstOngoingFullSyncPromise);\n }\n\n return this.#firstOngoingFullSyncPromise;\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * This method performs a comprehensive sync operation that:\n * 1. Identifies all local entropy wallets that can be synchronized\n * 2. Performs legacy account syncing if needed (for backwards compatibility)\n * - Disables subsequent legacy syncing by setting a flag in user storage\n * - Exits early if multichain account syncing is disabled after legacy sync\n * 3. Executes multichain account syncing for each wallet:\n * - Syncs wallet metadata bidirectionally\n * - Creates missing local groups from user storage data (or pushes local groups if none exist remotely)\n * - Refreshes local state to reflect newly created groups\n * - Syncs group metadata bidirectionally\n *\n * The sync is atomic per wallet with rollback on errors, but continues processing other wallets\n * if individual wallet sync fails. A global lock prevents concurrent sync operations.\n *\n * During this process, all other atomic multichain related user storage updates are blocked.\n *\n * @throws Will throw if the sync operation encounters unrecoverable errors\n */\n async #performFullSyncInner(): Promise<void> {\n // Prevent multiple syncs from running at the same time.\n // Also prevents atomic updates from being applied while syncing is in progress.\n if (this.isInProgress) {\n return;\n }\n\n // Set isAccountTreeSyncingInProgress immediately to prevent race conditions\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n // 1. Identifies all local entropy wallets that can be synchronized\n const localSyncableWallets = getLocalEntropyWallets(this.#context);\n\n if (!localSyncableWallets.length) {\n // No wallets to sync, just return. This shouldn't happen.\n return;\n }\n\n // 2. Iterate over each local wallet\n for (const wallet of localSyncableWallets) {\n const entropySourceId = wallet.metadata.entropy.id;\n\n let walletProfileId: ProfileId;\n let walletFromUserStorage: UserStorageSyncedWallet | null;\n let groupsFromUserStorage: UserStorageSyncedWalletGroup[];\n\n try {\n walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n [walletFromUserStorage, groupsFromUserStorage] = await Promise.all([\n getWalletFromUserStorage(this.#context, entropySourceId),\n getAllGroupsFromUserStorage(this.#context, entropySourceId),\n ]);\n\n // 2.1 Decide if we need to perform legacy account syncing\n if (\n !walletFromUserStorage ||\n !walletFromUserStorage.isLegacyAccountSyncingDisabled\n ) {\n // 2.2 Perform legacy account syncing\n // This will migrate legacy account data to the new structure.\n // This operation will only be performed once.\n await performLegacyAccountSyncing(\n this.#context,\n entropySourceId,\n walletProfileId,\n );\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n throw new Error(errorString);\n }\n\n // 3. Execute multichain account syncing\n let stateSnapshot: StateSnapshot | undefined;\n\n try {\n // 3.1 Wallet syncing\n // Create a state snapshot before processing each wallet for potential rollback\n stateSnapshot = createStateSnapshot(this.#context);\n\n // Sync wallet metadata bidirectionally\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n\n // 3.2 Groups syncing\n // If groups data does not exist in user storage yet, create it\n if (!groupsFromUserStorage.length) {\n // If no groups exist in user storage, we can push all groups from the wallet to the user storage and exit\n await pushGroupToUserStorageBatch(\n this.#context,\n getLocalGroupsForEntropyWallet(this.#context, wallet.id),\n entropySourceId,\n );\n\n continue; // No need to proceed with metadata comparison if groups are new\n }\n\n // Create local groups for each group from user storage if they do not exist\n // This will ensure that we have all groups available locally before syncing metadata\n await createLocalGroupsFromUserStorage(\n this.#context,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n\n // Sync group metadata bidirectionally\n await syncGroupsMetadata(\n this.#context,\n wallet,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n\n // Attempt to rollback state changes for this wallet\n try {\n if (!stateSnapshot) {\n throw new Error(\n `State snapshot is missing for wallet ${wallet.id}`,\n );\n }\n restoreStateFromSnapshot(this.#context, stateSnapshot);\n backupAndSyncLogger(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n } catch (rollbackError) {\n backupAndSyncLogger(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n backupAndSyncLogger('Error during multichain account syncing:', error);\n throw error;\n }\n\n this.#context.controllerStateUpdateFn((state) => {\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n });\n };\n\n // Execute the big sync function with tracing and ensure state cleanup\n try {\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\n );\n } finally {\n // Always reset state, regardless of success or failure\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n },\n );\n }\n }\n\n /**\n * Performs a single wallet's bidirectional metadata sync with user storage.\n *\n * @param walletId - The wallet ID to sync.\n */\n async #performSingleWalletSyncInner(\n walletId: AccountWalletId,\n ): Promise<void> {\n try {\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n const walletFromUserStorage = await getWalletFromUserStorage(\n this.#context,\n entropySourceId,\n );\n\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Performs a single group's bidirectional metadata sync with user storage.\n *\n * @param groupId - The group ID to sync.\n */\n async #performSingleGroupSyncInner(groupId: AccountGroupId): Promise<void> {\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const group = wallet.groups[groupId];\n if (!group) {\n return;\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n // Get the specific group from user storage\n const groupFromUserStorage = await getGroupFromUserStorage(\n this.#context,\n entropySourceId,\n group.metadata.entropy.groupIndex,\n );\n\n await syncGroupMetadata(\n this.#context,\n group,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(`Error in single group sync for ${groupId}:`, error);\n throw error;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletType } from '@metamask/account-api';\nimport type { UserStorageController } from '@metamask/profile-sync-controller';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountTreeControllerState } from '../../types';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport { TraceName } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n getAllGroupsFromUserStorage,\n getGroupFromUserStorage,\n getWalletFromUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage';\nimport {\n createStateSnapshot,\n restoreStateFromSnapshot,\n getLocalEntropyWallets,\n getLocalGroupsForEntropyWallet,\n toErrorMessage,\n} from '../utils';\nimport type { StateSnapshot } from '../utils';\n\n/**\n * Service responsible for managing all backup and sync operations.\n *\n * This service handles:\n * - Full sync operations\n * - Single item sync operations\n * - Sync queue management\n * - Sync state management\n */\nexport class BackupAndSyncService {\n readonly #context: BackupAndSyncContext;\n\n /**\n * Queue manager for atomic sync operations.\n */\n readonly #atomicSyncQueue: AtomicSyncQueue;\n\n /**\n * Cached promise for ongoing full sync operations.\n * Ensures multiple callers await the same sync operation.\n */\n #ongoingFullSyncPromise: Promise<void> | null = null;\n\n /**\n * Cached promise for the first ongoing full sync operation.\n * Ensures multiple callers await the same sync operation.\n */\n #firstOngoingFullSyncPromise: Promise<void> | null = null;\n\n constructor(context: BackupAndSyncContext) {\n this.#context = context;\n this.#atomicSyncQueue = new AtomicSyncQueue();\n }\n\n /**\n * Checks if syncing is currently in progress.\n *\n * @returns True if syncing is in progress.\n */\n get isInProgress(): boolean {\n return this.#context.controller.state.isAccountTreeSyncingInProgress;\n }\n\n /**\n * Checks if the account tree has been synced at least once.\n *\n * @returns True if the account tree has been synced at least once.\n */\n get hasSyncedAtLeastOnce(): boolean {\n return this.#context.controller.state\n .hasAccountTreeSyncingSyncedAtLeastOnce;\n }\n\n /**\n * Checks if backup and sync is enabled by checking UserStorageController state.\n *\n * @returns True if backup and sync + account syncing is enabled.\n */\n get isBackupAndSyncEnabled(): boolean {\n const userStorageControllerState = this.#context.messenger.call(\n 'UserStorageController:getState',\n );\n const { isAccountSyncingEnabled, isBackupAndSyncEnabled } =\n userStorageControllerState;\n\n return isBackupAndSyncEnabled && isAccountSyncingEnabled;\n }\n\n /**\n * Clears the atomic queue and resets ongoing operations.\n */\n clearState(): void {\n this.#atomicSyncQueue.clear();\n this.#ongoingFullSyncPromise = null;\n this.#firstOngoingFullSyncPromise = null;\n }\n\n /**\n * Handles changes to the user storage state.\n * Used to clear the backup and sync service state.\n *\n * @param state - The new user storage state.\n */\n handleUserStorageStateChange(\n state: UserStorageController.UserStorageControllerState,\n ): void {\n if (!state.isAccountSyncingEnabled || !state.isBackupAndSyncEnabled) {\n // If either syncing is disabled, clear the account tree state\n this.clearState();\n }\n }\n\n /**\n * Gets the entropy wallet associated with the given wallet ID.\n *\n * @param walletId - The wallet ID to look up.\n * @returns The associated entropy wallet, or undefined if not found.\n */\n #getEntropyWallet(\n walletId: AccountWalletId,\n ): AccountWalletEntropyObject | undefined {\n const wallet = this.#context.controller.state.accountTree.wallets[walletId];\n return wallet?.type === AccountWalletType.Entropy ? wallet : undefined;\n }\n\n /**\n * Sets up cleanup for ongoing sync promise tracking without affecting error propagation.\n *\n * @param promise - The promise to track and clean up\n * @returns The same promise (for chaining)\n */\n #setupOngoingPromiseCleanup(promise: Promise<void>): Promise<void> {\n this.#ongoingFullSyncPromise = promise;\n // Set up cleanup without affecting the returned promise\n promise\n .finally(() => {\n this.#ongoingFullSyncPromise = null;\n })\n .catch(() => {\n // Only ignore errors from the cleanup operation itself\n // The original promise errors are still propagated to callers\n });\n return promise;\n }\n\n /**\n * Enqueues a single wallet sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (!this.isBackupAndSyncEnabled || !this.hasSyncedAtLeastOnce) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleWalletSyncInner(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation (fire-and-forget).\n * If the first full sync has not yet occurred, it does nothing.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.isBackupAndSyncEnabled ||\n !this.hasSyncedAtLeastOnce ||\n // This prevents rate limiting scenarios where full syncs trigger group creations\n // that in turn enqueue the same single group syncs that the full sync just did.\n // This can very rarely lead to inconsistencies, but will be fixed on the next full sync.\n // TODO: let's improve this in the future by tracking the updates done in the full sync and\n // comparing against that.\n this.isInProgress\n ) {\n return;\n }\n\n // eslint-disable-next-line no-void\n void this.#atomicSyncQueue.enqueue(() =>\n this.#performSingleGroupSyncInner(groupId),\n );\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n * If a full sync is already in progress, it will return the ongoing promise.\n * This clears the atomic sync queue before starting the full sync.\n *\n * NOTE: in some very edge cases, this can be ran concurrently if triggered quickly after\n * toggling back and forth the backup and sync feature from the UI.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSync(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n // If there's an ongoing sync (including first sync), return it\n if (this.#ongoingFullSyncPromise) {\n return this.#ongoingFullSyncPromise;\n }\n\n // Create a new ongoing sync (sequential calls after previous completed)\n const newSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(() =>\n this.#performFullSyncInner(),\n );\n\n // First sync setup - create and cache the first sync promise\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = newSyncPromise;\n }\n\n return this.#setupOngoingPromiseCleanup(newSyncPromise);\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * If the first ever full sync is already in progress, it will return the ongoing promise.\n * If the first ever full sync has already completed, it will resolve and NOT start a new sync.\n *\n * This clears the atomic sync queue before starting the full sync.\n *\n * @returns A promise that resolves when the sync is complete.\n */\n async performFullSyncAtLeastOnce(): Promise<void> {\n if (!this.isBackupAndSyncEnabled) {\n return undefined;\n }\n\n if (!this.#firstOngoingFullSyncPromise) {\n this.#firstOngoingFullSyncPromise = this.#atomicSyncQueue.clearAndEnqueue(\n () => this.#performFullSyncInner(),\n );\n // eslint-disable-next-line no-void\n void this.#setupOngoingPromiseCleanup(this.#firstOngoingFullSyncPromise);\n }\n\n return this.#firstOngoingFullSyncPromise;\n }\n\n /**\n * Performs a full synchronization of the local account tree with user storage, ensuring consistency\n * between local state and cloud-stored account data.\n *\n * This method performs a comprehensive sync operation that:\n * 1. Identifies all local entropy wallets that can be synchronized\n * 2. Performs legacy account syncing if needed (for backwards compatibility)\n * - Disables subsequent legacy syncing by setting a flag in user storage\n * - Exits early if multichain account syncing is disabled after legacy sync\n * 3. Executes multichain account syncing for each wallet:\n * - Syncs wallet metadata bidirectionally\n * - Creates missing local groups from user storage data (or pushes local groups if none exist remotely)\n * - Refreshes local state to reflect newly created groups\n * - Syncs group metadata bidirectionally\n *\n * The sync is atomic per wallet with rollback on errors, but continues processing other wallets\n * if individual wallet sync fails. A global lock prevents concurrent sync operations.\n *\n * During this process, all other atomic multichain related user storage updates are blocked.\n *\n * @throws Will throw if the sync operation encounters unrecoverable errors\n */\n async #performFullSyncInner(): Promise<void> {\n // Prevent multiple syncs from running at the same time.\n // Also prevents atomic updates from being applied while syncing is in progress.\n if (this.isInProgress) {\n return;\n }\n\n // Set isAccountTreeSyncingInProgress immediately to prevent race conditions\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n // 1. Identifies all local entropy wallets that can be synchronized\n const localSyncableWallets = getLocalEntropyWallets(this.#context);\n\n if (!localSyncableWallets.length) {\n // No wallets to sync, just return. This shouldn't happen.\n return;\n }\n\n // 2. Iterate over each local wallet\n for (const wallet of localSyncableWallets) {\n const entropySourceId = wallet.metadata.entropy.id;\n\n let walletProfileId: ProfileId;\n let walletFromUserStorage: UserStorageSyncedWallet | null;\n let groupsFromUserStorage: UserStorageSyncedWalletGroup[];\n\n try {\n walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n [walletFromUserStorage, groupsFromUserStorage] = await Promise.all([\n getWalletFromUserStorage(this.#context, entropySourceId),\n getAllGroupsFromUserStorage(this.#context, entropySourceId),\n ]);\n\n // 2.1 Decide if we need to perform legacy account syncing\n if (\n !walletFromUserStorage ||\n !walletFromUserStorage.isLegacyAccountSyncingDisabled\n ) {\n // 2.2 Perform legacy account syncing\n // This will migrate legacy account data to the new structure.\n // This operation will only be performed once.\n await performLegacyAccountSyncing(\n this.#context,\n entropySourceId,\n walletProfileId,\n );\n }\n } catch (error) {\n const errorMessage = toErrorMessage(error);\n const errorString = `Legacy syncing failed for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n throw new Error(errorString);\n }\n\n // 3. Execute multichain account syncing\n let stateSnapshot: StateSnapshot | undefined;\n\n try {\n // 3.1 Wallet syncing\n // Create a state snapshot before processing each wallet for potential rollback\n stateSnapshot = createStateSnapshot(this.#context);\n\n // Sync wallet metadata bidirectionally\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n\n // 3.2 Groups syncing\n // If groups data does not exist in user storage yet, create it\n if (!groupsFromUserStorage.length) {\n // If no groups exist in user storage, we can push all groups from the wallet to the user storage and exit\n await pushGroupToUserStorageBatch(\n this.#context,\n getLocalGroupsForEntropyWallet(this.#context, wallet.id),\n entropySourceId,\n );\n\n continue; // No need to proceed with metadata comparison if groups are new\n }\n\n // Create local groups for each group from user storage if they do not exist\n // This will ensure that we have all groups available locally before syncing metadata\n await createLocalGroupsFromUserStorage(\n this.#context,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n\n // Sync group metadata bidirectionally\n await syncGroupsMetadata(\n this.#context,\n wallet,\n groupsFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n const errorMessage = toErrorMessage(error);\n const errorString = `Error during multichain account syncing for wallet ${wallet.id}: ${errorMessage}`;\n\n backupAndSyncLogger(errorString);\n\n // Attempt to rollback state changes for this wallet\n try {\n if (!stateSnapshot) {\n throw new Error(\n `State snapshot is missing for wallet ${wallet.id}`,\n );\n }\n restoreStateFromSnapshot(this.#context, stateSnapshot);\n backupAndSyncLogger(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n } catch (rollbackError) {\n backupAndSyncLogger(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n backupAndSyncLogger('Error during multichain account syncing:', error);\n throw error;\n }\n\n this.#context.controllerStateUpdateFn((state) => {\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n });\n };\n\n // Execute the big sync function with tracing and ensure state cleanup\n try {\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\n );\n } finally {\n // Always reset state, regardless of success or failure\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n },\n );\n }\n }\n\n /**\n * Performs a single wallet's bidirectional metadata sync with user storage.\n *\n * @param walletId - The wallet ID to sync.\n */\n async #performSingleWalletSyncInner(\n walletId: AccountWalletId,\n ): Promise<void> {\n try {\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n const walletFromUserStorage = await getWalletFromUserStorage(\n this.#context,\n entropySourceId,\n );\n\n await syncWalletMetadata(\n this.#context,\n wallet,\n walletFromUserStorage,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Performs a single group's bidirectional metadata sync with user storage.\n *\n * @param groupId - The group ID to sync.\n */\n async #performSingleGroupSyncInner(groupId: AccountGroupId): Promise<void> {\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet = this.#getEntropyWallet(walletId);\n if (!wallet) {\n return; // Only sync entropy wallets\n }\n\n const group = wallet.groups[groupId];\n if (!group) {\n return;\n }\n\n const entropySourceId = wallet.metadata.entropy.id;\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n // Get the specific group from user storage\n const groupFromUserStorage = await getGroupFromUserStorage(\n this.#context,\n entropySourceId,\n group.metadata.entropy.groupIndex,\n );\n\n await syncGroupMetadata(\n this.#context,\n group,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n backupAndSyncLogger(`Error in single group sync for ${groupId}:`, error);\n throw error;\n }\n }\n}\n"]}
@@ -8,6 +8,7 @@ const analytics_1 = require("../analytics/index.cjs");
8
8
  const types_1 = require("../types.cjs");
9
9
  const network_operations_1 = require("../user-storage/network-operations.cjs");
10
10
  const utils_1 = require("../utils/index.cjs");
11
+ const errors_1 = require("../utils/errors.cjs");
11
12
  /**
12
13
  * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).
13
14
  * This is an optimized version that creates all groups in one operation instead of
@@ -59,7 +60,7 @@ const createMultichainAccountGroupsBatch = async (context, entropySourceId, maxG
59
60
  // Any missing Snap accounts will be added later with alignment.
60
61
  (0, logger_1.backupAndSyncLogger)(`Failed to create account groups batch:`,
61
62
  // istanbul ignore next
62
- error instanceof Error ? error.message : String(error));
63
+ (0, errors_1.toErrorMessage)(error));
63
64
  }
64
65
  };
65
66
  exports.createMultichainAccountGroupsBatch = createMultichainAccountGroupsBatch;
@@ -1 +1 @@
1
- {"version":3,"file":"group.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"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","sourcesContent":["import { toMultichainAccountWalletId } from '@metamask/account-api';\n\nimport { compareAndSyncMetadata } from './metadata';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type { BackupAndSyncAnalyticsAction } from '../analytics';\nimport { BackupAndSyncAnalyticsEvent } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n pushGroupToUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage/network-operations';\nimport { getLocalGroupsForEntropyWallet } from '../utils';\n\n/**\n * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).\n * This is an optimized version that creates all groups in one operation instead of\n * creating them sequentially.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param maxGroupIndex - Number of account groups to create in batch.\n * @param profileId - The profile ID for analytics.\n * @param analyticsAction - The analytics action to log for each created group.\n * @returns Array of created group IDs.\n */\nexport const createMultichainAccountGroupsBatch = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n maxGroupIndex: number,\n profileId: ProfileId,\n analyticsAction: BackupAndSyncAnalyticsAction,\n): Promise<void> => {\n const numberOfAccountGroupsToCreate = maxGroupIndex + 1; // maxGroupIndex is zero-based, so we add 1 to get the count.\n backupAndSyncLogger(\n `Creating ${numberOfAccountGroupsToCreate} account groups (batch) for entropy source: ${entropySourceId}`,\n );\n\n // Capture the set of group indices that already exist before the batch call,\n // so we can correctly identify newly created groups after the call completes.\n const walletId = toMultichainAccountWalletId(entropySourceId);\n const existingGroupIndices = new Set(\n getLocalGroupsForEntropyWallet(context, walletId).map(\n (group) => group.metadata.entropy.groupIndex,\n ),\n );\n\n try {\n // Call the batched creation method (this is idempotent).\n const groups = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: entropySourceId,\n fromGroupIndex: 0,\n toGroupIndex: maxGroupIndex,\n },\n );\n\n // Contains all groups (existing + newly created).\n for (const group of groups) {\n // TODO: A group should not be null here, but EVM provider might fail to create some groups sometimes, which means\n // we can end up having an \"empty group\" for some time.\n if (group) {\n const didGroupAlreadyExist = existingGroupIndices.has(group.groupIndex);\n\n if (!didGroupAlreadyExist) {\n context.emitAnalyticsEventFn({\n action: analyticsAction,\n profileId,\n });\n }\n }\n }\n\n backupAndSyncLogger(`Successfully created ${groups.length} groups (batch)`);\n } catch (error) {\n // This can happen if the Snap Keyring is not ready yet when invoking\n // `MultichainAccountService:createMultichainAccountGroups`.\n // Since `MultichainAccountService:createMultichainAccountGroups` will at\n // least create the EVM account and the account group before throwing, we can safely\n // ignore this error and swallow it.\n // Any missing Snap accounts will be added later with alignment.\n\n backupAndSyncLogger(\n `Failed to create account groups batch:`,\n // istanbul ignore next\n error instanceof Error ? error.message : String(error),\n );\n }\n};\n\n/**\n * Creates local groups from user storage groups.\n *\n * @param context - The sync context containing controller and messenger.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function createLocalGroupsFromUserStorage(\n context: BackupAndSyncContext,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const maxGroupIndex = Math.max(\n ...groupsFromUserStorage.map((group) => group.groupIndex),\n );\n\n // Creating multichain account group is idempotent, so we can safely\n // re-create every groups starting from 0.\n // Use batch creation for better performance.\n await createMultichainAccountGroupsBatch(\n context,\n entropySourceId,\n maxGroupIndex,\n profileId,\n BackupAndSyncAnalyticsEvent.GroupAdded,\n );\n}\n\n/**\n * Syncs group metadata fields and determines if the group needs to be pushed to user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against.\n * @param profileId - The profile ID for analytics.\n * @returns A promise that resolves to true if the group needs to be pushed to user storage.\n */\nasync function syncGroupMetadataAndCheckIfPushNeeded(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null | undefined,\n profileId: ProfileId,\n): Promise<boolean> {\n const groupPersistedMetadata =\n context.controller.state.accountGroupsMetadata[localGroup.id];\n\n if (!groupFromUserStorage) {\n backupAndSyncLogger(\n `Group ${localGroup.id} did not exist in user storage, pushing to user storage...`,\n );\n\n return true;\n }\n\n // Track if we need to push this group to user storage\n let shouldPushGroup = false;\n\n // Compare and sync name metadata\n const shouldPushForName = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.name,\n userStorageMetadata: groupFromUserStorage.name,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.name.schema.value.is(value),\n applyLocalUpdate: (name: string) => {\n context.controller.setAccountGroupName(localGroup.id, name, true);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupRenamed,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForName;\n\n // Compare and sync pinned metadata\n const shouldPushForPinned = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.pinned,\n userStorageMetadata: groupFromUserStorage.pinned,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.pinned.schema.value.is(value),\n applyLocalUpdate: (pinned: boolean) => {\n context.controller.setAccountGroupPinned(localGroup.id, pinned);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupPinnedStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForPinned;\n\n // Compare and sync hidden metadata\n const shouldPushForHidden = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.hidden,\n userStorageMetadata: groupFromUserStorage.hidden,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.hidden.schema.value.is(value),\n applyLocalUpdate: (hidden: boolean) => {\n context.controller.setAccountGroupHidden(localGroup.id, hidden);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupHiddenStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForHidden;\n\n return shouldPushGroup;\n}\n\n/**\n * Syncs a single group's metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against (or null if it doesn't exist).\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupMetadata(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null,\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localGroup,\n groupFromUserStorage,\n profileId,\n );\n\n if (shouldPushGroup) {\n await pushGroupToUserStorage(context, localGroup, entropySourceId);\n }\n}\n\n/**\n * Syncs group metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param wallet - The local wallet containing the groups.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupsMetadata(\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const localSyncableGroupsToBePushedToUserStorage: AccountGroupMultichainAccountObject[] =\n [];\n\n const localSyncableGroups = getLocalGroupsForEntropyWallet(\n context,\n wallet.id,\n );\n\n for (const localSyncableGroup of localSyncableGroups) {\n const groupFromUserStorage = groupsFromUserStorage.find(\n (group) =>\n group.groupIndex === localSyncableGroup.metadata.entropy.groupIndex,\n );\n\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localSyncableGroup,\n groupFromUserStorage,\n profileId,\n );\n\n // Add to push list if any metadata needs to be updated in user storage\n if (shouldPushGroup) {\n localSyncableGroupsToBePushedToUserStorage.push(localSyncableGroup);\n }\n }\n\n // Push all groups that need to be updated to user storage\n if (localSyncableGroupsToBePushedToUserStorage.length > 0) {\n await pushGroupToUserStorageBatch(\n context,\n localSyncableGroupsToBePushedToUserStorage,\n entropySourceId,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"group.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"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","sourcesContent":["import { toMultichainAccountWalletId } from '@metamask/account-api';\n\nimport { compareAndSyncMetadata } from './metadata';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type { BackupAndSyncAnalyticsAction } from '../analytics';\nimport { BackupAndSyncAnalyticsEvent } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n pushGroupToUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage/network-operations';\nimport { getLocalGroupsForEntropyWallet } from '../utils';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).\n * This is an optimized version that creates all groups in one operation instead of\n * creating them sequentially.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param maxGroupIndex - Number of account groups to create in batch.\n * @param profileId - The profile ID for analytics.\n * @param analyticsAction - The analytics action to log for each created group.\n * @returns Array of created group IDs.\n */\nexport const createMultichainAccountGroupsBatch = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n maxGroupIndex: number,\n profileId: ProfileId,\n analyticsAction: BackupAndSyncAnalyticsAction,\n): Promise<void> => {\n const numberOfAccountGroupsToCreate = maxGroupIndex + 1; // maxGroupIndex is zero-based, so we add 1 to get the count.\n backupAndSyncLogger(\n `Creating ${numberOfAccountGroupsToCreate} account groups (batch) for entropy source: ${entropySourceId}`,\n );\n\n // Capture the set of group indices that already exist before the batch call,\n // so we can correctly identify newly created groups after the call completes.\n const walletId = toMultichainAccountWalletId(entropySourceId);\n const existingGroupIndices = new Set(\n getLocalGroupsForEntropyWallet(context, walletId).map(\n (group) => group.metadata.entropy.groupIndex,\n ),\n );\n\n try {\n // Call the batched creation method (this is idempotent).\n const groups = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: entropySourceId,\n fromGroupIndex: 0,\n toGroupIndex: maxGroupIndex,\n },\n );\n\n // Contains all groups (existing + newly created).\n for (const group of groups) {\n // TODO: A group should not be null here, but EVM provider might fail to create some groups sometimes, which means\n // we can end up having an \"empty group\" for some time.\n if (group) {\n const didGroupAlreadyExist = existingGroupIndices.has(group.groupIndex);\n\n if (!didGroupAlreadyExist) {\n context.emitAnalyticsEventFn({\n action: analyticsAction,\n profileId,\n });\n }\n }\n }\n\n backupAndSyncLogger(`Successfully created ${groups.length} groups (batch)`);\n } catch (error) {\n // This can happen if the Snap Keyring is not ready yet when invoking\n // `MultichainAccountService:createMultichainAccountGroups`.\n // Since `MultichainAccountService:createMultichainAccountGroups` will at\n // least create the EVM account and the account group before throwing, we can safely\n // ignore this error and swallow it.\n // Any missing Snap accounts will be added later with alignment.\n\n backupAndSyncLogger(\n `Failed to create account groups batch:`,\n // istanbul ignore next\n toErrorMessage(error),\n );\n }\n};\n\n/**\n * Creates local groups from user storage groups.\n *\n * @param context - The sync context containing controller and messenger.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function createLocalGroupsFromUserStorage(\n context: BackupAndSyncContext,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const maxGroupIndex = Math.max(\n ...groupsFromUserStorage.map((group) => group.groupIndex),\n );\n\n // Creating multichain account group is idempotent, so we can safely\n // re-create every groups starting from 0.\n // Use batch creation for better performance.\n await createMultichainAccountGroupsBatch(\n context,\n entropySourceId,\n maxGroupIndex,\n profileId,\n BackupAndSyncAnalyticsEvent.GroupAdded,\n );\n}\n\n/**\n * Syncs group metadata fields and determines if the group needs to be pushed to user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against.\n * @param profileId - The profile ID for analytics.\n * @returns A promise that resolves to true if the group needs to be pushed to user storage.\n */\nasync function syncGroupMetadataAndCheckIfPushNeeded(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null | undefined,\n profileId: ProfileId,\n): Promise<boolean> {\n const groupPersistedMetadata =\n context.controller.state.accountGroupsMetadata[localGroup.id];\n\n if (!groupFromUserStorage) {\n backupAndSyncLogger(\n `Group ${localGroup.id} did not exist in user storage, pushing to user storage...`,\n );\n\n return true;\n }\n\n // Track if we need to push this group to user storage\n let shouldPushGroup = false;\n\n // Compare and sync name metadata\n const shouldPushForName = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.name,\n userStorageMetadata: groupFromUserStorage.name,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.name.schema.value.is(value),\n applyLocalUpdate: (name: string) => {\n context.controller.setAccountGroupName(localGroup.id, name, true);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupRenamed,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForName;\n\n // Compare and sync pinned metadata\n const shouldPushForPinned = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.pinned,\n userStorageMetadata: groupFromUserStorage.pinned,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.pinned.schema.value.is(value),\n applyLocalUpdate: (pinned: boolean) => {\n context.controller.setAccountGroupPinned(localGroup.id, pinned);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupPinnedStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForPinned;\n\n // Compare and sync hidden metadata\n const shouldPushForHidden = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.hidden,\n userStorageMetadata: groupFromUserStorage.hidden,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.hidden.schema.value.is(value),\n applyLocalUpdate: (hidden: boolean) => {\n context.controller.setAccountGroupHidden(localGroup.id, hidden);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupHiddenStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForHidden;\n\n return shouldPushGroup;\n}\n\n/**\n * Syncs a single group's metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against (or null if it doesn't exist).\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupMetadata(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null,\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localGroup,\n groupFromUserStorage,\n profileId,\n );\n\n if (shouldPushGroup) {\n await pushGroupToUserStorage(context, localGroup, entropySourceId);\n }\n}\n\n/**\n * Syncs group metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param wallet - The local wallet containing the groups.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupsMetadata(\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const localSyncableGroupsToBePushedToUserStorage: AccountGroupMultichainAccountObject[] =\n [];\n\n const localSyncableGroups = getLocalGroupsForEntropyWallet(\n context,\n wallet.id,\n );\n\n for (const localSyncableGroup of localSyncableGroups) {\n const groupFromUserStorage = groupsFromUserStorage.find(\n (group) =>\n group.groupIndex === localSyncableGroup.metadata.entropy.groupIndex,\n );\n\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localSyncableGroup,\n groupFromUserStorage,\n profileId,\n );\n\n // Add to push list if any metadata needs to be updated in user storage\n if (shouldPushGroup) {\n localSyncableGroupsToBePushedToUserStorage.push(localSyncableGroup);\n }\n }\n\n // Push all groups that need to be updated to user storage\n if (localSyncableGroupsToBePushedToUserStorage.length > 0) {\n await pushGroupToUserStorageBatch(\n context,\n localSyncableGroupsToBePushedToUserStorage,\n entropySourceId,\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"group.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAAqB;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,oCAA0B;AAEnD,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,qBAAiB;AAOlB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kCAAkC,YACpC,oBAAoB,mBACZ,MAAM,iBACR,MAAM,aACV,SAAS,mBACH,4BAA4B,KAC5C,QAAQ,IAAI,CAyDd,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,oBAAoB,EAC7B,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAef;AAwFD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,mCAAmC,EAC/C,oBAAoB,EAAE,4BAA4B,GAAG,IAAI,EACzD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,0BAA0B,EAClC,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
1
+ {"version":3,"file":"group.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAAqB;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,oCAA0B;AAEnD,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,qBAAiB;AAQlB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kCAAkC,YACpC,oBAAoB,mBACZ,MAAM,iBACR,MAAM,aACV,SAAS,mBACH,4BAA4B,KAC5C,QAAQ,IAAI,CAyDd,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,oBAAoB,EAC7B,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAef;AAwFD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,mCAAmC,EAC/C,oBAAoB,EAAE,4BAA4B,GAAG,IAAI,EACzD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,0BAA0B,EAClC,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
@@ -1 +1 @@
1
- {"version":3,"file":"group.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAAqB;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,oCAA0B;AAEnD,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,qBAAiB;AAOlB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kCAAkC,YACpC,oBAAoB,mBACZ,MAAM,iBACR,MAAM,aACV,SAAS,mBACH,4BAA4B,KAC5C,QAAQ,IAAI,CAyDd,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,oBAAoB,EAC7B,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAef;AAwFD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,mCAAmC,EAC/C,oBAAoB,EAAE,4BAA4B,GAAG,IAAI,EACzD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,0BAA0B,EAClC,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
1
+ {"version":3,"file":"group.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAAqB;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,oCAA0B;AAEnD,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,qBAAiB;AAQlB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kCAAkC,YACpC,oBAAoB,mBACZ,MAAM,iBACR,MAAM,aACV,SAAS,mBACH,4BAA4B,KAC5C,QAAQ,IAAI,CAyDd,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,oBAAoB,EAC7B,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAef;AAwFD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,mCAAmC,EAC/C,oBAAoB,EAAE,4BAA4B,GAAG,IAAI,EACzD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,0BAA0B,EAClC,qBAAqB,EAAE,4BAA4B,EAAE,EACrD,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
@@ -5,6 +5,7 @@ import { BackupAndSyncAnalyticsEvent } from "../analytics/index.mjs";
5
5
  import { UserStorageSyncedWalletGroupSchema } from "../types.mjs";
6
6
  import { pushGroupToUserStorage, pushGroupToUserStorageBatch } from "../user-storage/network-operations.mjs";
7
7
  import { getLocalGroupsForEntropyWallet } from "../utils/index.mjs";
8
+ import { toErrorMessage } from "../utils/errors.mjs";
8
9
  /**
9
10
  * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).
10
11
  * This is an optimized version that creates all groups in one operation instead of
@@ -56,7 +57,7 @@ export const createMultichainAccountGroupsBatch = async (context, entropySourceI
56
57
  // Any missing Snap accounts will be added later with alignment.
57
58
  backupAndSyncLogger(`Failed to create account groups batch:`,
58
59
  // istanbul ignore next
59
- error instanceof Error ? error.message : String(error));
60
+ toErrorMessage(error));
60
61
  }
61
62
  };
62
63
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"group.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,8BAA8B;AAEpE,OAAO,EAAE,sBAAsB,EAAE,uBAAmB;AAEpD,OAAO,EAAE,mBAAmB,EAAE,yBAAqB;AAGnD,OAAO,EAAE,2BAA2B,EAAE,+BAAqB;AAE3D,OAAO,EAAE,kCAAkC,EAAE,qBAAiB;AAK9D,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC5B,+CAA2C;AAC5C,OAAO,EAAE,8BAA8B,EAAE,2BAAiB;AAE1D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,KAAK,EACrD,OAA6B,EAC7B,eAAuB,EACvB,aAAqB,EACrB,SAAoB,EACpB,eAA6C,EAC9B,EAAE;IACjB,MAAM,6BAA6B,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,6DAA6D;IACtH,mBAAmB,CACjB,YAAY,6BAA6B,+CAA+C,eAAe,EAAE,CAC1G,CAAC;IAEF,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,2BAA2B,CAAC,eAAe,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAClC,8BAA8B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CACnD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAC7C,CACF,CAAC;IAEF,IAAI,CAAC;QACH,yDAAyD;QACzD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,CACzC,wDAAwD,EACxD;YACE,aAAa,EAAE,eAAe;YAC9B,cAAc,EAAE,CAAC;YACjB,YAAY,EAAE,aAAa;SAC5B,CACF,CAAC;QAEF,kDAAkD;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,kHAAkH;YAClH,uDAAuD;YACvD,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,oBAAoB,GAAG,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAExE,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBAC1B,OAAO,CAAC,oBAAoB,CAAC;wBAC3B,MAAM,EAAE,eAAe;wBACvB,SAAS;qBACV,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,mBAAmB,CAAC,wBAAwB,MAAM,CAAC,MAAM,iBAAiB,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qEAAqE;QACrE,4DAA4D;QAC5D,yEAAyE;QACzE,oFAAoF;QACpF,oCAAoC;QACpC,gEAAgE;QAEhE,mBAAmB,CACjB,wCAAwC;QACxC,uBAAuB;QACvB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAA6B,EAC7B,qBAAqD,EACrD,eAAuB,EACvB,SAAoB;IAEpB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAC5B,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAC1D,CAAC;IAEF,oEAAoE;IACpE,0CAA0C;IAC1C,6CAA6C;IAC7C,MAAM,kCAAkC,CACtC,OAAO,EACP,eAAe,EACf,aAAa,EACb,SAAS,EACT,2BAA2B,CAAC,UAAU,CACvC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,qCAAqC,CAClD,OAA6B,EAC7B,UAA+C,EAC/C,oBAAqE,EACrE,SAAoB;IAEpB,MAAM,sBAAsB,GAC1B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAEhE,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,mBAAmB,CACjB,SAAS,UAAU,CAAC,EAAE,4DAA4D,CACnF,CAAC;QAEF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sDAAsD;IACtD,IAAI,eAAe,GAAG,KAAK,CAAC;IAE5B,iCAAiC;IACjC,MAAM,iBAAiB,GAAG,MAAM,sBAAsB,CAAC;QACrD,OAAO;QACP,aAAa,EAAE,sBAAsB,EAAE,IAAI;QAC3C,mBAAmB,EAAE,oBAAoB,CAAC,IAAI;QAC9C,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE,CAClC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;QACvE,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE;YACjC,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACpE,CAAC;QACD,SAAS,EAAE;YACT,MAAM,EAAE,2BAA2B,CAAC,YAAY;YAChD,SAAS;SACV;KACF,CAAC,CAAC;IAEH,eAAe,KAAf,eAAe,GAAK,iBAAiB,EAAC;IAEtC,mCAAmC;IACnC,MAAM,mBAAmB,GAAG,MAAM,sBAAsB,CAAC;QACvD,OAAO;QACP,aAAa,EAAE,sBAAsB,EAAE,MAAM;QAC7C,mBAAmB,EAAE,oBAAoB,CAAC,MAAM;QAChD,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE,CAClC,kCAAkC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;QACzE,gBAAgB,EAAE,CAAC,MAAe,EAAE,EAAE;YACpC,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QACD,SAAS,EAAE;YACT,MAAM,EAAE,2BAA2B,CAAC,wBAAwB;YAC5D,SAAS;SACV;KACF,CAAC,CAAC;IAEH,eAAe,KAAf,eAAe,GAAK,mBAAmB,EAAC;IAExC,mCAAmC;IACnC,MAAM,mBAAmB,GAAG,MAAM,sBAAsB,CAAC;QACvD,OAAO;QACP,aAAa,EAAE,sBAAsB,EAAE,MAAM;QAC7C,mBAAmB,EAAE,oBAAoB,CAAC,MAAM;QAChD,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE,CAClC,kCAAkC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;QACzE,gBAAgB,EAAE,CAAC,MAAe,EAAE,EAAE;YACpC,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QACD,SAAS,EAAE;YACT,MAAM,EAAE,2BAA2B,CAAC,wBAAwB;YAC5D,SAAS;SACV;KACF,CAAC,CAAC;IAEH,eAAe,KAAf,eAAe,GAAK,mBAAmB,EAAC;IAExC,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAA6B,EAC7B,UAA+C,EAC/C,oBAAyD,EACzD,eAAuB,EACvB,SAAoB;IAEpB,MAAM,eAAe,GAAG,MAAM,qCAAqC,CACjE,OAAO,EACP,UAAU,EACV,oBAAoB,EACpB,SAAS,CACV,CAAC;IAEF,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,sBAAsB,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;IACrE,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAA6B,EAC7B,MAAkC,EAClC,qBAAqD,EACrD,eAAuB,EACvB,SAAoB;IAEpB,MAAM,0CAA0C,GAC9C,EAAE,CAAC;IAEL,MAAM,mBAAmB,GAAG,8BAA8B,CACxD,OAAO,EACP,MAAM,CAAC,EAAE,CACV,CAAC;IAEF,KAAK,MAAM,kBAAkB,IAAI,mBAAmB,EAAE,CAAC;QACrD,MAAM,oBAAoB,GAAG,qBAAqB,CAAC,IAAI,CACrD,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,UAAU,KAAK,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CACtE,CAAC;QAEF,MAAM,eAAe,GAAG,MAAM,qCAAqC,CACjE,OAAO,EACP,kBAAkB,EAClB,oBAAoB,EACpB,SAAS,CACV,CAAC;QAEF,uEAAuE;QACvE,IAAI,eAAe,EAAE,CAAC;YACpB,0CAA0C,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,IAAI,0CAA0C,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,2BAA2B,CAC/B,OAAO,EACP,0CAA0C,EAC1C,eAAe,CAChB,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { toMultichainAccountWalletId } from '@metamask/account-api';\n\nimport { compareAndSyncMetadata } from './metadata';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type { BackupAndSyncAnalyticsAction } from '../analytics';\nimport { BackupAndSyncAnalyticsEvent } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n pushGroupToUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage/network-operations';\nimport { getLocalGroupsForEntropyWallet } from '../utils';\n\n/**\n * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).\n * This is an optimized version that creates all groups in one operation instead of\n * creating them sequentially.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param maxGroupIndex - Number of account groups to create in batch.\n * @param profileId - The profile ID for analytics.\n * @param analyticsAction - The analytics action to log for each created group.\n * @returns Array of created group IDs.\n */\nexport const createMultichainAccountGroupsBatch = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n maxGroupIndex: number,\n profileId: ProfileId,\n analyticsAction: BackupAndSyncAnalyticsAction,\n): Promise<void> => {\n const numberOfAccountGroupsToCreate = maxGroupIndex + 1; // maxGroupIndex is zero-based, so we add 1 to get the count.\n backupAndSyncLogger(\n `Creating ${numberOfAccountGroupsToCreate} account groups (batch) for entropy source: ${entropySourceId}`,\n );\n\n // Capture the set of group indices that already exist before the batch call,\n // so we can correctly identify newly created groups after the call completes.\n const walletId = toMultichainAccountWalletId(entropySourceId);\n const existingGroupIndices = new Set(\n getLocalGroupsForEntropyWallet(context, walletId).map(\n (group) => group.metadata.entropy.groupIndex,\n ),\n );\n\n try {\n // Call the batched creation method (this is idempotent).\n const groups = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: entropySourceId,\n fromGroupIndex: 0,\n toGroupIndex: maxGroupIndex,\n },\n );\n\n // Contains all groups (existing + newly created).\n for (const group of groups) {\n // TODO: A group should not be null here, but EVM provider might fail to create some groups sometimes, which means\n // we can end up having an \"empty group\" for some time.\n if (group) {\n const didGroupAlreadyExist = existingGroupIndices.has(group.groupIndex);\n\n if (!didGroupAlreadyExist) {\n context.emitAnalyticsEventFn({\n action: analyticsAction,\n profileId,\n });\n }\n }\n }\n\n backupAndSyncLogger(`Successfully created ${groups.length} groups (batch)`);\n } catch (error) {\n // This can happen if the Snap Keyring is not ready yet when invoking\n // `MultichainAccountService:createMultichainAccountGroups`.\n // Since `MultichainAccountService:createMultichainAccountGroups` will at\n // least create the EVM account and the account group before throwing, we can safely\n // ignore this error and swallow it.\n // Any missing Snap accounts will be added later with alignment.\n\n backupAndSyncLogger(\n `Failed to create account groups batch:`,\n // istanbul ignore next\n error instanceof Error ? error.message : String(error),\n );\n }\n};\n\n/**\n * Creates local groups from user storage groups.\n *\n * @param context - The sync context containing controller and messenger.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function createLocalGroupsFromUserStorage(\n context: BackupAndSyncContext,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const maxGroupIndex = Math.max(\n ...groupsFromUserStorage.map((group) => group.groupIndex),\n );\n\n // Creating multichain account group is idempotent, so we can safely\n // re-create every groups starting from 0.\n // Use batch creation for better performance.\n await createMultichainAccountGroupsBatch(\n context,\n entropySourceId,\n maxGroupIndex,\n profileId,\n BackupAndSyncAnalyticsEvent.GroupAdded,\n );\n}\n\n/**\n * Syncs group metadata fields and determines if the group needs to be pushed to user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against.\n * @param profileId - The profile ID for analytics.\n * @returns A promise that resolves to true if the group needs to be pushed to user storage.\n */\nasync function syncGroupMetadataAndCheckIfPushNeeded(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null | undefined,\n profileId: ProfileId,\n): Promise<boolean> {\n const groupPersistedMetadata =\n context.controller.state.accountGroupsMetadata[localGroup.id];\n\n if (!groupFromUserStorage) {\n backupAndSyncLogger(\n `Group ${localGroup.id} did not exist in user storage, pushing to user storage...`,\n );\n\n return true;\n }\n\n // Track if we need to push this group to user storage\n let shouldPushGroup = false;\n\n // Compare and sync name metadata\n const shouldPushForName = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.name,\n userStorageMetadata: groupFromUserStorage.name,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.name.schema.value.is(value),\n applyLocalUpdate: (name: string) => {\n context.controller.setAccountGroupName(localGroup.id, name, true);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupRenamed,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForName;\n\n // Compare and sync pinned metadata\n const shouldPushForPinned = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.pinned,\n userStorageMetadata: groupFromUserStorage.pinned,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.pinned.schema.value.is(value),\n applyLocalUpdate: (pinned: boolean) => {\n context.controller.setAccountGroupPinned(localGroup.id, pinned);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupPinnedStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForPinned;\n\n // Compare and sync hidden metadata\n const shouldPushForHidden = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.hidden,\n userStorageMetadata: groupFromUserStorage.hidden,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.hidden.schema.value.is(value),\n applyLocalUpdate: (hidden: boolean) => {\n context.controller.setAccountGroupHidden(localGroup.id, hidden);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupHiddenStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForHidden;\n\n return shouldPushGroup;\n}\n\n/**\n * Syncs a single group's metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against (or null if it doesn't exist).\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupMetadata(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null,\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localGroup,\n groupFromUserStorage,\n profileId,\n );\n\n if (shouldPushGroup) {\n await pushGroupToUserStorage(context, localGroup, entropySourceId);\n }\n}\n\n/**\n * Syncs group metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param wallet - The local wallet containing the groups.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupsMetadata(\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const localSyncableGroupsToBePushedToUserStorage: AccountGroupMultichainAccountObject[] =\n [];\n\n const localSyncableGroups = getLocalGroupsForEntropyWallet(\n context,\n wallet.id,\n );\n\n for (const localSyncableGroup of localSyncableGroups) {\n const groupFromUserStorage = groupsFromUserStorage.find(\n (group) =>\n group.groupIndex === localSyncableGroup.metadata.entropy.groupIndex,\n );\n\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localSyncableGroup,\n groupFromUserStorage,\n profileId,\n );\n\n // Add to push list if any metadata needs to be updated in user storage\n if (shouldPushGroup) {\n localSyncableGroupsToBePushedToUserStorage.push(localSyncableGroup);\n }\n }\n\n // Push all groups that need to be updated to user storage\n if (localSyncableGroupsToBePushedToUserStorage.length > 0) {\n await pushGroupToUserStorageBatch(\n context,\n localSyncableGroupsToBePushedToUserStorage,\n entropySourceId,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"group.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/group.ts"],"names":[],"mappings":"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","sourcesContent":["import { toMultichainAccountWalletId } from '@metamask/account-api';\n\nimport { compareAndSyncMetadata } from './metadata';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type { BackupAndSyncAnalyticsAction } from '../analytics';\nimport { BackupAndSyncAnalyticsEvent } from '../analytics';\nimport type { ProfileId } from '../authentication';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport type {\n BackupAndSyncContext,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport {\n pushGroupToUserStorage,\n pushGroupToUserStorageBatch,\n} from '../user-storage/network-operations';\nimport { getLocalGroupsForEntropyWallet } from '../utils';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Creates multiple multichain account groups in batch (from 0 to maxGroupIndex).\n * This is an optimized version that creates all groups in one operation instead of\n * creating them sequentially.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param maxGroupIndex - Number of account groups to create in batch.\n * @param profileId - The profile ID for analytics.\n * @param analyticsAction - The analytics action to log for each created group.\n * @returns Array of created group IDs.\n */\nexport const createMultichainAccountGroupsBatch = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n maxGroupIndex: number,\n profileId: ProfileId,\n analyticsAction: BackupAndSyncAnalyticsAction,\n): Promise<void> => {\n const numberOfAccountGroupsToCreate = maxGroupIndex + 1; // maxGroupIndex is zero-based, so we add 1 to get the count.\n backupAndSyncLogger(\n `Creating ${numberOfAccountGroupsToCreate} account groups (batch) for entropy source: ${entropySourceId}`,\n );\n\n // Capture the set of group indices that already exist before the batch call,\n // so we can correctly identify newly created groups after the call completes.\n const walletId = toMultichainAccountWalletId(entropySourceId);\n const existingGroupIndices = new Set(\n getLocalGroupsForEntropyWallet(context, walletId).map(\n (group) => group.metadata.entropy.groupIndex,\n ),\n );\n\n try {\n // Call the batched creation method (this is idempotent).\n const groups = await context.messenger.call(\n 'MultichainAccountService:createMultichainAccountGroups',\n {\n entropySource: entropySourceId,\n fromGroupIndex: 0,\n toGroupIndex: maxGroupIndex,\n },\n );\n\n // Contains all groups (existing + newly created).\n for (const group of groups) {\n // TODO: A group should not be null here, but EVM provider might fail to create some groups sometimes, which means\n // we can end up having an \"empty group\" for some time.\n if (group) {\n const didGroupAlreadyExist = existingGroupIndices.has(group.groupIndex);\n\n if (!didGroupAlreadyExist) {\n context.emitAnalyticsEventFn({\n action: analyticsAction,\n profileId,\n });\n }\n }\n }\n\n backupAndSyncLogger(`Successfully created ${groups.length} groups (batch)`);\n } catch (error) {\n // This can happen if the Snap Keyring is not ready yet when invoking\n // `MultichainAccountService:createMultichainAccountGroups`.\n // Since `MultichainAccountService:createMultichainAccountGroups` will at\n // least create the EVM account and the account group before throwing, we can safely\n // ignore this error and swallow it.\n // Any missing Snap accounts will be added later with alignment.\n\n backupAndSyncLogger(\n `Failed to create account groups batch:`,\n // istanbul ignore next\n toErrorMessage(error),\n );\n }\n};\n\n/**\n * Creates local groups from user storage groups.\n *\n * @param context - The sync context containing controller and messenger.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function createLocalGroupsFromUserStorage(\n context: BackupAndSyncContext,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const maxGroupIndex = Math.max(\n ...groupsFromUserStorage.map((group) => group.groupIndex),\n );\n\n // Creating multichain account group is idempotent, so we can safely\n // re-create every groups starting from 0.\n // Use batch creation for better performance.\n await createMultichainAccountGroupsBatch(\n context,\n entropySourceId,\n maxGroupIndex,\n profileId,\n BackupAndSyncAnalyticsEvent.GroupAdded,\n );\n}\n\n/**\n * Syncs group metadata fields and determines if the group needs to be pushed to user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against.\n * @param profileId - The profile ID for analytics.\n * @returns A promise that resolves to true if the group needs to be pushed to user storage.\n */\nasync function syncGroupMetadataAndCheckIfPushNeeded(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null | undefined,\n profileId: ProfileId,\n): Promise<boolean> {\n const groupPersistedMetadata =\n context.controller.state.accountGroupsMetadata[localGroup.id];\n\n if (!groupFromUserStorage) {\n backupAndSyncLogger(\n `Group ${localGroup.id} did not exist in user storage, pushing to user storage...`,\n );\n\n return true;\n }\n\n // Track if we need to push this group to user storage\n let shouldPushGroup = false;\n\n // Compare and sync name metadata\n const shouldPushForName = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.name,\n userStorageMetadata: groupFromUserStorage.name,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.name.schema.value.is(value),\n applyLocalUpdate: (name: string) => {\n context.controller.setAccountGroupName(localGroup.id, name, true);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupRenamed,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForName;\n\n // Compare and sync pinned metadata\n const shouldPushForPinned = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.pinned,\n userStorageMetadata: groupFromUserStorage.pinned,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.pinned.schema.value.is(value),\n applyLocalUpdate: (pinned: boolean) => {\n context.controller.setAccountGroupPinned(localGroup.id, pinned);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupPinnedStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForPinned;\n\n // Compare and sync hidden metadata\n const shouldPushForHidden = await compareAndSyncMetadata({\n context,\n localMetadata: groupPersistedMetadata?.hidden,\n userStorageMetadata: groupFromUserStorage.hidden,\n validateUserStorageValue: (value) =>\n UserStorageSyncedWalletGroupSchema.schema.hidden.schema.value.is(value),\n applyLocalUpdate: (hidden: boolean) => {\n context.controller.setAccountGroupHidden(localGroup.id, hidden);\n },\n analytics: {\n action: BackupAndSyncAnalyticsEvent.GroupHiddenStatusChanged,\n profileId,\n },\n });\n\n shouldPushGroup ||= shouldPushForHidden;\n\n return shouldPushGroup;\n}\n\n/**\n * Syncs a single group's metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param localGroup - The local group to sync.\n * @param groupFromUserStorage - The group from user storage to compare against (or null if it doesn't exist).\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupMetadata(\n context: BackupAndSyncContext,\n localGroup: AccountGroupMultichainAccountObject,\n groupFromUserStorage: UserStorageSyncedWalletGroup | null,\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localGroup,\n groupFromUserStorage,\n profileId,\n );\n\n if (shouldPushGroup) {\n await pushGroupToUserStorage(context, localGroup, entropySourceId);\n }\n}\n\n/**\n * Syncs group metadata between local and user storage.\n *\n * @param context - The sync context containing controller and messenger.\n * @param wallet - The local wallet containing the groups.\n * @param groupsFromUserStorage - Array of groups from user storage.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport async function syncGroupsMetadata(\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n groupsFromUserStorage: UserStorageSyncedWalletGroup[],\n entropySourceId: string,\n profileId: ProfileId,\n): Promise<void> {\n const localSyncableGroupsToBePushedToUserStorage: AccountGroupMultichainAccountObject[] =\n [];\n\n const localSyncableGroups = getLocalGroupsForEntropyWallet(\n context,\n wallet.id,\n );\n\n for (const localSyncableGroup of localSyncableGroups) {\n const groupFromUserStorage = groupsFromUserStorage.find(\n (group) =>\n group.groupIndex === localSyncableGroup.metadata.entropy.groupIndex,\n );\n\n const shouldPushGroup = await syncGroupMetadataAndCheckIfPushNeeded(\n context,\n localSyncableGroup,\n groupFromUserStorage,\n profileId,\n );\n\n // Add to push list if any metadata needs to be updated in user storage\n if (shouldPushGroup) {\n localSyncableGroupsToBePushedToUserStorage.push(localSyncableGroup);\n }\n }\n\n // Push all groups that need to be updated to user storage\n if (localSyncableGroupsToBePushedToUserStorage.length > 0) {\n await pushGroupToUserStorageBatch(\n context,\n localSyncableGroupsToBePushedToUserStorage,\n entropySourceId,\n );\n }\n}\n"]}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseLegacyAccountFromUserStorageResponse = exports.parseGroupFromUserStorageResponse = exports.parseWalletFromUserStorageResponse = exports.formatGroupForUserStorageUsage = exports.formatWalletForUserStorageUsage = void 0;
4
+ const superstruct_1 = require("@metamask/superstruct");
4
5
  const validation_1 = require("./validation.cjs");
6
+ const logger_1 = require("../../logger.cjs");
7
+ const types_1 = require("../types.cjs");
8
+ const errors_1 = require("../utils/errors.cjs");
5
9
  /**
6
10
  * Formats the wallet for user storage usage.
7
11
  * This function extracts the necessary metadata from the wallet
@@ -32,10 +36,20 @@ exports.formatWalletForUserStorageUsage = formatWalletForUserStorageUsage;
32
36
  const formatGroupForUserStorageUsage = (context, group) => {
33
37
  // This can be null if the user has not manually set a name, pinned or hidden the group
34
38
  const persistedGroupMetadata = context.controller.state.accountGroupsMetadata[group.id];
35
- return {
36
- ...(persistedGroupMetadata ?? {}),
37
- groupIndex: group.metadata.entropy.groupIndex,
38
- };
39
+ const { groupIndex } = group.metadata.entropy;
40
+ try {
41
+ // We mask and we try catch, since `mask` will throw if the persisted metadata has
42
+ // fields with wrong types.
43
+ return (0, superstruct_1.mask)({
44
+ ...(persistedGroupMetadata ?? {}),
45
+ groupIndex,
46
+ }, types_1.UserStorageSyncedWalletGroupSchema);
47
+ }
48
+ catch (error) {
49
+ (0, logger_1.backupAndSyncLogger)(`Error trying to format group for user storage usage: ${(0, errors_1.toErrorMessage)(error)}`);
50
+ // If anything goes wrong with this group, we use blank metadata for it.
51
+ return { groupIndex };
52
+ }
39
53
  };
40
54
  exports.formatGroupForUserStorageUsage = formatGroupForUserStorageUsage;
41
55
  /**
@@ -54,7 +68,7 @@ const parseWalletFromUserStorageResponse = (wallet) => {
54
68
  return walletData;
55
69
  }
56
70
  catch (error) {
57
- throw new Error(`Error trying to parse wallet from user storage response: ${error instanceof Error ? error.message : String(error)}`);
71
+ throw new Error(`Error trying to parse wallet from user storage response: ${(0, errors_1.toErrorMessage)(error)}`);
58
72
  }
59
73
  };
60
74
  exports.parseWalletFromUserStorageResponse = parseWalletFromUserStorageResponse;
@@ -74,7 +88,7 @@ const parseGroupFromUserStorageResponse = (group) => {
74
88
  return groupData;
75
89
  }
76
90
  catch (error) {
77
- throw new Error(`Error trying to parse group from user storage response: ${error instanceof Error ? error.message : String(error)}`);
91
+ throw new Error(`Error trying to parse group from user storage response: ${(0, errors_1.toErrorMessage)(error)}`);
78
92
  }
79
93
  };
80
94
  exports.parseGroupFromUserStorageResponse = parseGroupFromUserStorageResponse;
@@ -94,7 +108,7 @@ const parseLegacyAccountFromUserStorageResponse = (account) => {
94
108
  return accountData;
95
109
  }
96
110
  catch (error) {
97
- throw new Error(`Error trying to parse legacy account from user storage response: ${error instanceof Error ? error.message : String(error)}`);
111
+ throw new Error(`Error trying to parse legacy account from user storage response: ${(0, errors_1.toErrorMessage)(error)}`);
98
112
  }
99
113
  };
100
114
  exports.parseLegacyAccountFromUserStorageResponse = parseLegacyAccountFromUserStorageResponse;
@@ -1 +1 @@
1
- {"version":3,"file":"format-utils.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":";;;AAAA,iDAIsB;AAUtB;;;;;;;;GAQG;AACI,MAAM,+BAA+B,GAAG,CAC7C,OAA6B,EAC7B,MAAkC,EACT,EAAE;IAC3B,2DAA2D;IAC3D,MAAM,uBAAuB,GAC3B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE7D,OAAO;QACL,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC;QAClC,8BAA8B,EAAE,IAAI,EAAE,yHAAyH;KAChK,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,+BAA+B,mCAY1C;AAEF;;;;;;;;GAQG;AACI,MAAM,8BAA8B,GAAG,CAC5C,OAA6B,EAC7B,KAA0C,EACZ,EAAE;IAChC,uFAAuF;IACvF,MAAM,sBAAsB,GAC1B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAE3D,OAAO;QACL,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC;QACjC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU;KAC9C,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,8BAA8B,kCAYzC;AAEF;;;;;;;;GAQG;AACI,MAAM,kCAAkC,GAAG,CAChD,MAAc,EACW,EAAE;IAC3B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtC,IAAA,yCAA4B,EAAC,UAAU,CAAC,CAAC;QACzC,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,4DAA4D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACrH,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,kCAAkC,sCAY7C;AAEF;;;;;;;;GAQG;AACI,MAAM,iCAAiC,GAAG,CAC/C,KAAa,EACiB,EAAE;IAChC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAA,wCAA2B,EAAC,SAAS,CAAC,CAAC;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,2DAA2D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACpH,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,iCAAiC,qCAY5C;AAEF;;;;;;;;GAQG;AACI,MAAM,yCAAyC,GAAG,CACvD,OAAe,EACiB,EAAE;IAClC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAA,gDAAmC,EAAC,WAAW,CAAC,CAAC;QACjD,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,oEAAoE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7H,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,yCAAyC,6CAYpD","sourcesContent":["import {\n assertValidUserStorageWallet,\n assertValidUserStorageGroup,\n assertValidLegacyUserStorageAccount,\n} from './validation';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\n\n/**\n * Formats the wallet for user storage usage.\n * This function extracts the necessary metadata from the wallet\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet object to format.\n * @returns The formatted wallet for user storage.\n */\nexport const formatWalletForUserStorageUsage = (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): UserStorageSyncedWallet => {\n // This can be null if the user has not manually set a name\n const persistedWalletMetadata =\n context.controller.state.accountWalletsMetadata[wallet.id];\n\n return {\n ...(persistedWalletMetadata ?? {}),\n isLegacyAccountSyncingDisabled: true, // If we're here, it means legacy account syncing has been performed at least once, so we can disable it for this wallet.\n };\n};\n\n/**\n * Formats the group for user storage usage.\n * This function extracts the necessary metadata from the group\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param group - The group object to format.\n * @returns The formatted group for user storage.\n */\nexport const formatGroupForUserStorageUsage = (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n): UserStorageSyncedWalletGroup => {\n // This can be null if the user has not manually set a name, pinned or hidden the group\n const persistedGroupMetadata =\n context.controller.state.accountGroupsMetadata[group.id];\n\n return {\n ...(persistedGroupMetadata ?? {}),\n groupIndex: group.metadata.entropy.groupIndex,\n };\n};\n\n/**\n * Parses the wallet from user storage response.\n * This function attempts to parse the wallet data from a string format\n * and returns it as a UserStorageSyncedWallet object.\n *\n * @param wallet - The wallet data in string format.\n * @returns The parsed UserStorageSyncedWallet object.\n * @throws If the wallet data is not in valid JSON format or fails validation.\n */\nexport const parseWalletFromUserStorageResponse = (\n wallet: string,\n): UserStorageSyncedWallet => {\n try {\n const walletData = JSON.parse(wallet);\n assertValidUserStorageWallet(walletData);\n return walletData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse wallet from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n\n/**\n * Parses the group from user storage response.\n * This function attempts to parse the group data from a string format\n * and returns it as a UserStorageSyncedWalletGroup object.\n *\n * @param group - The group data in string format.\n * @returns The parsed UserStorageSyncedWalletGroup object.\n * @throws If the group data is not in valid JSON format or fails validation.\n */\nexport const parseGroupFromUserStorageResponse = (\n group: string,\n): UserStorageSyncedWalletGroup => {\n try {\n const groupData = JSON.parse(group);\n assertValidUserStorageGroup(groupData);\n return groupData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse group from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n\n/**\n * Parses the legacy account from user storage response.\n * This function attempts to parse the account data from a string format\n * and returns it as a LegacyUserStorageSyncedAccount object.\n *\n * @param account - The account data in string format.\n * @returns The parsed LegacyUserStorageSyncedAccount object.\n * @throws If the account data is not in valid JSON format or fails validation.\n */\nexport const parseLegacyAccountFromUserStorageResponse = (\n account: string,\n): LegacyUserStorageSyncedAccount => {\n try {\n const accountData = JSON.parse(account);\n assertValidLegacyUserStorageAccount(accountData);\n return accountData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse legacy account from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n"]}
1
+ {"version":3,"file":"format-utils.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":";;;AAAA,uDAA6C;AAE7C,iDAIsB;AAEtB,6CAAmD;AAQnD,wCAA8D;AAC9D,gDAAiD;AAEjD;;;;;;;;GAQG;AACI,MAAM,+BAA+B,GAAG,CAC7C,OAA6B,EAC7B,MAAkC,EACT,EAAE;IAC3B,2DAA2D;IAC3D,MAAM,uBAAuB,GAC3B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE7D,OAAO;QACL,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC;QAClC,8BAA8B,EAAE,IAAI,EAAE,yHAAyH;KAChK,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,+BAA+B,mCAY1C;AAEF;;;;;;;;GAQG;AACI,MAAM,8BAA8B,GAAG,CAC5C,OAA6B,EAC7B,KAA0C,EACZ,EAAE;IAChC,uFAAuF;IACvF,MAAM,sBAAsB,GAC1B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;IAE9C,IAAI,CAAC;QACH,kFAAkF;QAClF,2BAA2B;QAC3B,OAAO,IAAA,kBAAI,EACT;YACE,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC;YACjC,UAAU;SACX,EACD,0CAAkC,CACnC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,4BAAmB,EACjB,wDAAwD,IAAA,uBAAc,EAAC,KAAK,CAAC,EAAE,CAChF,CAAC;QAEF,wEAAwE;QACxE,OAAO,EAAE,UAAU,EAAE,CAAC;IACxB,CAAC;AACH,CAAC,CAAC;AA3BW,QAAA,8BAA8B,kCA2BzC;AAEF;;;;;;;;GAQG;AACI,MAAM,kCAAkC,GAAG,CAChD,MAAc,EACW,EAAE;IAC3B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtC,IAAA,yCAA4B,EAAC,UAAU,CAAC,CAAC;QACzC,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,4DAA4D,IAAA,uBAAc,EAAC,KAAK,CAAC,EAAE,CACpF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,kCAAkC,sCAY7C;AAEF;;;;;;;;GAQG;AACI,MAAM,iCAAiC,GAAG,CAC/C,KAAa,EACiB,EAAE;IAChC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAA,wCAA2B,EAAC,SAAS,CAAC,CAAC;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,2DAA2D,IAAA,uBAAc,EAAC,KAAK,CAAC,EAAE,CACnF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,iCAAiC,qCAY5C;AAEF;;;;;;;;GAQG;AACI,MAAM,yCAAyC,GAAG,CACvD,OAAe,EACiB,EAAE;IAClC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAA,gDAAmC,EAAC,WAAW,CAAC,CAAC;QACjD,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,oEAAoE,IAAA,uBAAc,EAAC,KAAK,CAAC,EAAE,CAC5F,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,yCAAyC,6CAYpD","sourcesContent":["import { mask } from '@metamask/superstruct';\n\nimport {\n assertValidUserStorageWallet,\n assertValidUserStorageGroup,\n assertValidLegacyUserStorageAccount,\n} from './validation';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Formats the wallet for user storage usage.\n * This function extracts the necessary metadata from the wallet\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet object to format.\n * @returns The formatted wallet for user storage.\n */\nexport const formatWalletForUserStorageUsage = (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): UserStorageSyncedWallet => {\n // This can be null if the user has not manually set a name\n const persistedWalletMetadata =\n context.controller.state.accountWalletsMetadata[wallet.id];\n\n return {\n ...(persistedWalletMetadata ?? {}),\n isLegacyAccountSyncingDisabled: true, // If we're here, it means legacy account syncing has been performed at least once, so we can disable it for this wallet.\n };\n};\n\n/**\n * Formats the group for user storage usage.\n * This function extracts the necessary metadata from the group\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param group - The group object to format.\n * @returns The formatted group for user storage.\n */\nexport const formatGroupForUserStorageUsage = (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n): UserStorageSyncedWalletGroup => {\n // This can be null if the user has not manually set a name, pinned or hidden the group\n const persistedGroupMetadata =\n context.controller.state.accountGroupsMetadata[group.id];\n const { groupIndex } = group.metadata.entropy;\n\n try {\n // We mask and we try catch, since `mask` will throw if the persisted metadata has\n // fields with wrong types.\n return mask(\n {\n ...(persistedGroupMetadata ?? {}),\n groupIndex,\n },\n UserStorageSyncedWalletGroupSchema,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error trying to format group for user storage usage: ${toErrorMessage(error)}`,\n );\n\n // If anything goes wrong with this group, we use blank metadata for it.\n return { groupIndex };\n }\n};\n\n/**\n * Parses the wallet from user storage response.\n * This function attempts to parse the wallet data from a string format\n * and returns it as a UserStorageSyncedWallet object.\n *\n * @param wallet - The wallet data in string format.\n * @returns The parsed UserStorageSyncedWallet object.\n * @throws If the wallet data is not in valid JSON format or fails validation.\n */\nexport const parseWalletFromUserStorageResponse = (\n wallet: string,\n): UserStorageSyncedWallet => {\n try {\n const walletData = JSON.parse(wallet);\n assertValidUserStorageWallet(walletData);\n return walletData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse wallet from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n\n/**\n * Parses the group from user storage response.\n * This function attempts to parse the group data from a string format\n * and returns it as a UserStorageSyncedWalletGroup object.\n *\n * @param group - The group data in string format.\n * @returns The parsed UserStorageSyncedWalletGroup object.\n * @throws If the group data is not in valid JSON format or fails validation.\n */\nexport const parseGroupFromUserStorageResponse = (\n group: string,\n): UserStorageSyncedWalletGroup => {\n try {\n const groupData = JSON.parse(group);\n assertValidUserStorageGroup(groupData);\n return groupData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse group from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n\n/**\n * Parses the legacy account from user storage response.\n * This function attempts to parse the account data from a string format\n * and returns it as a LegacyUserStorageSyncedAccount object.\n *\n * @param account - The account data in string format.\n * @returns The parsed LegacyUserStorageSyncedAccount object.\n * @throws If the account data is not in valid JSON format or fails validation.\n */\nexport const parseLegacyAccountFromUserStorageResponse = (\n account: string,\n): LegacyUserStorageSyncedAccount => {\n try {\n const accountData = JSON.parse(account);\n assertValidLegacyUserStorageAccount(accountData);\n return accountData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse legacy account from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"format-utils.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AACvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAElB;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,UACrB,0BAA0B,KACjC,uBASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,YAChC,oBAAoB,SACtB,mCAAmC,KACzC,4BASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,WACrC,MAAM,KACb,uBAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,UACrC,MAAM,KACZ,4BAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yCAAyC,YAC3C,MAAM,KACd,8BAUF,CAAC"}
1
+ {"version":3,"file":"format-utils.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAIlB;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,UACrB,0BAA0B,KACjC,uBASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,YAChC,oBAAoB,SACtB,mCAAmC,KACzC,4BAwBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,WACrC,MAAM,KACb,uBAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,UACrC,MAAM,KACZ,4BAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yCAAyC,YAC3C,MAAM,KACd,8BAUF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format-utils.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AACvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAElB;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,UACrB,0BAA0B,KACjC,uBASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,YAChC,oBAAoB,SACtB,mCAAmC,KACzC,4BASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,WACrC,MAAM,KACb,uBAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,UACrC,MAAM,KACZ,4BAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yCAAyC,YAC3C,MAAM,KACd,8BAUF,CAAC"}
1
+ {"version":3,"file":"format-utils.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAIlB;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,UACrB,0BAA0B,KACjC,uBASF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,YAChC,oBAAoB,SACtB,mCAAmC,KACzC,4BAwBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,WACrC,MAAM,KACb,uBAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,UACrC,MAAM,KACZ,4BAUF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yCAAyC,YAC3C,MAAM,KACd,8BAUF,CAAC"}
@@ -1,4 +1,8 @@
1
+ import { mask } from "@metamask/superstruct";
1
2
  import { assertValidUserStorageWallet, assertValidUserStorageGroup, assertValidLegacyUserStorageAccount } from "./validation.mjs";
3
+ import { backupAndSyncLogger } from "../../logger.mjs";
4
+ import { UserStorageSyncedWalletGroupSchema } from "../types.mjs";
5
+ import { toErrorMessage } from "../utils/errors.mjs";
2
6
  /**
3
7
  * Formats the wallet for user storage usage.
4
8
  * This function extracts the necessary metadata from the wallet
@@ -28,10 +32,20 @@ export const formatWalletForUserStorageUsage = (context, wallet) => {
28
32
  export const formatGroupForUserStorageUsage = (context, group) => {
29
33
  // This can be null if the user has not manually set a name, pinned or hidden the group
30
34
  const persistedGroupMetadata = context.controller.state.accountGroupsMetadata[group.id];
31
- return {
32
- ...(persistedGroupMetadata ?? {}),
33
- groupIndex: group.metadata.entropy.groupIndex,
34
- };
35
+ const { groupIndex } = group.metadata.entropy;
36
+ try {
37
+ // We mask and we try catch, since `mask` will throw if the persisted metadata has
38
+ // fields with wrong types.
39
+ return mask({
40
+ ...(persistedGroupMetadata ?? {}),
41
+ groupIndex,
42
+ }, UserStorageSyncedWalletGroupSchema);
43
+ }
44
+ catch (error) {
45
+ backupAndSyncLogger(`Error trying to format group for user storage usage: ${toErrorMessage(error)}`);
46
+ // If anything goes wrong with this group, we use blank metadata for it.
47
+ return { groupIndex };
48
+ }
35
49
  };
36
50
  /**
37
51
  * Parses the wallet from user storage response.
@@ -49,7 +63,7 @@ export const parseWalletFromUserStorageResponse = (wallet) => {
49
63
  return walletData;
50
64
  }
51
65
  catch (error) {
52
- throw new Error(`Error trying to parse wallet from user storage response: ${error instanceof Error ? error.message : String(error)}`);
66
+ throw new Error(`Error trying to parse wallet from user storage response: ${toErrorMessage(error)}`);
53
67
  }
54
68
  };
55
69
  /**
@@ -68,7 +82,7 @@ export const parseGroupFromUserStorageResponse = (group) => {
68
82
  return groupData;
69
83
  }
70
84
  catch (error) {
71
- throw new Error(`Error trying to parse group from user storage response: ${error instanceof Error ? error.message : String(error)}`);
85
+ throw new Error(`Error trying to parse group from user storage response: ${toErrorMessage(error)}`);
72
86
  }
73
87
  };
74
88
  /**
@@ -87,7 +101,7 @@ export const parseLegacyAccountFromUserStorageResponse = (account) => {
87
101
  return accountData;
88
102
  }
89
103
  catch (error) {
90
- throw new Error(`Error trying to parse legacy account from user storage response: ${error instanceof Error ? error.message : String(error)}`);
104
+ throw new Error(`Error trying to parse legacy account from user storage response: ${toErrorMessage(error)}`);
91
105
  }
92
106
  };
93
107
  //# sourceMappingURL=format-utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-utils.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAC3B,mCAAmC,EACpC,yBAAqB;AAUtB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,OAA6B,EAC7B,MAAkC,EACT,EAAE;IAC3B,2DAA2D;IAC3D,MAAM,uBAAuB,GAC3B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE7D,OAAO;QACL,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC;QAClC,8BAA8B,EAAE,IAAI,EAAE,yHAAyH;KAChK,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,OAA6B,EAC7B,KAA0C,EACZ,EAAE;IAChC,uFAAuF;IACvF,MAAM,sBAAsB,GAC1B,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAE3D,OAAO;QACL,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC;QACjC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU;KAC9C,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAChD,MAAc,EACW,EAAE;IAC3B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtC,4BAA4B,CAAC,UAAU,CAAC,CAAC;QACzC,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,4DAA4D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACrH,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAC/C,KAAa,EACiB,EAAE;IAChC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,2BAA2B,CAAC,SAAS,CAAC,CAAC;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,2DAA2D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACpH,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yCAAyC,GAAG,CACvD,OAAe,EACiB,EAAE;IAClC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,mCAAmC,CAAC,WAAW,CAAC,CAAC;QACjD,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,oEAAoE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7H,CAAC;IACJ,CAAC;AACH,CAAC,CAAC","sourcesContent":["import {\n assertValidUserStorageWallet,\n assertValidUserStorageGroup,\n assertValidLegacyUserStorageAccount,\n} from './validation';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\n\n/**\n * Formats the wallet for user storage usage.\n * This function extracts the necessary metadata from the wallet\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet object to format.\n * @returns The formatted wallet for user storage.\n */\nexport const formatWalletForUserStorageUsage = (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): UserStorageSyncedWallet => {\n // This can be null if the user has not manually set a name\n const persistedWalletMetadata =\n context.controller.state.accountWalletsMetadata[wallet.id];\n\n return {\n ...(persistedWalletMetadata ?? {}),\n isLegacyAccountSyncingDisabled: true, // If we're here, it means legacy account syncing has been performed at least once, so we can disable it for this wallet.\n };\n};\n\n/**\n * Formats the group for user storage usage.\n * This function extracts the necessary metadata from the group\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param group - The group object to format.\n * @returns The formatted group for user storage.\n */\nexport const formatGroupForUserStorageUsage = (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n): UserStorageSyncedWalletGroup => {\n // This can be null if the user has not manually set a name, pinned or hidden the group\n const persistedGroupMetadata =\n context.controller.state.accountGroupsMetadata[group.id];\n\n return {\n ...(persistedGroupMetadata ?? {}),\n groupIndex: group.metadata.entropy.groupIndex,\n };\n};\n\n/**\n * Parses the wallet from user storage response.\n * This function attempts to parse the wallet data from a string format\n * and returns it as a UserStorageSyncedWallet object.\n *\n * @param wallet - The wallet data in string format.\n * @returns The parsed UserStorageSyncedWallet object.\n * @throws If the wallet data is not in valid JSON format or fails validation.\n */\nexport const parseWalletFromUserStorageResponse = (\n wallet: string,\n): UserStorageSyncedWallet => {\n try {\n const walletData = JSON.parse(wallet);\n assertValidUserStorageWallet(walletData);\n return walletData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse wallet from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n\n/**\n * Parses the group from user storage response.\n * This function attempts to parse the group data from a string format\n * and returns it as a UserStorageSyncedWalletGroup object.\n *\n * @param group - The group data in string format.\n * @returns The parsed UserStorageSyncedWalletGroup object.\n * @throws If the group data is not in valid JSON format or fails validation.\n */\nexport const parseGroupFromUserStorageResponse = (\n group: string,\n): UserStorageSyncedWalletGroup => {\n try {\n const groupData = JSON.parse(group);\n assertValidUserStorageGroup(groupData);\n return groupData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse group from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n\n/**\n * Parses the legacy account from user storage response.\n * This function attempts to parse the account data from a string format\n * and returns it as a LegacyUserStorageSyncedAccount object.\n *\n * @param account - The account data in string format.\n * @returns The parsed LegacyUserStorageSyncedAccount object.\n * @throws If the account data is not in valid JSON format or fails validation.\n */\nexport const parseLegacyAccountFromUserStorageResponse = (\n account: string,\n): LegacyUserStorageSyncedAccount => {\n try {\n const accountData = JSON.parse(account);\n assertValidLegacyUserStorageAccount(accountData);\n return accountData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse legacy account from user storage response: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n};\n"]}
1
+ {"version":3,"file":"format-utils.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/format-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { mask } from '@metamask/superstruct';\n\nimport {\n assertValidUserStorageWallet,\n assertValidUserStorageGroup,\n assertValidLegacyUserStorageAccount,\n} from './validation';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport { UserStorageSyncedWalletGroupSchema } from '../types';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Formats the wallet for user storage usage.\n * This function extracts the necessary metadata from the wallet\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet object to format.\n * @returns The formatted wallet for user storage.\n */\nexport const formatWalletForUserStorageUsage = (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): UserStorageSyncedWallet => {\n // This can be null if the user has not manually set a name\n const persistedWalletMetadata =\n context.controller.state.accountWalletsMetadata[wallet.id];\n\n return {\n ...(persistedWalletMetadata ?? {}),\n isLegacyAccountSyncingDisabled: true, // If we're here, it means legacy account syncing has been performed at least once, so we can disable it for this wallet.\n };\n};\n\n/**\n * Formats the group for user storage usage.\n * This function extracts the necessary metadata from the group\n * and formats it according to the user storage requirements.\n *\n * @param context - The backup and sync context.\n * @param group - The group object to format.\n * @returns The formatted group for user storage.\n */\nexport const formatGroupForUserStorageUsage = (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n): UserStorageSyncedWalletGroup => {\n // This can be null if the user has not manually set a name, pinned or hidden the group\n const persistedGroupMetadata =\n context.controller.state.accountGroupsMetadata[group.id];\n const { groupIndex } = group.metadata.entropy;\n\n try {\n // We mask and we try catch, since `mask` will throw if the persisted metadata has\n // fields with wrong types.\n return mask(\n {\n ...(persistedGroupMetadata ?? {}),\n groupIndex,\n },\n UserStorageSyncedWalletGroupSchema,\n );\n } catch (error) {\n backupAndSyncLogger(\n `Error trying to format group for user storage usage: ${toErrorMessage(error)}`,\n );\n\n // If anything goes wrong with this group, we use blank metadata for it.\n return { groupIndex };\n }\n};\n\n/**\n * Parses the wallet from user storage response.\n * This function attempts to parse the wallet data from a string format\n * and returns it as a UserStorageSyncedWallet object.\n *\n * @param wallet - The wallet data in string format.\n * @returns The parsed UserStorageSyncedWallet object.\n * @throws If the wallet data is not in valid JSON format or fails validation.\n */\nexport const parseWalletFromUserStorageResponse = (\n wallet: string,\n): UserStorageSyncedWallet => {\n try {\n const walletData = JSON.parse(wallet);\n assertValidUserStorageWallet(walletData);\n return walletData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse wallet from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n\n/**\n * Parses the group from user storage response.\n * This function attempts to parse the group data from a string format\n * and returns it as a UserStorageSyncedWalletGroup object.\n *\n * @param group - The group data in string format.\n * @returns The parsed UserStorageSyncedWalletGroup object.\n * @throws If the group data is not in valid JSON format or fails validation.\n */\nexport const parseGroupFromUserStorageResponse = (\n group: string,\n): UserStorageSyncedWalletGroup => {\n try {\n const groupData = JSON.parse(group);\n assertValidUserStorageGroup(groupData);\n return groupData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse group from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n\n/**\n * Parses the legacy account from user storage response.\n * This function attempts to parse the account data from a string format\n * and returns it as a LegacyUserStorageSyncedAccount object.\n *\n * @param account - The account data in string format.\n * @returns The parsed LegacyUserStorageSyncedAccount object.\n * @throws If the account data is not in valid JSON format or fails validation.\n */\nexport const parseLegacyAccountFromUserStorageResponse = (\n account: string,\n): LegacyUserStorageSyncedAccount => {\n try {\n const accountData = JSON.parse(account);\n assertValidLegacyUserStorageAccount(accountData);\n return accountData;\n } catch (error: unknown) {\n throw new Error(\n `Error trying to parse legacy account from user storage response: ${toErrorMessage(error)}`,\n );\n }\n};\n"]}
@@ -6,6 +6,7 @@ const constants_1 = require("./constants.cjs");
6
6
  const format_utils_1 = require("./format-utils.cjs");
7
7
  const network_utils_1 = require("./network-utils.cjs");
8
8
  const logger_1 = require("../../logger.cjs");
9
+ const errors_1 = require("../utils/errors.cjs");
9
10
  /**
10
11
  * Retrieves the wallet from user storage.
11
12
  *
@@ -26,7 +27,7 @@ const getWalletFromUserStorage = async (context, entropySourceId) => {
26
27
  return (0, format_utils_1.parseWalletFromUserStorageResponse)(walletData);
27
28
  }
28
29
  catch (error) {
29
- (0, logger_1.backupAndSyncLogger)(`Failed to parse wallet data from user storage: ${error instanceof Error ? error.message : String(error)}`);
30
+ (0, logger_1.backupAndSyncLogger)(`Failed to parse wallet data from user storage: ${(0, errors_1.toErrorMessage)(error)}`);
30
31
  return null;
31
32
  }
32
33
  });
@@ -73,7 +74,7 @@ const getAllGroupsFromUserStorage = async (context, entropySourceId) => {
73
74
  return (0, format_utils_1.parseGroupFromUserStorageResponse)(stringifiedGroup);
74
75
  }
75
76
  catch (error) {
76
- (0, logger_1.backupAndSyncLogger)(`Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`);
77
+ (0, logger_1.backupAndSyncLogger)(`Failed to parse group data from user storage: ${(0, errors_1.toErrorMessage)(error)}`);
77
78
  return null;
78
79
  }
79
80
  })
@@ -103,7 +104,7 @@ const getGroupFromUserStorage = async (context, entropySourceId, groupIndex) =>
103
104
  return (0, format_utils_1.parseGroupFromUserStorageResponse)(groupData);
104
105
  }
105
106
  catch (error) {
106
- (0, logger_1.backupAndSyncLogger)(`Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`);
107
+ (0, logger_1.backupAndSyncLogger)(`Failed to parse group data from user storage: ${(0, errors_1.toErrorMessage)(error)}`);
107
108
  return null;
108
109
  }
109
110
  });
@@ -173,7 +174,7 @@ const getAllLegacyUserStorageAccounts = async (context, entropySourceId) => {
173
174
  return (0, format_utils_1.parseLegacyAccountFromUserStorageResponse)(stringifiedAccount);
174
175
  }
175
176
  catch (error) {
176
- (0, logger_1.backupAndSyncLogger)(`Failed to parse legacy account data from user storage: ${error instanceof Error ? error.message : String(error)}`);
177
+ (0, logger_1.backupAndSyncLogger)(`Failed to parse legacy account data from user storage: ${(0, errors_1.toErrorMessage)(error)}`);
177
178
  return null;
178
179
  }
179
180
  })
@@ -1 +1 @@
1
- {"version":3,"file":"network-operations.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import { SDK } from '@metamask/profile-sync-controller';\n\nimport {\n USER_STORAGE_GROUPS_FEATURE_KEY,\n USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY,\n USER_STORAGE_WALLETS_FEATURE_KEY,\n} from './constants';\nimport {\n formatWalletForUserStorageUsage,\n formatGroupForUserStorageUsage,\n parseWalletFromUserStorageResponse,\n parseGroupFromUserStorageResponse,\n parseLegacyAccountFromUserStorageResponse,\n} from './format-utils';\nimport { executeWithRetry } from './network-utils';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\n\n/**\n * Retrieves the wallet from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns The wallet from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getWalletFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWallet | null> => {\n return executeWithRetry(async () => {\n const walletData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n entropySourceId,\n );\n if (!walletData) {\n return null;\n }\n\n try {\n backupAndSyncLogger(\n `Retrieved wallet data from user storage: ${JSON.stringify(walletData)}`,\n );\n return parseWalletFromUserStorageResponse(walletData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse wallet data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes the wallet to user storage.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet to push to user storage.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted wallet data.\n */\nexport const pushWalletToUserStorage = async (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedWallet = formatWalletForUserStorageUsage(context, wallet);\n const stringifiedWallet = JSON.stringify(formattedWallet);\n const entropySourceId = wallet.metadata.entropy.id;\n\n backupAndSyncLogger(`Pushing wallet to user storage: ${stringifiedWallet}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n stringifiedWallet,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves all groups from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns An array of groups from user storage.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllGroupsFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWalletGroup[]> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}`,\n entropySourceId,\n );\n if (!groupData) {\n return [];\n }\n\n const allGroups = groupData\n .map((stringifiedGroup) => {\n try {\n return parseGroupFromUserStorageResponse(stringifiedGroup);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n })\n .filter((group): group is UserStorageSyncedWalletGroup => group !== null);\n\n backupAndSyncLogger(\n `Retrieved groups from user storage: ${JSON.stringify(allGroups)}`,\n );\n\n return allGroups;\n });\n};\n\n/**\n * Retrieves a single group from user storage by group index.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @param groupIndex - The group index to retrieve.\n * @returns The group from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getGroupFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n groupIndex: number,\n): Promise<UserStorageSyncedWalletGroup | null> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${groupIndex}`,\n entropySourceId,\n );\n if (!groupData) {\n return null;\n }\n\n try {\n return parseGroupFromUserStorageResponse(groupData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes a group to user storage.\n *\n * @param context - The backup and sync context.\n * @param group - The group to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted group data.\n */\nexport const pushGroupToUserStorage = async (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroup = formatGroupForUserStorageUsage(context, group);\n const stringifiedGroup = JSON.stringify(formattedGroup);\n\n backupAndSyncLogger(`Pushing group to user storage: ${stringifiedGroup}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${formattedGroup.groupIndex}`,\n stringifiedGroup,\n entropySourceId,\n );\n });\n};\n\n/**\n * Pushes a batch of groups to user storage.\n *\n * @param context - The backup and sync context.\n * @param groups - The groups to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on any of the formatted group data.\n */\nexport const pushGroupToUserStorageBatch = async (\n context: BackupAndSyncContext,\n groups: AccountGroupMultichainAccountObject[],\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroups = groups.map((group) =>\n formatGroupForUserStorageUsage(context, group),\n );\n\n const entries: [string, string][] = formattedGroups.map((group) => [\n String(group.groupIndex),\n JSON.stringify(group),\n ]);\n\n backupAndSyncLogger(\n `Pushing groups to user storage: ${entries.map(([_, value]) => value).join(', ')}`,\n );\n\n return await context.messenger.call(\n 'UserStorageController:performBatchSetStorage',\n USER_STORAGE_GROUPS_FEATURE_KEY,\n entries,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves legacy user storage accounts for a specific entropy source ID.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID to retrieve data for.\n * @returns A promise that resolves with the legacy user storage accounts.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllLegacyUserStorageAccounts = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<LegacyUserStorageSyncedAccount[]> => {\n return executeWithRetry(async () => {\n const accountsData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n SDK.USER_STORAGE_FEATURE_NAMES.accounts,\n entropySourceId,\n );\n\n if (!accountsData) {\n return [];\n }\n\n const allAccounts = accountsData\n .map((stringifiedAccount) => {\n try {\n return parseLegacyAccountFromUserStorageResponse(stringifiedAccount);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse legacy account data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n })\n .filter(\n (account): account is LegacyUserStorageSyncedAccount =>\n account !== null,\n );\n\n backupAndSyncLogger(\n `Retrieved legacy accounts from user storage: ${JSON.stringify(allAccounts)}`,\n );\n\n return allAccounts;\n });\n};\n"]}
1
+ {"version":3,"file":"network-operations.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import { SDK } from '@metamask/profile-sync-controller';\n\nimport {\n USER_STORAGE_GROUPS_FEATURE_KEY,\n USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY,\n USER_STORAGE_WALLETS_FEATURE_KEY,\n} from './constants';\nimport {\n formatWalletForUserStorageUsage,\n formatGroupForUserStorageUsage,\n parseWalletFromUserStorageResponse,\n parseGroupFromUserStorageResponse,\n parseLegacyAccountFromUserStorageResponse,\n} from './format-utils';\nimport { executeWithRetry } from './network-utils';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Retrieves the wallet from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns The wallet from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getWalletFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWallet | null> => {\n return executeWithRetry(async () => {\n const walletData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n entropySourceId,\n );\n if (!walletData) {\n return null;\n }\n\n try {\n backupAndSyncLogger(\n `Retrieved wallet data from user storage: ${JSON.stringify(walletData)}`,\n );\n return parseWalletFromUserStorageResponse(walletData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse wallet data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes the wallet to user storage.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet to push to user storage.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted wallet data.\n */\nexport const pushWalletToUserStorage = async (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedWallet = formatWalletForUserStorageUsage(context, wallet);\n const stringifiedWallet = JSON.stringify(formattedWallet);\n const entropySourceId = wallet.metadata.entropy.id;\n\n backupAndSyncLogger(`Pushing wallet to user storage: ${stringifiedWallet}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n stringifiedWallet,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves all groups from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns An array of groups from user storage.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllGroupsFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWalletGroup[]> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}`,\n entropySourceId,\n );\n if (!groupData) {\n return [];\n }\n\n const allGroups = groupData\n .map((stringifiedGroup) => {\n try {\n return parseGroupFromUserStorageResponse(stringifiedGroup);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n })\n .filter((group): group is UserStorageSyncedWalletGroup => group !== null);\n\n backupAndSyncLogger(\n `Retrieved groups from user storage: ${JSON.stringify(allGroups)}`,\n );\n\n return allGroups;\n });\n};\n\n/**\n * Retrieves a single group from user storage by group index.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @param groupIndex - The group index to retrieve.\n * @returns The group from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getGroupFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n groupIndex: number,\n): Promise<UserStorageSyncedWalletGroup | null> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${groupIndex}`,\n entropySourceId,\n );\n if (!groupData) {\n return null;\n }\n\n try {\n return parseGroupFromUserStorageResponse(groupData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes a group to user storage.\n *\n * @param context - The backup and sync context.\n * @param group - The group to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted group data.\n */\nexport const pushGroupToUserStorage = async (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroup = formatGroupForUserStorageUsage(context, group);\n const stringifiedGroup = JSON.stringify(formattedGroup);\n\n backupAndSyncLogger(`Pushing group to user storage: ${stringifiedGroup}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${formattedGroup.groupIndex}`,\n stringifiedGroup,\n entropySourceId,\n );\n });\n};\n\n/**\n * Pushes a batch of groups to user storage.\n *\n * @param context - The backup and sync context.\n * @param groups - The groups to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on any of the formatted group data.\n */\nexport const pushGroupToUserStorageBatch = async (\n context: BackupAndSyncContext,\n groups: AccountGroupMultichainAccountObject[],\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroups = groups.map((group) =>\n formatGroupForUserStorageUsage(context, group),\n );\n\n const entries: [string, string][] = formattedGroups.map((group) => [\n String(group.groupIndex),\n JSON.stringify(group),\n ]);\n\n backupAndSyncLogger(\n `Pushing groups to user storage: ${entries.map(([_, value]) => value).join(', ')}`,\n );\n\n return await context.messenger.call(\n 'UserStorageController:performBatchSetStorage',\n USER_STORAGE_GROUPS_FEATURE_KEY,\n entries,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves legacy user storage accounts for a specific entropy source ID.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID to retrieve data for.\n * @returns A promise that resolves with the legacy user storage accounts.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllLegacyUserStorageAccounts = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<LegacyUserStorageSyncedAccount[]> => {\n return executeWithRetry(async () => {\n const accountsData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n SDK.USER_STORAGE_FEATURE_NAMES.accounts,\n entropySourceId,\n );\n\n if (!accountsData) {\n return [];\n }\n\n const allAccounts = accountsData\n .map((stringifiedAccount) => {\n try {\n return parseLegacyAccountFromUserStorageResponse(stringifiedAccount);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse legacy account data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n })\n .filter(\n (account): account is LegacyUserStorageSyncedAccount =>\n account !== null,\n );\n\n backupAndSyncLogger(\n `Retrieved legacy accounts from user storage: ${JSON.stringify(allAccounts)}`,\n );\n\n return allAccounts;\n });\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"network-operations.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAElB;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,YAC1B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,uBAAuB,GAAG,IAAI,CAuBxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,UACrB,0BAA0B,KACjC,QAAQ,IAAI,CAed,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,4BAA4B,EAAE,CA8BxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,mBACZ,MAAM,cACX,MAAM,KACjB,QAAQ,4BAA4B,GAAG,IAAI,CAoB7C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,YACxB,oBAAoB,SACtB,mCAAmC,mBACzB,MAAM,KACtB,QAAQ,IAAI,CAcd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,UACrB,mCAAmC,EAAE,mBAC5B,MAAM,KACtB,QAAQ,IAAI,CAsBd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,8BAA8B,EAAE,CAkC1C,CAAC"}
1
+ {"version":3,"file":"network-operations.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAGlB;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,YAC1B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,uBAAuB,GAAG,IAAI,CAuBxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,UACrB,0BAA0B,KACjC,QAAQ,IAAI,CAed,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,4BAA4B,EAAE,CA8BxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,mBACZ,MAAM,cACX,MAAM,KACjB,QAAQ,4BAA4B,GAAG,IAAI,CAoB7C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,YACxB,oBAAoB,SACtB,mCAAmC,mBACzB,MAAM,KACtB,QAAQ,IAAI,CAcd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,UACrB,mCAAmC,EAAE,mBAC5B,MAAM,KACtB,QAAQ,IAAI,CAsBd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,8BAA8B,EAAE,CAkC1C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"network-operations.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAElB;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,YAC1B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,uBAAuB,GAAG,IAAI,CAuBxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,UACrB,0BAA0B,KACjC,QAAQ,IAAI,CAed,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,4BAA4B,EAAE,CA8BxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,mBACZ,MAAM,cACX,MAAM,KACjB,QAAQ,4BAA4B,GAAG,IAAI,CAoB7C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,YACxB,oBAAoB,SACtB,mCAAmC,mBACzB,MAAM,KACtB,QAAQ,IAAI,CAcd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,UACrB,mCAAmC,EAAE,mBAC5B,MAAM,KACtB,QAAQ,IAAI,CAsBd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,8BAA8B,EAAE,CAkC1C,CAAC"}
1
+ {"version":3,"file":"network-operations.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mCAAmC,EAAE,wBAAoB;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAqB;AAC/D,OAAO,KAAK,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,4BAA4B,EAC7B,qBAAiB;AAGlB;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,YAC1B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,uBAAuB,GAAG,IAAI,CAuBxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,UACrB,0BAA0B,KACjC,QAAQ,IAAI,CAed,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,4BAA4B,EAAE,CA8BxC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,YACzB,oBAAoB,mBACZ,MAAM,cACX,MAAM,KACjB,QAAQ,4BAA4B,GAAG,IAAI,CAoB7C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,YACxB,oBAAoB,SACtB,mCAAmC,mBACzB,MAAM,KACtB,QAAQ,IAAI,CAcd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,UACrB,mCAAmC,EAAE,mBAC5B,MAAM,KACtB,QAAQ,IAAI,CAsBd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,YACjC,oBAAoB,mBACZ,MAAM,KACtB,QAAQ,8BAA8B,EAAE,CAkC1C,CAAC"}
@@ -3,6 +3,7 @@ import { USER_STORAGE_GROUPS_FEATURE_KEY, USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY
3
3
  import { formatWalletForUserStorageUsage, formatGroupForUserStorageUsage, parseWalletFromUserStorageResponse, parseGroupFromUserStorageResponse, parseLegacyAccountFromUserStorageResponse } from "./format-utils.mjs";
4
4
  import { executeWithRetry } from "./network-utils.mjs";
5
5
  import { backupAndSyncLogger } from "../../logger.mjs";
6
+ import { toErrorMessage } from "../utils/errors.mjs";
6
7
  /**
7
8
  * Retrieves the wallet from user storage.
8
9
  *
@@ -23,7 +24,7 @@ export const getWalletFromUserStorage = async (context, entropySourceId) => {
23
24
  return parseWalletFromUserStorageResponse(walletData);
24
25
  }
25
26
  catch (error) {
26
- backupAndSyncLogger(`Failed to parse wallet data from user storage: ${error instanceof Error ? error.message : String(error)}`);
27
+ backupAndSyncLogger(`Failed to parse wallet data from user storage: ${toErrorMessage(error)}`);
27
28
  return null;
28
29
  }
29
30
  });
@@ -68,7 +69,7 @@ export const getAllGroupsFromUserStorage = async (context, entropySourceId) => {
68
69
  return parseGroupFromUserStorageResponse(stringifiedGroup);
69
70
  }
70
71
  catch (error) {
71
- backupAndSyncLogger(`Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`);
72
+ backupAndSyncLogger(`Failed to parse group data from user storage: ${toErrorMessage(error)}`);
72
73
  return null;
73
74
  }
74
75
  })
@@ -97,7 +98,7 @@ export const getGroupFromUserStorage = async (context, entropySourceId, groupInd
97
98
  return parseGroupFromUserStorageResponse(groupData);
98
99
  }
99
100
  catch (error) {
100
- backupAndSyncLogger(`Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`);
101
+ backupAndSyncLogger(`Failed to parse group data from user storage: ${toErrorMessage(error)}`);
101
102
  return null;
102
103
  }
103
104
  });
@@ -164,7 +165,7 @@ export const getAllLegacyUserStorageAccounts = async (context, entropySourceId)
164
165
  return parseLegacyAccountFromUserStorageResponse(stringifiedAccount);
165
166
  }
166
167
  catch (error) {
167
- backupAndSyncLogger(`Failed to parse legacy account data from user storage: ${error instanceof Error ? error.message : String(error)}`);
168
+ backupAndSyncLogger(`Failed to parse legacy account data from user storage: ${toErrorMessage(error)}`);
168
169
  return null;
169
170
  }
170
171
  })
@@ -1 +1 @@
1
- {"version":3,"file":"network-operations.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import { SDK } from '@metamask/profile-sync-controller';\n\nimport {\n USER_STORAGE_GROUPS_FEATURE_KEY,\n USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY,\n USER_STORAGE_WALLETS_FEATURE_KEY,\n} from './constants';\nimport {\n formatWalletForUserStorageUsage,\n formatGroupForUserStorageUsage,\n parseWalletFromUserStorageResponse,\n parseGroupFromUserStorageResponse,\n parseLegacyAccountFromUserStorageResponse,\n} from './format-utils';\nimport { executeWithRetry } from './network-utils';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\n\n/**\n * Retrieves the wallet from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns The wallet from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getWalletFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWallet | null> => {\n return executeWithRetry(async () => {\n const walletData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n entropySourceId,\n );\n if (!walletData) {\n return null;\n }\n\n try {\n backupAndSyncLogger(\n `Retrieved wallet data from user storage: ${JSON.stringify(walletData)}`,\n );\n return parseWalletFromUserStorageResponse(walletData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse wallet data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes the wallet to user storage.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet to push to user storage.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted wallet data.\n */\nexport const pushWalletToUserStorage = async (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedWallet = formatWalletForUserStorageUsage(context, wallet);\n const stringifiedWallet = JSON.stringify(formattedWallet);\n const entropySourceId = wallet.metadata.entropy.id;\n\n backupAndSyncLogger(`Pushing wallet to user storage: ${stringifiedWallet}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n stringifiedWallet,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves all groups from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns An array of groups from user storage.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllGroupsFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWalletGroup[]> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}`,\n entropySourceId,\n );\n if (!groupData) {\n return [];\n }\n\n const allGroups = groupData\n .map((stringifiedGroup) => {\n try {\n return parseGroupFromUserStorageResponse(stringifiedGroup);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n })\n .filter((group): group is UserStorageSyncedWalletGroup => group !== null);\n\n backupAndSyncLogger(\n `Retrieved groups from user storage: ${JSON.stringify(allGroups)}`,\n );\n\n return allGroups;\n });\n};\n\n/**\n * Retrieves a single group from user storage by group index.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @param groupIndex - The group index to retrieve.\n * @returns The group from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getGroupFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n groupIndex: number,\n): Promise<UserStorageSyncedWalletGroup | null> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${groupIndex}`,\n entropySourceId,\n );\n if (!groupData) {\n return null;\n }\n\n try {\n return parseGroupFromUserStorageResponse(groupData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes a group to user storage.\n *\n * @param context - The backup and sync context.\n * @param group - The group to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted group data.\n */\nexport const pushGroupToUserStorage = async (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroup = formatGroupForUserStorageUsage(context, group);\n const stringifiedGroup = JSON.stringify(formattedGroup);\n\n backupAndSyncLogger(`Pushing group to user storage: ${stringifiedGroup}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${formattedGroup.groupIndex}`,\n stringifiedGroup,\n entropySourceId,\n );\n });\n};\n\n/**\n * Pushes a batch of groups to user storage.\n *\n * @param context - The backup and sync context.\n * @param groups - The groups to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on any of the formatted group data.\n */\nexport const pushGroupToUserStorageBatch = async (\n context: BackupAndSyncContext,\n groups: AccountGroupMultichainAccountObject[],\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroups = groups.map((group) =>\n formatGroupForUserStorageUsage(context, group),\n );\n\n const entries: [string, string][] = formattedGroups.map((group) => [\n String(group.groupIndex),\n JSON.stringify(group),\n ]);\n\n backupAndSyncLogger(\n `Pushing groups to user storage: ${entries.map(([_, value]) => value).join(', ')}`,\n );\n\n return await context.messenger.call(\n 'UserStorageController:performBatchSetStorage',\n USER_STORAGE_GROUPS_FEATURE_KEY,\n entries,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves legacy user storage accounts for a specific entropy source ID.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID to retrieve data for.\n * @returns A promise that resolves with the legacy user storage accounts.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllLegacyUserStorageAccounts = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<LegacyUserStorageSyncedAccount[]> => {\n return executeWithRetry(async () => {\n const accountsData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n SDK.USER_STORAGE_FEATURE_NAMES.accounts,\n entropySourceId,\n );\n\n if (!accountsData) {\n return [];\n }\n\n const allAccounts = accountsData\n .map((stringifiedAccount) => {\n try {\n return parseLegacyAccountFromUserStorageResponse(stringifiedAccount);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse legacy account data from user storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n return null;\n }\n })\n .filter(\n (account): account is LegacyUserStorageSyncedAccount =>\n account !== null,\n );\n\n backupAndSyncLogger(\n `Retrieved legacy accounts from user storage: ${JSON.stringify(allAccounts)}`,\n );\n\n return allAccounts;\n });\n};\n"]}
1
+ {"version":3,"file":"network-operations.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/user-storage/network-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import { SDK } from '@metamask/profile-sync-controller';\n\nimport {\n USER_STORAGE_GROUPS_FEATURE_KEY,\n USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY,\n USER_STORAGE_WALLETS_FEATURE_KEY,\n} from './constants';\nimport {\n formatWalletForUserStorageUsage,\n formatGroupForUserStorageUsage,\n parseWalletFromUserStorageResponse,\n parseGroupFromUserStorageResponse,\n parseLegacyAccountFromUserStorageResponse,\n} from './format-utils';\nimport { executeWithRetry } from './network-utils';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport { backupAndSyncLogger } from '../../logger';\nimport type { AccountWalletEntropyObject } from '../../wallet';\nimport type {\n BackupAndSyncContext,\n LegacyUserStorageSyncedAccount,\n UserStorageSyncedWallet,\n UserStorageSyncedWalletGroup,\n} from '../types';\nimport { toErrorMessage } from '../utils/errors';\n\n/**\n * Retrieves the wallet from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns The wallet from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getWalletFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWallet | null> => {\n return executeWithRetry(async () => {\n const walletData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n entropySourceId,\n );\n if (!walletData) {\n return null;\n }\n\n try {\n backupAndSyncLogger(\n `Retrieved wallet data from user storage: ${JSON.stringify(walletData)}`,\n );\n return parseWalletFromUserStorageResponse(walletData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse wallet data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes the wallet to user storage.\n *\n * @param context - The backup and sync context.\n * @param wallet - The wallet to push to user storage.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted wallet data.\n */\nexport const pushWalletToUserStorage = async (\n context: BackupAndSyncContext,\n wallet: AccountWalletEntropyObject,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedWallet = formatWalletForUserStorageUsage(context, wallet);\n const stringifiedWallet = JSON.stringify(formattedWallet);\n const entropySourceId = wallet.metadata.entropy.id;\n\n backupAndSyncLogger(`Pushing wallet to user storage: ${stringifiedWallet}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_WALLETS_FEATURE_KEY}.${USER_STORAGE_WALLETS_FEATURE_ENTRY_KEY}`,\n stringifiedWallet,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves all groups from user storage.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @returns An array of groups from user storage.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllGroupsFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<UserStorageSyncedWalletGroup[]> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}`,\n entropySourceId,\n );\n if (!groupData) {\n return [];\n }\n\n const allGroups = groupData\n .map((stringifiedGroup) => {\n try {\n return parseGroupFromUserStorageResponse(stringifiedGroup);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n })\n .filter((group): group is UserStorageSyncedWalletGroup => group !== null);\n\n backupAndSyncLogger(\n `Retrieved groups from user storage: ${JSON.stringify(allGroups)}`,\n );\n\n return allGroups;\n });\n};\n\n/**\n * Retrieves a single group from user storage by group index.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID.\n * @param groupIndex - The group index to retrieve.\n * @returns The group from user storage or null if not found or invalid.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getGroupFromUserStorage = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n groupIndex: number,\n): Promise<UserStorageSyncedWalletGroup | null> => {\n return executeWithRetry(async () => {\n const groupData = await context.messenger.call(\n 'UserStorageController:performGetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${groupIndex}`,\n entropySourceId,\n );\n if (!groupData) {\n return null;\n }\n\n try {\n return parseGroupFromUserStorageResponse(groupData);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse group data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n });\n};\n\n/**\n * Pushes a group to user storage.\n *\n * @param context - The backup and sync context.\n * @param group - The group to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on the formatted group data.\n */\nexport const pushGroupToUserStorage = async (\n context: BackupAndSyncContext,\n group: AccountGroupMultichainAccountObject,\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroup = formatGroupForUserStorageUsage(context, group);\n const stringifiedGroup = JSON.stringify(formattedGroup);\n\n backupAndSyncLogger(`Pushing group to user storage: ${stringifiedGroup}`);\n\n return await context.messenger.call(\n 'UserStorageController:performSetStorage',\n `${USER_STORAGE_GROUPS_FEATURE_KEY}.${formattedGroup.groupIndex}`,\n stringifiedGroup,\n entropySourceId,\n );\n });\n};\n\n/**\n * Pushes a batch of groups to user storage.\n *\n * @param context - The backup and sync context.\n * @param groups - The groups to push to user storage.\n * @param entropySourceId - The entropy source ID.\n * @returns A promise that resolves when the operation is complete.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n * @throws When JSON.stringify fails on any of the formatted group data.\n */\nexport const pushGroupToUserStorageBatch = async (\n context: BackupAndSyncContext,\n groups: AccountGroupMultichainAccountObject[],\n entropySourceId: string,\n): Promise<void> => {\n return executeWithRetry(async () => {\n const formattedGroups = groups.map((group) =>\n formatGroupForUserStorageUsage(context, group),\n );\n\n const entries: [string, string][] = formattedGroups.map((group) => [\n String(group.groupIndex),\n JSON.stringify(group),\n ]);\n\n backupAndSyncLogger(\n `Pushing groups to user storage: ${entries.map(([_, value]) => value).join(', ')}`,\n );\n\n return await context.messenger.call(\n 'UserStorageController:performBatchSetStorage',\n USER_STORAGE_GROUPS_FEATURE_KEY,\n entries,\n entropySourceId,\n );\n });\n};\n\n/**\n * Retrieves legacy user storage accounts for a specific entropy source ID.\n *\n * @param context - The backup and sync context.\n * @param entropySourceId - The entropy source ID to retrieve data for.\n * @returns A promise that resolves with the legacy user storage accounts.\n * @throws When network operations fail after maximum retry attempts.\n * @throws When messenger calls to UserStorageController fail due to authentication errors, encryption/decryption failures, or network issues.\n */\nexport const getAllLegacyUserStorageAccounts = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n): Promise<LegacyUserStorageSyncedAccount[]> => {\n return executeWithRetry(async () => {\n const accountsData = await context.messenger.call(\n 'UserStorageController:performGetStorageAllFeatureEntries',\n SDK.USER_STORAGE_FEATURE_NAMES.accounts,\n entropySourceId,\n );\n\n if (!accountsData) {\n return [];\n }\n\n const allAccounts = accountsData\n .map((stringifiedAccount) => {\n try {\n return parseLegacyAccountFromUserStorageResponse(stringifiedAccount);\n } catch (error) {\n backupAndSyncLogger(\n `Failed to parse legacy account data from user storage: ${toErrorMessage(error)}`,\n );\n return null;\n }\n })\n .filter(\n (account): account is LegacyUserStorageSyncedAccount =>\n account !== null,\n );\n\n backupAndSyncLogger(\n `Retrieved legacy accounts from user storage: ${JSON.stringify(allAccounts)}`,\n );\n\n return allAccounts;\n });\n};\n"]}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toErrorMessage = void 0;
4
+ /**
5
+ * Extracts a string message from an unknown error value.
6
+ *
7
+ * @param error - The caught error value.
8
+ * @returns The error message string.
9
+ */
10
+ function toErrorMessage(error) {
11
+ return error instanceof Error ? error.message : String(error);
12
+ }
13
+ exports.toErrorMessage = toErrorMessage;
14
+ //# sourceMappingURL=errors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/errors.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,KAAc;IAC3C,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAFD,wCAEC","sourcesContent":["/**\n * Extracts a string message from an unknown error value.\n *\n * @param error - The caught error value.\n * @returns The error message string.\n */\nexport function toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Extracts a string message from an unknown error value.
3
+ *
4
+ * @param error - The caught error value.
5
+ * @returns The error message string.
6
+ */
7
+ export declare function toErrorMessage(error: unknown): string;
8
+ //# sourceMappingURL=errors.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Extracts a string message from an unknown error value.
3
+ *
4
+ * @param error - The caught error value.
5
+ * @returns The error message string.
6
+ */
7
+ export declare function toErrorMessage(error: unknown): string;
8
+ //# sourceMappingURL=errors.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Extracts a string message from an unknown error value.
3
+ *
4
+ * @param error - The caught error value.
5
+ * @returns The error message string.
6
+ */
7
+ export function toErrorMessage(error) {
8
+ return error instanceof Error ? error.message : String(error);
9
+ }
10
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC","sourcesContent":["/**\n * Extracts a string message from an unknown error value.\n *\n * @param error - The caught error value.\n * @returns The error message string.\n */\nexport function toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
@@ -14,5 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.toErrorMessage = void 0;
17
18
  __exportStar(require("./controller.cjs"), exports);
19
+ var errors_1 = require("./errors.cjs");
20
+ Object.defineProperty(exports, "toErrorMessage", { enumerable: true, get: function () { return errors_1.toErrorMessage; } });
18
21
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAA6B","sourcesContent":["export * from './controller';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mDAA6B;AAC7B,uCAA0C;AAAjC,wGAAA,cAAc,OAAA","sourcesContent":["export * from './controller';\nexport { toErrorMessage } from './errors';\n"]}
@@ -1,2 +1,3 @@
1
1
  export * from "./controller.cjs";
2
+ export { toErrorMessage } from "./errors.cjs";
2
3
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B;AAC7B,OAAO,EAAE,cAAc,EAAE,qBAAiB"}
@@ -1,2 +1,3 @@
1
1
  export * from "./controller.mjs";
2
+ export { toErrorMessage } from "./errors.mjs";
2
3
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B;AAC7B,OAAO,EAAE,cAAc,EAAE,qBAAiB"}
@@ -1,2 +1,3 @@
1
1
  export * from "./controller.mjs";
2
+ export { toErrorMessage } from "./errors.mjs";
2
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B","sourcesContent":["export * from './controller';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/utils/index.ts"],"names":[],"mappings":"AAAA,iCAA6B;AAC7B,OAAO,EAAE,cAAc,EAAE,qBAAiB","sourcesContent":["export * from './controller';\nexport { toErrorMessage } from './errors';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/account-tree-controller",
3
- "version": "5.0.1-preview-2d89a26",
3
+ "version": "5.0.1-preview-afe010990",
4
4
  "description": "Controller to group account together based on some pre-defined rules",
5
5
  "keywords": [
6
6
  "MetaMask",