@metamask/profile-sync-controller 20.0.0 → 21.0.0

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 (140) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/dist/controllers/authentication/AuthenticationController.cjs +16 -9
  3. package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
  4. package/dist/controllers/authentication/AuthenticationController.d.cts +6 -1
  5. package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
  6. package/dist/controllers/authentication/AuthenticationController.d.mts +6 -1
  7. package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
  8. package/dist/controllers/authentication/AuthenticationController.mjs +16 -9
  9. package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
  10. package/dist/controllers/user-storage/UserStorageController.cjs +24 -47
  11. package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
  12. package/dist/controllers/user-storage/UserStorageController.d.cts +16 -49
  13. package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
  14. package/dist/controllers/user-storage/UserStorageController.d.mts +16 -49
  15. package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
  16. package/dist/controllers/user-storage/UserStorageController.mjs +24 -47
  17. package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
  18. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs +185 -166
  19. package/dist/controllers/user-storage/account-syncing/controller-integration.cjs.map +1 -1
  20. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts +3 -0
  21. package/dist/controllers/user-storage/account-syncing/controller-integration.d.cts.map +1 -1
  22. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts +3 -0
  23. package/dist/controllers/user-storage/account-syncing/controller-integration.d.mts.map +1 -1
  24. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs +185 -166
  25. package/dist/controllers/user-storage/account-syncing/controller-integration.mjs.map +1 -1
  26. package/dist/controllers/user-storage/account-syncing/types.cjs.map +1 -1
  27. package/dist/controllers/user-storage/account-syncing/types.d.cts +2 -0
  28. package/dist/controllers/user-storage/account-syncing/types.d.cts.map +1 -1
  29. package/dist/controllers/user-storage/account-syncing/types.d.mts +2 -0
  30. package/dist/controllers/user-storage/account-syncing/types.d.mts.map +1 -1
  31. package/dist/controllers/user-storage/account-syncing/types.mjs.map +1 -1
  32. package/dist/controllers/user-storage/constants.cjs +14 -1
  33. package/dist/controllers/user-storage/constants.cjs.map +1 -1
  34. package/dist/controllers/user-storage/constants.d.cts +11 -0
  35. package/dist/controllers/user-storage/constants.d.cts.map +1 -1
  36. package/dist/controllers/user-storage/constants.d.mts +11 -0
  37. package/dist/controllers/user-storage/constants.d.mts.map +1 -1
  38. package/dist/controllers/user-storage/constants.mjs +13 -0
  39. package/dist/controllers/user-storage/constants.mjs.map +1 -1
  40. package/dist/controllers/user-storage/contact-syncing/controller-integration.cjs +227 -162
  41. package/dist/controllers/user-storage/contact-syncing/controller-integration.cjs.map +1 -1
  42. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.cts +3 -0
  43. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.cts.map +1 -1
  44. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.mts +3 -0
  45. package/dist/controllers/user-storage/contact-syncing/controller-integration.d.mts.map +1 -1
  46. package/dist/controllers/user-storage/contact-syncing/controller-integration.mjs +227 -162
  47. package/dist/controllers/user-storage/contact-syncing/controller-integration.mjs.map +1 -1
  48. package/dist/controllers/user-storage/contact-syncing/types.cjs.map +1 -1
  49. package/dist/controllers/user-storage/contact-syncing/types.d.cts +2 -0
  50. package/dist/controllers/user-storage/contact-syncing/types.d.cts.map +1 -1
  51. package/dist/controllers/user-storage/contact-syncing/types.d.mts +2 -0
  52. package/dist/controllers/user-storage/contact-syncing/types.d.mts.map +1 -1
  53. package/dist/controllers/user-storage/contact-syncing/types.mjs.map +1 -1
  54. package/dist/controllers/user-storage/mocks/mockResponses.cjs +1 -1
  55. package/dist/controllers/user-storage/mocks/mockResponses.cjs.map +1 -1
  56. package/dist/controllers/user-storage/mocks/mockResponses.d.cts +9 -9
  57. package/dist/controllers/user-storage/mocks/mockResponses.d.cts.map +1 -1
  58. package/dist/controllers/user-storage/mocks/mockResponses.d.mts +9 -9
  59. package/dist/controllers/user-storage/mocks/mockResponses.d.mts.map +1 -1
  60. package/dist/controllers/user-storage/mocks/mockResponses.mjs +1 -1
  61. package/dist/controllers/user-storage/mocks/mockResponses.mjs.map +1 -1
  62. package/dist/controllers/user-storage/types.cjs.map +1 -1
  63. package/dist/controllers/user-storage/types.d.cts +3 -3
  64. package/dist/controllers/user-storage/types.d.cts.map +1 -1
  65. package/dist/controllers/user-storage/types.d.mts +3 -3
  66. package/dist/controllers/user-storage/types.d.mts.map +1 -1
  67. package/dist/controllers/user-storage/types.mjs.map +1 -1
  68. package/dist/sdk/user-storage.cjs +3 -9
  69. package/dist/sdk/user-storage.cjs.map +1 -1
  70. package/dist/sdk/user-storage.d.cts +0 -1
  71. package/dist/sdk/user-storage.d.cts.map +1 -1
  72. package/dist/sdk/user-storage.d.mts +0 -1
  73. package/dist/sdk/user-storage.d.mts.map +1 -1
  74. package/dist/sdk/user-storage.mjs +3 -9
  75. package/dist/sdk/user-storage.mjs.map +1 -1
  76. package/dist/shared/storage-schema.cjs +9 -32
  77. package/dist/shared/storage-schema.cjs.map +1 -1
  78. package/dist/shared/storage-schema.d.cts +10 -42
  79. package/dist/shared/storage-schema.d.cts.map +1 -1
  80. package/dist/shared/storage-schema.d.mts +10 -42
  81. package/dist/shared/storage-schema.d.mts.map +1 -1
  82. package/dist/shared/storage-schema.mjs +8 -31
  83. package/dist/shared/storage-schema.mjs.map +1 -1
  84. package/package.json +1 -3
  85. package/dist/controllers/user-storage/network-syncing/add-network-utils.cjs +0 -36
  86. package/dist/controllers/user-storage/network-syncing/add-network-utils.cjs.map +0 -1
  87. package/dist/controllers/user-storage/network-syncing/add-network-utils.d.cts +0 -22
  88. package/dist/controllers/user-storage/network-syncing/add-network-utils.d.cts.map +0 -1
  89. package/dist/controllers/user-storage/network-syncing/add-network-utils.d.mts +0 -22
  90. package/dist/controllers/user-storage/network-syncing/add-network-utils.d.mts.map +0 -1
  91. package/dist/controllers/user-storage/network-syncing/add-network-utils.mjs +0 -31
  92. package/dist/controllers/user-storage/network-syncing/add-network-utils.mjs.map +0 -1
  93. package/dist/controllers/user-storage/network-syncing/controller-integration.cjs +0 -193
  94. package/dist/controllers/user-storage/network-syncing/controller-integration.cjs.map +0 -1
  95. package/dist/controllers/user-storage/network-syncing/controller-integration.d.cts +0 -57
  96. package/dist/controllers/user-storage/network-syncing/controller-integration.d.cts.map +0 -1
  97. package/dist/controllers/user-storage/network-syncing/controller-integration.d.mts +0 -57
  98. package/dist/controllers/user-storage/network-syncing/controller-integration.d.mts.map +0 -1
  99. package/dist/controllers/user-storage/network-syncing/controller-integration.mjs +0 -191
  100. package/dist/controllers/user-storage/network-syncing/controller-integration.mjs.map +0 -1
  101. package/dist/controllers/user-storage/network-syncing/services.cjs +0 -79
  102. package/dist/controllers/user-storage/network-syncing/services.cjs.map +0 -1
  103. package/dist/controllers/user-storage/network-syncing/services.d.cts +0 -34
  104. package/dist/controllers/user-storage/network-syncing/services.d.cts.map +0 -1
  105. package/dist/controllers/user-storage/network-syncing/services.d.mts +0 -34
  106. package/dist/controllers/user-storage/network-syncing/services.d.mts.map +0 -1
  107. package/dist/controllers/user-storage/network-syncing/services.mjs +0 -73
  108. package/dist/controllers/user-storage/network-syncing/services.mjs.map +0 -1
  109. package/dist/controllers/user-storage/network-syncing/sync-all.cjs +0 -153
  110. package/dist/controllers/user-storage/network-syncing/sync-all.cjs.map +0 -1
  111. package/dist/controllers/user-storage/network-syncing/sync-all.d.cts +0 -32
  112. package/dist/controllers/user-storage/network-syncing/sync-all.d.cts.map +0 -1
  113. package/dist/controllers/user-storage/network-syncing/sync-all.d.mts +0 -32
  114. package/dist/controllers/user-storage/network-syncing/sync-all.d.mts.map +0 -1
  115. package/dist/controllers/user-storage/network-syncing/sync-all.mjs +0 -145
  116. package/dist/controllers/user-storage/network-syncing/sync-all.mjs.map +0 -1
  117. package/dist/controllers/user-storage/network-syncing/sync-mutations.cjs +0 -28
  118. package/dist/controllers/user-storage/network-syncing/sync-mutations.cjs.map +0 -1
  119. package/dist/controllers/user-storage/network-syncing/sync-mutations.d.cts +0 -17
  120. package/dist/controllers/user-storage/network-syncing/sync-mutations.d.cts.map +0 -1
  121. package/dist/controllers/user-storage/network-syncing/sync-mutations.d.mts +0 -17
  122. package/dist/controllers/user-storage/network-syncing/sync-mutations.d.mts.map +0 -1
  123. package/dist/controllers/user-storage/network-syncing/sync-mutations.mjs +0 -22
  124. package/dist/controllers/user-storage/network-syncing/sync-mutations.mjs.map +0 -1
  125. package/dist/controllers/user-storage/network-syncing/types.cjs +0 -16
  126. package/dist/controllers/user-storage/network-syncing/types.cjs.map +0 -1
  127. package/dist/controllers/user-storage/network-syncing/types.d.cts +0 -15
  128. package/dist/controllers/user-storage/network-syncing/types.d.cts.map +0 -1
  129. package/dist/controllers/user-storage/network-syncing/types.d.mts +0 -15
  130. package/dist/controllers/user-storage/network-syncing/types.d.mts.map +0 -1
  131. package/dist/controllers/user-storage/network-syncing/types.mjs +0 -11
  132. package/dist/controllers/user-storage/network-syncing/types.mjs.map +0 -1
  133. package/dist/controllers/user-storage/network-syncing/update-network-utils.cjs +0 -129
  134. package/dist/controllers/user-storage/network-syncing/update-network-utils.cjs.map +0 -1
  135. package/dist/controllers/user-storage/network-syncing/update-network-utils.d.cts +0 -59
  136. package/dist/controllers/user-storage/network-syncing/update-network-utils.d.cts.map +0 -1
  137. package/dist/controllers/user-storage/network-syncing/update-network-utils.d.mts +0 -59
  138. package/dist/controllers/user-storage/network-syncing/update-network-utils.d.mts.map +0 -1
  139. package/dist/controllers/user-storage/network-syncing/update-network-utils.mjs +0 -122
  140. package/dist/controllers/user-storage/network-syncing/update-network-utils.mjs.map +0 -1
@@ -3,4 +3,15 @@ export declare const BACKUPANDSYNC_FEATURES: {
3
3
  readonly accountSyncing: "accountSyncing";
4
4
  readonly contactSyncing: "contactSyncing";
5
5
  };
6
+ /**
7
+ * Trace names for UserStorage syncing operations
8
+ */
9
+ export declare const TraceName: {
10
+ readonly ContactSyncFull: "Contact Sync Full";
11
+ readonly ContactSyncSaveBatch: "Contact Sync Save Batch";
12
+ readonly ContactSyncUpdateRemote: "Contact Sync Update Remote";
13
+ readonly ContactSyncDeleteRemote: "Contact Sync Delete Remote";
14
+ readonly AccountSyncFull: "Account Sync Full";
15
+ readonly AccountSyncSaveIndividual: "Account Sync Save Individual";
16
+ };
6
17
  //# sourceMappingURL=constants.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.cts","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC"}
1
+ {"version":3,"file":"constants.d.cts","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;CAUZ,CAAC"}
@@ -3,4 +3,15 @@ export declare const BACKUPANDSYNC_FEATURES: {
3
3
  readonly accountSyncing: "accountSyncing";
4
4
  readonly contactSyncing: "contactSyncing";
5
5
  };
6
+ /**
7
+ * Trace names for UserStorage syncing operations
8
+ */
9
+ export declare const TraceName: {
10
+ readonly ContactSyncFull: "Contact Sync Full";
11
+ readonly ContactSyncSaveBatch: "Contact Sync Save Batch";
12
+ readonly ContactSyncUpdateRemote: "Contact Sync Update Remote";
13
+ readonly ContactSyncDeleteRemote: "Contact Sync Delete Remote";
14
+ readonly AccountSyncFull: "Account Sync Full";
15
+ readonly AccountSyncSaveIndividual: "Account Sync Save Individual";
16
+ };
6
17
  //# sourceMappingURL=constants.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC"}
1
+ {"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;CAUZ,CAAC"}
@@ -3,4 +3,17 @@ export const BACKUPANDSYNC_FEATURES = {
3
3
  accountSyncing: 'accountSyncing',
4
4
  contactSyncing: 'contactSyncing',
5
5
  };
6
+ /**
7
+ * Trace names for UserStorage syncing operations
8
+ */
9
+ export const TraceName = {
10
+ // Contact syncing traces
11
+ ContactSyncFull: 'Contact Sync Full',
12
+ ContactSyncSaveBatch: 'Contact Sync Save Batch',
13
+ ContactSyncUpdateRemote: 'Contact Sync Update Remote',
14
+ ContactSyncDeleteRemote: 'Contact Sync Delete Remote',
15
+ // Account syncing traces
16
+ AccountSyncFull: 'Account Sync Full',
17
+ AccountSyncSaveIndividual: 'Account Sync Save Individual',
18
+ };
6
19
  //# sourceMappingURL=constants.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE,MAAM;IACZ,cAAc,EAAE,gBAAgB;IAChC,cAAc,EAAE,gBAAgB;CACxB,CAAC","sourcesContent":["export const BACKUPANDSYNC_FEATURES = {\n main: 'main',\n accountSyncing: 'accountSyncing',\n contactSyncing: 'contactSyncing',\n} as const;\n"]}
1
+ {"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE,MAAM;IACZ,cAAc,EAAE,gBAAgB;IAChC,cAAc,EAAE,gBAAgB;CACxB,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,yBAAyB;IACzB,eAAe,EAAE,mBAAmB;IACpC,oBAAoB,EAAE,yBAAyB;IAC/C,uBAAuB,EAAE,4BAA4B;IACrD,uBAAuB,EAAE,4BAA4B;IAErD,yBAAyB;IACzB,eAAe,EAAE,mBAAmB;IACpC,yBAAyB,EAAE,8BAA8B;CACjD,CAAC","sourcesContent":["export const BACKUPANDSYNC_FEATURES = {\n main: 'main',\n accountSyncing: 'accountSyncing',\n contactSyncing: 'contactSyncing',\n} as const;\n\n/**\n * Trace names for UserStorage syncing operations\n */\nexport const TraceName = {\n // Contact syncing traces\n ContactSyncFull: 'Contact Sync Full',\n ContactSyncSaveBatch: 'Contact Sync Save Batch',\n ContactSyncUpdateRemote: 'Contact Sync Update Remote',\n ContactSyncDeleteRemote: 'Contact Sync Delete Remote',\n\n // Account syncing traces\n AccountSyncFull: 'Account Sync Full',\n AccountSyncSaveIndividual: 'Account Sync Save Individual',\n} as const;\n"]}
@@ -5,6 +5,7 @@ const sync_utils_1 = require("./sync-utils.cjs");
5
5
  const utils_1 = require("./utils.cjs");
6
6
  const utils_2 = require("./utils.cjs");
7
7
  const storage_schema_1 = require("../../../shared/storage-schema.cjs");
8
+ const constants_1 = require("../constants.cjs");
8
9
  /**
9
10
  * Creates a unique key for a contact based on chainId and address
10
11
  *
@@ -35,138 +36,163 @@ function createContactKey(contact) {
35
36
  *
36
37
  * @param config - Parameters used for syncing callbacks
37
38
  * @param options - Parameters used for syncing operations
39
+ * @returns Promise that resolves when contact synchronization is complete
38
40
  */
39
41
  async function syncContactsWithUserStorage(config, options) {
40
- const { getMessenger, getUserStorageControllerInstance } = options;
42
+ const { getMessenger, getUserStorageControllerInstance, trace } = options;
41
43
  const { onContactSyncErroneousSituation, onContactUpdated, onContactDeleted, } = config;
42
- try {
43
- // Cannot perform sync, conditions not met
44
- if (!(0, sync_utils_1.canPerformContactSyncing)(options)) {
45
- return;
46
- }
47
- // Activate sync semaphore to prevent event loops
48
- await getUserStorageControllerInstance().setIsContactSyncingInProgress(true);
49
- // Get all local contacts from AddressBookController (exclude chain "*" contacts)
50
- const localVisibleContacts = getMessenger()
51
- .call('AddressBookController:list')
52
- .filter((contact) => !(0, utils_2.isContactBridgedFromAccounts)(contact))
53
- .filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
54
- // Get remote contacts from user storage API
55
- const remoteContacts = await getRemoteContacts(options);
56
- // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)
57
- const validRemoteContacts = remoteContacts?.filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
58
- // Prepare maps for efficient lookup
59
- const localContactsMap = new Map();
60
- const remoteContactsMap = new Map();
61
- localVisibleContacts.forEach((contact) => {
62
- const key = createContactKey(contact);
63
- localContactsMap.set(key, contact);
64
- });
65
- validRemoteContacts.forEach((contact) => {
66
- const key = createContactKey(contact);
67
- remoteContactsMap.set(key, contact);
68
- });
69
- // Lists to track contacts that need to be synced
70
- const contactsToAddOrUpdateLocally = [];
71
- const contactsToDeleteLocally = [];
72
- const contactsToUpdateRemotely = [];
73
- // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates
74
- for (const remoteContact of validRemoteContacts) {
75
- const key = createContactKey(remoteContact);
76
- const localContact = localContactsMap.get(key);
77
- // Handle remote contact based on its status and local existence
78
- if (remoteContact.deletedAt) {
79
- // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally
80
- if (localContact) {
81
- contactsToDeleteLocally.push(remoteContact);
82
- }
83
- }
84
- else if (!localContact) {
85
- // SCENARIO 2: New contact from remote - import to local
86
- contactsToAddOrUpdateLocally.push(remoteContact);
87
- }
88
- else {
89
- // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts
90
- const hasContentDifference = localContact.name !== remoteContact.name ||
91
- localContact.memo !== remoteContact.memo;
92
- if (hasContentDifference) {
93
- // Check timestamps to determine which version to keep
94
- const localTimestamp = localContact.lastUpdatedAt || 0;
95
- const remoteTimestamp = remoteContact.lastUpdatedAt || 0;
96
- if (localTimestamp >= remoteTimestamp) {
97
- // Local is newer (or same age) - use local version
98
- contactsToUpdateRemotely.push(localContact);
44
+ // Cannot perform sync, conditions not met
45
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options)) {
46
+ return;
47
+ }
48
+ // NOTE: Pre-sync operations (canPerformContactSyncing, AddressBookController:list, getRemoteContacts)
49
+ // are intentionally outside try-catch to let errors bubble up to Sentry for better debugging.
50
+ // Only "erroneous situation" errors during sync logic itself should be caught.
51
+ // Get all local contacts from AddressBookController (exclude chain "*" contacts)
52
+ const localVisibleContacts = getMessenger()
53
+ .call('AddressBookController:list')
54
+ .filter((contact) => !(0, utils_2.isContactBridgedFromAccounts)(contact))
55
+ .filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
56
+ // Get remote contacts from user storage API
57
+ const remoteContacts = await getRemoteContacts(options);
58
+ // Filter remote contacts to exclude invalid ones (or empty array if no remote contacts)
59
+ const validRemoteContacts = remoteContacts?.filter((contact) => contact.address && contact.chainId && contact.name?.trim()) || [];
60
+ const performSync = async () => {
61
+ try {
62
+ // Activate sync semaphore to prevent event loops
63
+ await getUserStorageControllerInstance().setIsContactSyncingInProgress(true);
64
+ // Prepare maps for efficient lookup
65
+ const localContactsMap = new Map();
66
+ const remoteContactsMap = new Map();
67
+ localVisibleContacts.forEach((contact) => {
68
+ const key = createContactKey(contact);
69
+ localContactsMap.set(key, contact);
70
+ });
71
+ validRemoteContacts.forEach((contact) => {
72
+ const key = createContactKey(contact);
73
+ remoteContactsMap.set(key, contact);
74
+ });
75
+ // Lists to track contacts that need to be synced
76
+ const contactsToAddOrUpdateLocally = [];
77
+ const contactsToDeleteLocally = [];
78
+ const contactsToUpdateRemotely = [];
79
+ // SCENARIO 2 & 6: Process remote contacts - handle new device sync and remote updates
80
+ for (const remoteContact of validRemoteContacts) {
81
+ const key = createContactKey(remoteContact);
82
+ const localContact = localContactsMap.get(key);
83
+ // Handle remote contact based on its status and local existence
84
+ if (remoteContact.deletedAt) {
85
+ // SCENARIO 8: Remote deletion - should be applied locally if contact exists locally
86
+ if (localContact) {
87
+ contactsToDeleteLocally.push(remoteContact);
99
88
  }
100
- else {
101
- // Remote is newer - use remote version
102
- contactsToAddOrUpdateLocally.push(remoteContact);
89
+ }
90
+ else if (!localContact) {
91
+ // SCENARIO 2: New contact from remote - import to local
92
+ contactsToAddOrUpdateLocally.push(remoteContact);
93
+ }
94
+ else {
95
+ // SCENARIO 4 & 6: Contact exists on both sides - check for conflicts
96
+ const hasContentDifference = localContact.name !== remoteContact.name ||
97
+ localContact.memo !== remoteContact.memo;
98
+ if (hasContentDifference) {
99
+ // Check timestamps to determine which version to keep
100
+ const localTimestamp = localContact.lastUpdatedAt || 0;
101
+ const remoteTimestamp = remoteContact.lastUpdatedAt || 0;
102
+ if (localTimestamp >= remoteTimestamp) {
103
+ // Local is newer (or same age) - use local version
104
+ contactsToUpdateRemotely.push(localContact);
105
+ }
106
+ else {
107
+ // Remote is newer - use remote version
108
+ contactsToAddOrUpdateLocally.push(remoteContact);
109
+ }
103
110
  }
111
+ // Else: content is identical, no action needed
104
112
  }
105
- // Else: content is identical, no action needed
106
113
  }
107
- }
108
- // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts
109
- for (const localContact of localVisibleContacts) {
110
- const key = createContactKey(localContact);
111
- const remoteContact = remoteContactsMap.get(key);
112
- if (!remoteContact) {
113
- // New local contact or first sync - add to remote
114
- contactsToUpdateRemotely.push(localContact);
114
+ // SCENARIO 1, 3 & 5: Process local contacts not in remote - handles first sync and new local contacts
115
+ for (const localContact of localVisibleContacts) {
116
+ const key = createContactKey(localContact);
117
+ const remoteContact = remoteContactsMap.get(key);
118
+ if (!remoteContact) {
119
+ // New local contact or first sync - add to remote
120
+ contactsToUpdateRemotely.push(localContact);
121
+ }
115
122
  }
116
- }
117
- // Apply local deletions
118
- for (const contact of contactsToDeleteLocally) {
119
- try {
123
+ // Apply local deletions
124
+ // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry
125
+ // for debugging. Previous versions silently suppressed these errors which made
126
+ // troubleshooting contact sync issues difficult.
127
+ for (const contact of contactsToDeleteLocally) {
120
128
  getMessenger().call('AddressBookController:delete', contact.chainId, contact.address);
121
129
  if (onContactDeleted) {
122
130
  onContactDeleted();
123
131
  }
124
132
  }
125
- catch (error) {
126
- console.error('Error deleting contact:', error);
127
- }
128
- }
129
- // Apply local additions/updates
130
- for (const contact of contactsToAddOrUpdateLocally) {
131
- if (!contact.deletedAt) {
132
- try {
133
+ // Apply local additions/updates
134
+ // Note: Individual errors are intentionally NOT caught here to ensure they reach Sentry
135
+ // for debugging. Previous versions silently suppressed these errors which made
136
+ // troubleshooting contact sync issues difficult.
137
+ for (const contact of contactsToAddOrUpdateLocally) {
138
+ if (!contact.deletedAt) {
133
139
  getMessenger().call('AddressBookController:set', contact.address, contact.name || '', contact.chainId, contact.memo || '', contact.addressType);
134
140
  if (onContactUpdated) {
135
141
  onContactUpdated();
136
142
  }
137
143
  }
138
- catch (error) {
139
- console.error('Error updating contact:', error);
144
+ }
145
+ // Apply changes to remote storage
146
+ if (contactsToUpdateRemotely.length > 0) {
147
+ const updatedRemoteContacts = {};
148
+ for (const localContact of contactsToUpdateRemotely) {
149
+ const key = createContactKey(localContact);
150
+ updatedRemoteContacts[key] = {
151
+ ...remoteContactsMap.get(key),
152
+ ...localContact,
153
+ lastUpdatedAt: Date.now(), // mark as updated
154
+ };
140
155
  }
156
+ // Save updated contacts to remote storage
157
+ await saveContactsToUserStorage(Object.values(updatedRemoteContacts), options);
141
158
  }
142
159
  }
143
- // Apply changes to remote storage
144
- if (contactsToUpdateRemotely.length > 0) {
145
- const updatedRemoteContacts = {};
146
- for (const localContact of contactsToUpdateRemotely) {
147
- const key = createContactKey(localContact);
148
- updatedRemoteContacts[key] = {
149
- ...remoteContactsMap.get(key),
150
- ...localContact,
151
- lastUpdatedAt: Date.now(), // mark as updated
152
- };
160
+ catch (error) {
161
+ if (onContactSyncErroneousSituation) {
162
+ onContactSyncErroneousSituation('Error synchronizing contacts', {
163
+ error,
164
+ });
165
+ // Re-throw the error to be handled by the caller
166
+ throw error;
153
167
  }
154
- // Save updated contacts to remote storage
155
- await saveContactsToUserStorage(Object.values(updatedRemoteContacts), options);
156
168
  }
157
- }
158
- catch (error) {
159
- if (onContactSyncErroneousSituation) {
160
- onContactSyncErroneousSituation('Error synchronizing contacts', {
161
- error,
162
- });
163
- // Re-throw the error to be handled by the caller
164
- throw error;
169
+ finally {
170
+ await getUserStorageControllerInstance().setIsContactSyncingInProgress(false);
165
171
  }
172
+ };
173
+ if (trace) {
174
+ // Gather pre-sync metrics for performance analysis
175
+ const initialLocalContacts = localVisibleContacts;
176
+ const initialValidRemoteContacts = validRemoteContacts;
177
+ await trace({
178
+ name: constants_1.TraceName.ContactSyncFull,
179
+ data: {
180
+ localContactCount: initialLocalContacts.length,
181
+ remoteContactCount: initialValidRemoteContacts.length,
182
+ isFirstSync: initialValidRemoteContacts.length === 0 &&
183
+ initialLocalContacts.length > 0,
184
+ isNewDeviceSync: initialLocalContacts.length === 0 &&
185
+ initialValidRemoteContacts.length > 0,
186
+ isRegularSync: initialLocalContacts.length > 0 &&
187
+ initialValidRemoteContacts.length > 0,
188
+ hasDataToSync: initialLocalContacts.length > 0 ||
189
+ initialValidRemoteContacts.length > 0,
190
+ expectedWorkload: initialLocalContacts.length + initialValidRemoteContacts.length,
191
+ },
192
+ }, performSync);
193
+ return;
166
194
  }
167
- finally {
168
- await getUserStorageControllerInstance().setIsContactSyncingInProgress(false);
169
- }
195
+ await performSync();
170
196
  }
171
197
  exports.syncContactsWithUserStorage = syncContactsWithUserStorage;
172
198
  /**
@@ -198,19 +224,34 @@ async function getRemoteContacts(options) {
198
224
  *
199
225
  * @param contacts - The contacts to save to user storage
200
226
  * @param options - Parameters used for saving contacts
227
+ * @returns Promise that resolves when contacts are saved
201
228
  */
202
229
  async function saveContactsToUserStorage(contacts, options) {
203
- const { getUserStorageControllerInstance } = options;
204
- if (!contacts || contacts.length === 0) {
205
- return;
206
- }
207
- // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs
208
- const storageEntries = contacts.map((contact) => {
209
- const key = createContactKey(contact);
210
- const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(contact);
211
- return [key, JSON.stringify(storageEntry)];
212
- });
213
- await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook, storageEntries);
230
+ const { getUserStorageControllerInstance, trace } = options;
231
+ const saveContacts = async () => {
232
+ if (!contacts || contacts.length === 0) {
233
+ return;
234
+ }
235
+ // Convert each AddressBookEntry to UserStorageContactEntry format and create key-value pairs
236
+ const storageEntries = contacts.map((contact) => {
237
+ const key = createContactKey(contact);
238
+ const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(contact);
239
+ return [key, JSON.stringify(storageEntry)];
240
+ });
241
+ await getUserStorageControllerInstance().performBatchSetStorage(storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook, storageEntries);
242
+ };
243
+ return trace
244
+ ? await trace({
245
+ name: constants_1.TraceName.ContactSyncSaveBatch,
246
+ data: {
247
+ contactCount: contacts.length,
248
+ // Performance scaling indicators
249
+ hasBatchOperations: contacts.length > 1,
250
+ chainCount: new Set(contacts.map((c) => c.chainId)).size,
251
+ hasMemosCount: contacts.filter((c) => c.memo?.length).length,
252
+ },
253
+ }, saveContacts)
254
+ : await saveContacts();
214
255
  }
215
256
  /**
216
257
  * Updates a single contact in remote storage without performing a full sync
@@ -218,24 +259,41 @@ async function saveContactsToUserStorage(contacts, options) {
218
259
  *
219
260
  * @param contact - The contact that was updated locally
220
261
  * @param options - Parameters used for syncing operations
262
+ * @returns Promise that resolves when the contact is updated
221
263
  */
222
264
  async function updateContactInRemoteStorage(contact, options) {
223
- if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
224
- !contact.address ||
225
- !contact.chainId ||
226
- !contact.name?.trim()) {
227
- return;
228
- }
229
- const { getUserStorageControllerInstance } = options;
230
- // Create an updated entry with timestamp
231
- const updatedEntry = {
232
- ...contact,
233
- lastUpdatedAt: contact.lastUpdatedAt || Date.now(),
265
+ const { trace } = options;
266
+ const updateContact = async () => {
267
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
268
+ !contact.address ||
269
+ !contact.chainId ||
270
+ !contact.name?.trim()) {
271
+ return;
272
+ }
273
+ const { getUserStorageControllerInstance } = options;
274
+ // Create an updated entry with timestamp
275
+ const updatedEntry = {
276
+ ...contact,
277
+ lastUpdatedAt: contact.lastUpdatedAt || Date.now(),
278
+ };
279
+ const key = createContactKey(contact);
280
+ const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(updatedEntry);
281
+ // Save individual contact to remote storage
282
+ await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(storageEntry));
234
283
  };
235
- const key = createContactKey(contact);
236
- const storageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(updatedEntry);
237
- // Save individual contact to remote storage
238
- await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(storageEntry));
284
+ if (trace) {
285
+ return await trace({
286
+ name: constants_1.TraceName.ContactSyncUpdateRemote,
287
+ data: {
288
+ chainId: contact.chainId,
289
+ // Performance indicators
290
+ hasTimestamp: Boolean(contact.lastUpdatedAt),
291
+ hasMemo: Boolean(contact.memo?.length),
292
+ isUpdate: Boolean(contact.lastUpdatedAt), // vs new contact
293
+ },
294
+ }, updateContact);
295
+ }
296
+ return await updateContact();
239
297
  }
240
298
  exports.updateContactInRemoteStorage = updateContactInRemoteStorage;
241
299
  /**
@@ -244,38 +302,45 @@ exports.updateContactInRemoteStorage = updateContactInRemoteStorage;
244
302
  *
245
303
  * @param contact - The contact that was deleted locally (contains at least address and chainId)
246
304
  * @param options - Parameters used for syncing operations
305
+ * @returns Promise that resolves when the contact is marked as deleted
247
306
  */
248
307
  async function deleteContactInRemoteStorage(contact, options) {
249
- if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
250
- !contact.address ||
251
- !contact.chainId ||
252
- !contact.name?.trim()) {
253
- return;
254
- }
255
- const { getUserStorageControllerInstance } = options;
256
- const key = createContactKey(contact);
257
- try {
258
- // Try to get the existing contact first
259
- const existingContactJson = await getUserStorageControllerInstance().performGetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`);
260
- if (existingContactJson) {
261
- // Mark the existing contact as deleted
262
- const existingStorageEntry = JSON.parse(existingContactJson);
263
- const existingContact = (0, utils_1.mapUserStorageEntryToAddressBookEntry)(existingStorageEntry);
264
- const now = Date.now();
265
- const deletedContact = {
266
- ...existingContact,
267
- deletedAt: now,
268
- lastUpdatedAt: now,
269
- };
270
- const deletedStorageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(deletedContact);
271
- // Save the deleted contact back to storage
272
- await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(deletedStorageEntry));
308
+ const { trace } = options;
309
+ const deleteContact = async () => {
310
+ if (!(0, sync_utils_1.canPerformContactSyncing)(options) ||
311
+ !contact.address ||
312
+ !contact.chainId ||
313
+ !contact.name?.trim()) {
314
+ return;
273
315
  }
274
- }
275
- catch {
276
- // If contact doesn't exist in remote storage, no need to mark as deleted
277
- console.warn('Contact not found in remote storage for deletion:', key);
278
- }
316
+ const { getUserStorageControllerInstance } = options;
317
+ const key = createContactKey(contact);
318
+ try {
319
+ // Try to get the existing contact first
320
+ const existingContactJson = await getUserStorageControllerInstance().performGetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`);
321
+ if (existingContactJson) {
322
+ // Mark the existing contact as deleted
323
+ const existingStorageEntry = JSON.parse(existingContactJson);
324
+ const existingContact = (0, utils_1.mapUserStorageEntryToAddressBookEntry)(existingStorageEntry);
325
+ const now = Date.now();
326
+ const deletedContact = {
327
+ ...existingContact,
328
+ deletedAt: now,
329
+ lastUpdatedAt: now,
330
+ };
331
+ const deletedStorageEntry = (0, utils_1.mapAddressBookEntryToUserStorageEntry)(deletedContact);
332
+ // Save the deleted contact back to storage
333
+ await getUserStorageControllerInstance().performSetStorage(`${storage_schema_1.USER_STORAGE_FEATURE_NAMES.addressBook}.${key}`, JSON.stringify(deletedStorageEntry));
334
+ }
335
+ }
336
+ catch {
337
+ // If contact doesn't exist in remote storage, no need to mark as deleted
338
+ console.warn('Contact not found in remote storage for deletion:', key);
339
+ }
340
+ };
341
+ return trace
342
+ ? await trace({ name: constants_1.TraceName.ContactSyncDeleteRemote }, deleteContact)
343
+ : await deleteContact();
279
344
  }
280
345
  exports.deleteContactInRemoteStorage = deleteContactInRemoteStorage;
281
346
  //# sourceMappingURL=controller-integration.cjs.map