@metamask-previews/profile-metrics-controller 0.0.0-preview-d6fe4594 → 0.0.0-preview-bc80feb8

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/CHANGELOG.md CHANGED
@@ -9,6 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
- - Initial release ([#7194](https://github.com/MetaMask/core/pull/7194))
12
+ - Initial release ([#7194](https://github.com/MetaMask/core/pull/7194), [#7196](https://github.com/MetaMask/core/pull/7196))
13
13
 
14
14
  [Unreleased]: https://github.com/MetaMask/core/
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _ProfileMetricsController_instances, _ProfileMetricsController_mutex, _ProfileMetricsController_assertUserOptedIn, _ProfileMetricsController_getMetaMetricsId, _ProfileMetricsController_queueFirstSyncIfNeeded, _ProfileMetricsController_addAccountToQueue, _ProfileMetricsController_removeAccountFromQueue;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ProfileMetricsController = exports.getDefaultProfileMetricsControllerState = exports.controllerName = void 0;
16
+ const polling_controller_1 = require("@metamask/polling-controller");
17
+ const async_mutex_1 = require("async-mutex");
18
+ /**
19
+ * The name of the {@link ProfileMetricsController}, used to namespace the
20
+ * controller's actions and events and to namespace the controller's state data
21
+ * when composed with other controllers.
22
+ */
23
+ exports.controllerName = 'ProfileMetricsController';
24
+ /**
25
+ * The metadata for each property in {@link ProfileMetricsControllerState}.
26
+ */
27
+ const profileMetricsControllerMetadata = {
28
+ initialEnqueueCompleted: {
29
+ persist: true,
30
+ includeInDebugSnapshot: true,
31
+ includeInStateLogs: true,
32
+ usedInUi: false,
33
+ },
34
+ syncQueue: {
35
+ persist: true,
36
+ includeInDebugSnapshot: false,
37
+ includeInStateLogs: true,
38
+ usedInUi: false,
39
+ },
40
+ };
41
+ /**
42
+ * Constructs the default {@link ProfileMetricsController} state. This allows
43
+ * consumers to provide a partial state object when initializing the controller
44
+ * and also helps in constructing complete state objects for this controller in
45
+ * tests.
46
+ *
47
+ * @returns The default {@link ProfileMetricsController} state.
48
+ */
49
+ function getDefaultProfileMetricsControllerState() {
50
+ return {
51
+ initialEnqueueCompleted: false,
52
+ syncQueue: {},
53
+ };
54
+ }
55
+ exports.getDefaultProfileMetricsControllerState = getDefaultProfileMetricsControllerState;
56
+ const MESSENGER_EXPOSED_METHODS = [];
57
+ class ProfileMetricsController extends (0, polling_controller_1.StaticIntervalPollingController)() {
58
+ /**
59
+ * Constructs a new {@link ProfileMetricsController}.
60
+ *
61
+ * @param args - The constructor arguments.
62
+ * @param args.messenger - The messenger suited for this controller.
63
+ * @param args.state - The desired state with which to initialize this
64
+ * controller. Missing properties will be filled in with defaults.
65
+ * @param args.assertUserOptedIn - A function that asserts whether the user has
66
+ * opted in to user profile features. If the user has not opted in, sync
67
+ * operations will be no-ops.
68
+ * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID
69
+ * of the user.
70
+ * @param args.interval - The interval, in milliseconds, at which the controller will
71
+ * attempt to send user profile data. Defaults to 10 seconds.
72
+ */
73
+ constructor({ messenger, state, assertUserOptedIn, getMetaMetricsId, interval = 10 * 1000, }) {
74
+ super({
75
+ messenger,
76
+ metadata: profileMetricsControllerMetadata,
77
+ name: exports.controllerName,
78
+ state: {
79
+ ...getDefaultProfileMetricsControllerState(),
80
+ ...state,
81
+ },
82
+ });
83
+ _ProfileMetricsController_instances.add(this);
84
+ _ProfileMetricsController_mutex.set(this, new async_mutex_1.Mutex());
85
+ _ProfileMetricsController_assertUserOptedIn.set(this, void 0);
86
+ _ProfileMetricsController_getMetaMetricsId.set(this, void 0);
87
+ __classPrivateFieldSet(this, _ProfileMetricsController_assertUserOptedIn, assertUserOptedIn, "f");
88
+ __classPrivateFieldSet(this, _ProfileMetricsController_getMetaMetricsId, getMetaMetricsId, "f");
89
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
90
+ this.messenger.subscribe('KeyringController:unlock', () => {
91
+ this.startPolling(null);
92
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_queueFirstSyncIfNeeded).call(this).catch(console.error);
93
+ });
94
+ this.messenger.subscribe('KeyringController:lock', () => {
95
+ this.stopAllPolling();
96
+ });
97
+ this.messenger.subscribe('AccountsController:accountAdded', (account) => {
98
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_addAccountToQueue).call(this, account).catch(console.error);
99
+ });
100
+ this.messenger.subscribe('AccountsController:accountRemoved', (account) => {
101
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_removeAccountFromQueue).call(this, account).catch(console.error);
102
+ });
103
+ this.setIntervalLength(interval);
104
+ }
105
+ /**
106
+ * Execute a single poll to sync user profile data.
107
+ *
108
+ * The queued accounts are sent to the ProfileMetricsService, and the sync
109
+ * queue is cleared. This operation is mutexed to prevent concurrent
110
+ * executions.
111
+ *
112
+ * @returns A promise that resolves when the poll is complete.
113
+ */
114
+ async _executePoll() {
115
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
116
+ if (!__classPrivateFieldGet(this, _ProfileMetricsController_assertUserOptedIn, "f").call(this)) {
117
+ return;
118
+ }
119
+ for (const [entropySourceId, accounts] of Object.entries(this.state.syncQueue)) {
120
+ try {
121
+ await this.messenger.call('ProfileMetricsService:submitMetrics', {
122
+ metametricsId: __classPrivateFieldGet(this, _ProfileMetricsController_getMetaMetricsId, "f").call(this),
123
+ entropySourceId: entropySourceId === 'null' ? null : entropySourceId,
124
+ accounts,
125
+ });
126
+ this.update((state) => {
127
+ delete state.syncQueue[entropySourceId];
128
+ });
129
+ }
130
+ catch (error) {
131
+ // We want to log the error but continue processing other batches.
132
+ console.error(`Failed to submit profile metrics for entropy source ID ${entropySourceId}:`, error);
133
+ }
134
+ }
135
+ });
136
+ }
137
+ }
138
+ exports.ProfileMetricsController = ProfileMetricsController;
139
+ _ProfileMetricsController_mutex = new WeakMap(), _ProfileMetricsController_assertUserOptedIn = new WeakMap(), _ProfileMetricsController_getMetaMetricsId = new WeakMap(), _ProfileMetricsController_instances = new WeakSet(), _ProfileMetricsController_queueFirstSyncIfNeeded =
140
+ /**
141
+ * Add existing accounts to the sync queue if it has not been done yet.
142
+ *
143
+ * This method ensures that the first sync is only executed once,
144
+ * and only if the user has opted in to user profile features.
145
+ */
146
+ async function _ProfileMetricsController_queueFirstSyncIfNeeded() {
147
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
148
+ if (this.state.initialEnqueueCompleted) {
149
+ return;
150
+ }
151
+ const newGroupedAccounts = groupAccountsByEntropySourceId(this.messenger.call('AccountsController:listAccounts'));
152
+ this.update((state) => {
153
+ for (const key of Object.keys(newGroupedAccounts)) {
154
+ if (!state.syncQueue[key]) {
155
+ state.syncQueue[key] = [];
156
+ }
157
+ state.syncQueue[key].push(...newGroupedAccounts[key]);
158
+ }
159
+ state.initialEnqueueCompleted = true;
160
+ });
161
+ });
162
+ }, _ProfileMetricsController_addAccountToQueue =
163
+ /**
164
+ * Queue the given account to be synced at the next poll.
165
+ *
166
+ * @param account - The account to sync.
167
+ */
168
+ async function _ProfileMetricsController_addAccountToQueue(account) {
169
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
170
+ this.update((state) => {
171
+ const entropySourceId = getAccountEntropySourceId(account) || 'null';
172
+ if (!state.syncQueue[entropySourceId]) {
173
+ state.syncQueue[entropySourceId] = [];
174
+ }
175
+ state.syncQueue[entropySourceId].push({
176
+ address: account.address,
177
+ scopes: account.scopes,
178
+ });
179
+ });
180
+ });
181
+ }, _ProfileMetricsController_removeAccountFromQueue =
182
+ /**
183
+ * Remove the given account from the sync queue.
184
+ *
185
+ * @param account - The account address to remove.
186
+ */
187
+ async function _ProfileMetricsController_removeAccountFromQueue(account) {
188
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
189
+ this.update((state) => {
190
+ for (const [entropySourceId, groupedAddresses] of Object.entries(state.syncQueue)) {
191
+ const index = groupedAddresses.findIndex(({ address }) => address === account);
192
+ if (index === -1) {
193
+ continue;
194
+ }
195
+ groupedAddresses.splice(index, 1);
196
+ if (groupedAddresses.length === 0) {
197
+ delete state.syncQueue[entropySourceId];
198
+ }
199
+ break;
200
+ }
201
+ });
202
+ });
203
+ };
204
+ /**
205
+ * Retrieves the entropy source ID from the given account, if it exists.
206
+ *
207
+ * @param account - The account from which to retrieve the entropy source ID.
208
+ * @returns The entropy source ID, or null if it does not exist.
209
+ */
210
+ function getAccountEntropySourceId(account) {
211
+ if (account.options.entropy && account.options.entropy.type === 'mnemonic') {
212
+ return account.options.entropy.id;
213
+ }
214
+ return null;
215
+ }
216
+ /**
217
+ * Groups accounts by their entropy source ID.
218
+ *
219
+ * @param accounts - The accounts to group.
220
+ * @returns An object where each key is an entropy source ID and each value is
221
+ * an array of account addresses associated with that entropy source ID.
222
+ */
223
+ function groupAccountsByEntropySourceId(accounts) {
224
+ return accounts.reduce((result, account) => {
225
+ const entropySourceId = getAccountEntropySourceId(account);
226
+ const key = entropySourceId || 'null';
227
+ if (!result[key]) {
228
+ result[key] = [];
229
+ }
230
+ result[key].push({ address: account.address, scopes: account.scopes });
231
+ return result;
232
+ }, {});
233
+ }
234
+ //# sourceMappingURL=ProfileMetricsController.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetricsController.cjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n AccountsControllerAccountRemovedEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsServiceMethodActions } from '.';\nimport type { AccountWithScopes } from './ProfileMetricsService';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = [] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | AccountsControllerListAccountsAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n this.startPolling(null);\n this.#queueFirstSyncIfNeeded().catch(console.error);\n });\n\n this.messenger.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, and the sync\n * queue is cleared. This operation is mutexed to prevent concurrent\n * executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId:\n entropySourceId === 'null' ? null : entropySourceId,\n accounts,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Add existing accounts to the sync queue if it has not been done yet.\n *\n * This method ensures that the first sync is only executed once,\n * and only if the user has opted in to user profile features.\n */\n async #queueFirstSyncIfNeeded() {\n await this.#mutex.runExclusive(async () => {\n if (this.state.initialEnqueueCompleted) {\n return;\n }\n const newGroupedAccounts = groupAccountsByEntropySourceId(\n this.messenger.call('AccountsController:listAccounts'),\n );\n this.update((state) => {\n for (const key of Object.keys(newGroupedAccounts)) {\n if (!state.syncQueue[key]) {\n state.syncQueue[key] = [];\n }\n state.syncQueue[key].push(...newGroupedAccounts[key]);\n }\n state.initialEnqueueCompleted = true;\n });\n });\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount) {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) || 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string) {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy && account.options.entropy.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId || 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
@@ -0,0 +1,121 @@
1
+ /// <reference types="node" />
2
+ import type { AccountsControllerAccountAddedEvent, AccountsControllerListAccountsAction, AccountsControllerAccountRemovedEvent } from "@metamask/accounts-controller";
3
+ import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
4
+ import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
5
+ import type { Messenger } from "@metamask/messenger";
6
+ import type { ProfileMetricsServiceMethodActions } from "./index.cjs";
7
+ import type { AccountWithScopes } from "./ProfileMetricsService.cjs";
8
+ /**
9
+ * The name of the {@link ProfileMetricsController}, used to namespace the
10
+ * controller's actions and events and to namespace the controller's state data
11
+ * when composed with other controllers.
12
+ */
13
+ export declare const controllerName = "ProfileMetricsController";
14
+ /**
15
+ * Describes the shape of the state object for {@link ProfileMetricsController}.
16
+ */
17
+ export type ProfileMetricsControllerState = {
18
+ /**
19
+ * Whether existing accounts have been added
20
+ * to the queue.
21
+ */
22
+ initialEnqueueCompleted: boolean;
23
+ /**
24
+ * The queue of accounts to be synced.
25
+ * Each key is an entropy source ID, and each value is an array of account
26
+ * addresses associated with that entropy source. Accounts with no entropy
27
+ * source ID are grouped under the key "null".
28
+ */
29
+ syncQueue: Record<string, AccountWithScopes[]>;
30
+ };
31
+ /**
32
+ * Constructs the default {@link ProfileMetricsController} state. This allows
33
+ * consumers to provide a partial state object when initializing the controller
34
+ * and also helps in constructing complete state objects for this controller in
35
+ * tests.
36
+ *
37
+ * @returns The default {@link ProfileMetricsController} state.
38
+ */
39
+ export declare function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState;
40
+ /**
41
+ * Retrieves the state of the {@link ProfileMetricsController}.
42
+ */
43
+ export type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<typeof controllerName, ProfileMetricsControllerState>;
44
+ /**
45
+ * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.
46
+ */
47
+ export type ProfileMetricsControllerActions = ProfileMetricsControllerGetStateAction | ProfileMetricsServiceMethodActions;
48
+ /**
49
+ * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.
50
+ */
51
+ type AllowedActions = ProfileMetricsServiceMethodActions | AccountsControllerListAccountsAction;
52
+ /**
53
+ * Published when the state of {@link ProfileMetricsController} changes.
54
+ */
55
+ export type ProfileMetricsControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, ProfileMetricsControllerState>;
56
+ /**
57
+ * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.
58
+ */
59
+ export type ProfileMetricsControllerEvents = ProfileMetricsControllerStateChangeEvent;
60
+ /**
61
+ * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes
62
+ * to.
63
+ */
64
+ type AllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent | AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
65
+ /**
66
+ * The messenger restricted to actions and events accessed by
67
+ * {@link ProfileMetricsController}.
68
+ */
69
+ export type ProfileMetricsControllerMessenger = Messenger<typeof controllerName, ProfileMetricsControllerActions | AllowedActions, ProfileMetricsControllerEvents | AllowedEvents>;
70
+ declare const ProfileMetricsController_base: (abstract new (...args: any[]) => {
71
+ readonly "__#15@#intervalIds": Record<string, NodeJS.Timeout>;
72
+ "__#15@#intervalLength": number | undefined;
73
+ setIntervalLength(intervalLength: number): void;
74
+ getIntervalLength(): number | undefined;
75
+ _startPolling(input: import("@metamask/utils").Json): void;
76
+ _stopPollingByPollingTokenSetId(key: string): void;
77
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
78
+ readonly "__#3@#callbacks": Map<string, Set<(input: import("@metamask/utils").Json) => void>>;
79
+ _executePoll(input: import("@metamask/utils").Json): Promise<void>;
80
+ startPolling(input: import("@metamask/utils").Json): string;
81
+ stopAllPolling(): void;
82
+ stopPollingByPollingToken(pollingToken: string): void;
83
+ onPollingComplete(input: import("@metamask/utils").Json, callback: (input: import("@metamask/utils").Json) => void): void;
84
+ }) & typeof import("@metamask/base-controller").BaseController;
85
+ export declare class ProfileMetricsController extends ProfileMetricsController_base<typeof controllerName, ProfileMetricsControllerState, ProfileMetricsControllerMessenger> {
86
+ #private;
87
+ /**
88
+ * Constructs a new {@link ProfileMetricsController}.
89
+ *
90
+ * @param args - The constructor arguments.
91
+ * @param args.messenger - The messenger suited for this controller.
92
+ * @param args.state - The desired state with which to initialize this
93
+ * controller. Missing properties will be filled in with defaults.
94
+ * @param args.assertUserOptedIn - A function that asserts whether the user has
95
+ * opted in to user profile features. If the user has not opted in, sync
96
+ * operations will be no-ops.
97
+ * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID
98
+ * of the user.
99
+ * @param args.interval - The interval, in milliseconds, at which the controller will
100
+ * attempt to send user profile data. Defaults to 10 seconds.
101
+ */
102
+ constructor({ messenger, state, assertUserOptedIn, getMetaMetricsId, interval, }: {
103
+ messenger: ProfileMetricsControllerMessenger;
104
+ state?: Partial<ProfileMetricsControllerState>;
105
+ interval?: number;
106
+ assertUserOptedIn: () => boolean;
107
+ getMetaMetricsId: () => string;
108
+ });
109
+ /**
110
+ * Execute a single poll to sync user profile data.
111
+ *
112
+ * The queued accounts are sent to the ProfileMetricsService, and the sync
113
+ * queue is cleared. This operation is mutexed to prevent concurrent
114
+ * executions.
115
+ *
116
+ * @returns A promise that resolves when the poll is complete.
117
+ */
118
+ _executePoll(): Promise<void>;
119
+ }
120
+ export {};
121
+ //# sourceMappingURL=ProfileMetricsController.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetricsController.d.cts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,oCAAoC,EACpC,qCAAqC,EACtC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,kCAAkC,EAAE,oBAAU;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oCAAgC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;CAChD,CAAC;AAoBF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAKvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,kCAAkC,CAAC;AAEvC;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;;;;;;;;;;;;;;AAEF,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IAOC;;;;;;;;;;;;;;OAcG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,GACrB,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;KAChC;IAuCD;;;;;;;;OAQG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAqGpC"}
@@ -0,0 +1,121 @@
1
+ /// <reference types="node" />
2
+ import type { AccountsControllerAccountAddedEvent, AccountsControllerListAccountsAction, AccountsControllerAccountRemovedEvent } from "@metamask/accounts-controller";
3
+ import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
4
+ import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
5
+ import type { Messenger } from "@metamask/messenger";
6
+ import type { ProfileMetricsServiceMethodActions } from "./index.mjs";
7
+ import type { AccountWithScopes } from "./ProfileMetricsService.mjs";
8
+ /**
9
+ * The name of the {@link ProfileMetricsController}, used to namespace the
10
+ * controller's actions and events and to namespace the controller's state data
11
+ * when composed with other controllers.
12
+ */
13
+ export declare const controllerName = "ProfileMetricsController";
14
+ /**
15
+ * Describes the shape of the state object for {@link ProfileMetricsController}.
16
+ */
17
+ export type ProfileMetricsControllerState = {
18
+ /**
19
+ * Whether existing accounts have been added
20
+ * to the queue.
21
+ */
22
+ initialEnqueueCompleted: boolean;
23
+ /**
24
+ * The queue of accounts to be synced.
25
+ * Each key is an entropy source ID, and each value is an array of account
26
+ * addresses associated with that entropy source. Accounts with no entropy
27
+ * source ID are grouped under the key "null".
28
+ */
29
+ syncQueue: Record<string, AccountWithScopes[]>;
30
+ };
31
+ /**
32
+ * Constructs the default {@link ProfileMetricsController} state. This allows
33
+ * consumers to provide a partial state object when initializing the controller
34
+ * and also helps in constructing complete state objects for this controller in
35
+ * tests.
36
+ *
37
+ * @returns The default {@link ProfileMetricsController} state.
38
+ */
39
+ export declare function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState;
40
+ /**
41
+ * Retrieves the state of the {@link ProfileMetricsController}.
42
+ */
43
+ export type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<typeof controllerName, ProfileMetricsControllerState>;
44
+ /**
45
+ * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.
46
+ */
47
+ export type ProfileMetricsControllerActions = ProfileMetricsControllerGetStateAction | ProfileMetricsServiceMethodActions;
48
+ /**
49
+ * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.
50
+ */
51
+ type AllowedActions = ProfileMetricsServiceMethodActions | AccountsControllerListAccountsAction;
52
+ /**
53
+ * Published when the state of {@link ProfileMetricsController} changes.
54
+ */
55
+ export type ProfileMetricsControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, ProfileMetricsControllerState>;
56
+ /**
57
+ * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.
58
+ */
59
+ export type ProfileMetricsControllerEvents = ProfileMetricsControllerStateChangeEvent;
60
+ /**
61
+ * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes
62
+ * to.
63
+ */
64
+ type AllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent | AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
65
+ /**
66
+ * The messenger restricted to actions and events accessed by
67
+ * {@link ProfileMetricsController}.
68
+ */
69
+ export type ProfileMetricsControllerMessenger = Messenger<typeof controllerName, ProfileMetricsControllerActions | AllowedActions, ProfileMetricsControllerEvents | AllowedEvents>;
70
+ declare const ProfileMetricsController_base: (abstract new (...args: any[]) => {
71
+ readonly "__#15@#intervalIds": Record<string, NodeJS.Timeout>;
72
+ "__#15@#intervalLength": number | undefined;
73
+ setIntervalLength(intervalLength: number): void;
74
+ getIntervalLength(): number | undefined;
75
+ _startPolling(input: import("@metamask/utils").Json): void;
76
+ _stopPollingByPollingTokenSetId(key: string): void;
77
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
78
+ readonly "__#3@#callbacks": Map<string, Set<(input: import("@metamask/utils").Json) => void>>;
79
+ _executePoll(input: import("@metamask/utils").Json): Promise<void>;
80
+ startPolling(input: import("@metamask/utils").Json): string;
81
+ stopAllPolling(): void;
82
+ stopPollingByPollingToken(pollingToken: string): void;
83
+ onPollingComplete(input: import("@metamask/utils").Json, callback: (input: import("@metamask/utils").Json) => void): void;
84
+ }) & typeof import("@metamask/base-controller").BaseController;
85
+ export declare class ProfileMetricsController extends ProfileMetricsController_base<typeof controllerName, ProfileMetricsControllerState, ProfileMetricsControllerMessenger> {
86
+ #private;
87
+ /**
88
+ * Constructs a new {@link ProfileMetricsController}.
89
+ *
90
+ * @param args - The constructor arguments.
91
+ * @param args.messenger - The messenger suited for this controller.
92
+ * @param args.state - The desired state with which to initialize this
93
+ * controller. Missing properties will be filled in with defaults.
94
+ * @param args.assertUserOptedIn - A function that asserts whether the user has
95
+ * opted in to user profile features. If the user has not opted in, sync
96
+ * operations will be no-ops.
97
+ * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID
98
+ * of the user.
99
+ * @param args.interval - The interval, in milliseconds, at which the controller will
100
+ * attempt to send user profile data. Defaults to 10 seconds.
101
+ */
102
+ constructor({ messenger, state, assertUserOptedIn, getMetaMetricsId, interval, }: {
103
+ messenger: ProfileMetricsControllerMessenger;
104
+ state?: Partial<ProfileMetricsControllerState>;
105
+ interval?: number;
106
+ assertUserOptedIn: () => boolean;
107
+ getMetaMetricsId: () => string;
108
+ });
109
+ /**
110
+ * Execute a single poll to sync user profile data.
111
+ *
112
+ * The queued accounts are sent to the ProfileMetricsService, and the sync
113
+ * queue is cleared. This operation is mutexed to prevent concurrent
114
+ * executions.
115
+ *
116
+ * @returns A promise that resolves when the poll is complete.
117
+ */
118
+ _executePoll(): Promise<void>;
119
+ }
120
+ export {};
121
+ //# sourceMappingURL=ProfileMetricsController.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetricsController.d.mts","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,oCAAoC,EACpC,qCAAqC,EACtC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,qCAAqC;AAEtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,kCAAkC,EAAE,oBAAU;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oCAAgC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;CAChD,CAAC;AAoBF;;;;;;;GAOG;AACH,wBAAgB,uCAAuC,IAAI,6BAA6B,CAKvF;AAID;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAAG,wBAAwB,CAC3E,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GACvC,sCAAsC,GACtC,kCAAkC,CAAC;AAEvC;;GAEG;AACH,KAAK,cAAc,GACf,kCAAkC,GAClC,oCAAoC,CAAC;AAEzC;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAClD,0BAA0B,CACxB,OAAO,cAAc,EACrB,6BAA6B,CAC9B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACxC,wCAAwC,CAAC;AAE3C;;;GAGG;AACH,KAAK,aAAa,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,SAAS,CACvD,OAAO,cAAc,EACrB,+BAA+B,GAAG,cAAc,EAChD,8BAA8B,GAAG,aAAa,CAC/C,CAAC;;;;;;;;;;;;;;;;AAEF,qBAAa,wBAAyB,SAAQ,8BAC5C,OAAO,cAAc,EACrB,6BAA6B,EAC7B,iCAAiC,CAClC;;IAOC;;;;;;;;;;;;;;OAcG;gBACS,EACV,SAAS,EACT,KAAK,EACL,iBAAiB,EACjB,gBAAgB,EAChB,QAAoB,GACrB,EAAE;QACD,SAAS,EAAE,iCAAiC,CAAC;QAC7C,KAAK,CAAC,EAAE,OAAO,CAAC,6BAA6B,CAAC,CAAC;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QACjC,gBAAgB,EAAE,MAAM,MAAM,CAAC;KAChC;IAuCD;;;;;;;;OAQG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAqGpC"}
@@ -0,0 +1,229 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _ProfileMetricsController_instances, _ProfileMetricsController_mutex, _ProfileMetricsController_assertUserOptedIn, _ProfileMetricsController_getMetaMetricsId, _ProfileMetricsController_queueFirstSyncIfNeeded, _ProfileMetricsController_addAccountToQueue, _ProfileMetricsController_removeAccountFromQueue;
13
+ import { StaticIntervalPollingController } from "@metamask/polling-controller";
14
+ import { Mutex } from "async-mutex";
15
+ /**
16
+ * The name of the {@link ProfileMetricsController}, used to namespace the
17
+ * controller's actions and events and to namespace the controller's state data
18
+ * when composed with other controllers.
19
+ */
20
+ export const controllerName = 'ProfileMetricsController';
21
+ /**
22
+ * The metadata for each property in {@link ProfileMetricsControllerState}.
23
+ */
24
+ const profileMetricsControllerMetadata = {
25
+ initialEnqueueCompleted: {
26
+ persist: true,
27
+ includeInDebugSnapshot: true,
28
+ includeInStateLogs: true,
29
+ usedInUi: false,
30
+ },
31
+ syncQueue: {
32
+ persist: true,
33
+ includeInDebugSnapshot: false,
34
+ includeInStateLogs: true,
35
+ usedInUi: false,
36
+ },
37
+ };
38
+ /**
39
+ * Constructs the default {@link ProfileMetricsController} state. This allows
40
+ * consumers to provide a partial state object when initializing the controller
41
+ * and also helps in constructing complete state objects for this controller in
42
+ * tests.
43
+ *
44
+ * @returns The default {@link ProfileMetricsController} state.
45
+ */
46
+ export function getDefaultProfileMetricsControllerState() {
47
+ return {
48
+ initialEnqueueCompleted: false,
49
+ syncQueue: {},
50
+ };
51
+ }
52
+ const MESSENGER_EXPOSED_METHODS = [];
53
+ export class ProfileMetricsController extends StaticIntervalPollingController() {
54
+ /**
55
+ * Constructs a new {@link ProfileMetricsController}.
56
+ *
57
+ * @param args - The constructor arguments.
58
+ * @param args.messenger - The messenger suited for this controller.
59
+ * @param args.state - The desired state with which to initialize this
60
+ * controller. Missing properties will be filled in with defaults.
61
+ * @param args.assertUserOptedIn - A function that asserts whether the user has
62
+ * opted in to user profile features. If the user has not opted in, sync
63
+ * operations will be no-ops.
64
+ * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID
65
+ * of the user.
66
+ * @param args.interval - The interval, in milliseconds, at which the controller will
67
+ * attempt to send user profile data. Defaults to 10 seconds.
68
+ */
69
+ constructor({ messenger, state, assertUserOptedIn, getMetaMetricsId, interval = 10 * 1000, }) {
70
+ super({
71
+ messenger,
72
+ metadata: profileMetricsControllerMetadata,
73
+ name: controllerName,
74
+ state: {
75
+ ...getDefaultProfileMetricsControllerState(),
76
+ ...state,
77
+ },
78
+ });
79
+ _ProfileMetricsController_instances.add(this);
80
+ _ProfileMetricsController_mutex.set(this, new Mutex());
81
+ _ProfileMetricsController_assertUserOptedIn.set(this, void 0);
82
+ _ProfileMetricsController_getMetaMetricsId.set(this, void 0);
83
+ __classPrivateFieldSet(this, _ProfileMetricsController_assertUserOptedIn, assertUserOptedIn, "f");
84
+ __classPrivateFieldSet(this, _ProfileMetricsController_getMetaMetricsId, getMetaMetricsId, "f");
85
+ this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
86
+ this.messenger.subscribe('KeyringController:unlock', () => {
87
+ this.startPolling(null);
88
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_queueFirstSyncIfNeeded).call(this).catch(console.error);
89
+ });
90
+ this.messenger.subscribe('KeyringController:lock', () => {
91
+ this.stopAllPolling();
92
+ });
93
+ this.messenger.subscribe('AccountsController:accountAdded', (account) => {
94
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_addAccountToQueue).call(this, account).catch(console.error);
95
+ });
96
+ this.messenger.subscribe('AccountsController:accountRemoved', (account) => {
97
+ __classPrivateFieldGet(this, _ProfileMetricsController_instances, "m", _ProfileMetricsController_removeAccountFromQueue).call(this, account).catch(console.error);
98
+ });
99
+ this.setIntervalLength(interval);
100
+ }
101
+ /**
102
+ * Execute a single poll to sync user profile data.
103
+ *
104
+ * The queued accounts are sent to the ProfileMetricsService, and the sync
105
+ * queue is cleared. This operation is mutexed to prevent concurrent
106
+ * executions.
107
+ *
108
+ * @returns A promise that resolves when the poll is complete.
109
+ */
110
+ async _executePoll() {
111
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
112
+ if (!__classPrivateFieldGet(this, _ProfileMetricsController_assertUserOptedIn, "f").call(this)) {
113
+ return;
114
+ }
115
+ for (const [entropySourceId, accounts] of Object.entries(this.state.syncQueue)) {
116
+ try {
117
+ await this.messenger.call('ProfileMetricsService:submitMetrics', {
118
+ metametricsId: __classPrivateFieldGet(this, _ProfileMetricsController_getMetaMetricsId, "f").call(this),
119
+ entropySourceId: entropySourceId === 'null' ? null : entropySourceId,
120
+ accounts,
121
+ });
122
+ this.update((state) => {
123
+ delete state.syncQueue[entropySourceId];
124
+ });
125
+ }
126
+ catch (error) {
127
+ // We want to log the error but continue processing other batches.
128
+ console.error(`Failed to submit profile metrics for entropy source ID ${entropySourceId}:`, error);
129
+ }
130
+ }
131
+ });
132
+ }
133
+ }
134
+ _ProfileMetricsController_mutex = new WeakMap(), _ProfileMetricsController_assertUserOptedIn = new WeakMap(), _ProfileMetricsController_getMetaMetricsId = new WeakMap(), _ProfileMetricsController_instances = new WeakSet(), _ProfileMetricsController_queueFirstSyncIfNeeded =
135
+ /**
136
+ * Add existing accounts to the sync queue if it has not been done yet.
137
+ *
138
+ * This method ensures that the first sync is only executed once,
139
+ * and only if the user has opted in to user profile features.
140
+ */
141
+ async function _ProfileMetricsController_queueFirstSyncIfNeeded() {
142
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
143
+ if (this.state.initialEnqueueCompleted) {
144
+ return;
145
+ }
146
+ const newGroupedAccounts = groupAccountsByEntropySourceId(this.messenger.call('AccountsController:listAccounts'));
147
+ this.update((state) => {
148
+ for (const key of Object.keys(newGroupedAccounts)) {
149
+ if (!state.syncQueue[key]) {
150
+ state.syncQueue[key] = [];
151
+ }
152
+ state.syncQueue[key].push(...newGroupedAccounts[key]);
153
+ }
154
+ state.initialEnqueueCompleted = true;
155
+ });
156
+ });
157
+ }, _ProfileMetricsController_addAccountToQueue =
158
+ /**
159
+ * Queue the given account to be synced at the next poll.
160
+ *
161
+ * @param account - The account to sync.
162
+ */
163
+ async function _ProfileMetricsController_addAccountToQueue(account) {
164
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
165
+ this.update((state) => {
166
+ const entropySourceId = getAccountEntropySourceId(account) || 'null';
167
+ if (!state.syncQueue[entropySourceId]) {
168
+ state.syncQueue[entropySourceId] = [];
169
+ }
170
+ state.syncQueue[entropySourceId].push({
171
+ address: account.address,
172
+ scopes: account.scopes,
173
+ });
174
+ });
175
+ });
176
+ }, _ProfileMetricsController_removeAccountFromQueue =
177
+ /**
178
+ * Remove the given account from the sync queue.
179
+ *
180
+ * @param account - The account address to remove.
181
+ */
182
+ async function _ProfileMetricsController_removeAccountFromQueue(account) {
183
+ await __classPrivateFieldGet(this, _ProfileMetricsController_mutex, "f").runExclusive(async () => {
184
+ this.update((state) => {
185
+ for (const [entropySourceId, groupedAddresses] of Object.entries(state.syncQueue)) {
186
+ const index = groupedAddresses.findIndex(({ address }) => address === account);
187
+ if (index === -1) {
188
+ continue;
189
+ }
190
+ groupedAddresses.splice(index, 1);
191
+ if (groupedAddresses.length === 0) {
192
+ delete state.syncQueue[entropySourceId];
193
+ }
194
+ break;
195
+ }
196
+ });
197
+ });
198
+ };
199
+ /**
200
+ * Retrieves the entropy source ID from the given account, if it exists.
201
+ *
202
+ * @param account - The account from which to retrieve the entropy source ID.
203
+ * @returns The entropy source ID, or null if it does not exist.
204
+ */
205
+ function getAccountEntropySourceId(account) {
206
+ if (account.options.entropy && account.options.entropy.type === 'mnemonic') {
207
+ return account.options.entropy.id;
208
+ }
209
+ return null;
210
+ }
211
+ /**
212
+ * Groups accounts by their entropy source ID.
213
+ *
214
+ * @param accounts - The accounts to group.
215
+ * @returns An object where each key is an entropy source ID and each value is
216
+ * an array of account addresses associated with that entropy source ID.
217
+ */
218
+ function groupAccountsByEntropySourceId(accounts) {
219
+ return accounts.reduce((result, account) => {
220
+ const entropySourceId = getAccountEntropySourceId(account);
221
+ const key = entropySourceId || 'null';
222
+ if (!result[key]) {
223
+ result[key] = [];
224
+ }
225
+ result[key].push({ address: account.address, scopes: account.scopes });
226
+ return result;
227
+ }, {});
228
+ }
229
+ //# sourceMappingURL=ProfileMetricsController.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileMetricsController.mjs","sourceRoot":"","sources":["../src/ProfileMetricsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n AccountsControllerAccountRemovedEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type {\n KeyringControllerLockEvent,\n KeyringControllerUnlockEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Messenger } from '@metamask/messenger';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { Mutex } from 'async-mutex';\n\nimport type { ProfileMetricsServiceMethodActions } from '.';\nimport type { AccountWithScopes } from './ProfileMetricsService';\n\n/**\n * The name of the {@link ProfileMetricsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'ProfileMetricsController';\n\n/**\n * Describes the shape of the state object for {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerState = {\n /**\n * Whether existing accounts have been added\n * to the queue.\n */\n initialEnqueueCompleted: boolean;\n /**\n * The queue of accounts to be synced.\n * Each key is an entropy source ID, and each value is an array of account\n * addresses associated with that entropy source. Accounts with no entropy\n * source ID are grouped under the key \"null\".\n */\n syncQueue: Record<string, AccountWithScopes[]>;\n};\n\n/**\n * The metadata for each property in {@link ProfileMetricsControllerState}.\n */\nconst profileMetricsControllerMetadata = {\n initialEnqueueCompleted: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n syncQueue: {\n persist: true,\n includeInDebugSnapshot: false,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<ProfileMetricsControllerState>;\n\n/**\n * Constructs the default {@link ProfileMetricsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link ProfileMetricsController} state.\n */\nexport function getDefaultProfileMetricsControllerState(): ProfileMetricsControllerState {\n return {\n initialEnqueueCompleted: false,\n syncQueue: {},\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = [] as const;\n\n/**\n * Retrieves the state of the {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n ProfileMetricsControllerState\n>;\n\n/**\n * Actions that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerActions =\n | ProfileMetricsControllerGetStateAction\n | ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsControllerMessenger} calls.\n */\ntype AllowedActions =\n | ProfileMetricsServiceMethodActions\n | AccountsControllerListAccountsAction;\n\n/**\n * Published when the state of {@link ProfileMetricsController} changes.\n */\nexport type ProfileMetricsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n ProfileMetricsControllerState\n >;\n\n/**\n * Events that {@link ProfileMetricsControllerMessenger} exposes to other consumers.\n */\nexport type ProfileMetricsControllerEvents =\n ProfileMetricsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link ProfileMetricsControllerMessenger} subscribes\n * to.\n */\ntype AllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link ProfileMetricsController}.\n */\nexport type ProfileMetricsControllerMessenger = Messenger<\n typeof controllerName,\n ProfileMetricsControllerActions | AllowedActions,\n ProfileMetricsControllerEvents | AllowedEvents\n>;\n\nexport class ProfileMetricsController extends StaticIntervalPollingController()<\n typeof controllerName,\n ProfileMetricsControllerState,\n ProfileMetricsControllerMessenger\n> {\n readonly #mutex = new Mutex();\n\n readonly #assertUserOptedIn: () => boolean;\n\n readonly #getMetaMetricsId: () => string;\n\n /**\n * Constructs a new {@link ProfileMetricsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.assertUserOptedIn - A function that asserts whether the user has\n * opted in to user profile features. If the user has not opted in, sync\n * operations will be no-ops.\n * @param args.getMetaMetricsId - A function that returns the MetaMetrics ID\n * of the user.\n * @param args.interval - The interval, in milliseconds, at which the controller will\n * attempt to send user profile data. Defaults to 10 seconds.\n */\n constructor({\n messenger,\n state,\n assertUserOptedIn,\n getMetaMetricsId,\n interval = 10 * 1000,\n }: {\n messenger: ProfileMetricsControllerMessenger;\n state?: Partial<ProfileMetricsControllerState>;\n interval?: number;\n assertUserOptedIn: () => boolean;\n getMetaMetricsId: () => string;\n }) {\n super({\n messenger,\n metadata: profileMetricsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultProfileMetricsControllerState(),\n ...state,\n },\n });\n\n this.#assertUserOptedIn = assertUserOptedIn;\n this.#getMetaMetricsId = getMetaMetricsId;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.messenger.subscribe('KeyringController:unlock', () => {\n this.startPolling(null);\n this.#queueFirstSyncIfNeeded().catch(console.error);\n });\n\n this.messenger.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messenger.subscribe('AccountsController:accountAdded', (account) => {\n this.#addAccountToQueue(account).catch(console.error);\n });\n\n this.messenger.subscribe('AccountsController:accountRemoved', (account) => {\n this.#removeAccountFromQueue(account).catch(console.error);\n });\n\n this.setIntervalLength(interval);\n }\n\n /**\n * Execute a single poll to sync user profile data.\n *\n * The queued accounts are sent to the ProfileMetricsService, and the sync\n * queue is cleared. This operation is mutexed to prevent concurrent\n * executions.\n *\n * @returns A promise that resolves when the poll is complete.\n */\n async _executePoll(): Promise<void> {\n await this.#mutex.runExclusive(async () => {\n if (!this.#assertUserOptedIn()) {\n return;\n }\n for (const [entropySourceId, accounts] of Object.entries(\n this.state.syncQueue,\n )) {\n try {\n await this.messenger.call('ProfileMetricsService:submitMetrics', {\n metametricsId: this.#getMetaMetricsId(),\n entropySourceId:\n entropySourceId === 'null' ? null : entropySourceId,\n accounts,\n });\n this.update((state) => {\n delete state.syncQueue[entropySourceId];\n });\n } catch (error) {\n // We want to log the error but continue processing other batches.\n console.error(\n `Failed to submit profile metrics for entropy source ID ${entropySourceId}:`,\n error,\n );\n }\n }\n });\n }\n\n /**\n * Add existing accounts to the sync queue if it has not been done yet.\n *\n * This method ensures that the first sync is only executed once,\n * and only if the user has opted in to user profile features.\n */\n async #queueFirstSyncIfNeeded() {\n await this.#mutex.runExclusive(async () => {\n if (this.state.initialEnqueueCompleted) {\n return;\n }\n const newGroupedAccounts = groupAccountsByEntropySourceId(\n this.messenger.call('AccountsController:listAccounts'),\n );\n this.update((state) => {\n for (const key of Object.keys(newGroupedAccounts)) {\n if (!state.syncQueue[key]) {\n state.syncQueue[key] = [];\n }\n state.syncQueue[key].push(...newGroupedAccounts[key]);\n }\n state.initialEnqueueCompleted = true;\n });\n });\n }\n\n /**\n * Queue the given account to be synced at the next poll.\n *\n * @param account - The account to sync.\n */\n async #addAccountToQueue(account: InternalAccount) {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n const entropySourceId = getAccountEntropySourceId(account) || 'null';\n if (!state.syncQueue[entropySourceId]) {\n state.syncQueue[entropySourceId] = [];\n }\n state.syncQueue[entropySourceId].push({\n address: account.address,\n scopes: account.scopes,\n });\n });\n });\n }\n\n /**\n * Remove the given account from the sync queue.\n *\n * @param account - The account address to remove.\n */\n async #removeAccountFromQueue(account: string) {\n await this.#mutex.runExclusive(async () => {\n this.update((state) => {\n for (const [entropySourceId, groupedAddresses] of Object.entries(\n state.syncQueue,\n )) {\n const index = groupedAddresses.findIndex(\n ({ address }) => address === account,\n );\n if (index === -1) {\n continue;\n }\n groupedAddresses.splice(index, 1);\n if (groupedAddresses.length === 0) {\n delete state.syncQueue[entropySourceId];\n }\n break;\n }\n });\n });\n }\n}\n\n/**\n * Retrieves the entropy source ID from the given account, if it exists.\n *\n * @param account - The account from which to retrieve the entropy source ID.\n * @returns The entropy source ID, or null if it does not exist.\n */\nfunction getAccountEntropySourceId(account: InternalAccount): string | null {\n if (account.options.entropy && account.options.entropy.type === 'mnemonic') {\n return account.options.entropy.id;\n }\n return null;\n}\n\n/**\n * Groups accounts by their entropy source ID.\n *\n * @param accounts - The accounts to group.\n * @returns An object where each key is an entropy source ID and each value is\n * an array of account addresses associated with that entropy source ID.\n */\nfunction groupAccountsByEntropySourceId(\n accounts: InternalAccount[],\n): Record<string, AccountWithScopes[]> {\n return accounts.reduce(\n (result: Record<string, AccountWithScopes[]>, account) => {\n const entropySourceId = getAccountEntropySourceId(account);\n const key = entropySourceId || 'null';\n if (!result[key]) {\n result[key] = [];\n }\n result[key].push({ address: account.address, scopes: account.scopes });\n return result;\n },\n {},\n );\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serviceName = exports.ProfileMetricsService = void 0;
3
+ exports.serviceName = exports.ProfileMetricsService = exports.getDefaultProfileMetricsControllerState = exports.ProfileMetricsController = void 0;
4
+ var ProfileMetricsController_1 = require("./ProfileMetricsController.cjs");
5
+ Object.defineProperty(exports, "ProfileMetricsController", { enumerable: true, get: function () { return ProfileMetricsController_1.ProfileMetricsController; } });
6
+ Object.defineProperty(exports, "getDefaultProfileMetricsControllerState", { enumerable: true, get: function () { return ProfileMetricsController_1.getDefaultProfileMetricsControllerState; } });
4
7
  var ProfileMetricsService_1 = require("./ProfileMetricsService.cjs");
5
8
  Object.defineProperty(exports, "ProfileMetricsService", { enumerable: true, get: function () { return ProfileMetricsService_1.ProfileMetricsService; } });
6
9
  Object.defineProperty(exports, "serviceName", { enumerable: true, get: function () { return ProfileMetricsService_1.serviceName; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAMA,qEAA6E;AAApE,8HAAA,qBAAqB,OAAA;AAAE,oHAAA,WAAW,OAAA","sourcesContent":["export type {\n ProfileMetricsServiceActions,\n ProfileMetricsServiceEvents,\n ProfileMetricsServiceMessenger,\n ProfileMetricsSubmitMetricsRequest,\n} from './ProfileMetricsService';\nexport { ProfileMetricsService, serviceName } from './ProfileMetricsService';\nexport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAQA,2EAGoC;AAFlC,oIAAA,wBAAwB,OAAA;AACxB,mJAAA,uCAAuC,OAAA;AAQzC,qEAA6E;AAApE,8HAAA,qBAAqB,OAAA;AAAE,oHAAA,WAAW,OAAA","sourcesContent":["export type {\n ProfileMetricsControllerActions,\n ProfileMetricsControllerEvents,\n ProfileMetricsControllerGetStateAction,\n ProfileMetricsControllerMessenger,\n ProfileMetricsControllerState,\n ProfileMetricsControllerStateChangeEvent,\n} from './ProfileMetricsController';\nexport {\n ProfileMetricsController,\n getDefaultProfileMetricsControllerState,\n} from './ProfileMetricsController';\nexport type {\n ProfileMetricsServiceActions,\n ProfileMetricsServiceEvents,\n ProfileMetricsServiceMessenger,\n ProfileMetricsSubmitMetricsRequest,\n} from './ProfileMetricsService';\nexport { ProfileMetricsService, serviceName } from './ProfileMetricsService';\nexport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ export type { ProfileMetricsControllerActions, ProfileMetricsControllerEvents, ProfileMetricsControllerGetStateAction, ProfileMetricsControllerMessenger, ProfileMetricsControllerState, ProfileMetricsControllerStateChangeEvent, } from "./ProfileMetricsController.cjs";
2
+ export { ProfileMetricsController, getDefaultProfileMetricsControllerState, } from "./ProfileMetricsController.cjs";
1
3
  export type { ProfileMetricsServiceActions, ProfileMetricsServiceEvents, ProfileMetricsServiceMessenger, ProfileMetricsSubmitMetricsRequest, } from "./ProfileMetricsService.cjs";
2
4
  export { ProfileMetricsService, serviceName } from "./ProfileMetricsService.cjs";
3
5
  export type { ProfileMetricsServiceMethodActions } from "./ProfileMetricsService-method-action-types.cjs";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,kCAAkC,GACnC,oCAAgC;AACjC,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC;AAC7E,YAAY,EAAE,kCAAkC,EAAE,wDAAoD"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,+BAA+B,EAC/B,8BAA8B,EAC9B,sCAAsC,EACtC,iCAAiC,EACjC,6BAA6B,EAC7B,wCAAwC,GACzC,uCAAmC;AACpC,OAAO,EACL,wBAAwB,EACxB,uCAAuC,GACxC,uCAAmC;AACpC,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,kCAAkC,GACnC,oCAAgC;AACjC,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC;AAC7E,YAAY,EAAE,kCAAkC,EAAE,wDAAoD"}
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ export type { ProfileMetricsControllerActions, ProfileMetricsControllerEvents, ProfileMetricsControllerGetStateAction, ProfileMetricsControllerMessenger, ProfileMetricsControllerState, ProfileMetricsControllerStateChangeEvent, } from "./ProfileMetricsController.mjs";
2
+ export { ProfileMetricsController, getDefaultProfileMetricsControllerState, } from "./ProfileMetricsController.mjs";
1
3
  export type { ProfileMetricsServiceActions, ProfileMetricsServiceEvents, ProfileMetricsServiceMessenger, ProfileMetricsSubmitMetricsRequest, } from "./ProfileMetricsService.mjs";
2
4
  export { ProfileMetricsService, serviceName } from "./ProfileMetricsService.mjs";
3
5
  export type { ProfileMetricsServiceMethodActions } from "./ProfileMetricsService-method-action-types.mjs";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,kCAAkC,GACnC,oCAAgC;AACjC,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC;AAC7E,YAAY,EAAE,kCAAkC,EAAE,wDAAoD"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,+BAA+B,EAC/B,8BAA8B,EAC9B,sCAAsC,EACtC,iCAAiC,EACjC,6BAA6B,EAC7B,wCAAwC,GACzC,uCAAmC;AACpC,OAAO,EACL,wBAAwB,EACxB,uCAAuC,GACxC,uCAAmC;AACpC,YAAY,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,EAC9B,kCAAkC,GACnC,oCAAgC;AACjC,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC;AAC7E,YAAY,EAAE,kCAAkC,EAAE,wDAAoD"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,3 @@
1
+ export { ProfileMetricsController, getDefaultProfileMetricsControllerState } from "./ProfileMetricsController.mjs";
1
2
  export { ProfileMetricsService, serviceName } from "./ProfileMetricsService.mjs";
2
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC","sourcesContent":["export type {\n ProfileMetricsServiceActions,\n ProfileMetricsServiceEvents,\n ProfileMetricsServiceMessenger,\n ProfileMetricsSubmitMetricsRequest,\n} from './ProfileMetricsService';\nexport { ProfileMetricsService, serviceName } from './ProfileMetricsService';\nexport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,wBAAwB,EACxB,uCAAuC,EACxC,uCAAmC;AAOpC,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,oCAAgC","sourcesContent":["export type {\n ProfileMetricsControllerActions,\n ProfileMetricsControllerEvents,\n ProfileMetricsControllerGetStateAction,\n ProfileMetricsControllerMessenger,\n ProfileMetricsControllerState,\n ProfileMetricsControllerStateChangeEvent,\n} from './ProfileMetricsController';\nexport {\n ProfileMetricsController,\n getDefaultProfileMetricsControllerState,\n} from './ProfileMetricsController';\nexport type {\n ProfileMetricsServiceActions,\n ProfileMetricsServiceEvents,\n ProfileMetricsServiceMessenger,\n ProfileMetricsSubmitMetricsRequest,\n} from './ProfileMetricsService';\nexport { ProfileMetricsService, serviceName } from './ProfileMetricsService';\nexport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/profile-metrics-controller",
3
- "version": "0.0.0-preview-d6fe4594",
3
+ "version": "0.0.0-preview-bc80feb8",
4
4
  "description": "Sample package to illustrate best practices for controllers",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -48,14 +48,19 @@
48
48
  "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
49
49
  },
50
50
  "dependencies": {
51
+ "@metamask/accounts-controller": "^35.0.0",
51
52
  "@metamask/base-controller": "^9.0.0",
52
53
  "@metamask/controller-utils": "^11.16.0",
54
+ "@metamask/keyring-controller": "^25.0.0",
53
55
  "@metamask/messenger": "^0.3.0",
56
+ "@metamask/polling-controller": "^16.0.0",
54
57
  "@metamask/profile-sync-controller": "^27.0.0",
55
- "@metamask/utils": "^11.8.1"
58
+ "@metamask/utils": "^11.8.1",
59
+ "async-mutex": "^0.5.0"
56
60
  },
57
61
  "devDependencies": {
58
62
  "@metamask/auto-changelog": "^3.4.4",
63
+ "@metamask/keyring-internal-api": "^9.0.0",
59
64
  "@ts-bridge/cli": "^0.6.4",
60
65
  "@types/jest": "^27.4.1",
61
66
  "deepmerge": "^4.2.2",