@metamask-previews/profile-metrics-controller 4.0.2-preview-aa202ec10 → 4.0.2-preview-6cd1bb2f5

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
@@ -11,11 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  - Bump `@metamask/transaction-controller` from `^69.0.0` to `^69.2.1` ([#9568](https://github.com/MetaMask/core/pull/9568), [#9589](https://github.com/MetaMask/core/pull/9589), [#9593](https://github.com/MetaMask/core/pull/9593))
13
13
 
14
- ### Fixed
15
-
16
- - Disable in-request retries on `ProfileMetricsService:submitMetrics` by default (`maxRetries: 0`) ([#9667](https://github.com/MetaMask/core/pull/9667))
17
- - Proof nonces are single-use, so retrying a spent `submitMetrics` payload caused `PUT /profile/accounts` 400s; `fetchNonces` still retries because a failed fetch soft-degrades to a proof-less submit that clears the queue.
18
-
19
14
  ## [4.0.2]
20
15
 
21
16
  ### Changed
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
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
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _ProfileMetricsService_instances, _a, _ProfileMetricsService_messenger, _ProfileMetricsService_fetch, _ProfileMetricsService_fetchNoncesPolicy, _ProfileMetricsService_submitMetricsPolicy, _ProfileMetricsService_baseURL, _ProfileMetricsService_fetchNoncesChunk;
13
+ var _ProfileMetricsService_instances, _a, _ProfileMetricsService_messenger, _ProfileMetricsService_fetch, _ProfileMetricsService_policy, _ProfileMetricsService_baseURL, _ProfileMetricsService_fetchNoncesChunk;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getAuthUrl = exports.ProfileMetricsService = exports.MAX_NONCE_BATCH_SIZE = exports.serviceName = void 0;
16
16
  const controller_utils_1 = require("@metamask/controller-utils");
@@ -60,11 +60,6 @@ class ProfileMetricsService {
60
60
  * `node-fetch`).
61
61
  * @param args.policyOptions - Options to pass to `createServicePolicy`, which
62
62
  * is used to wrap each request. See {@link CreateServicePolicyOptions}.
63
- * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;
64
- * the controller poll re-fetches nonces and retries failed submits).
65
- * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch
66
- * soft-degrades to a proof-less submit that clears the queue, so retries
67
- * here are what preserve proofs across transient errors.
68
63
  * @param args.env - The environment to determine the correct API endpoints.
69
64
  */
70
65
  constructor({ messenger, fetch: fetchFunction, policyOptions = {}, env = profile_sync_controller_1.SDK.Env.DEV, }) {
@@ -78,18 +73,11 @@ class ProfileMetricsService {
78
73
  */
79
74
  _ProfileMetricsService_fetch.set(this, void 0);
80
75
  /**
81
- * Policy for `fetchNonces`. Minting new nonces is safe to retry.
76
+ * The policy that wraps the request.
82
77
  *
83
78
  * @see {@link createServicePolicy}
84
79
  */
85
- _ProfileMetricsService_fetchNoncesPolicy.set(this, void 0);
86
- /**
87
- * Policy for `submitMetrics`. Proof payloads embed single-use nonces, so
88
- * in-request retries are disabled by default.
89
- *
90
- * @see {@link createServicePolicy}
91
- */
92
- _ProfileMetricsService_submitMetricsPolicy.set(this, void 0);
80
+ _ProfileMetricsService_policy.set(this, void 0);
93
81
  /**
94
82
  * The API base URL environment.
95
83
  */
@@ -97,16 +85,7 @@ class ProfileMetricsService {
97
85
  this.name = exports.serviceName;
98
86
  __classPrivateFieldSet(this, _ProfileMetricsService_messenger, messenger, "f");
99
87
  __classPrivateFieldSet(this, _ProfileMetricsService_fetch, fetchFunction, "f");
100
- __classPrivateFieldSet(this, _ProfileMetricsService_fetchNoncesPolicy, (0, controller_utils_1.createServicePolicy)(policyOptions), "f");
101
- // Keep the circuit breaker scaled to the retry count the same way
102
- // `createServicePolicy` derives `DEFAULT_MAX_CONSECUTIVE_FAILURES`:
103
- // `(1 + maxRetries) * 3` failed attempts ≈ 3 failed `execute()` calls.
104
- const submitMaxRetries = policyOptions.maxRetries ?? 0;
105
- __classPrivateFieldSet(this, _ProfileMetricsService_submitMetricsPolicy, (0, controller_utils_1.createServicePolicy)({
106
- ...policyOptions,
107
- maxRetries: submitMaxRetries,
108
- maxConsecutiveFailures: policyOptions.maxConsecutiveFailures ?? (1 + submitMaxRetries) * 3,
109
- }), "f");
88
+ __classPrivateFieldSet(this, _ProfileMetricsService_policy, (0, controller_utils_1.createServicePolicy)(policyOptions), "f");
110
89
  __classPrivateFieldSet(this, _ProfileMetricsService_baseURL, getAuthUrl(env), "f");
111
90
  __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
112
91
  }
@@ -121,14 +100,7 @@ class ProfileMetricsService {
121
100
  * @see {@link createServicePolicy}
122
101
  */
123
102
  onRetry(listener) {
124
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onRetry(listener);
125
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onRetry(listener);
126
- return {
127
- dispose: () => {
128
- fetchDisposable.dispose();
129
- submitDisposable.dispose();
130
- },
131
- };
103
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onRetry(listener);
132
104
  }
133
105
  /**
134
106
  * Registers a handler that will be called after a set number of retry rounds
@@ -140,14 +112,7 @@ class ProfileMetricsService {
140
112
  * @see {@link createServicePolicy}
141
113
  */
142
114
  onBreak(listener) {
143
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onBreak(listener);
144
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onBreak(listener);
145
- return {
146
- dispose: () => {
147
- fetchDisposable.dispose();
148
- submitDisposable.dispose();
149
- },
150
- };
115
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onBreak(listener);
151
116
  }
152
117
  /**
153
118
  * Registers a handler that will be called under one of two circumstances:
@@ -167,14 +132,7 @@ class ProfileMetricsService {
167
132
  * {@link CockatielEvent}.
168
133
  */
169
134
  onDegraded(listener) {
170
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onDegraded(listener);
171
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onDegraded(listener);
172
- return {
173
- dispose: () => {
174
- fetchDisposable.dispose();
175
- submitDisposable.dispose();
176
- },
177
- };
135
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onDegraded(listener);
178
136
  }
179
137
  /**
180
138
  * Fetch single-use nonces from the auth API, one per identifier.
@@ -215,7 +173,7 @@ class ProfileMetricsService {
215
173
  * @returns The response from the API.
216
174
  */
217
175
  async submitMetrics(data) {
218
- await __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").execute(async () => {
176
+ await __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").execute(async () => {
219
177
  const authToken = await __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").call('AuthenticationController:getBearerToken', data.entropySourceId ?? undefined);
220
178
  const url = new URL(`${__classPrivateFieldGet(this, _ProfileMetricsService_baseURL, "f")}/profile/accounts`);
221
179
  const localResponse = await __classPrivateFieldGet(this, _ProfileMetricsService_fetch, "f").call(this, url, {
@@ -242,11 +200,11 @@ class ProfileMetricsService {
242
200
  }
243
201
  }
244
202
  exports.ProfileMetricsService = ProfileMetricsService;
245
- _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _ProfileMetricsService_fetch = new WeakMap(), _ProfileMetricsService_fetchNoncesPolicy = new WeakMap(), _ProfileMetricsService_submitMetricsPolicy = new WeakMap(), _ProfileMetricsService_baseURL = new WeakMap(), _ProfileMetricsService_instances = new WeakSet(), _ProfileMetricsService_fetchNoncesChunk =
203
+ _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _ProfileMetricsService_fetch = new WeakMap(), _ProfileMetricsService_policy = new WeakMap(), _ProfileMetricsService_baseURL = new WeakMap(), _ProfileMetricsService_instances = new WeakSet(), _ProfileMetricsService_fetchNoncesChunk =
246
204
  /**
247
205
  * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of
248
- * identifiers. Wrapped in {@link #fetchNoncesPolicy} for retry / degraded /
249
- * circuit semantics.
206
+ * identifiers. Wrapped in {@link #policy} for retry / degraded / circuit
207
+ * semantics consistent with the rest of the service.
250
208
  *
251
209
  * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.
252
210
  * @param entropySourceId - The entropy source ID forwarded to the bearer
@@ -254,7 +212,7 @@ _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _P
254
212
  * @returns A map of identifier -> nonce for this chunk.
255
213
  */
256
214
  async function _ProfileMetricsService_fetchNoncesChunk(identifiers, entropySourceId) {
257
- return await __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").execute(async () => {
215
+ return await __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").execute(async () => {
258
216
  const authToken = await __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").call('AuthenticationController:getBearerToken', entropySourceId ?? undefined);
259
217
  const url = new URL(`${__classPrivateFieldGet(this, _ProfileMetricsService_baseURL, "f")}/nonce/batch`);
260
218
  const localResponse = await __classPrivateFieldGet(this, _ProfileMetricsService_fetch, "f").call(this, url, {
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileMetricsService.cjs","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport { SDK } from '@metamask/profile-sync-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n number,\n string,\n type as structType,\n} from '@metamask/superstruct';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types.js';\n\n/**\n * The shape of an entry in the `POST /api/v2/nonce/batch` response body.\n *\n * `identifier` echoes the request identifier verbatim, mirroring the\n * documented behavior of the single-account `GET /api/v2/nonce` endpoint on\n * the same auth service. Defined with `type()` (not `object()`) so the\n * client tolerates additive server-side schema changes.\n */\nconst NonceBatchResponseStruct = array(\n structType({\n expires_in: number(),\n identifier: string(),\n nonce: string(),\n }),\n);\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ProfileMetricsService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'ProfileMetricsService';\n\n/**\n * A cryptographic proof that the caller controls the private key of an\n * account, as defined by the `PUT /api/v2/profile/accounts` endpoint of the\n * auth API. When present, the server verifies the signature against\n * `metamask:proof-of-ownership:<nonce>:<canonical address>` and permanently\n * marks the account as `verified: true`.\n */\nexport type AccountOwnershipProof = {\n /**\n * Single-use nonce obtained from {@link ProfileMetricsService.fetchNonces}.\n * Consumed by the server on verification; replay is not possible.\n */\n nonce: string;\n /**\n * Chain-native signature of `metamask:proof-of-ownership:<nonce>:<address>`,\n * always 0x-prefixed. The exact format varies by chain (see the auth API\n * spec — EIP-191 for `eip155`, ed25519 for `solana`, TIP-191 for `tron`,\n * BIP-322 for `bip122`).\n */\n signature: string;\n};\n\n/**\n * An account address along with its associated scopes and an optional\n * ownership proof.\n */\nexport type AccountWithScopes = {\n address: string;\n scopes: `${string}:${string}`[];\n proof?: AccountOwnershipProof;\n};\n\n/**\n * The shape of the request object for submitting metrics.\n */\nexport type ProfileMetricsSubmitMetricsRequest = {\n metametricsId: string;\n entropySourceId?: string | null;\n accounts: AccountWithScopes[];\n};\n\n/**\n * The shape of the request object for fetching a batch of single-use nonces.\n */\nexport type ProfileMetricsFetchNoncesRequest = {\n /**\n * The identifiers (canonical addresses) to mint a nonce for. The auth API\n * accepts between 1 and {@link MAX_NONCE_BATCH_SIZE} identifiers per call.\n */\n identifiers: string[];\n /**\n * The entropy source ID to use when fetching a bearer token. Pass `null` or\n * omit for accounts that do not belong to any entropy source.\n */\n entropySourceId?: string | null;\n};\n\n/**\n * Maximum number of identifiers the auth API will mint nonces for in a single\n * `POST /api/v2/nonce/batch` request. {@link ProfileMetricsService.fetchNonces}\n * uses this as the chunk size when the caller requests more than this many\n * nonces at once.\n */\nexport const MAX_NONCE_BATCH_SIZE = 50;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = ['submitMetrics', 'fetchNonces'] as const;\n\n/**\n * Actions that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceActions = ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsService} calls.\n */\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\n/**\n * Events that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceEvents = never;\n\n/**\n * Events from other messengers that {@link ProfileMetricsService} subscribes\n * to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link ProfileMetricsService}.\n */\nexport type ProfileMetricsServiceMessenger = Messenger<\n typeof serviceName,\n ProfileMetricsServiceActions | AllowedActions,\n ProfileMetricsServiceEvents | AllowedEvents\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * A service for submitting user profile metrics (metrics ID and accounts).\n */\nexport class ProfileMetricsService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['messenger'];\n\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['fetch'];\n\n /**\n * Policy for `fetchNonces`. Minting new nonces is safe to retry.\n *\n * @see {@link createServicePolicy}\n */\n readonly #fetchNoncesPolicy: ServicePolicy;\n\n /**\n * Policy for `submitMetrics`. Proof payloads embed single-use nonces, so\n * in-request retries are disabled by default.\n *\n * @see {@link createServicePolicy}\n */\n readonly #submitMetricsPolicy: ServicePolicy;\n\n /**\n * The API base URL environment.\n */\n readonly #baseURL: string;\n\n /**\n * Constructs a new ProfileMetricsService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request. If\n * your JavaScript environment supports `fetch` natively, you'll probably want\n * to pass that; otherwise you can pass an equivalent (such as `fetch` via\n * `node-fetch`).\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;\n * the controller poll re-fetches nonces and retries failed submits).\n * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch\n * soft-degrades to a proof-less submit that clears the queue, so retries\n * here are what preserve proofs across transient errors.\n * @param args.env - The environment to determine the correct API endpoints.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n policyOptions = {},\n env = SDK.Env.DEV,\n }: {\n messenger: ProfileMetricsServiceMessenger;\n fetch: typeof fetch;\n policyOptions?: CreateServicePolicyOptions;\n env?: SDK.Env;\n }) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#fetch = fetchFunction;\n this.#fetchNoncesPolicy = createServicePolicy(policyOptions);\n // Keep the circuit breaker scaled to the retry count the same way\n // `createServicePolicy` derives `DEFAULT_MAX_CONSECUTIVE_FAILURES`:\n // `(1 + maxRetries) * 3` failed attempts ≈ 3 failed `execute()` calls.\n const submitMaxRetries = policyOptions.maxRetries ?? 0;\n this.#submitMetricsPolicy = createServicePolicy({\n ...policyOptions,\n maxRetries: submitMaxRetries,\n maxConsecutiveFailures:\n policyOptions.maxConsecutiveFailures ?? (1 + submitMaxRetries) * 3,\n });\n this.#baseURL = getAuthUrl(env);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a non-500\n * response, causing a retry. Primarily useful in tests where timers are being\n * mocked.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onRetry(listener);\n const submitDisposable = this.#submitMetricsPolicy.onRetry(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onBreak(listener);\n const submitDisposable = this.#submitMetricsPolicy.onBreak(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Registers a handler that will be called under one of two circumstances:\n *\n * 1. After a set number of retries prove that requests to the API\n * consistently result in one of the following failures:\n * 1. A connection initiation error\n * 2. A connection reset error\n * 3. A timeout error\n * 4. A non-JSON response\n * 5. A 502, 503, or 504 response\n * 2. After a successful request is made to the API, but the response takes\n * longer than a set duration to return.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onDegraded(listener);\n const submitDisposable = this.#submitMetricsPolicy.onDegraded(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Fetch single-use nonces from the auth API, one per identifier.\n *\n * Requests larger than {@link MAX_NONCE_BATCH_SIZE} are split into multiple\n * `POST /api/v2/nonce/batch` calls fired in parallel; the resulting maps are\n * merged into a single record. Each chunk independently goes through the\n * service policy (retry, circuit-breaker, degraded). If any chunk ultimately\n * fails, the whole call rejects so the caller can soft-degrade the entire\n * entropy-source batch consistently.\n *\n * The returned record is keyed by the auth API's echoed `identifier` field\n * (`response[i].identifier -> response[i].nonce`). The call asserts that\n * the response identifier set is exactly the requested set; any mismatch\n * (missing, extra, or duplicated identifier) causes the chunk to throw so\n * the caller never silently proceeds with partial nonces.\n *\n * @param data - The identifiers to mint nonces for, plus the optional\n * entropy source ID used to scope the bearer token.\n * @returns A map of identifier -> nonce.\n * @throws {RangeError} if no identifiers are provided.\n */\n async fetchNonces(\n data: ProfileMetricsFetchNoncesRequest,\n ): Promise<Record<string, string>> {\n if (data.identifiers.length === 0) {\n throw new RangeError(\n 'ProfileMetricsService.fetchNonces requires at least 1 identifier.',\n );\n }\n const chunks: string[][] = [];\n for (let i = 0; i < data.identifiers.length; i += MAX_NONCE_BATCH_SIZE) {\n chunks.push(data.identifiers.slice(i, i + MAX_NONCE_BATCH_SIZE));\n }\n const chunkResults = await Promise.all(\n chunks.map((identifiers) =>\n this.#fetchNoncesChunk(identifiers, data.entropySourceId),\n ),\n );\n return Object.assign({}, ...chunkResults);\n }\n\n /**\n * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of\n * identifiers. Wrapped in {@link #fetchNoncesPolicy} for retry / degraded /\n * circuit semantics.\n *\n * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.\n * @param entropySourceId - The entropy source ID forwarded to the bearer\n * token resolver.\n * @returns A map of identifier -> nonce for this chunk.\n */\n async #fetchNoncesChunk(\n identifiers: string[],\n entropySourceId: string | null | undefined,\n ): Promise<Record<string, string>> {\n return await this.#fetchNoncesPolicy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/nonce/batch`);\n const localResponse = await this.#fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ identifiers }),\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n const body: unknown = await localResponse.json();\n if (!NonceBatchResponseStruct.is(body)) {\n throw new Error(`Malformed response received from '${url.toString()}'`);\n }\n const result: Record<string, string> = {};\n for (const entry of body) {\n result[entry.identifier] = entry.nonce;\n }\n const echoesRequest =\n body.length === identifiers.length &&\n identifiers.every((id) =>\n Object.prototype.hasOwnProperty.call(result, id),\n );\n if (!echoesRequest) {\n throw new Error(\n `Fetching '${url.toString()}' returned a response whose identifier set does not match the request`,\n );\n }\n return result;\n });\n }\n\n /**\n * Submit metrics to the API.\n *\n * @param data - The data to send in the metrics update request.\n * @returns The response from the API.\n */\n async submitMetrics(data: ProfileMetricsSubmitMetricsRequest): Promise<void> {\n await this.#submitMetricsPolicy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n data.entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/profile/accounts`);\n const localResponse = await this.#fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n metametrics_id: data.metametricsId,\n accounts: data.accounts,\n }),\n // The auth API is stateless (no cookies used)\n // prevent marketing cookies scoped to\n // .metamask.io from being forwarded to api which\n // causes 431 Request Header Fields Too Large errors.\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n }\n}\n\n/**\n * Returns the base URL for the given environment.\n *\n * @param env - The environment to get the URL for.\n * @returns The base URL for the environment.\n */\nexport function getAuthUrl(env: SDK.Env): string {\n return `${SDK.getEnvUrls(env).authApiUrl}/api/v2`;\n}\n"]}
1
+ {"version":3,"file":"ProfileMetricsService.cjs","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport { SDK } from '@metamask/profile-sync-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n number,\n string,\n type as structType,\n} from '@metamask/superstruct';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types.js';\n\n/**\n * The shape of an entry in the `POST /api/v2/nonce/batch` response body.\n *\n * `identifier` echoes the request identifier verbatim, mirroring the\n * documented behavior of the single-account `GET /api/v2/nonce` endpoint on\n * the same auth service. Defined with `type()` (not `object()`) so the\n * client tolerates additive server-side schema changes.\n */\nconst NonceBatchResponseStruct = array(\n structType({\n expires_in: number(),\n identifier: string(),\n nonce: string(),\n }),\n);\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ProfileMetricsService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'ProfileMetricsService';\n\n/**\n * A cryptographic proof that the caller controls the private key of an\n * account, as defined by the `PUT /api/v2/profile/accounts` endpoint of the\n * auth API. When present, the server verifies the signature against\n * `metamask:proof-of-ownership:<nonce>:<canonical address>` and permanently\n * marks the account as `verified: true`.\n */\nexport type AccountOwnershipProof = {\n /**\n * Single-use nonce obtained from {@link ProfileMetricsService.fetchNonces}.\n * Consumed by the server on verification; replay is not possible.\n */\n nonce: string;\n /**\n * Chain-native signature of `metamask:proof-of-ownership:<nonce>:<address>`,\n * always 0x-prefixed. The exact format varies by chain (see the auth API\n * spec — EIP-191 for `eip155`, ed25519 for `solana`, TIP-191 for `tron`,\n * BIP-322 for `bip122`).\n */\n signature: string;\n};\n\n/**\n * An account address along with its associated scopes and an optional\n * ownership proof.\n */\nexport type AccountWithScopes = {\n address: string;\n scopes: `${string}:${string}`[];\n proof?: AccountOwnershipProof;\n};\n\n/**\n * The shape of the request object for submitting metrics.\n */\nexport type ProfileMetricsSubmitMetricsRequest = {\n metametricsId: string;\n entropySourceId?: string | null;\n accounts: AccountWithScopes[];\n};\n\n/**\n * The shape of the request object for fetching a batch of single-use nonces.\n */\nexport type ProfileMetricsFetchNoncesRequest = {\n /**\n * The identifiers (canonical addresses) to mint a nonce for. The auth API\n * accepts between 1 and {@link MAX_NONCE_BATCH_SIZE} identifiers per call.\n */\n identifiers: string[];\n /**\n * The entropy source ID to use when fetching a bearer token. Pass `null` or\n * omit for accounts that do not belong to any entropy source.\n */\n entropySourceId?: string | null;\n};\n\n/**\n * Maximum number of identifiers the auth API will mint nonces for in a single\n * `POST /api/v2/nonce/batch` request. {@link ProfileMetricsService.fetchNonces}\n * uses this as the chunk size when the caller requests more than this many\n * nonces at once.\n */\nexport const MAX_NONCE_BATCH_SIZE = 50;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = ['submitMetrics', 'fetchNonces'] as const;\n\n/**\n * Actions that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceActions = ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsService} calls.\n */\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\n/**\n * Events that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceEvents = never;\n\n/**\n * Events from other messengers that {@link ProfileMetricsService} subscribes\n * to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link ProfileMetricsService}.\n */\nexport type ProfileMetricsServiceMessenger = Messenger<\n typeof serviceName,\n ProfileMetricsServiceActions | AllowedActions,\n ProfileMetricsServiceEvents | AllowedEvents\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * A service for submitting user profile metrics (metrics ID and accounts).\n */\nexport class ProfileMetricsService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['messenger'];\n\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['fetch'];\n\n /**\n * The policy that wraps the request.\n *\n * @see {@link createServicePolicy}\n */\n readonly #policy: ServicePolicy;\n\n /**\n * The API base URL environment.\n */\n readonly #baseURL: string;\n\n /**\n * Constructs a new ProfileMetricsService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request. If\n * your JavaScript environment supports `fetch` natively, you'll probably want\n * to pass that; otherwise you can pass an equivalent (such as `fetch` via\n * `node-fetch`).\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n * @param args.env - The environment to determine the correct API endpoints.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n policyOptions = {},\n env = SDK.Env.DEV,\n }: {\n messenger: ProfileMetricsServiceMessenger;\n fetch: typeof fetch;\n policyOptions?: CreateServicePolicyOptions;\n env?: SDK.Env;\n }) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#fetch = fetchFunction;\n this.#policy = createServicePolicy(policyOptions);\n this.#baseURL = getAuthUrl(env);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a non-500\n * response, causing a retry. Primarily useful in tests where timers are being\n * mocked.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n return this.#policy.onBreak(listener);\n }\n\n /**\n * Registers a handler that will be called under one of two circumstances:\n *\n * 1. After a set number of retries prove that requests to the API\n * consistently result in one of the following failures:\n * 1. A connection initiation error\n * 2. A connection reset error\n * 3. A timeout error\n * 4. A non-JSON response\n * 5. A 502, 503, or 504 response\n * 2. After a successful request is made to the API, but the response takes\n * longer than a set duration to return.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n return this.#policy.onDegraded(listener);\n }\n\n /**\n * Fetch single-use nonces from the auth API, one per identifier.\n *\n * Requests larger than {@link MAX_NONCE_BATCH_SIZE} are split into multiple\n * `POST /api/v2/nonce/batch` calls fired in parallel; the resulting maps are\n * merged into a single record. Each chunk independently goes through the\n * service policy (retry, circuit-breaker, degraded). If any chunk ultimately\n * fails, the whole call rejects so the caller can soft-degrade the entire\n * entropy-source batch consistently.\n *\n * The returned record is keyed by the auth API's echoed `identifier` field\n * (`response[i].identifier -> response[i].nonce`). The call asserts that\n * the response identifier set is exactly the requested set; any mismatch\n * (missing, extra, or duplicated identifier) causes the chunk to throw so\n * the caller never silently proceeds with partial nonces.\n *\n * @param data - The identifiers to mint nonces for, plus the optional\n * entropy source ID used to scope the bearer token.\n * @returns A map of identifier -> nonce.\n * @throws {RangeError} if no identifiers are provided.\n */\n async fetchNonces(\n data: ProfileMetricsFetchNoncesRequest,\n ): Promise<Record<string, string>> {\n if (data.identifiers.length === 0) {\n throw new RangeError(\n 'ProfileMetricsService.fetchNonces requires at least 1 identifier.',\n );\n }\n const chunks: string[][] = [];\n for (let i = 0; i < data.identifiers.length; i += MAX_NONCE_BATCH_SIZE) {\n chunks.push(data.identifiers.slice(i, i + MAX_NONCE_BATCH_SIZE));\n }\n const chunkResults = await Promise.all(\n chunks.map((identifiers) =>\n this.#fetchNoncesChunk(identifiers, data.entropySourceId),\n ),\n );\n return Object.assign({}, ...chunkResults);\n }\n\n /**\n * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of\n * identifiers. Wrapped in {@link #policy} for retry / degraded / circuit\n * semantics consistent with the rest of the service.\n *\n * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.\n * @param entropySourceId - The entropy source ID forwarded to the bearer\n * token resolver.\n * @returns A map of identifier -> nonce for this chunk.\n */\n async #fetchNoncesChunk(\n identifiers: string[],\n entropySourceId: string | null | undefined,\n ): Promise<Record<string, string>> {\n return await this.#policy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/nonce/batch`);\n const localResponse = await this.#fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ identifiers }),\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n const body: unknown = await localResponse.json();\n if (!NonceBatchResponseStruct.is(body)) {\n throw new Error(`Malformed response received from '${url.toString()}'`);\n }\n const result: Record<string, string> = {};\n for (const entry of body) {\n result[entry.identifier] = entry.nonce;\n }\n const echoesRequest =\n body.length === identifiers.length &&\n identifiers.every((id) =>\n Object.prototype.hasOwnProperty.call(result, id),\n );\n if (!echoesRequest) {\n throw new Error(\n `Fetching '${url.toString()}' returned a response whose identifier set does not match the request`,\n );\n }\n return result;\n });\n }\n\n /**\n * Submit metrics to the API.\n *\n * @param data - The data to send in the metrics update request.\n * @returns The response from the API.\n */\n async submitMetrics(data: ProfileMetricsSubmitMetricsRequest): Promise<void> {\n await this.#policy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n data.entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/profile/accounts`);\n const localResponse = await this.#fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n metametrics_id: data.metametricsId,\n accounts: data.accounts,\n }),\n // The auth API is stateless (no cookies used)\n // prevent marketing cookies scoped to\n // .metamask.io from being forwarded to api which\n // causes 431 Request Header Fields Too Large errors.\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n }\n}\n\n/**\n * Returns the base URL for the given environment.\n *\n * @param env - The environment to get the URL for.\n * @returns The base URL for the environment.\n */\nexport function getAuthUrl(env: SDK.Env): string {\n return `${SDK.getEnvUrls(env).authApiUrl}/api/v2`;\n}\n"]}
@@ -111,11 +111,6 @@ export declare class ProfileMetricsService {
111
111
  * `node-fetch`).
112
112
  * @param args.policyOptions - Options to pass to `createServicePolicy`, which
113
113
  * is used to wrap each request. See {@link CreateServicePolicyOptions}.
114
- * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;
115
- * the controller poll re-fetches nonces and retries failed submits).
116
- * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch
117
- * soft-degrades to a proof-less submit that clears the queue, so retries
118
- * here are what preserve proofs across transient errors.
119
114
  * @param args.env - The environment to determine the correct API endpoints.
120
115
  */
121
116
  constructor({ messenger, fetch: fetchFunction, policyOptions, env, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileMetricsService.d.cts","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProfileMetricsService.d.cts","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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"}
@@ -111,11 +111,6 @@ export declare class ProfileMetricsService {
111
111
  * `node-fetch`).
112
112
  * @param args.policyOptions - Options to pass to `createServicePolicy`, which
113
113
  * is used to wrap each request. See {@link CreateServicePolicyOptions}.
114
- * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;
115
- * the controller poll re-fetches nonces and retries failed submits).
116
- * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch
117
- * soft-degrades to a proof-less submit that clears the queue, so retries
118
- * here are what preserve proofs across transient errors.
119
114
  * @param args.env - The environment to determine the correct API endpoints.
120
115
  */
121
116
  constructor({ messenger, fetch: fetchFunction, policyOptions, env, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileMetricsService.d.mts","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProfileMetricsService.d.mts","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
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
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _ProfileMetricsService_instances, _a, _ProfileMetricsService_messenger, _ProfileMetricsService_fetch, _ProfileMetricsService_fetchNoncesPolicy, _ProfileMetricsService_submitMetricsPolicy, _ProfileMetricsService_baseURL, _ProfileMetricsService_fetchNoncesChunk;
12
+ var _ProfileMetricsService_instances, _a, _ProfileMetricsService_messenger, _ProfileMetricsService_fetch, _ProfileMetricsService_policy, _ProfileMetricsService_baseURL, _ProfileMetricsService_fetchNoncesChunk;
13
13
  import { createServicePolicy, HttpError } from "@metamask/controller-utils";
14
14
  import { SDK } from "@metamask/profile-sync-controller";
15
15
  import { array, number, string, type as structType } from "@metamask/superstruct";
@@ -57,11 +57,6 @@ export class ProfileMetricsService {
57
57
  * `node-fetch`).
58
58
  * @param args.policyOptions - Options to pass to `createServicePolicy`, which
59
59
  * is used to wrap each request. See {@link CreateServicePolicyOptions}.
60
- * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;
61
- * the controller poll re-fetches nonces and retries failed submits).
62
- * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch
63
- * soft-degrades to a proof-less submit that clears the queue, so retries
64
- * here are what preserve proofs across transient errors.
65
60
  * @param args.env - The environment to determine the correct API endpoints.
66
61
  */
67
62
  constructor({ messenger, fetch: fetchFunction, policyOptions = {}, env = SDK.Env.DEV, }) {
@@ -75,18 +70,11 @@ export class ProfileMetricsService {
75
70
  */
76
71
  _ProfileMetricsService_fetch.set(this, void 0);
77
72
  /**
78
- * Policy for `fetchNonces`. Minting new nonces is safe to retry.
73
+ * The policy that wraps the request.
79
74
  *
80
75
  * @see {@link createServicePolicy}
81
76
  */
82
- _ProfileMetricsService_fetchNoncesPolicy.set(this, void 0);
83
- /**
84
- * Policy for `submitMetrics`. Proof payloads embed single-use nonces, so
85
- * in-request retries are disabled by default.
86
- *
87
- * @see {@link createServicePolicy}
88
- */
89
- _ProfileMetricsService_submitMetricsPolicy.set(this, void 0);
77
+ _ProfileMetricsService_policy.set(this, void 0);
90
78
  /**
91
79
  * The API base URL environment.
92
80
  */
@@ -94,16 +82,7 @@ export class ProfileMetricsService {
94
82
  this.name = serviceName;
95
83
  __classPrivateFieldSet(this, _ProfileMetricsService_messenger, messenger, "f");
96
84
  __classPrivateFieldSet(this, _ProfileMetricsService_fetch, fetchFunction, "f");
97
- __classPrivateFieldSet(this, _ProfileMetricsService_fetchNoncesPolicy, createServicePolicy(policyOptions), "f");
98
- // Keep the circuit breaker scaled to the retry count the same way
99
- // `createServicePolicy` derives `DEFAULT_MAX_CONSECUTIVE_FAILURES`:
100
- // `(1 + maxRetries) * 3` failed attempts ≈ 3 failed `execute()` calls.
101
- const submitMaxRetries = policyOptions.maxRetries ?? 0;
102
- __classPrivateFieldSet(this, _ProfileMetricsService_submitMetricsPolicy, createServicePolicy({
103
- ...policyOptions,
104
- maxRetries: submitMaxRetries,
105
- maxConsecutiveFailures: policyOptions.maxConsecutiveFailures ?? (1 + submitMaxRetries) * 3,
106
- }), "f");
85
+ __classPrivateFieldSet(this, _ProfileMetricsService_policy, createServicePolicy(policyOptions), "f");
107
86
  __classPrivateFieldSet(this, _ProfileMetricsService_baseURL, getAuthUrl(env), "f");
108
87
  __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
109
88
  }
@@ -118,14 +97,7 @@ export class ProfileMetricsService {
118
97
  * @see {@link createServicePolicy}
119
98
  */
120
99
  onRetry(listener) {
121
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onRetry(listener);
122
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onRetry(listener);
123
- return {
124
- dispose: () => {
125
- fetchDisposable.dispose();
126
- submitDisposable.dispose();
127
- },
128
- };
100
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onRetry(listener);
129
101
  }
130
102
  /**
131
103
  * Registers a handler that will be called after a set number of retry rounds
@@ -137,14 +109,7 @@ export class ProfileMetricsService {
137
109
  * @see {@link createServicePolicy}
138
110
  */
139
111
  onBreak(listener) {
140
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onBreak(listener);
141
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onBreak(listener);
142
- return {
143
- dispose: () => {
144
- fetchDisposable.dispose();
145
- submitDisposable.dispose();
146
- },
147
- };
112
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onBreak(listener);
148
113
  }
149
114
  /**
150
115
  * Registers a handler that will be called under one of two circumstances:
@@ -164,14 +129,7 @@ export class ProfileMetricsService {
164
129
  * {@link CockatielEvent}.
165
130
  */
166
131
  onDegraded(listener) {
167
- const fetchDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").onDegraded(listener);
168
- const submitDisposable = __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").onDegraded(listener);
169
- return {
170
- dispose: () => {
171
- fetchDisposable.dispose();
172
- submitDisposable.dispose();
173
- },
174
- };
132
+ return __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").onDegraded(listener);
175
133
  }
176
134
  /**
177
135
  * Fetch single-use nonces from the auth API, one per identifier.
@@ -212,7 +170,7 @@ export class ProfileMetricsService {
212
170
  * @returns The response from the API.
213
171
  */
214
172
  async submitMetrics(data) {
215
- await __classPrivateFieldGet(this, _ProfileMetricsService_submitMetricsPolicy, "f").execute(async () => {
173
+ await __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").execute(async () => {
216
174
  const authToken = await __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").call('AuthenticationController:getBearerToken', data.entropySourceId ?? undefined);
217
175
  const url = new URL(`${__classPrivateFieldGet(this, _ProfileMetricsService_baseURL, "f")}/profile/accounts`);
218
176
  const localResponse = await __classPrivateFieldGet(this, _ProfileMetricsService_fetch, "f").call(this, url, {
@@ -238,11 +196,11 @@ export class ProfileMetricsService {
238
196
  });
239
197
  }
240
198
  }
241
- _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _ProfileMetricsService_fetch = new WeakMap(), _ProfileMetricsService_fetchNoncesPolicy = new WeakMap(), _ProfileMetricsService_submitMetricsPolicy = new WeakMap(), _ProfileMetricsService_baseURL = new WeakMap(), _ProfileMetricsService_instances = new WeakSet(), _ProfileMetricsService_fetchNoncesChunk =
199
+ _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _ProfileMetricsService_fetch = new WeakMap(), _ProfileMetricsService_policy = new WeakMap(), _ProfileMetricsService_baseURL = new WeakMap(), _ProfileMetricsService_instances = new WeakSet(), _ProfileMetricsService_fetchNoncesChunk =
242
200
  /**
243
201
  * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of
244
- * identifiers. Wrapped in {@link #fetchNoncesPolicy} for retry / degraded /
245
- * circuit semantics.
202
+ * identifiers. Wrapped in {@link #policy} for retry / degraded / circuit
203
+ * semantics consistent with the rest of the service.
246
204
  *
247
205
  * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.
248
206
  * @param entropySourceId - The entropy source ID forwarded to the bearer
@@ -250,7 +208,7 @@ _a = ProfileMetricsService, _ProfileMetricsService_messenger = new WeakMap(), _P
250
208
  * @returns A map of identifier -> nonce for this chunk.
251
209
  */
252
210
  async function _ProfileMetricsService_fetchNoncesChunk(identifiers, entropySourceId) {
253
- return await __classPrivateFieldGet(this, _ProfileMetricsService_fetchNoncesPolicy, "f").execute(async () => {
211
+ return await __classPrivateFieldGet(this, _ProfileMetricsService_policy, "f").execute(async () => {
254
212
  const authToken = await __classPrivateFieldGet(this, _ProfileMetricsService_messenger, "f").call('AuthenticationController:getBearerToken', entropySourceId ?? undefined);
255
213
  const url = new URL(`${__classPrivateFieldGet(this, _ProfileMetricsService_baseURL, "f")}/nonce/batch`);
256
214
  const localResponse = await __classPrivateFieldGet(this, _ProfileMetricsService_fetch, "f").call(this, url, {
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileMetricsService.mjs","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport { SDK } from '@metamask/profile-sync-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n number,\n string,\n type as structType,\n} from '@metamask/superstruct';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types.js';\n\n/**\n * The shape of an entry in the `POST /api/v2/nonce/batch` response body.\n *\n * `identifier` echoes the request identifier verbatim, mirroring the\n * documented behavior of the single-account `GET /api/v2/nonce` endpoint on\n * the same auth service. Defined with `type()` (not `object()`) so the\n * client tolerates additive server-side schema changes.\n */\nconst NonceBatchResponseStruct = array(\n structType({\n expires_in: number(),\n identifier: string(),\n nonce: string(),\n }),\n);\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ProfileMetricsService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'ProfileMetricsService';\n\n/**\n * A cryptographic proof that the caller controls the private key of an\n * account, as defined by the `PUT /api/v2/profile/accounts` endpoint of the\n * auth API. When present, the server verifies the signature against\n * `metamask:proof-of-ownership:<nonce>:<canonical address>` and permanently\n * marks the account as `verified: true`.\n */\nexport type AccountOwnershipProof = {\n /**\n * Single-use nonce obtained from {@link ProfileMetricsService.fetchNonces}.\n * Consumed by the server on verification; replay is not possible.\n */\n nonce: string;\n /**\n * Chain-native signature of `metamask:proof-of-ownership:<nonce>:<address>`,\n * always 0x-prefixed. The exact format varies by chain (see the auth API\n * spec — EIP-191 for `eip155`, ed25519 for `solana`, TIP-191 for `tron`,\n * BIP-322 for `bip122`).\n */\n signature: string;\n};\n\n/**\n * An account address along with its associated scopes and an optional\n * ownership proof.\n */\nexport type AccountWithScopes = {\n address: string;\n scopes: `${string}:${string}`[];\n proof?: AccountOwnershipProof;\n};\n\n/**\n * The shape of the request object for submitting metrics.\n */\nexport type ProfileMetricsSubmitMetricsRequest = {\n metametricsId: string;\n entropySourceId?: string | null;\n accounts: AccountWithScopes[];\n};\n\n/**\n * The shape of the request object for fetching a batch of single-use nonces.\n */\nexport type ProfileMetricsFetchNoncesRequest = {\n /**\n * The identifiers (canonical addresses) to mint a nonce for. The auth API\n * accepts between 1 and {@link MAX_NONCE_BATCH_SIZE} identifiers per call.\n */\n identifiers: string[];\n /**\n * The entropy source ID to use when fetching a bearer token. Pass `null` or\n * omit for accounts that do not belong to any entropy source.\n */\n entropySourceId?: string | null;\n};\n\n/**\n * Maximum number of identifiers the auth API will mint nonces for in a single\n * `POST /api/v2/nonce/batch` request. {@link ProfileMetricsService.fetchNonces}\n * uses this as the chunk size when the caller requests more than this many\n * nonces at once.\n */\nexport const MAX_NONCE_BATCH_SIZE = 50;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = ['submitMetrics', 'fetchNonces'] as const;\n\n/**\n * Actions that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceActions = ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsService} calls.\n */\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\n/**\n * Events that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceEvents = never;\n\n/**\n * Events from other messengers that {@link ProfileMetricsService} subscribes\n * to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link ProfileMetricsService}.\n */\nexport type ProfileMetricsServiceMessenger = Messenger<\n typeof serviceName,\n ProfileMetricsServiceActions | AllowedActions,\n ProfileMetricsServiceEvents | AllowedEvents\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * A service for submitting user profile metrics (metrics ID and accounts).\n */\nexport class ProfileMetricsService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['messenger'];\n\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['fetch'];\n\n /**\n * Policy for `fetchNonces`. Minting new nonces is safe to retry.\n *\n * @see {@link createServicePolicy}\n */\n readonly #fetchNoncesPolicy: ServicePolicy;\n\n /**\n * Policy for `submitMetrics`. Proof payloads embed single-use nonces, so\n * in-request retries are disabled by default.\n *\n * @see {@link createServicePolicy}\n */\n readonly #submitMetricsPolicy: ServicePolicy;\n\n /**\n * The API base URL environment.\n */\n readonly #baseURL: string;\n\n /**\n * Constructs a new ProfileMetricsService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request. If\n * your JavaScript environment supports `fetch` natively, you'll probably want\n * to pass that; otherwise you can pass an equivalent (such as `fetch` via\n * `node-fetch`).\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n * `submitMetrics` defaults to `maxRetries: 0` (proof nonces are single-use;\n * the controller poll re-fetches nonces and retries failed submits).\n * `fetchNonces` keeps the normal retry defaults — a failed nonce fetch\n * soft-degrades to a proof-less submit that clears the queue, so retries\n * here are what preserve proofs across transient errors.\n * @param args.env - The environment to determine the correct API endpoints.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n policyOptions = {},\n env = SDK.Env.DEV,\n }: {\n messenger: ProfileMetricsServiceMessenger;\n fetch: typeof fetch;\n policyOptions?: CreateServicePolicyOptions;\n env?: SDK.Env;\n }) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#fetch = fetchFunction;\n this.#fetchNoncesPolicy = createServicePolicy(policyOptions);\n // Keep the circuit breaker scaled to the retry count the same way\n // `createServicePolicy` derives `DEFAULT_MAX_CONSECUTIVE_FAILURES`:\n // `(1 + maxRetries) * 3` failed attempts ≈ 3 failed `execute()` calls.\n const submitMaxRetries = policyOptions.maxRetries ?? 0;\n this.#submitMetricsPolicy = createServicePolicy({\n ...policyOptions,\n maxRetries: submitMaxRetries,\n maxConsecutiveFailures:\n policyOptions.maxConsecutiveFailures ?? (1 + submitMaxRetries) * 3,\n });\n this.#baseURL = getAuthUrl(env);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a non-500\n * response, causing a retry. Primarily useful in tests where timers are being\n * mocked.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onRetry(listener);\n const submitDisposable = this.#submitMetricsPolicy.onRetry(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onBreak(listener);\n const submitDisposable = this.#submitMetricsPolicy.onBreak(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Registers a handler that will be called under one of two circumstances:\n *\n * 1. After a set number of retries prove that requests to the API\n * consistently result in one of the following failures:\n * 1. A connection initiation error\n * 2. A connection reset error\n * 3. A timeout error\n * 4. A non-JSON response\n * 5. A 502, 503, or 504 response\n * 2. After a successful request is made to the API, but the response takes\n * longer than a set duration to return.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n const fetchDisposable = this.#fetchNoncesPolicy.onDegraded(listener);\n const submitDisposable = this.#submitMetricsPolicy.onDegraded(listener);\n return {\n dispose: (): void => {\n fetchDisposable.dispose();\n submitDisposable.dispose();\n },\n };\n }\n\n /**\n * Fetch single-use nonces from the auth API, one per identifier.\n *\n * Requests larger than {@link MAX_NONCE_BATCH_SIZE} are split into multiple\n * `POST /api/v2/nonce/batch` calls fired in parallel; the resulting maps are\n * merged into a single record. Each chunk independently goes through the\n * service policy (retry, circuit-breaker, degraded). If any chunk ultimately\n * fails, the whole call rejects so the caller can soft-degrade the entire\n * entropy-source batch consistently.\n *\n * The returned record is keyed by the auth API's echoed `identifier` field\n * (`response[i].identifier -> response[i].nonce`). The call asserts that\n * the response identifier set is exactly the requested set; any mismatch\n * (missing, extra, or duplicated identifier) causes the chunk to throw so\n * the caller never silently proceeds with partial nonces.\n *\n * @param data - The identifiers to mint nonces for, plus the optional\n * entropy source ID used to scope the bearer token.\n * @returns A map of identifier -> nonce.\n * @throws {RangeError} if no identifiers are provided.\n */\n async fetchNonces(\n data: ProfileMetricsFetchNoncesRequest,\n ): Promise<Record<string, string>> {\n if (data.identifiers.length === 0) {\n throw new RangeError(\n 'ProfileMetricsService.fetchNonces requires at least 1 identifier.',\n );\n }\n const chunks: string[][] = [];\n for (let i = 0; i < data.identifiers.length; i += MAX_NONCE_BATCH_SIZE) {\n chunks.push(data.identifiers.slice(i, i + MAX_NONCE_BATCH_SIZE));\n }\n const chunkResults = await Promise.all(\n chunks.map((identifiers) =>\n this.#fetchNoncesChunk(identifiers, data.entropySourceId),\n ),\n );\n return Object.assign({}, ...chunkResults);\n }\n\n /**\n * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of\n * identifiers. Wrapped in {@link #fetchNoncesPolicy} for retry / degraded /\n * circuit semantics.\n *\n * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.\n * @param entropySourceId - The entropy source ID forwarded to the bearer\n * token resolver.\n * @returns A map of identifier -> nonce for this chunk.\n */\n async #fetchNoncesChunk(\n identifiers: string[],\n entropySourceId: string | null | undefined,\n ): Promise<Record<string, string>> {\n return await this.#fetchNoncesPolicy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/nonce/batch`);\n const localResponse = await this.#fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ identifiers }),\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n const body: unknown = await localResponse.json();\n if (!NonceBatchResponseStruct.is(body)) {\n throw new Error(`Malformed response received from '${url.toString()}'`);\n }\n const result: Record<string, string> = {};\n for (const entry of body) {\n result[entry.identifier] = entry.nonce;\n }\n const echoesRequest =\n body.length === identifiers.length &&\n identifiers.every((id) =>\n Object.prototype.hasOwnProperty.call(result, id),\n );\n if (!echoesRequest) {\n throw new Error(\n `Fetching '${url.toString()}' returned a response whose identifier set does not match the request`,\n );\n }\n return result;\n });\n }\n\n /**\n * Submit metrics to the API.\n *\n * @param data - The data to send in the metrics update request.\n * @returns The response from the API.\n */\n async submitMetrics(data: ProfileMetricsSubmitMetricsRequest): Promise<void> {\n await this.#submitMetricsPolicy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n data.entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/profile/accounts`);\n const localResponse = await this.#fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n metametrics_id: data.metametricsId,\n accounts: data.accounts,\n }),\n // The auth API is stateless (no cookies used)\n // prevent marketing cookies scoped to\n // .metamask.io from being forwarded to api which\n // causes 431 Request Header Fields Too Large errors.\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n }\n}\n\n/**\n * Returns the base URL for the given environment.\n *\n * @param env - The environment to get the URL for.\n * @returns The base URL for the environment.\n */\nexport function getAuthUrl(env: SDK.Env): string {\n return `${SDK.getEnvUrls(env).authApiUrl}/api/v2`;\n}\n"]}
1
+ {"version":3,"file":"ProfileMetricsService.mjs","sourceRoot":"","sources":["../src/ProfileMetricsService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport { SDK } from '@metamask/profile-sync-controller';\nimport type { AuthenticationController } from '@metamask/profile-sync-controller';\nimport {\n array,\n number,\n string,\n type as structType,\n} from '@metamask/superstruct';\nimport type { IDisposable } from 'cockatiel';\n\nimport type { ProfileMetricsServiceMethodActions } from './ProfileMetricsService-method-action-types.js';\n\n/**\n * The shape of an entry in the `POST /api/v2/nonce/batch` response body.\n *\n * `identifier` echoes the request identifier verbatim, mirroring the\n * documented behavior of the single-account `GET /api/v2/nonce` endpoint on\n * the same auth service. Defined with `type()` (not `object()`) so the\n * client tolerates additive server-side schema changes.\n */\nconst NonceBatchResponseStruct = array(\n structType({\n expires_in: number(),\n identifier: string(),\n nonce: string(),\n }),\n);\n\n// === GENERAL ===\n\n/**\n * The name of the {@link ProfileMetricsService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'ProfileMetricsService';\n\n/**\n * A cryptographic proof that the caller controls the private key of an\n * account, as defined by the `PUT /api/v2/profile/accounts` endpoint of the\n * auth API. When present, the server verifies the signature against\n * `metamask:proof-of-ownership:<nonce>:<canonical address>` and permanently\n * marks the account as `verified: true`.\n */\nexport type AccountOwnershipProof = {\n /**\n * Single-use nonce obtained from {@link ProfileMetricsService.fetchNonces}.\n * Consumed by the server on verification; replay is not possible.\n */\n nonce: string;\n /**\n * Chain-native signature of `metamask:proof-of-ownership:<nonce>:<address>`,\n * always 0x-prefixed. The exact format varies by chain (see the auth API\n * spec — EIP-191 for `eip155`, ed25519 for `solana`, TIP-191 for `tron`,\n * BIP-322 for `bip122`).\n */\n signature: string;\n};\n\n/**\n * An account address along with its associated scopes and an optional\n * ownership proof.\n */\nexport type AccountWithScopes = {\n address: string;\n scopes: `${string}:${string}`[];\n proof?: AccountOwnershipProof;\n};\n\n/**\n * The shape of the request object for submitting metrics.\n */\nexport type ProfileMetricsSubmitMetricsRequest = {\n metametricsId: string;\n entropySourceId?: string | null;\n accounts: AccountWithScopes[];\n};\n\n/**\n * The shape of the request object for fetching a batch of single-use nonces.\n */\nexport type ProfileMetricsFetchNoncesRequest = {\n /**\n * The identifiers (canonical addresses) to mint a nonce for. The auth API\n * accepts between 1 and {@link MAX_NONCE_BATCH_SIZE} identifiers per call.\n */\n identifiers: string[];\n /**\n * The entropy source ID to use when fetching a bearer token. Pass `null` or\n * omit for accounts that do not belong to any entropy source.\n */\n entropySourceId?: string | null;\n};\n\n/**\n * Maximum number of identifiers the auth API will mint nonces for in a single\n * `POST /api/v2/nonce/batch` request. {@link ProfileMetricsService.fetchNonces}\n * uses this as the chunk size when the caller requests more than this many\n * nonces at once.\n */\nexport const MAX_NONCE_BATCH_SIZE = 50;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = ['submitMetrics', 'fetchNonces'] as const;\n\n/**\n * Actions that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceActions = ProfileMetricsServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link ProfileMetricsService} calls.\n */\ntype AllowedActions =\n AuthenticationController.AuthenticationControllerGetBearerTokenAction;\n\n/**\n * Events that {@link ProfileMetricsService} exposes to other consumers.\n */\nexport type ProfileMetricsServiceEvents = never;\n\n/**\n * Events from other messengers that {@link ProfileMetricsService} subscribes\n * to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link ProfileMetricsService}.\n */\nexport type ProfileMetricsServiceMessenger = Messenger<\n typeof serviceName,\n ProfileMetricsServiceActions | AllowedActions,\n ProfileMetricsServiceEvents | AllowedEvents\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * A service for submitting user profile metrics (metrics ID and accounts).\n */\nexport class ProfileMetricsService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n /**\n * The messenger suited for this service.\n */\n readonly #messenger: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['messenger'];\n\n /**\n * A function that can be used to make an HTTP request.\n */\n readonly #fetch: ConstructorParameters<\n typeof ProfileMetricsService\n >[0]['fetch'];\n\n /**\n * The policy that wraps the request.\n *\n * @see {@link createServicePolicy}\n */\n readonly #policy: ServicePolicy;\n\n /**\n * The API base URL environment.\n */\n readonly #baseURL: string;\n\n /**\n * Constructs a new ProfileMetricsService object.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.fetch - A function that can be used to make an HTTP request. If\n * your JavaScript environment supports `fetch` natively, you'll probably want\n * to pass that; otherwise you can pass an equivalent (such as `fetch` via\n * `node-fetch`).\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n * @param args.env - The environment to determine the correct API endpoints.\n */\n constructor({\n messenger,\n fetch: fetchFunction,\n policyOptions = {},\n env = SDK.Env.DEV,\n }: {\n messenger: ProfileMetricsServiceMessenger;\n fetch: typeof fetch;\n policyOptions?: CreateServicePolicyOptions;\n env?: SDK.Env;\n }) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#fetch = fetchFunction;\n this.#policy = createServicePolicy(policyOptions);\n this.#baseURL = getAuthUrl(env);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a non-500\n * response, causing a retry. Primarily useful in tests where timers are being\n * mocked.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n return this.#policy.onBreak(listener);\n }\n\n /**\n * Registers a handler that will be called under one of two circumstances:\n *\n * 1. After a set number of retries prove that requests to the API\n * consistently result in one of the following failures:\n * 1. A connection initiation error\n * 2. A connection reset error\n * 3. A timeout error\n * 4. A non-JSON response\n * 5. A 502, 503, or 504 response\n * 2. After a successful request is made to the API, but the response takes\n * longer than a set duration to return.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler. See\n * {@link CockatielEvent}.\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n return this.#policy.onDegraded(listener);\n }\n\n /**\n * Fetch single-use nonces from the auth API, one per identifier.\n *\n * Requests larger than {@link MAX_NONCE_BATCH_SIZE} are split into multiple\n * `POST /api/v2/nonce/batch` calls fired in parallel; the resulting maps are\n * merged into a single record. Each chunk independently goes through the\n * service policy (retry, circuit-breaker, degraded). If any chunk ultimately\n * fails, the whole call rejects so the caller can soft-degrade the entire\n * entropy-source batch consistently.\n *\n * The returned record is keyed by the auth API's echoed `identifier` field\n * (`response[i].identifier -> response[i].nonce`). The call asserts that\n * the response identifier set is exactly the requested set; any mismatch\n * (missing, extra, or duplicated identifier) causes the chunk to throw so\n * the caller never silently proceeds with partial nonces.\n *\n * @param data - The identifiers to mint nonces for, plus the optional\n * entropy source ID used to scope the bearer token.\n * @returns A map of identifier -> nonce.\n * @throws {RangeError} if no identifiers are provided.\n */\n async fetchNonces(\n data: ProfileMetricsFetchNoncesRequest,\n ): Promise<Record<string, string>> {\n if (data.identifiers.length === 0) {\n throw new RangeError(\n 'ProfileMetricsService.fetchNonces requires at least 1 identifier.',\n );\n }\n const chunks: string[][] = [];\n for (let i = 0; i < data.identifiers.length; i += MAX_NONCE_BATCH_SIZE) {\n chunks.push(data.identifiers.slice(i, i + MAX_NONCE_BATCH_SIZE));\n }\n const chunkResults = await Promise.all(\n chunks.map((identifiers) =>\n this.#fetchNoncesChunk(identifiers, data.entropySourceId),\n ),\n );\n return Object.assign({}, ...chunkResults);\n }\n\n /**\n * Mint nonces for a single ≤ {@link MAX_NONCE_BATCH_SIZE}-sized chunk of\n * identifiers. Wrapped in {@link #policy} for retry / degraded / circuit\n * semantics consistent with the rest of the service.\n *\n * @param identifiers - The identifiers in this chunk. Must be 1..MAX_NONCE_BATCH_SIZE.\n * @param entropySourceId - The entropy source ID forwarded to the bearer\n * token resolver.\n * @returns A map of identifier -> nonce for this chunk.\n */\n async #fetchNoncesChunk(\n identifiers: string[],\n entropySourceId: string | null | undefined,\n ): Promise<Record<string, string>> {\n return await this.#policy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/nonce/batch`);\n const localResponse = await this.#fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ identifiers }),\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n const body: unknown = await localResponse.json();\n if (!NonceBatchResponseStruct.is(body)) {\n throw new Error(`Malformed response received from '${url.toString()}'`);\n }\n const result: Record<string, string> = {};\n for (const entry of body) {\n result[entry.identifier] = entry.nonce;\n }\n const echoesRequest =\n body.length === identifiers.length &&\n identifiers.every((id) =>\n Object.prototype.hasOwnProperty.call(result, id),\n );\n if (!echoesRequest) {\n throw new Error(\n `Fetching '${url.toString()}' returned a response whose identifier set does not match the request`,\n );\n }\n return result;\n });\n }\n\n /**\n * Submit metrics to the API.\n *\n * @param data - The data to send in the metrics update request.\n * @returns The response from the API.\n */\n async submitMetrics(data: ProfileMetricsSubmitMetricsRequest): Promise<void> {\n await this.#policy.execute(async () => {\n const authToken = await this.#messenger.call(\n 'AuthenticationController:getBearerToken',\n data.entropySourceId ?? undefined,\n );\n const url = new URL(`${this.#baseURL}/profile/accounts`);\n const localResponse = await this.#fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${authToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n metametrics_id: data.metametricsId,\n accounts: data.accounts,\n }),\n // The auth API is stateless (no cookies used)\n // prevent marketing cookies scoped to\n // .metamask.io from being forwarded to api which\n // causes 431 Request Header Fields Too Large errors.\n credentials: 'omit',\n });\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Fetching '${url.toString()}' failed with status '${localResponse.status}'`,\n );\n }\n return localResponse;\n });\n }\n}\n\n/**\n * Returns the base URL for the given environment.\n *\n * @param env - The environment to get the URL for.\n * @returns The base URL for the environment.\n */\nexport function getAuthUrl(env: SDK.Env): string {\n return `${SDK.getEnvUrls(env).authApiUrl}/api/v2`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/profile-metrics-controller",
3
- "version": "4.0.2-preview-aa202ec10",
3
+ "version": "4.0.2-preview-6cd1bb2f5",
4
4
  "description": "Manages user profile metrics",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "devDependencies": {
75
75
  "@metamask/auto-changelog": "^6.1.0",
76
- "@metamask/keyring-internal-api": "^11.0.2",
76
+ "@metamask/keyring-internal-api": "^11.0.1",
77
77
  "@ts-bridge/cli": "^0.6.4",
78
78
  "@types/jest": "^30.0.0",
79
79
  "deepmerge": "^4.2.2",