@metamask-previews/account-tree-controller 0.12.1-preview-b13da658 → 0.12.1-preview-236bab4a
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.
- package/dist/backup-and-sync/service/atomic-sync-queue.cjs +7 -8
- package/dist/backup-and-sync/service/atomic-sync-queue.cjs.map +1 -1
- package/dist/backup-and-sync/service/atomic-sync-queue.d.cts +4 -5
- package/dist/backup-and-sync/service/atomic-sync-queue.d.cts.map +1 -1
- package/dist/backup-and-sync/service/atomic-sync-queue.d.mts +4 -5
- package/dist/backup-and-sync/service/atomic-sync-queue.d.mts.map +1 -1
- package/dist/backup-and-sync/service/atomic-sync-queue.mjs +7 -8
- package/dist/backup-and-sync/service/atomic-sync-queue.mjs.map +1 -1
- package/dist/backup-and-sync/service/index.cjs +2 -2
- package/dist/backup-and-sync/service/index.cjs.map +1 -1
- package/dist/backup-and-sync/service/index.d.cts.map +1 -1
- package/dist/backup-and-sync/service/index.d.mts.map +1 -1
- package/dist/backup-and-sync/service/index.mjs +2 -2
- package/dist/backup-and-sync/service/index.mjs.map +1 -1
- package/dist/backup-and-sync/syncing/legacy.cjs +11 -6
- package/dist/backup-and-sync/syncing/legacy.cjs.map +1 -1
- package/dist/backup-and-sync/syncing/legacy.d.cts.map +1 -1
- package/dist/backup-and-sync/syncing/legacy.d.mts.map +1 -1
- package/dist/backup-and-sync/syncing/legacy.mjs +11 -6
- package/dist/backup-and-sync/syncing/legacy.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -37,12 +37,13 @@ class AtomicSyncQueue {
|
|
|
37
37
|
/**
|
|
38
38
|
* Enqueues an atomic sync function for processing.
|
|
39
39
|
*
|
|
40
|
+
* @param context - The backup and sync context.
|
|
40
41
|
* @param syncFunction - The sync function to enqueue.
|
|
41
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
42
42
|
*/
|
|
43
|
-
enqueue(
|
|
44
|
-
// Block enqueueing if big sync is running
|
|
45
|
-
if (
|
|
43
|
+
enqueue(context, syncFunction) {
|
|
44
|
+
// Block enqueueing if big sync is running or if no initial sync has occurred
|
|
45
|
+
if (context.controller.state.isAccountTreeSyncingInProgress ||
|
|
46
|
+
!context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
const syncEvent = {
|
|
@@ -61,11 +62,9 @@ class AtomicSyncQueue {
|
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
64
|
* Processes the atomic sync queue.
|
|
64
|
-
*
|
|
65
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
66
65
|
*/
|
|
67
|
-
async process(
|
|
68
|
-
if (__classPrivateFieldGet(this, _AtomicSyncQueue_isProcessingInProgress, "f")
|
|
66
|
+
async process() {
|
|
67
|
+
if (__classPrivateFieldGet(this, _AtomicSyncQueue_isProcessingInProgress, "f")) {
|
|
69
68
|
return;
|
|
70
69
|
}
|
|
71
70
|
if (__classPrivateFieldGet(this, _AtomicSyncQueue_queue, "f").length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-sync-queue.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,8CAA4C;AAE5C;;;GAGG;AACH,MAAa,eAAe;IAgB1B,YAAY,qBAAqC;QAfjD;;WAEG;QACM,iCAA4B,EAAE,EAAC;QAExC;;WAEG;QACH,kDAA0B,KAAK,EAAC;QAEhC;;WAEG;QACM,yDAAsC;QAG7C,uBAAA,IAAI,0CAA0B,qBAAqB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAA,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,OAAO,CACL,
|
|
1
|
+
{"version":3,"file":"atomic-sync-queue.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,8CAA4C;AAE5C;;;GAGG;AACH,MAAa,eAAe;IAgB1B,YAAY,qBAAqC;QAfjD;;WAEG;QACM,iCAA4B,EAAE,EAAC;QAExC;;WAEG;QACH,kDAA0B,KAAK,EAAC;QAEhC;;WAEG;QACM,yDAAsC;QAG7C,uBAAA,IAAI,0CAA0B,qBAAqB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAA,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,OAAO,CACL,OAA6B,EAC7B,YAAiC;QAEjC,6EAA6E;QAC7E,IACE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,8BAA8B;YACvD,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,sCAAsC,EAChE;YACA,OAAO;SACR;QAED,MAAM,SAAS,GAAoB;YACjC,EAAE,EAAE,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACnE,OAAO,EAAE,YAAY;SACtB,CAAC;QAEF,uBAAA,IAAI,8BAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE5B,gDAAgD;QAChD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC7B,IAAI,uBAAA,IAAI,8CAAuB,MAA3B,IAAI,CAAyB,EAAE;oBACjC,wBAAgB,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;iBACtE;YACH,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,uBAAA,IAAI,+CAAwB,EAAE;YAChC,OAAO;SACR;QAED,IAAI,uBAAA,IAAI,8BAAO,CAAC,MAAM,KAAK,CAAC,EAAE;YAC5B,OAAO;SACR;QAED,uBAAA,IAAI,2CAA2B,IAAI,MAAA,CAAC;QAEpC,IAAI;YACF,OAAO,uBAAA,IAAI,8BAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,MAAM,KAAK,GAAG,uBAAA,IAAI,8BAAO,CAAC,KAAK,EAAE,CAAC;gBAClC,0BAA0B;gBAC1B,IAAI,CAAC,KAAK,EAAE;oBACV,MAAM;iBACP;gBAED,IAAI;oBACF,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;iBACvB;gBAAC,OAAO,KAAK,EAAE;oBACd,IAAI,uBAAA,IAAI,8CAAuB,MAA3B,IAAI,CAAyB,EAAE;wBACjC,wBAAgB,CAAC,KAAK,CACpB,uCAAuC,KAAK,CAAC,EAAE,EAAE,EACjD,KAAK,CACN,CAAC;qBACH;iBACF;aACF;SACF;gBAAS;YACR,uBAAA,IAAI,2CAA2B,KAAK,MAAA,CAAC;SACtC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,uBAAA,IAAI,8BAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,8BAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAI,YAAY;QACd,OAAO,uBAAA,IAAI,+CAAwB,CAAC;IACtC,CAAC;CACF;AAtHD,0CAsHC","sourcesContent":["import type { AtomicSyncEvent, BackupAndSyncContext } from '../types';\nimport { contextualLogger } from '../utils';\n\n/**\n * Manages atomic sync operations in a queue to prevent concurrent execution\n * and ensure proper ordering of sync events.\n */\nexport class AtomicSyncQueue {\n /**\n * Queue for atomic sync events that need to be processed asynchronously.\n */\n readonly #queue: AtomicSyncEvent[] = [];\n\n /**\n * Flag to prevent multiple queue processing operations from running concurrently.\n */\n #isProcessingInProgress = false;\n\n /**\n * Debug logging configuration function.\n */\n readonly #getEnableDebugLogging: () => boolean;\n\n constructor(getEnableDebugLogging?: () => boolean) {\n this.#getEnableDebugLogging = getEnableDebugLogging ?? (() => false);\n }\n\n /**\n * Enqueues an atomic sync function for processing.\n *\n * @param context - The backup and sync context.\n * @param syncFunction - The sync function to enqueue.\n */\n enqueue(\n context: BackupAndSyncContext,\n syncFunction: () => Promise<void>,\n ): void {\n // Block enqueueing if big sync is running or if no initial sync has occurred\n if (\n context.controller.state.isAccountTreeSyncingInProgress ||\n !context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n const syncEvent: AtomicSyncEvent = {\n id: `sync-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n execute: syncFunction,\n };\n\n this.#queue.push(syncEvent);\n\n // Process queue asynchronously without blocking\n setTimeout(() => {\n this.process().catch((error) => {\n if (this.#getEnableDebugLogging()) {\n contextualLogger.error('Error processing atomic sync queue:', error);\n }\n });\n }, 0);\n }\n\n /**\n * Processes the atomic sync queue.\n */\n async process(): Promise<void> {\n if (this.#isProcessingInProgress) {\n return;\n }\n\n if (this.#queue.length === 0) {\n return;\n }\n\n this.#isProcessingInProgress = true;\n\n try {\n while (this.#queue.length > 0) {\n const event = this.#queue.shift();\n /* istanbul ignore next */\n if (!event) {\n break;\n }\n\n try {\n await event.execute();\n } catch (error) {\n if (this.#getEnableDebugLogging()) {\n contextualLogger.error(\n `Failed to process atomic sync event ${event.id}`,\n error,\n );\n }\n }\n }\n } finally {\n this.#isProcessingInProgress = false;\n }\n }\n\n /**\n * Clears all pending sync events from the queue.\n * Useful when big sync starts to prevent stale updates.\n */\n clear(): void {\n this.#queue.length = 0;\n }\n\n /**\n * Gets the current queue size.\n *\n * @returns The number of pending sync events.\n */\n get size(): number {\n return this.#queue.length;\n }\n\n /**\n * Checks if queue processing is currently in progress.\n *\n * @returns True if processing is in progress.\n */\n get isProcessing(): boolean {\n return this.#isProcessingInProgress;\n }\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BackupAndSyncContext } from "../types.cjs";
|
|
1
2
|
/**
|
|
2
3
|
* Manages atomic sync operations in a queue to prevent concurrent execution
|
|
3
4
|
* and ensure proper ordering of sync events.
|
|
@@ -8,16 +9,14 @@ export declare class AtomicSyncQueue {
|
|
|
8
9
|
/**
|
|
9
10
|
* Enqueues an atomic sync function for processing.
|
|
10
11
|
*
|
|
12
|
+
* @param context - The backup and sync context.
|
|
11
13
|
* @param syncFunction - The sync function to enqueue.
|
|
12
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
13
14
|
*/
|
|
14
|
-
enqueue(syncFunction: () => Promise<void
|
|
15
|
+
enqueue(context: BackupAndSyncContext, syncFunction: () => Promise<void>): void;
|
|
15
16
|
/**
|
|
16
17
|
* Processes the atomic sync queue.
|
|
17
|
-
*
|
|
18
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
19
18
|
*/
|
|
20
|
-
process(
|
|
19
|
+
process(): Promise<void>;
|
|
21
20
|
/**
|
|
22
21
|
* Clears all pending sync events from the queue.
|
|
23
22
|
* Useful when big sync starts to prevent stale updates.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-sync-queue.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"atomic-sync-queue.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,oBAAoB,EAAE,qBAAiB;AAGtE;;;GAGG;AACH,qBAAa,eAAe;;gBAgBd,qBAAqB,CAAC,EAAE,MAAM,OAAO;IAIjD;;;;;OAKG;IACH,OAAO,CACL,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAChC,IAAI;IA0BP;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC9B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BackupAndSyncContext } from "../types.mjs";
|
|
1
2
|
/**
|
|
2
3
|
* Manages atomic sync operations in a queue to prevent concurrent execution
|
|
3
4
|
* and ensure proper ordering of sync events.
|
|
@@ -8,16 +9,14 @@ export declare class AtomicSyncQueue {
|
|
|
8
9
|
/**
|
|
9
10
|
* Enqueues an atomic sync function for processing.
|
|
10
11
|
*
|
|
12
|
+
* @param context - The backup and sync context.
|
|
11
13
|
* @param syncFunction - The sync function to enqueue.
|
|
12
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
13
14
|
*/
|
|
14
|
-
enqueue(syncFunction: () => Promise<void
|
|
15
|
+
enqueue(context: BackupAndSyncContext, syncFunction: () => Promise<void>): void;
|
|
15
16
|
/**
|
|
16
17
|
* Processes the atomic sync queue.
|
|
17
|
-
*
|
|
18
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
19
18
|
*/
|
|
20
|
-
process(
|
|
19
|
+
process(): Promise<void>;
|
|
21
20
|
/**
|
|
22
21
|
* Clears all pending sync events from the queue.
|
|
23
22
|
* Useful when big sync starts to prevent stale updates.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-sync-queue.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"atomic-sync-queue.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,oBAAoB,EAAE,qBAAiB;AAGtE;;;GAGG;AACH,qBAAa,eAAe;;gBAgBd,qBAAqB,CAAC,EAAE,MAAM,OAAO;IAIjD;;;;;OAKG;IACH,OAAO,CACL,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAChC,IAAI;IA0BP;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC9B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;CACF"}
|
|
@@ -34,12 +34,13 @@ export class AtomicSyncQueue {
|
|
|
34
34
|
/**
|
|
35
35
|
* Enqueues an atomic sync function for processing.
|
|
36
36
|
*
|
|
37
|
+
* @param context - The backup and sync context.
|
|
37
38
|
* @param syncFunction - The sync function to enqueue.
|
|
38
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
39
39
|
*/
|
|
40
|
-
enqueue(
|
|
41
|
-
// Block enqueueing if big sync is running
|
|
42
|
-
if (
|
|
40
|
+
enqueue(context, syncFunction) {
|
|
41
|
+
// Block enqueueing if big sync is running or if no initial sync has occurred
|
|
42
|
+
if (context.controller.state.isAccountTreeSyncingInProgress ||
|
|
43
|
+
!context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
const syncEvent = {
|
|
@@ -58,11 +59,9 @@ export class AtomicSyncQueue {
|
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Processes the atomic sync queue.
|
|
61
|
-
*
|
|
62
|
-
* @param isBigSyncInProgress - Whether big sync is currently running.
|
|
63
62
|
*/
|
|
64
|
-
async process(
|
|
65
|
-
if (__classPrivateFieldGet(this, _AtomicSyncQueue_isProcessingInProgress, "f")
|
|
63
|
+
async process() {
|
|
64
|
+
if (__classPrivateFieldGet(this, _AtomicSyncQueue_isProcessingInProgress, "f")) {
|
|
66
65
|
return;
|
|
67
66
|
}
|
|
68
67
|
if (__classPrivateFieldGet(this, _AtomicSyncQueue_queue, "f").length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-sync-queue.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,gBAAgB,EAAE,2BAAiB;AAE5C;;;GAGG;AACH,MAAM,OAAO,eAAe;IAgB1B,YAAY,qBAAqC;QAfjD;;WAEG;QACM,iCAA4B,EAAE,EAAC;QAExC;;WAEG;QACH,kDAA0B,KAAK,EAAC;QAEhC;;WAEG;QACM,yDAAsC;QAG7C,uBAAA,IAAI,0CAA0B,qBAAqB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAA,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,OAAO,CACL,
|
|
1
|
+
{"version":3,"file":"atomic-sync-queue.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/atomic-sync-queue.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,gBAAgB,EAAE,2BAAiB;AAE5C;;;GAGG;AACH,MAAM,OAAO,eAAe;IAgB1B,YAAY,qBAAqC;QAfjD;;WAEG;QACM,iCAA4B,EAAE,EAAC;QAExC;;WAEG;QACH,kDAA0B,KAAK,EAAC;QAEhC;;WAEG;QACM,yDAAsC;QAG7C,uBAAA,IAAI,0CAA0B,qBAAqB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAA,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACH,OAAO,CACL,OAA6B,EAC7B,YAAiC;QAEjC,6EAA6E;QAC7E,IACE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,8BAA8B;YACvD,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,sCAAsC,EAChE;YACA,OAAO;SACR;QAED,MAAM,SAAS,GAAoB;YACjC,EAAE,EAAE,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;YACnE,OAAO,EAAE,YAAY;SACtB,CAAC;QAEF,uBAAA,IAAI,8BAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE5B,gDAAgD;QAChD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC7B,IAAI,uBAAA,IAAI,8CAAuB,MAA3B,IAAI,CAAyB,EAAE;oBACjC,gBAAgB,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;iBACtE;YACH,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,uBAAA,IAAI,+CAAwB,EAAE;YAChC,OAAO;SACR;QAED,IAAI,uBAAA,IAAI,8BAAO,CAAC,MAAM,KAAK,CAAC,EAAE;YAC5B,OAAO;SACR;QAED,uBAAA,IAAI,2CAA2B,IAAI,MAAA,CAAC;QAEpC,IAAI;YACF,OAAO,uBAAA,IAAI,8BAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,MAAM,KAAK,GAAG,uBAAA,IAAI,8BAAO,CAAC,KAAK,EAAE,CAAC;gBAClC,0BAA0B;gBAC1B,IAAI,CAAC,KAAK,EAAE;oBACV,MAAM;iBACP;gBAED,IAAI;oBACF,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;iBACvB;gBAAC,OAAO,KAAK,EAAE;oBACd,IAAI,uBAAA,IAAI,8CAAuB,MAA3B,IAAI,CAAyB,EAAE;wBACjC,gBAAgB,CAAC,KAAK,CACpB,uCAAuC,KAAK,CAAC,EAAE,EAAE,EACjD,KAAK,CACN,CAAC;qBACH;iBACF;aACF;SACF;gBAAS;YACR,uBAAA,IAAI,2CAA2B,KAAK,MAAA,CAAC;SACtC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,uBAAA,IAAI,8BAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,8BAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAI,YAAY;QACd,OAAO,uBAAA,IAAI,+CAAwB,CAAC;IACtC,CAAC;CACF","sourcesContent":["import type { AtomicSyncEvent, BackupAndSyncContext } from '../types';\nimport { contextualLogger } from '../utils';\n\n/**\n * Manages atomic sync operations in a queue to prevent concurrent execution\n * and ensure proper ordering of sync events.\n */\nexport class AtomicSyncQueue {\n /**\n * Queue for atomic sync events that need to be processed asynchronously.\n */\n readonly #queue: AtomicSyncEvent[] = [];\n\n /**\n * Flag to prevent multiple queue processing operations from running concurrently.\n */\n #isProcessingInProgress = false;\n\n /**\n * Debug logging configuration function.\n */\n readonly #getEnableDebugLogging: () => boolean;\n\n constructor(getEnableDebugLogging?: () => boolean) {\n this.#getEnableDebugLogging = getEnableDebugLogging ?? (() => false);\n }\n\n /**\n * Enqueues an atomic sync function for processing.\n *\n * @param context - The backup and sync context.\n * @param syncFunction - The sync function to enqueue.\n */\n enqueue(\n context: BackupAndSyncContext,\n syncFunction: () => Promise<void>,\n ): void {\n // Block enqueueing if big sync is running or if no initial sync has occurred\n if (\n context.controller.state.isAccountTreeSyncingInProgress ||\n !context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n const syncEvent: AtomicSyncEvent = {\n id: `sync-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n execute: syncFunction,\n };\n\n this.#queue.push(syncEvent);\n\n // Process queue asynchronously without blocking\n setTimeout(() => {\n this.process().catch((error) => {\n if (this.#getEnableDebugLogging()) {\n contextualLogger.error('Error processing atomic sync queue:', error);\n }\n });\n }, 0);\n }\n\n /**\n * Processes the atomic sync queue.\n */\n async process(): Promise<void> {\n if (this.#isProcessingInProgress) {\n return;\n }\n\n if (this.#queue.length === 0) {\n return;\n }\n\n this.#isProcessingInProgress = true;\n\n try {\n while (this.#queue.length > 0) {\n const event = this.#queue.shift();\n /* istanbul ignore next */\n if (!event) {\n break;\n }\n\n try {\n await event.execute();\n } catch (error) {\n if (this.#getEnableDebugLogging()) {\n contextualLogger.error(\n `Failed to process atomic sync event ${event.id}`,\n error,\n );\n }\n }\n }\n } finally {\n this.#isProcessingInProgress = false;\n }\n }\n\n /**\n * Clears all pending sync events from the queue.\n * Useful when big sync starts to prevent stale updates.\n */\n clear(): void {\n this.#queue.length = 0;\n }\n\n /**\n * Gets the current queue size.\n *\n * @returns The number of pending sync events.\n */\n get size(): number {\n return this.#queue.length;\n }\n\n /**\n * Checks if queue processing is currently in progress.\n *\n * @returns True if processing is in progress.\n */\n get isProcessing(): boolean {\n return this.#isProcessingInProgress;\n }\n}\n"]}
|
|
@@ -58,7 +58,7 @@ class BackupAndSyncService {
|
|
|
58
58
|
if (!__classPrivateFieldGet(this, _BackupAndSyncService_context, "f").controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
-
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(() => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleWalletSync).call(this, walletId)
|
|
61
|
+
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), () => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleWalletSync).call(this, walletId));
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Enqueues a single group sync operation.
|
|
@@ -69,7 +69,7 @@ class BackupAndSyncService {
|
|
|
69
69
|
if (!__classPrivateFieldGet(this, _BackupAndSyncService_context, "f").controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(() => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleGroupSync).call(this, groupId)
|
|
72
|
+
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), () => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleGroupSync).call(this, groupId));
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* Performs a full synchronization of the local account tree with user storage, ensuring consistency
|
|
@@ -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';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountTreeControllerState } from '../../types';\nimport { TraceName } from '../analytics';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncSingleGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type { BackupAndSyncContext } 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 contextualLogger,\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 (performFullSync)\n * - Single item sync operations (wallet and group syncing)\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 constructor(context: BackupAndSyncContext) {\n this.#context = context;\n\n // Initialize with debug logging from context\n this.#atomicSyncQueue = new AtomicSyncQueue(\n () => context.enableDebugLogging,\n );\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 * Enqueues a single wallet sync operation.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(\n () => this.#performSingleWalletSync(walletId),\n this.isInProgress,\n );\n }\n\n /**\n * Enqueues a single group sync operation.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(\n () => this.#performSingleGroupSync(groupId),\n this.isInProgress,\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 *\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 performFullSync(): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping full sync operation.',\n );\n }\n return;\n }\n\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 // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Clear stale atomic syncs - big sync supersedes them\n this.#atomicSyncQueue.clear();\n\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 let stateSnapshot: StateSnapshot | undefined;\n const entropySourceId = wallet.metadata.entropy.id;\n\n try {\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n const [walletFromUserStorage, groupsFromUserStorage] =\n 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\n // 3. Execute multichain account syncing\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error syncing wallet ${wallet.id}:`,\n error instanceof Error ? error.message : String(error),\n );\n }\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 if (this.#context.enableDebugLogging) {\n contextualLogger.log(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n }\n } catch (rollbackError) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n 'Error during multichain account syncing:',\n error,\n );\n }\n throw error;\n } finally {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n },\n );\n }\n };\n\n // Execute the big sync function with tracing\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\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 #performSingleWalletSync(walletId: AccountWalletId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single wallet sync operation.',\n );\n }\n return;\n }\n\n try {\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\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 #performSingleGroupSync(groupId: AccountGroupId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single group sync operation.',\n );\n }\n return;\n }\n\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 syncSingleGroupMetadata(\n this.#context,\n group as AccountGroupMultichainAccountObject,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single group sync for ${groupId}:`,\n error,\n );\n }\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';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountTreeControllerState } from '../../types';\nimport { TraceName } from '../analytics';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncSingleGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type { BackupAndSyncContext } 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 contextualLogger,\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 (performFullSync)\n * - Single item sync operations (wallet and group syncing)\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 constructor(context: BackupAndSyncContext) {\n this.#context = context;\n\n // Initialize with debug logging from context\n this.#atomicSyncQueue = new AtomicSyncQueue(\n () => context.enableDebugLogging,\n );\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 * Enqueues a single wallet sync operation.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(this.#context, () =>\n this.#performSingleWalletSync(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(this.#context, () =>\n this.#performSingleGroupSync(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 *\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 performFullSync(): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping full sync operation.',\n );\n }\n return;\n }\n\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 // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Clear stale atomic syncs - big sync supersedes them\n this.#atomicSyncQueue.clear();\n\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 let stateSnapshot: StateSnapshot | undefined;\n const entropySourceId = wallet.metadata.entropy.id;\n\n try {\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n const [walletFromUserStorage, groupsFromUserStorage] =\n 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\n // 3. Execute multichain account syncing\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error syncing wallet ${wallet.id}:`,\n error instanceof Error ? error.message : String(error),\n );\n }\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 if (this.#context.enableDebugLogging) {\n contextualLogger.log(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n }\n } catch (rollbackError) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n 'Error during multichain account syncing:',\n error,\n );\n }\n throw error;\n } finally {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n },\n );\n }\n };\n\n // Execute the big sync function with tracing\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\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 #performSingleWalletSync(walletId: AccountWalletId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single wallet sync operation.',\n );\n }\n return;\n }\n\n try {\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\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 #performSingleGroupSync(groupId: AccountGroupId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single group sync operation.',\n );\n }\n return;\n }\n\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 syncSingleGroupMetadata(\n this.#context,\n group as AccountGroupMultichainAccountObject,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single group sync for ${groupId}:`,\n error,\n );\n }\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,oBAAoB,EAAE,qBAAiB;AAgBrD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAQnB,OAAO,EAAE,oBAAoB;IASzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;
|
|
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,oBAAoB,EAAE,qBAAiB;AAgBrD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAQnB,OAAO,EAAE,oBAAoB;IASzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAYxD;;;;OAIG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAYrD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CA6RvC"}
|
|
@@ -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,oBAAoB,EAAE,qBAAiB;AAgBrD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAQnB,OAAO,EAAE,oBAAoB;IASzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;
|
|
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,oBAAoB,EAAE,qBAAiB;AAgBrD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;;gBAQnB,OAAO,EAAE,oBAAoB;IASzC;;;;OAIG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAYxD;;;;OAIG;IACH,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAYrD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CA6RvC"}
|
|
@@ -55,7 +55,7 @@ export class BackupAndSyncService {
|
|
|
55
55
|
if (!__classPrivateFieldGet(this, _BackupAndSyncService_context, "f").controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
|
-
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(() => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleWalletSync).call(this, walletId)
|
|
58
|
+
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), () => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleWalletSync).call(this, walletId));
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Enqueues a single group sync operation.
|
|
@@ -66,7 +66,7 @@ export class BackupAndSyncService {
|
|
|
66
66
|
if (!__classPrivateFieldGet(this, _BackupAndSyncService_context, "f").controller.state.hasAccountTreeSyncingSyncedAtLeastOnce) {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
-
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(() => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleGroupSync).call(this, groupId)
|
|
69
|
+
__classPrivateFieldGet(this, _BackupAndSyncService_atomicSyncQueue, "f").enqueue(__classPrivateFieldGet(this, _BackupAndSyncService_context, "f"), () => __classPrivateFieldGet(this, _BackupAndSyncService_instances, "m", _BackupAndSyncService_performSingleGroupSync).call(this, groupId));
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Performs a full synchronization of the local account tree with user storage, ensuring consistency
|
|
@@ -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';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountTreeControllerState } from '../../types';\nimport { TraceName } from '../analytics';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncSingleGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type { BackupAndSyncContext } 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 contextualLogger,\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 (performFullSync)\n * - Single item sync operations (wallet and group syncing)\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 constructor(context: BackupAndSyncContext) {\n this.#context = context;\n\n // Initialize with debug logging from context\n this.#atomicSyncQueue = new AtomicSyncQueue(\n () => context.enableDebugLogging,\n );\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 * Enqueues a single wallet sync operation.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(\n () => this.#performSingleWalletSync(walletId),\n this.isInProgress,\n );\n }\n\n /**\n * Enqueues a single group sync operation.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(\n () => this.#performSingleGroupSync(groupId),\n this.isInProgress,\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 *\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 performFullSync(): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping full sync operation.',\n );\n }\n return;\n }\n\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 // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Clear stale atomic syncs - big sync supersedes them\n this.#atomicSyncQueue.clear();\n\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 let stateSnapshot: StateSnapshot | undefined;\n const entropySourceId = wallet.metadata.entropy.id;\n\n try {\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n const [walletFromUserStorage, groupsFromUserStorage] =\n 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\n // 3. Execute multichain account syncing\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error syncing wallet ${wallet.id}:`,\n error instanceof Error ? error.message : String(error),\n );\n }\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 if (this.#context.enableDebugLogging) {\n contextualLogger.log(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n }\n } catch (rollbackError) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n 'Error during multichain account syncing:',\n error,\n );\n }\n throw error;\n } finally {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n },\n );\n }\n };\n\n // Execute the big sync function with tracing\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\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 #performSingleWalletSync(walletId: AccountWalletId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single wallet sync operation.',\n );\n }\n return;\n }\n\n try {\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\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 #performSingleGroupSync(groupId: AccountGroupId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single group sync operation.',\n );\n }\n return;\n }\n\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 syncSingleGroupMetadata(\n this.#context,\n group as AccountGroupMultichainAccountObject,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single group sync for ${groupId}:`,\n error,\n );\n }\n throw error;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/service/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,iBAAiB,EAAE,8BAA8B;AAE1D,OAAO,EAAE,eAAe,EAAE,gCAA4B;AAGtD,OAAO,EAAE,SAAS,EAAE,+BAAqB;AACzC,OAAO,EAAE,YAAY,EAAE,oCAA0B;AACjD,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EACnB,6BAAmB;AAEpB,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC5B,kCAAwB;AACzB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,8BAA8B,EAC9B,gBAAgB,EACjB,2BAAiB;AAGlB;;;;;;;;GAQG;AACH,MAAM,OAAO,oBAAoB;IAQ/B,YAAY,OAA6B;;QAPhC,gDAA+B;QAExC;;WAEG;QACM,wDAAkC;QAGzC,uBAAA,IAAI,iCAAY,OAAO,MAAA,CAAC;QAExB,6CAA6C;QAC7C,uBAAA,IAAI,yCAAoB,IAAI,eAAe,CACzC,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CACjC,MAAA,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,IAAI,YAAY;QACd,OAAO,uBAAA,IAAI,qCAAS,CAAC,UAAU,CAAC,KAAK,CAAC,8BAA8B,CAAC;IACvE,CAAC;IAED;;;;OAIG;IACH,uBAAuB,CAAC,QAAyB;QAC/C,IACE,CAAC,uBAAA,IAAI,qCAAS,CAAC,UAAU,CAAC,KAAK,CAAC,sCAAsC,EACtE;YACA,OAAO;SACR;QAED,uBAAA,IAAI,6CAAiB,CAAC,OAAO,CAAC,uBAAA,IAAI,qCAAS,EAAE,GAAG,EAAE,CAChD,uBAAA,IAAI,sFAAyB,MAA7B,IAAI,EAA0B,QAAQ,CAAC,CACxC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,sBAAsB,CAAC,OAAuB;QAC5C,IACE,CAAC,uBAAA,IAAI,qCAAS,CAAC,UAAU,CAAC,KAAK,CAAC,sCAAsC,EACtE;YACA,OAAO;SACR;QAED,uBAAA,IAAI,6CAAiB,CAAC,OAAO,CAAC,uBAAA,IAAI,qCAAS,EAAE,GAAG,EAAE,CAChD,uBAAA,IAAI,qFAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CACtC,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,eAAe;QACnB,IAAI,uBAAA,IAAI,qCAAS,CAAC,+BAA+B,EAAE;YACjD,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;gBACpC,gBAAgB,CAAC,IAAI,CACnB,uEAAuE,CACxE,CAAC;aACH;YACD,OAAO;SACR;QAED,wDAAwD;QACxD,gFAAgF;QAChF,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,OAAO;SACR;QAED,4DAA4D;QAC5D,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;YAC3B,IAAI;gBACF,uBAAA,IAAI,qCAAS,CAAC,uBAAuB,CACnC,CAAC,KAAiC,EAAE,EAAE;oBACpC,KAAK,CAAC,8BAA8B,GAAG,IAAI,CAAC;gBAC9C,CAAC,CACF,CAAC;gBAEF,sDAAsD;gBACtD,uBAAA,IAAI,6CAAiB,CAAC,KAAK,EAAE,CAAC;gBAE9B,mEAAmE;gBACnE,MAAM,oBAAoB,GAAG,sBAAsB,CAAC,uBAAA,IAAI,qCAAS,CAAC,CAAC;gBAEnE,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE;oBAChC,0DAA0D;oBAC1D,OAAO;iBACR;gBAED,oCAAoC;gBACpC,KAAK,MAAM,MAAM,IAAI,oBAAoB,EAAE;oBACzC,IAAI,aAAwC,CAAC;oBAC7C,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAEnD,IAAI;wBACF,MAAM,eAAe,GAAG,MAAM,YAAY,CACxC,uBAAA,IAAI,qCAAS,EACb,eAAe,CAChB,CAAC;wBAEF,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,GAClD,MAAM,OAAO,CAAC,GAAG,CAAC;4BAChB,wBAAwB,CAAC,uBAAA,IAAI,qCAAS,EAAE,eAAe,CAAC;4BACxD,2BAA2B,CAAC,uBAAA,IAAI,qCAAS,EAAE,eAAe,CAAC;yBAC5D,CAAC,CAAC;wBAEL,0DAA0D;wBAC1D,IACE,CAAC,qBAAqB;4BACtB,CAAC,qBAAqB,CAAC,8BAA8B,EACrD;4BACA,qCAAqC;4BACrC,8DAA8D;4BAC9D,8CAA8C;4BAC9C,MAAM,2BAA2B,CAC/B,uBAAA,IAAI,qCAAS,EACb,eAAe,EACf,eAAe,CAChB,CAAC;yBACH;wBAED,wCAAwC;wBACxC,qBAAqB;wBACrB,+EAA+E;wBAC/E,aAAa,GAAG,mBAAmB,CAAC,uBAAA,IAAI,qCAAS,CAAC,CAAC;wBAEnD,uCAAuC;wBACvC,MAAM,kBAAkB,CACtB,uBAAA,IAAI,qCAAS,EACb,MAAM,EACN,qBAAqB,EACrB,eAAe,CAChB,CAAC;wBAEF,qBAAqB;wBACrB,+DAA+D;wBAC/D,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE;4BACjC,0GAA0G;4BAC1G,MAAM,2BAA2B,CAC/B,uBAAA,IAAI,qCAAS,EACb,8BAA8B,CAAC,uBAAA,IAAI,qCAAS,EAAE,MAAM,CAAC,EAAE,CAAC,EACxD,eAAe,CAChB,CAAC;4BAEF,SAAS,CAAC,gEAAgE;yBAC3E;wBAED,4EAA4E;wBAC5E,qFAAqF;wBACrF,MAAM,gCAAgC,CACpC,uBAAA,IAAI,qCAAS,EACb,qBAAqB,EACrB,eAAe,EACf,eAAe,CAChB,CAAC;wBAEF,sCAAsC;wBACtC,MAAM,kBAAkB,CACtB,uBAAA,IAAI,qCAAS,EACb,MAAM,EACN,qBAAqB,EACrB,eAAe,EACf,eAAe,CAChB,CAAC;qBACH;oBAAC,OAAO,KAAK,EAAE;wBACd,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;4BACpC,gBAAgB,CAAC,KAAK,CACpB,wBAAwB,MAAM,CAAC,EAAE,GAAG,EACpC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;yBACH;wBAED,oDAAoD;wBACpD,IAAI;4BACF,IAAI,CAAC,aAAa,EAAE;gCAClB,MAAM,IAAI,KAAK,CACb,wCAAwC,MAAM,CAAC,EAAE,EAAE,CACpD,CAAC;6BACH;4BACD,wBAAwB,CAAC,uBAAA,IAAI,qCAAS,EAAE,aAAa,CAAC,CAAC;4BACvD,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;gCACpC,gBAAgB,CAAC,GAAG,CAClB,wCAAwC,MAAM,CAAC,EAAE,EAAE,CACpD,CAAC;6BACH;yBACF;wBAAC,OAAO,aAAa,EAAE;4BACtB,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;gCACpC,gBAAgB,CAAC,KAAK,CACpB,uCAAuC,MAAM,CAAC,EAAE,GAAG,EACnD,aAAa,YAAY,KAAK;oCAC5B,CAAC,CAAC,aAAa,CAAC,OAAO;oCACvB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAC1B,CAAC;6BACH;yBACF;wBAED,+DAA+D;wBAC/D,SAAS;qBACV;iBACF;aACF;YAAC,OAAO,KAAK,EAAE;gBACd,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;oBACpC,gBAAgB,CAAC,KAAK,CACpB,0CAA0C,EAC1C,KAAK,CACN,CAAC;iBACH;gBACD,MAAM,KAAK,CAAC;aACb;oBAAS;gBACR,uBAAA,IAAI,qCAAS,CAAC,uBAAuB,CACnC,CAAC,KAAiC,EAAE,EAAE;oBACpC,KAAK,CAAC,8BAA8B,GAAG,KAAK,CAAC;oBAC7C,KAAK,CAAC,sCAAsC,GAAG,IAAI,CAAC;gBACtD,CAAC,CACF,CAAC;aACH;QACH,CAAC,CAAC;QAEF,6CAA6C;QAC7C,MAAM,uBAAA,IAAI,qCAAS,CAAC,OAAO,CACzB;YACE,IAAI,EAAE,SAAS,CAAC,eAAe;SAChC,EACD,SAAS,CACV,CAAC;IACJ,CAAC;CAiHF;;AA/GC;;;;GAIG;AACH,KAAK,wDAA0B,QAAyB;IACtD,IAAI,uBAAA,IAAI,qCAAS,CAAC,+BAA+B,EAAE;QACjD,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;YACpC,gBAAgB,CAAC,IAAI,CACnB,gFAAgF,CACjF,CAAC;SACH;QACD,OAAO;KACR;IAED,IAAI;QACF,MAAM,MAAM,GACV,uBAAA,IAAI,qCAAS,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YACxD,OAAO,CAAC,4BAA4B;SACrC;QAED,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,eAAe,GAAG,MAAM,YAAY,CACxC,uBAAA,IAAI,qCAAS,EACb,eAAe,CAChB,CAAC;QACF,MAAM,qBAAqB,GAAG,MAAM,wBAAwB,CAC1D,uBAAA,IAAI,qCAAS,EACb,eAAe,CAChB,CAAC;QAEF,MAAM,kBAAkB,CACtB,uBAAA,IAAI,qCAAS,EACb,MAAM,EACN,qBAAqB,EACrB,eAAe,CAChB,CAAC;KACH;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;YACpC,gBAAgB,CAAC,KAAK,CACpB,mCAAmC,QAAQ,GAAG,EAC9C,KAAK,CACN,CAAC;SACH;QACD,MAAM,KAAK,CAAC;KACb;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,uDAAyB,OAAuB;IACnD,IAAI,uBAAA,IAAI,qCAAS,CAAC,+BAA+B,EAAE;QACjD,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;YACpC,gBAAgB,CAAC,IAAI,CACnB,+EAA+E,CAChF,CAAC;SACH;QACD,OAAO;KACR;IAED,IAAI;QACF,MAAM,QAAQ,GAAG,uBAAA,IAAI,qCAAS,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC,QAAQ,EAAE;YACb,OAAO;SACR;QAED,MAAM,MAAM,GACV,uBAAA,IAAI,qCAAS,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,OAAO,EAAE;YACxD,OAAO,CAAC,4BAA4B;SACrC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,EAAE;YACV,OAAO;SACR;QAED,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,eAAe,GAAG,MAAM,YAAY,CACxC,uBAAA,IAAI,qCAAS,EACb,eAAe,CAChB,CAAC;QAEF,2CAA2C;QAC3C,MAAM,oBAAoB,GAAG,MAAM,uBAAuB,CACxD,uBAAA,IAAI,qCAAS,EACb,eAAe,EACf,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAClC,CAAC;QAEF,MAAM,uBAAuB,CAC3B,uBAAA,IAAI,qCAAS,EACb,KAA4C,EAC5C,oBAAoB,EACpB,eAAe,EACf,eAAe,CAChB,CAAC;KACH;IAAC,OAAO,KAAK,EAAE;QACd,IAAI,uBAAA,IAAI,qCAAS,CAAC,kBAAkB,EAAE;YACpC,gBAAgB,CAAC,KAAK,CACpB,kCAAkC,OAAO,GAAG,EAC5C,KAAK,CACN,CAAC;SACH;QACD,MAAM,KAAK,CAAC;KACb;AACH,CAAC","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletType } from '@metamask/account-api';\n\nimport { AtomicSyncQueue } from './atomic-sync-queue';\nimport type { AccountGroupMultichainAccountObject } from '../../group';\nimport type { AccountTreeControllerState } from '../../types';\nimport { TraceName } from '../analytics';\nimport { getProfileId } from '../authentication';\nimport {\n createLocalGroupsFromUserStorage,\n performLegacyAccountSyncing,\n syncGroupsMetadata,\n syncSingleGroupMetadata,\n syncWalletMetadata,\n} from '../syncing';\nimport type { BackupAndSyncContext } 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 contextualLogger,\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 (performFullSync)\n * - Single item sync operations (wallet and group syncing)\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 constructor(context: BackupAndSyncContext) {\n this.#context = context;\n\n // Initialize with debug logging from context\n this.#atomicSyncQueue = new AtomicSyncQueue(\n () => context.enableDebugLogging,\n );\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 * Enqueues a single wallet sync operation.\n *\n * @param walletId - The wallet ID to sync.\n */\n enqueueSingleWalletSync(walletId: AccountWalletId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(this.#context, () =>\n this.#performSingleWalletSync(walletId),\n );\n }\n\n /**\n * Enqueues a single group sync operation.\n *\n * @param groupId - The group ID to sync.\n */\n enqueueSingleGroupSync(groupId: AccountGroupId): void {\n if (\n !this.#context.controller.state.hasAccountTreeSyncingSyncedAtLeastOnce\n ) {\n return;\n }\n\n this.#atomicSyncQueue.enqueue(this.#context, () =>\n this.#performSingleGroupSync(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 *\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 performFullSync(): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping full sync operation.',\n );\n }\n return;\n }\n\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 // Encapsulate the sync logic in a function to allow tracing\n const bigSyncFn = async () => {\n try {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = true;\n },\n );\n\n // Clear stale atomic syncs - big sync supersedes them\n this.#atomicSyncQueue.clear();\n\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 let stateSnapshot: StateSnapshot | undefined;\n const entropySourceId = wallet.metadata.entropy.id;\n\n try {\n const walletProfileId = await getProfileId(\n this.#context,\n entropySourceId,\n );\n\n const [walletFromUserStorage, groupsFromUserStorage] =\n 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\n // 3. Execute multichain account syncing\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error syncing wallet ${wallet.id}:`,\n error instanceof Error ? error.message : String(error),\n );\n }\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 if (this.#context.enableDebugLogging) {\n contextualLogger.log(\n `Rolled back state changes for wallet ${wallet.id}`,\n );\n }\n } catch (rollbackError) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Failed to rollback state for wallet ${wallet.id}:`,\n rollbackError instanceof Error\n ? rollbackError.message\n : String(rollbackError),\n );\n }\n }\n\n // Continue with next wallet instead of failing the entire sync\n continue;\n }\n }\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n 'Error during multichain account syncing:',\n error,\n );\n }\n throw error;\n } finally {\n this.#context.controllerStateUpdateFn(\n (state: AccountTreeControllerState) => {\n state.isAccountTreeSyncingInProgress = false;\n state.hasAccountTreeSyncingSyncedAtLeastOnce = true;\n },\n );\n }\n };\n\n // Execute the big sync function with tracing\n await this.#context.traceFn(\n {\n name: TraceName.AccountSyncFull,\n },\n bigSyncFn,\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 #performSingleWalletSync(walletId: AccountWalletId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single wallet sync operation.',\n );\n }\n return;\n }\n\n try {\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single wallet sync for ${walletId}:`,\n error,\n );\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 #performSingleGroupSync(groupId: AccountGroupId): Promise<void> {\n if (this.#context.disableMultichainAccountSyncing) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.warn(\n 'Multichain account syncing is disabled. Skipping single group sync operation.',\n );\n }\n return;\n }\n\n try {\n const walletId = this.#context.groupIdToWalletId.get(groupId);\n if (!walletId) {\n return;\n }\n\n const wallet =\n this.#context.controller.state.accountTree.wallets[walletId];\n if (!wallet || wallet.type !== AccountWalletType.Entropy) {\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 syncSingleGroupMetadata(\n this.#context,\n group as AccountGroupMultichainAccountObject,\n groupFromUserStorage,\n entropySourceId,\n walletProfileId,\n );\n } catch (error) {\n if (this.#context.enableDebugLogging) {\n contextualLogger.error(\n `Error in single group sync for ${groupId}:`,\n error,\n );\n }\n throw error;\n }\n }\n}\n"]}
|
|
@@ -18,7 +18,7 @@ const utils_1 = require("../utils/index.cjs");
|
|
|
18
18
|
* @param profileId - The profile ID for analytics.
|
|
19
19
|
*/
|
|
20
20
|
const performLegacyAccountSyncing = async (context, entropySourceId, profileId) => {
|
|
21
|
-
// 1. Get
|
|
21
|
+
// 1. Get legacy account syncing data
|
|
22
22
|
const legacyAccountsFromUserStorage = await (0, user_storage_1.getAllLegacyUserStorageAccounts)(context, entropySourceId);
|
|
23
23
|
if (legacyAccountsFromUserStorage.length === 0) {
|
|
24
24
|
if (context.enableDebugLogging) {
|
|
@@ -30,16 +30,21 @@ const performLegacyAccountSyncing = async (context, entropySourceId, profileId)
|
|
|
30
30
|
});
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
// 2. Create account groups
|
|
34
|
-
const
|
|
35
|
-
|
|
33
|
+
// 2. Create account groups accordingly
|
|
34
|
+
const numberOfAccountGroupsToCreate = legacyAccountsFromUserStorage.length;
|
|
35
|
+
if (context.enableDebugLogging) {
|
|
36
|
+
utils_1.contextualLogger.info(`Creating ${numberOfAccountGroupsToCreate} account groups for legacy accounts`);
|
|
37
|
+
}
|
|
36
38
|
if (numberOfAccountGroupsToCreate > 0) {
|
|
37
39
|
for (let i = 0; i < numberOfAccountGroupsToCreate; i++) {
|
|
40
|
+
if (context.enableDebugLogging) {
|
|
41
|
+
utils_1.contextualLogger.info(`Creating account group ${i} for legacy account`);
|
|
42
|
+
}
|
|
38
43
|
await (0, group_1.createMultichainAccountGroup)(context, entropySourceId, i, profileId, analytics_1.BackupAndSyncAnalyticsEvents.LEGACY_GROUP_ADDED_FROM_ACCOUNT);
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
// 3. Rename account groups if needed
|
|
42
|
-
const
|
|
47
|
+
const localAccountGroups = (0, utils_1.getLocalGroupsForEntropyWallet)(context, `entropy:${entropySourceId}`);
|
|
43
48
|
for (const legacyAccount of legacyAccountsFromUserStorage) {
|
|
44
49
|
// n: name
|
|
45
50
|
// a: EVM address
|
|
@@ -53,7 +58,7 @@ const performLegacyAccountSyncing = async (context, entropySourceId, profileId)
|
|
|
53
58
|
const localGroupId = (0, accounts_controller_1.getUUIDFromAddressOfNormalAccount)(a);
|
|
54
59
|
if (n) {
|
|
55
60
|
// Find the local group that corresponds to this EVM address
|
|
56
|
-
const localGroup =
|
|
61
|
+
const localGroup = localAccountGroups.find((group) => group.accounts.some((accountId) => accountId === localGroupId));
|
|
57
62
|
if (localGroup) {
|
|
58
63
|
context.controller.setAccountGroupName(localGroup.id, n);
|
|
59
64
|
context.emitAnalyticsEventFn({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":";;;AAAA,uEAAkF;AAElF,uCAAuD;AACvD,sDAA4D;AAE5D,4DAAkE;AAClE,8CAA4E;AAE5E;;;;;;;;;;GAUG;AACI,MAAM,2BAA2B,GAAG,KAAK,EAC9C,OAA6B,EAC7B,eAAuB,EACvB,SAAiB,EACjB,EAAE;IACF,
|
|
1
|
+
{"version":3,"file":"legacy.cjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":";;;AAAA,uEAAkF;AAElF,uCAAuD;AACvD,sDAA4D;AAE5D,4DAAkE;AAClE,8CAA4E;AAE5E;;;;;;;;;;GAUG;AACI,MAAM,2BAA2B,GAAG,KAAK,EAC9C,OAA6B,EAC7B,eAAuB,EACvB,SAAiB,EACjB,EAAE;IACF,qCAAqC;IACrC,MAAM,6BAA6B,GAAG,MAAM,IAAA,8CAA+B,EACzE,OAAO,EACP,eAAe,CAChB,CAAC;IACF,IAAI,6BAA6B,CAAC,MAAM,KAAK,CAAC,EAAE;QAC9C,IAAI,OAAO,CAAC,kBAAkB,EAAE;YAC9B,wBAAgB,CAAC,IAAI,CACnB,qDAAqD,CACtD,CAAC;SACH;QAED,OAAO,CAAC,oBAAoB,CAAC;YAC3B,MAAM,EAAE,wCAA4B,CAAC,mBAAmB;YACxD,SAAS;SACV,CAAC,CAAC;QAEH,OAAO;KACR;IAED,uCAAuC;IACvC,MAAM,6BAA6B,GAAG,6BAA6B,CAAC,MAAM,CAAC;IAE3E,IAAI,OAAO,CAAC,kBAAkB,EAAE;QAC9B,wBAAgB,CAAC,IAAI,CACnB,YAAY,6BAA6B,qCAAqC,CAC/E,CAAC;KACH;IAED,IAAI,6BAA6B,GAAG,CAAC,EAAE;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,6BAA6B,EAAE,CAAC,EAAE,EAAE;YACtD,IAAI,OAAO,CAAC,kBAAkB,EAAE;gBAC9B,wBAAgB,CAAC,IAAI,CAAC,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;aACzE;YACD,MAAM,IAAA,oCAA4B,EAChC,OAAO,EACP,eAAe,EACf,CAAC,EACD,SAAS,EACT,wCAA4B,CAAC,+BAA+B,CAC7D,CAAC;SACH;KACF;IAED,qCAAqC;IACrC,MAAM,kBAAkB,GAAG,IAAA,sCAA8B,EACvD,OAAO,EACP,WAAW,eAAe,EAAE,CAC7B,CAAC;IACF,KAAK,MAAM,aAAa,IAAI,6BAA6B,EAAE;QACzD,UAAU;QACV,iBAAiB;QACjB,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,aAAa,CAAC;QAC/B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE;YACZ,IAAI,OAAO,CAAC,kBAAkB,EAAE;gBAC9B,wBAAgB,CAAC,IAAI,CACnB,qEAAqE,IAAI,CAAC,SAAS,CACjF,aAAa,CACd,EAAE,CACJ,CAAC;aACH;YACD,SAAS;SACV;QACD,MAAM,YAAY,GAAG,IAAA,uDAAiC,EAAC,CAAC,CAAC,CAAC;QAE1D,IAAI,CAAC,EAAE;YACL,4DAA4D;YAC5D,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACnD,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,YAAY,CAAC,CAC/D,CAAC;YACF,IAAI,UAAU,EAAE;gBACd,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAEzD,OAAO,CAAC,oBAAoB,CAAC;oBAC3B,MAAM,EAAE,wCAA4B,CAAC,oBAAoB;oBACzD,SAAS;oBACT,qBAAqB,EAAE,wBAAwB,UAAU,CAAC,EAAE,OAAO,CAAC,EAAE;iBACvE,CAAC,CAAC;aACJ;SACF;KACF;IAED,OAAO,CAAC,oBAAoB,CAAC;QAC3B,MAAM,EAAE,wCAA4B,CAAC,mBAAmB;QACxD,SAAS;KACV,CAAC,CAAC;AACL,CAAC,CAAC;AA3FW,QAAA,2BAA2B,+BA2FtC","sourcesContent":["import { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\n\nimport { createMultichainAccountGroup } from './group';\nimport { BackupAndSyncAnalyticsEvents } from '../analytics';\nimport type { BackupAndSyncContext } from '../types';\nimport { getAllLegacyUserStorageAccounts } from '../user-storage';\nimport { contextualLogger, getLocalGroupsForEntropyWallet } from '../utils';\n\n/**\n * Performs a stripped down version of legacy account syncing, replacing the current\n * UserStorageController:syncInternalAccountsWithUserStorage call.\n * This ensures legacy (V1) account syncing data is correctly migrated to\n * the new AccountTreeController data structure. It should only happen\n * once per wallet.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport const performLegacyAccountSyncing = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n profileId: string,\n) => {\n // 1. Get legacy account syncing data\n const legacyAccountsFromUserStorage = await getAllLegacyUserStorageAccounts(\n context,\n entropySourceId,\n );\n if (legacyAccountsFromUserStorage.length === 0) {\n if (context.enableDebugLogging) {\n contextualLogger.info(\n 'No legacy accounts, skipping legacy account syncing',\n );\n }\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_SYNCING_DONE,\n profileId,\n });\n\n return;\n }\n\n // 2. Create account groups accordingly\n const numberOfAccountGroupsToCreate = legacyAccountsFromUserStorage.length;\n\n if (context.enableDebugLogging) {\n contextualLogger.info(\n `Creating ${numberOfAccountGroupsToCreate} account groups for legacy accounts`,\n );\n }\n\n if (numberOfAccountGroupsToCreate > 0) {\n for (let i = 0; i < numberOfAccountGroupsToCreate; i++) {\n if (context.enableDebugLogging) {\n contextualLogger.info(`Creating account group ${i} for legacy account`);\n }\n await createMultichainAccountGroup(\n context,\n entropySourceId,\n i,\n profileId,\n BackupAndSyncAnalyticsEvents.LEGACY_GROUP_ADDED_FROM_ACCOUNT,\n );\n }\n }\n\n // 3. Rename account groups if needed\n const localAccountGroups = getLocalGroupsForEntropyWallet(\n context,\n `entropy:${entropySourceId}`,\n );\n for (const legacyAccount of legacyAccountsFromUserStorage) {\n // n: name\n // a: EVM address\n const { n, a } = legacyAccount;\n if (!a || !n) {\n if (context.enableDebugLogging) {\n contextualLogger.warn(\n `Legacy account data is missing name or address, skipping account: ${JSON.stringify(\n legacyAccount,\n )}`,\n );\n }\n continue;\n }\n const localGroupId = getUUIDFromAddressOfNormalAccount(a);\n\n if (n) {\n // Find the local group that corresponds to this EVM address\n const localGroup = localAccountGroups.find((group) =>\n group.accounts.some((accountId) => accountId === localGroupId),\n );\n if (localGroup) {\n context.controller.setAccountGroupName(localGroup.id, n);\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_GROUP_RENAMED,\n profileId,\n additionalDescription: `Renamed legacy group ${localGroup.id} to ${n}`,\n });\n }\n }\n }\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_SYNCING_DONE,\n profileId,\n });\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAiB;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,aACZ,MAAM,
|
|
1
|
+
{"version":3,"file":"legacy.d.cts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAiB;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,aACZ,MAAM,kBAwFlB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAiB;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,aACZ,MAAM,
|
|
1
|
+
{"version":3,"file":"legacy.d.mts","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAiB;AAIrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,YAC7B,oBAAoB,mBACZ,MAAM,aACZ,MAAM,kBAwFlB,CAAC"}
|
|
@@ -15,7 +15,7 @@ import { contextualLogger, getLocalGroupsForEntropyWallet } from "../utils/index
|
|
|
15
15
|
* @param profileId - The profile ID for analytics.
|
|
16
16
|
*/
|
|
17
17
|
export const performLegacyAccountSyncing = async (context, entropySourceId, profileId) => {
|
|
18
|
-
// 1. Get
|
|
18
|
+
// 1. Get legacy account syncing data
|
|
19
19
|
const legacyAccountsFromUserStorage = await getAllLegacyUserStorageAccounts(context, entropySourceId);
|
|
20
20
|
if (legacyAccountsFromUserStorage.length === 0) {
|
|
21
21
|
if (context.enableDebugLogging) {
|
|
@@ -27,16 +27,21 @@ export const performLegacyAccountSyncing = async (context, entropySourceId, prof
|
|
|
27
27
|
});
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
// 2. Create account groups
|
|
31
|
-
const
|
|
32
|
-
|
|
30
|
+
// 2. Create account groups accordingly
|
|
31
|
+
const numberOfAccountGroupsToCreate = legacyAccountsFromUserStorage.length;
|
|
32
|
+
if (context.enableDebugLogging) {
|
|
33
|
+
contextualLogger.info(`Creating ${numberOfAccountGroupsToCreate} account groups for legacy accounts`);
|
|
34
|
+
}
|
|
33
35
|
if (numberOfAccountGroupsToCreate > 0) {
|
|
34
36
|
for (let i = 0; i < numberOfAccountGroupsToCreate; i++) {
|
|
37
|
+
if (context.enableDebugLogging) {
|
|
38
|
+
contextualLogger.info(`Creating account group ${i} for legacy account`);
|
|
39
|
+
}
|
|
35
40
|
await createMultichainAccountGroup(context, entropySourceId, i, profileId, BackupAndSyncAnalyticsEvents.LEGACY_GROUP_ADDED_FROM_ACCOUNT);
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
// 3. Rename account groups if needed
|
|
39
|
-
const
|
|
44
|
+
const localAccountGroups = getLocalGroupsForEntropyWallet(context, `entropy:${entropySourceId}`);
|
|
40
45
|
for (const legacyAccount of legacyAccountsFromUserStorage) {
|
|
41
46
|
// n: name
|
|
42
47
|
// a: EVM address
|
|
@@ -50,7 +55,7 @@ export const performLegacyAccountSyncing = async (context, entropySourceId, prof
|
|
|
50
55
|
const localGroupId = getUUIDFromAddressOfNormalAccount(a);
|
|
51
56
|
if (n) {
|
|
52
57
|
// Find the local group that corresponds to this EVM address
|
|
53
|
-
const localGroup =
|
|
58
|
+
const localGroup = localAccountGroups.find((group) => group.accounts.some((accountId) => accountId === localGroupId));
|
|
54
59
|
if (localGroup) {
|
|
55
60
|
context.controller.setAccountGroupName(localGroup.id, n);
|
|
56
61
|
context.emitAnalyticsEventFn({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,sCAAsC;AAElF,OAAO,EAAE,4BAA4B,EAAE,oBAAgB;AACvD,OAAO,EAAE,4BAA4B,EAAE,+BAAqB;AAE5D,OAAO,EAAE,+BAA+B,EAAE,kCAAwB;AAClE,OAAO,EAAE,gBAAgB,EAAE,8BAA8B,EAAE,2BAAiB;AAE5E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,EAC9C,OAA6B,EAC7B,eAAuB,EACvB,SAAiB,EACjB,EAAE;IACF,
|
|
1
|
+
{"version":3,"file":"legacy.mjs","sourceRoot":"","sources":["../../../src/backup-and-sync/syncing/legacy.ts"],"names":[],"mappings":"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","sourcesContent":["import { getUUIDFromAddressOfNormalAccount } from '@metamask/accounts-controller';\n\nimport { createMultichainAccountGroup } from './group';\nimport { BackupAndSyncAnalyticsEvents } from '../analytics';\nimport type { BackupAndSyncContext } from '../types';\nimport { getAllLegacyUserStorageAccounts } from '../user-storage';\nimport { contextualLogger, getLocalGroupsForEntropyWallet } from '../utils';\n\n/**\n * Performs a stripped down version of legacy account syncing, replacing the current\n * UserStorageController:syncInternalAccountsWithUserStorage call.\n * This ensures legacy (V1) account syncing data is correctly migrated to\n * the new AccountTreeController data structure. It should only happen\n * once per wallet.\n *\n * @param context - The sync context containing controller and messenger.\n * @param entropySourceId - The entropy source ID.\n * @param profileId - The profile ID for analytics.\n */\nexport const performLegacyAccountSyncing = async (\n context: BackupAndSyncContext,\n entropySourceId: string,\n profileId: string,\n) => {\n // 1. Get legacy account syncing data\n const legacyAccountsFromUserStorage = await getAllLegacyUserStorageAccounts(\n context,\n entropySourceId,\n );\n if (legacyAccountsFromUserStorage.length === 0) {\n if (context.enableDebugLogging) {\n contextualLogger.info(\n 'No legacy accounts, skipping legacy account syncing',\n );\n }\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_SYNCING_DONE,\n profileId,\n });\n\n return;\n }\n\n // 2. Create account groups accordingly\n const numberOfAccountGroupsToCreate = legacyAccountsFromUserStorage.length;\n\n if (context.enableDebugLogging) {\n contextualLogger.info(\n `Creating ${numberOfAccountGroupsToCreate} account groups for legacy accounts`,\n );\n }\n\n if (numberOfAccountGroupsToCreate > 0) {\n for (let i = 0; i < numberOfAccountGroupsToCreate; i++) {\n if (context.enableDebugLogging) {\n contextualLogger.info(`Creating account group ${i} for legacy account`);\n }\n await createMultichainAccountGroup(\n context,\n entropySourceId,\n i,\n profileId,\n BackupAndSyncAnalyticsEvents.LEGACY_GROUP_ADDED_FROM_ACCOUNT,\n );\n }\n }\n\n // 3. Rename account groups if needed\n const localAccountGroups = getLocalGroupsForEntropyWallet(\n context,\n `entropy:${entropySourceId}`,\n );\n for (const legacyAccount of legacyAccountsFromUserStorage) {\n // n: name\n // a: EVM address\n const { n, a } = legacyAccount;\n if (!a || !n) {\n if (context.enableDebugLogging) {\n contextualLogger.warn(\n `Legacy account data is missing name or address, skipping account: ${JSON.stringify(\n legacyAccount,\n )}`,\n );\n }\n continue;\n }\n const localGroupId = getUUIDFromAddressOfNormalAccount(a);\n\n if (n) {\n // Find the local group that corresponds to this EVM address\n const localGroup = localAccountGroups.find((group) =>\n group.accounts.some((accountId) => accountId === localGroupId),\n );\n if (localGroup) {\n context.controller.setAccountGroupName(localGroup.id, n);\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_GROUP_RENAMED,\n profileId,\n additionalDescription: `Renamed legacy group ${localGroup.id} to ${n}`,\n });\n }\n }\n }\n\n context.emitAnalyticsEventFn({\n action: BackupAndSyncAnalyticsEvents.LEGACY_SYNCING_DONE,\n profileId,\n });\n};\n"]}
|
package/package.json
CHANGED