@metamask-previews/analytics-controller 4.2.0-preview-9dfb538be → 4.3.0-preview-04cf473

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
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.3.0]
11
+
12
+ ### Added
13
+
14
+ - Allow an optional `properties` argument on `trackErrorAsAnalytics` so callers can attach extra payload fields such as `anonymous: true` while keeping sanitized `source` and `error_message` ([#11355](https://github.com/MetaMask/core/pull/11355))
15
+
10
16
  ## [4.2.0]
11
17
 
12
18
  ### Added
@@ -143,7 +149,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
143
149
 
144
150
  - Initial release of @metamask/analytics-controller. ([#7017](https://github.com/MetaMask/core/pull/7017), [#7202](https://github.com/MetaMask/core/pull/7202))
145
151
 
146
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@4.2.0...HEAD
152
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@4.3.0...HEAD
153
+ [4.3.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@4.2.0...@metamask/analytics-controller@4.3.0
147
154
  [4.2.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@4.1.0...@metamask/analytics-controller@4.2.0
148
155
  [4.1.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@4.0.0...@metamask/analytics-controller@4.1.0
149
156
  [4.0.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@3.2.0...@metamask/analytics-controller@4.0.0
package/README.md CHANGED
@@ -117,7 +117,7 @@ This feature is disabled by default. When disabled, every fragment method is a l
117
117
 
118
118
  ## Error events
119
119
 
120
- `trackErrorAsAnalytics` reports a caught error as an `Error Occurred` event. The properties are `source` and `error_message`. The message is whitespace-normalized and capped at 500 characters. Stack traces are not included.
120
+ `trackErrorAsAnalytics` reports a caught error as an `Error Occurred` event. The properties always include `source` and `error_message`. The message is whitespace-normalized and capped at 500 characters. Stack traces are not included. Callers may pass an optional `properties` argument.
121
121
 
122
122
  `source` must uniquely identify the error case (for example `swap-quote-fetch`). Do not reuse one value for every error in a file or package.
123
123
 
@@ -130,9 +130,11 @@ Volume is bounded per controller instance:
130
130
  - `sessionCap` defaults to `50`. A caller can only lower that ceiling.
131
131
 
132
132
  ```ts
133
- controller.trackErrorAsAnalytics(error, {
134
- source: 'swap-quote-fetch',
135
- });
133
+ controller.trackErrorAsAnalytics(
134
+ error,
135
+ { source: 'swap-quote-fetch' },
136
+ { anonymous: true },
137
+ );
136
138
  ```
137
139
 
138
140
  ## Lifecycle Hooks
@@ -18,9 +18,10 @@ export type AnalyticsControllerTrackEventAction = {
18
18
  /**
19
19
  * Report a caught error as an `Error Occurred` analytics event.
20
20
  *
21
- * Properties are `source` and a sanitized `error_message`. Whitespace is
22
- * collapsed and the message is capped at 500 characters. Stack traces are
23
- * never included.
21
+ * Properties always include `source` and a sanitized `error_message`.
22
+ * Whitespace is collapsed and the message is capped at 500 characters.
23
+ * Stack traces are never included. Optional `properties` are merged in
24
+ * as well.
24
25
  *
25
26
  * `source` must uniquely identify the error case (for example
26
27
  * `swap-quote-fetch`). Do not reuse one value for every error in a file
@@ -43,6 +44,7 @@ export type AnalyticsControllerTrackEventAction = {
43
44
  * @param options - Source and optional volume controls.
44
45
  * @param options.source - Unique discriminator for this error case.
45
46
  * @param options.volumeControls - Optional sampling, dedup, and session cap.
47
+ * @param properties - Optional extra properties.
46
48
  */
47
49
  export type AnalyticsControllerTrackErrorAsAnalyticsAction = {
48
50
  type: `AnalyticsController:trackErrorAsAnalytics`;
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsController-method-action-types.d.ts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsController-method-action-types.d.ts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsController-method-action-types.js","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AnalyticsController } from './AnalyticsController.js';\n\n/**\n * Track an analytics event.\n *\n * Events are only tracked if analytics is enabled.\n *\n * @param event - Analytics event with properties and sensitive properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerTrackEventAction = {\n type: `AnalyticsController:trackEvent`;\n handler: AnalyticsController['trackEvent'];\n};\n\n/**\n * Report a caught error as an `Error Occurred` analytics event.\n *\n * Properties are `source` and a sanitized `error_message`. Whitespace is\n * collapsed and the message is capped at 500 characters. Stack traces are\n * never included.\n *\n * `source` must uniquely identify the error case (for example\n * `swap-quote-fetch`). Do not reuse one value for every error in a file\n * or package.\n *\n * Sampling, a deduplication window, and a per-session cap bound volume.\n * Those controls default to always emitting, 10 seconds, and 50 events.\n * A caller can only lower the session cap. The budget belongs to this\n * controller instance and is not persisted.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the\n * pre-consent queue, and geolocation enrichment all apply. Opted-out\n * calls return before the volume controls run, so they do not consume\n * the session budget.\n *\n * Fail-safe: this method never throws. If delivery throws, the session\n * reservation is released.\n *\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param options.source - Unique discriminator for this error case.\n * @param options.volumeControls - Optional sampling, dedup, and session cap.\n */\nexport type AnalyticsControllerTrackErrorAsAnalyticsAction = {\n type: `AnalyticsController:trackErrorAsAnalytics`;\n handler: AnalyticsController['trackErrorAsAnalytics'];\n};\n\n/**\n * Identify a user for analytics.\n *\n * @param traits - User traits/properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerIdentifyAction = {\n type: `AnalyticsController:identify`;\n handler: AnalyticsController['identify'];\n};\n\n/**\n * Track a page or screen view.\n *\n * @param name - The identifier/name of the page or screen being viewed (e.g., \"home\", \"settings\", \"wallet\")\n * @param properties - Optional properties associated with the view\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerTrackViewAction = {\n type: `AnalyticsController:trackView`;\n handler: AnalyticsController['trackView'];\n};\n\n/**\n * Create an event fragment.\n *\n * A fragment accumulates properties across a user journey so that several\n * parts of a client can contribute to the same set of events without\n * re-deriving them. Declaring `successEvent` and `failureEvent` turns the\n * fragment into a funnel that {@link finalizeEventFragment} closes. Declaring\n * none of the event names makes it a pure property bag that the client reads\n * back with {@link getEventFragmentById} when it emits its own events.\n *\n * Any existing fragment with the same ID is replaced, so a new journey never\n * inherits properties from a stale one.\n *\n * Nothing is created unless the user is opted in, or undecided with the\n * pre-consent queue enabled, so an opted-out user accumulates no fragment\n * data.\n *\n * @param options - The fragment definition. An ID is generated when one is\n * not supplied.\n * @returns A read-only copy of the created fragment, or `undefined` when the\n * event fragments feature is disabled or the consent state does not allow\n * capture. Mutating the returned object does not change controller state.\n * Use {@link updateEventFragment} or {@link upsertEventFragment} to write.\n */\nexport type AnalyticsControllerCreateEventFragmentAction = {\n type: `AnalyticsController:createEventFragment`;\n handler: AnalyticsController['createEventFragment'];\n};\n\n/**\n * Write to an event fragment, creating a property bag if none exists.\n *\n * This is the ergonomic entry point for contributors that do not know\n * whether the journey has been started yet, and it avoids the read then\n * write race a caller would otherwise have to implement itself.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n */\nexport type AnalyticsControllerUpsertEventFragmentAction = {\n type: `AnalyticsController:upsertEventFragment`;\n handler: AnalyticsController['upsertEventFragment'];\n};\n\n/**\n * Write to an existing event fragment.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * Use {@link upsertEventFragment} when the fragment may not exist yet.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\nexport type AnalyticsControllerUpdateEventFragmentAction = {\n type: `AnalyticsController:updateEventFragment`;\n handler: AnalyticsController['updateEventFragment'];\n};\n\n/**\n * Read an event fragment.\n *\n * @param id - The fragment ID.\n * @returns A read-only copy of the fragment, or `undefined` when no fragment\n * has that ID, the event fragments feature is disabled, or the consent state\n * does not allow capture. Mutating the returned object does not change\n * controller state. Use {@link updateEventFragment} or\n * {@link upsertEventFragment} to write.\n */\nexport type AnalyticsControllerGetEventFragmentByIdAction = {\n type: `AnalyticsController:getEventFragmentById`;\n handler: AnalyticsController['getEventFragmentById'];\n};\n\n/**\n * Discard an event fragment without emitting anything.\n *\n * @param id - The fragment ID.\n */\nexport type AnalyticsControllerDeleteEventFragmentAction = {\n type: `AnalyticsController:deleteEventFragment`;\n handler: AnalyticsController['deleteEventFragment'];\n};\n\n/**\n * Close an event fragment, emitting its closing event and discarding it.\n *\n * The event emitted is `failureEvent` when the journey was abandoned and\n * `successEvent` otherwise. A fragment that does not declare the relevant\n * event name is discarded silently, which is what makes a pure property bag\n * possible.\n *\n * @param id - The fragment ID.\n * @param options - Finalization options.\n * @param options.abandoned - Whether the journey was abandoned.\n * @param options.context - Context merged over the fragment's own context.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\nexport type AnalyticsControllerFinalizeEventFragmentAction = {\n type: `AnalyticsController:finalizeEventFragment`;\n handler: AnalyticsController['finalizeEventFragment'];\n};\n\n/**\n * Opt in to analytics.\n *\n * Records that a consent decision has been made and replays any events that\n * were queued while the user was undecided.\n *\n * When geolocation enrichment is enabled, geolocation is resolved here (once\n * the user has consented) and awaited before the queued events are replayed,\n * so those events are enriched with the resolved location as they are sent.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\nexport type AnalyticsControllerOptInAction = {\n type: `AnalyticsController:optIn`;\n handler: AnalyticsController['optIn'];\n};\n\n/**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events and in-progress event fragments so nothing captured before the\n * decision is ever delivered.\n */\nexport type AnalyticsControllerOptOutAction = {\n type: `AnalyticsController:optOut`;\n handler: AnalyticsController['optOut'];\n};\n\n/**\n * Reset the consent decision back to undecided.\n *\n * Intended for client flows that restart onboarding. Clears the opt-in\n * preference and discards the delivery queue, but preserves any pre-consent\n * events so they can still be replayed if the user opts in again. The user is\n * treated as undecided again.\n *\n * In-progress event fragments are kept only while the undecided user can\n * still accumulate them, and discarded otherwise, so no fragment outlives the\n * consent state that allowed it.\n */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Opt in to marketing analytics.\n *\n * Independent of {@link optIn}. Replays queued marketing events.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\nexport type AnalyticsControllerOptInToMarketingAction = {\n type: `AnalyticsController:optInToMarketing`;\n handler: AnalyticsController['optInToMarketing'];\n};\n\n/**\n * Opt out of marketing analytics.\n *\n * Independent of {@link optOut}. Discards queued marketing events and\n * marketing event fragments.\n */\nexport type AnalyticsControllerOptOutOfMarketingAction = {\n type: `AnalyticsController:optOutOfMarketing`;\n handler: AnalyticsController['optOutOfMarketing'];\n};\n\n/**\n * Reset the marketing consent decision back to undecided.\n *\n * Independent of {@link resetConsentDecision}.\n */\nexport type AnalyticsControllerResetMarketingConsentDecisionAction = {\n type: `AnalyticsController:resetMarketingConsentDecision`;\n handler: AnalyticsController['resetMarketingConsentDecision'];\n};\n\n/**\n * Set the marketing campaign cookie ID.\n *\n * Stores the ID of the marketing campaign cookie (e.g. a Google Analytics\n * client ID) that was active when the user arrived. Pass `null` to clear it.\n * The value is automatically cleared by {@link optOutOfMarketing} and\n * {@link resetMarketingConsentDecision}.\n *\n * @param marketingCampaignCookieId - The marketing campaign cookie ID, or\n * `null` to clear it.\n */\nexport type AnalyticsControllerSetMarketingCampaignCookieIdAction = {\n type: `AnalyticsController:setMarketingCampaignCookieId`;\n handler: AnalyticsController['setMarketingCampaignCookieId'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerTrackErrorAsAnalyticsAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerCreateEventFragmentAction\n | AnalyticsControllerUpsertEventFragmentAction\n | AnalyticsControllerUpdateEventFragmentAction\n | AnalyticsControllerGetEventFragmentByIdAction\n | AnalyticsControllerDeleteEventFragmentAction\n | AnalyticsControllerFinalizeEventFragmentAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction\n | AnalyticsControllerOptInToMarketingAction\n | AnalyticsControllerOptOutOfMarketingAction\n | AnalyticsControllerResetMarketingConsentDecisionAction\n | AnalyticsControllerSetMarketingCampaignCookieIdAction;\n"]}
1
+ {"version":3,"file":"AnalyticsController-method-action-types.js","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { AnalyticsController } from './AnalyticsController.js';\n\n/**\n * Track an analytics event.\n *\n * Events are only tracked if analytics is enabled.\n *\n * @param event - Analytics event with properties and sensitive properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerTrackEventAction = {\n type: `AnalyticsController:trackEvent`;\n handler: AnalyticsController['trackEvent'];\n};\n\n/**\n * Report a caught error as an `Error Occurred` analytics event.\n *\n * Properties always include `source` and a sanitized `error_message`.\n * Whitespace is collapsed and the message is capped at 500 characters.\n * Stack traces are never included. Optional `properties` are merged in\n * as well.\n *\n * `source` must uniquely identify the error case (for example\n * `swap-quote-fetch`). Do not reuse one value for every error in a file\n * or package.\n *\n * Sampling, a deduplication window, and a per-session cap bound volume.\n * Those controls default to always emitting, 10 seconds, and 50 events.\n * A caller can only lower the session cap. The budget belongs to this\n * controller instance and is not persisted.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the\n * pre-consent queue, and geolocation enrichment all apply. Opted-out\n * calls return before the volume controls run, so they do not consume\n * the session budget.\n *\n * Fail-safe: this method never throws. If delivery throws, the session\n * reservation is released.\n *\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param options.source - Unique discriminator for this error case.\n * @param options.volumeControls - Optional sampling, dedup, and session cap.\n * @param properties - Optional extra properties.\n */\nexport type AnalyticsControllerTrackErrorAsAnalyticsAction = {\n type: `AnalyticsController:trackErrorAsAnalytics`;\n handler: AnalyticsController['trackErrorAsAnalytics'];\n};\n\n/**\n * Identify a user for analytics.\n *\n * @param traits - User traits/properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerIdentifyAction = {\n type: `AnalyticsController:identify`;\n handler: AnalyticsController['identify'];\n};\n\n/**\n * Track a page or screen view.\n *\n * @param name - The identifier/name of the page or screen being viewed (e.g., \"home\", \"settings\", \"wallet\")\n * @param properties - Optional properties associated with the view\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\nexport type AnalyticsControllerTrackViewAction = {\n type: `AnalyticsController:trackView`;\n handler: AnalyticsController['trackView'];\n};\n\n/**\n * Create an event fragment.\n *\n * A fragment accumulates properties across a user journey so that several\n * parts of a client can contribute to the same set of events without\n * re-deriving them. Declaring `successEvent` and `failureEvent` turns the\n * fragment into a funnel that {@link finalizeEventFragment} closes. Declaring\n * none of the event names makes it a pure property bag that the client reads\n * back with {@link getEventFragmentById} when it emits its own events.\n *\n * Any existing fragment with the same ID is replaced, so a new journey never\n * inherits properties from a stale one.\n *\n * Nothing is created unless the user is opted in, or undecided with the\n * pre-consent queue enabled, so an opted-out user accumulates no fragment\n * data.\n *\n * @param options - The fragment definition. An ID is generated when one is\n * not supplied.\n * @returns A read-only copy of the created fragment, or `undefined` when the\n * event fragments feature is disabled or the consent state does not allow\n * capture. Mutating the returned object does not change controller state.\n * Use {@link updateEventFragment} or {@link upsertEventFragment} to write.\n */\nexport type AnalyticsControllerCreateEventFragmentAction = {\n type: `AnalyticsController:createEventFragment`;\n handler: AnalyticsController['createEventFragment'];\n};\n\n/**\n * Write to an event fragment, creating a property bag if none exists.\n *\n * This is the ergonomic entry point for contributors that do not know\n * whether the journey has been started yet, and it avoids the read then\n * write race a caller would otherwise have to implement itself.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n */\nexport type AnalyticsControllerUpsertEventFragmentAction = {\n type: `AnalyticsController:upsertEventFragment`;\n handler: AnalyticsController['upsertEventFragment'];\n};\n\n/**\n * Write to an existing event fragment.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * Use {@link upsertEventFragment} when the fragment may not exist yet.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\nexport type AnalyticsControllerUpdateEventFragmentAction = {\n type: `AnalyticsController:updateEventFragment`;\n handler: AnalyticsController['updateEventFragment'];\n};\n\n/**\n * Read an event fragment.\n *\n * @param id - The fragment ID.\n * @returns A read-only copy of the fragment, or `undefined` when no fragment\n * has that ID, the event fragments feature is disabled, or the consent state\n * does not allow capture. Mutating the returned object does not change\n * controller state. Use {@link updateEventFragment} or\n * {@link upsertEventFragment} to write.\n */\nexport type AnalyticsControllerGetEventFragmentByIdAction = {\n type: `AnalyticsController:getEventFragmentById`;\n handler: AnalyticsController['getEventFragmentById'];\n};\n\n/**\n * Discard an event fragment without emitting anything.\n *\n * @param id - The fragment ID.\n */\nexport type AnalyticsControllerDeleteEventFragmentAction = {\n type: `AnalyticsController:deleteEventFragment`;\n handler: AnalyticsController['deleteEventFragment'];\n};\n\n/**\n * Close an event fragment, emitting its closing event and discarding it.\n *\n * The event emitted is `failureEvent` when the journey was abandoned and\n * `successEvent` otherwise. A fragment that does not declare the relevant\n * event name is discarded silently, which is what makes a pure property bag\n * possible.\n *\n * @param id - The fragment ID.\n * @param options - Finalization options.\n * @param options.abandoned - Whether the journey was abandoned.\n * @param options.context - Context merged over the fragment's own context.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\nexport type AnalyticsControllerFinalizeEventFragmentAction = {\n type: `AnalyticsController:finalizeEventFragment`;\n handler: AnalyticsController['finalizeEventFragment'];\n};\n\n/**\n * Opt in to analytics.\n *\n * Records that a consent decision has been made and replays any events that\n * were queued while the user was undecided.\n *\n * When geolocation enrichment is enabled, geolocation is resolved here (once\n * the user has consented) and awaited before the queued events are replayed,\n * so those events are enriched with the resolved location as they are sent.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\nexport type AnalyticsControllerOptInAction = {\n type: `AnalyticsController:optIn`;\n handler: AnalyticsController['optIn'];\n};\n\n/**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events and in-progress event fragments so nothing captured before the\n * decision is ever delivered.\n */\nexport type AnalyticsControllerOptOutAction = {\n type: `AnalyticsController:optOut`;\n handler: AnalyticsController['optOut'];\n};\n\n/**\n * Reset the consent decision back to undecided.\n *\n * Intended for client flows that restart onboarding. Clears the opt-in\n * preference and discards the delivery queue, but preserves any pre-consent\n * events so they can still be replayed if the user opts in again. The user is\n * treated as undecided again.\n *\n * In-progress event fragments are kept only while the undecided user can\n * still accumulate them, and discarded otherwise, so no fragment outlives the\n * consent state that allowed it.\n */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Opt in to marketing analytics.\n *\n * Independent of {@link optIn}. Replays queued marketing events.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\nexport type AnalyticsControllerOptInToMarketingAction = {\n type: `AnalyticsController:optInToMarketing`;\n handler: AnalyticsController['optInToMarketing'];\n};\n\n/**\n * Opt out of marketing analytics.\n *\n * Independent of {@link optOut}. Discards queued marketing events and\n * marketing event fragments.\n */\nexport type AnalyticsControllerOptOutOfMarketingAction = {\n type: `AnalyticsController:optOutOfMarketing`;\n handler: AnalyticsController['optOutOfMarketing'];\n};\n\n/**\n * Reset the marketing consent decision back to undecided.\n *\n * Independent of {@link resetConsentDecision}.\n */\nexport type AnalyticsControllerResetMarketingConsentDecisionAction = {\n type: `AnalyticsController:resetMarketingConsentDecision`;\n handler: AnalyticsController['resetMarketingConsentDecision'];\n};\n\n/**\n * Set the marketing campaign cookie ID.\n *\n * Stores the ID of the marketing campaign cookie (e.g. a Google Analytics\n * client ID) that was active when the user arrived. Pass `null` to clear it.\n * The value is automatically cleared by {@link optOutOfMarketing} and\n * {@link resetMarketingConsentDecision}.\n *\n * @param marketingCampaignCookieId - The marketing campaign cookie ID, or\n * `null` to clear it.\n */\nexport type AnalyticsControllerSetMarketingCampaignCookieIdAction = {\n type: `AnalyticsController:setMarketingCampaignCookieId`;\n handler: AnalyticsController['setMarketingCampaignCookieId'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerTrackErrorAsAnalyticsAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerCreateEventFragmentAction\n | AnalyticsControllerUpsertEventFragmentAction\n | AnalyticsControllerUpdateEventFragmentAction\n | AnalyticsControllerGetEventFragmentByIdAction\n | AnalyticsControllerDeleteEventFragmentAction\n | AnalyticsControllerFinalizeEventFragmentAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction\n | AnalyticsControllerOptInToMarketingAction\n | AnalyticsControllerOptOutOfMarketingAction\n | AnalyticsControllerResetMarketingConsentDecisionAction\n | AnalyticsControllerSetMarketingCampaignCookieIdAction;\n"]}
@@ -368,9 +368,10 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
368
368
  /**
369
369
  * Report a caught error as an `Error Occurred` analytics event.
370
370
  *
371
- * Properties are `source` and a sanitized `error_message`. Whitespace is
372
- * collapsed and the message is capped at 500 characters. Stack traces are
373
- * never included.
371
+ * Properties always include `source` and a sanitized `error_message`.
372
+ * Whitespace is collapsed and the message is capped at 500 characters.
373
+ * Stack traces are never included. Optional `properties` are merged in
374
+ * as well.
374
375
  *
375
376
  * `source` must uniquely identify the error case (for example
376
377
  * `swap-quote-fetch`). Do not reuse one value for every error in a file
@@ -393,8 +394,9 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
393
394
  * @param options - Source and optional volume controls.
394
395
  * @param options.source - Unique discriminator for this error case.
395
396
  * @param options.volumeControls - Optional sampling, dedup, and session cap.
397
+ * @param properties - Optional extra properties.
396
398
  */
397
- trackErrorAsAnalytics(error: unknown, options: TrackErrorAsAnalyticsOptions): void;
399
+ trackErrorAsAnalytics(error: unknown, options: TrackErrorAsAnalyticsOptions, properties?: AnalyticsEventProperties): void;
398
400
  /**
399
401
  * Identify a user for analytics.
400
402
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsController.d.ts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsController.d.ts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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"}
@@ -1221,9 +1221,10 @@ export class AnalyticsController extends BaseController {
1221
1221
  /**
1222
1222
  * Report a caught error as an `Error Occurred` analytics event.
1223
1223
  *
1224
- * Properties are `source` and a sanitized `error_message`. Whitespace is
1225
- * collapsed and the message is capped at 500 characters. Stack traces are
1226
- * never included.
1224
+ * Properties always include `source` and a sanitized `error_message`.
1225
+ * Whitespace is collapsed and the message is capped at 500 characters.
1226
+ * Stack traces are never included. Optional `properties` are merged in
1227
+ * as well.
1227
1228
  *
1228
1229
  * `source` must uniquely identify the error case (for example
1229
1230
  * `swap-quote-fetch`). Do not reuse one value for every error in a file
@@ -1246,13 +1247,14 @@ export class AnalyticsController extends BaseController {
1246
1247
  * @param options - Source and optional volume controls.
1247
1248
  * @param options.source - Unique discriminator for this error case.
1248
1249
  * @param options.volumeControls - Optional sampling, dedup, and session cap.
1250
+ * @param properties - Optional extra properties.
1249
1251
  */
1250
- trackErrorAsAnalytics(error, options) {
1252
+ trackErrorAsAnalytics(error, options, properties) {
1251
1253
  try {
1252
1254
  if (!this.#isCaptureAllowed(this.#purposesFromName(ERROR_OCCURRED_EVENT_NAME))) {
1253
1255
  return;
1254
1256
  }
1255
- const reserved = reserveErrorOccurredEvent(this.#errorTrackingSession, error, options);
1257
+ const reserved = reserveErrorOccurredEvent(this.#errorTrackingSession, error, options, properties);
1256
1258
  if (reserved === undefined) {
1257
1259
  return;
1258
1260
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsController.js","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ConfigRegistryControllerGetStateAction,\n ConfigRegistryControllerStateChangedEvent,\n} from '@metamask/config-registry-controller';\nimport type {\n GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash-es';\nimport { v4 as uuid } from 'uuid';\n\nimport type { AnalyticsControllerMethodActions } from './AnalyticsController-method-action-types.js';\nimport { validateAnalyticsControllerState } from './analyticsControllerStateValidator.js';\nimport { projectLogger as log } from './AnalyticsLogger.js';\nimport type {\n AnalyticsPlatformAdapter,\n AnalyticsDeliveryOptions,\n AnalyticsContext,\n AnalyticsEventProperties,\n AnalyticsLocationContext,\n AnalyticsUserTraits,\n AnalyticsTrackingEvent,\n} from './AnalyticsPlatformAdapter.types.js';\nimport type {\n AnalyticsEventFragment,\n AnalyticsEventFragmentFinalizeOptions,\n AnalyticsEventFragmentOptions,\n AnalyticsEventFragmentPayload,\n AnalyticsEventFragments,\n ReadonlyAnalyticsEventFragment,\n} from './EventFragment.types.js';\nimport { analyticsControllerSelectors } from './selectors.js';\nimport type {\n ErrorTrackingSession,\n TrackErrorAsAnalyticsOptions,\n} from './track-error-as-analytics.js';\nimport {\n ERROR_OCCURRED_EVENT_NAME,\n releaseErrorTrackingReservation,\n reserveErrorOccurredEvent,\n} from './track-error-as-analytics.js';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link AnalyticsController}, 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 = 'AnalyticsController';\n\n/**\n * Maximum age of a persisted event fragment, measured from\n * {@link AnalyticsEventFragment.lastUpdated}.\n *\n * Fragments older than this are discarded during {@link AnalyticsController.init}\n * without emitting a success or failure event. Confirmation journeys that span a\n * restart are expected to resume within this window; abandoned ones must not keep\n * `properties` or `sensitiveProperties` in storage indefinitely.\n */\nexport const EVENT_FRAGMENT_MAX_AGE = 24 * 60 * 60 * 1000;\n\n/**\n * Purposes for which an analytics payload can be used.\n */\nexport const AnalyticsPurpose = {\n Product: 'product',\n Marketing: 'marketing',\n} as const;\n\n/**\n * A purpose for which an analytics payload can be used.\n */\nexport type AnalyticsPurpose =\n (typeof AnalyticsPurpose)[keyof typeof AnalyticsPurpose];\n\n/**\n * Persisted event-purpose classification fetched from config registry.\n */\nexport type AnalyticsEventsConfig = {\n schemaVersion: string;\n version: string;\n timestamp: number;\n events: Record<string, AnalyticsPurpose[]>;\n};\n\n/**\n * Persisted queues on {@link AnalyticsControllerState}.\n */\nconst AnalyticsQueue = {\n EventQueue: 'eventQueue',\n PreConsentEventQueue: 'preConsentEventQueue',\n} as const;\n\ntype AnalyticsQueue = (typeof AnalyticsQueue)[keyof typeof AnalyticsQueue];\n\n// === STATE ===\n\n/**\n * Describes the shape of the state object for {@link AnalyticsController}.\n */\nexport type AnalyticsControllerState = {\n /**\n * Whether the user has opted in to analytics.\n */\n optedIn: boolean;\n\n /**\n * Whether the user has opted in to marketing analytics.\n *\n * Independent of {@link optedIn}. Named events in the remote marketing list\n * are governed only by this flag. Optional for backward compatibility with\n * persisted state that predates this field. Missing values are treated as\n * `false`.\n */\n optedInToMarketing?: boolean;\n\n /**\n * Whether the user has made a marketing consent decision (opted in or opted\n * out). Mirrors {@link consentDecisionMade} for the marketing purpose.\n * Optional for backward compatibility. Missing values are treated as `false`.\n */\n marketingConsentDecisionMade?: boolean;\n\n /**\n * Cached event-purpose configuration. Optional for backward compatibility.\n * Loaded from ConfigRegistryController state during init. Unlisted events\n * default to product-only.\n */\n eventsConfig?: AnalyticsEventsConfig;\n\n /**\n * User's UUIDv4 analytics identifier.\n * This is an identity (unique per user), not a preference.\n * Must be provided by the platform - the controller does not generate it.\n */\n analyticsId: string;\n\n /**\n * Persisted queue of analytics events waiting for delivery acknowledgement.\n * This is only used when event queue persistence is enabled.\n */\n eventQueue?: Record<string, Json>;\n\n /**\n * Whether the user has made a consent decision (opted in or opted out).\n *\n * This distinguishes the \"undecided\" state (e.g. during onboarding, before\n * the user has answered the analytics prompt) from an explicit opt-out.\n * Defaults to `false` and is set to `true` by {@link AnalyticsController.optIn}\n * or {@link AnalyticsController.optOut}, and back to `false` by\n * {@link AnalyticsController.resetConsentDecision}. Optional for backward\n * compatibility with persisted state that predates this field.\n */\n consentDecisionMade?: boolean;\n\n /**\n * Persisted queue of track events ({@link AnalyticsQueuedTrackEvent}) captured\n * while the user is undecided (no consent decision made yet). Replayed on\n * opt-in and cleared on opt-out.\n * Preserved across {@link AnalyticsController.resetConsentDecision} so onboarding\n * restarts do not drop install-time events.\n * This is only used when the pre-consent queue is enabled.\n */\n preConsentEventQueue?: Record<string, Json>;\n\n /**\n * Persisted event fragments ({@link AnalyticsEventFragment}) keyed by\n * fragment ID. Fragments accumulate properties across a user journey and are\n * removed when the journey is finalized or deleted. Fragments that set\n * `persist: true` can survive {@link AnalyticsController.init}, but only\n * while younger than {@link EVENT_FRAGMENT_MAX_AGE}.\n * This is only used when the event fragments feature is enabled.\n */\n eventFragments?: AnalyticsEventFragments;\n\n /**\n * Marketing campaign cookie ID set when the user arrives via a marketing\n * campaign. Cleared automatically when the user calls\n * {@link AnalyticsController.optOutOfMarketing} or\n * {@link AnalyticsController.resetMarketingConsentDecision}. Optional for backward\n * compatibility with persisted state that predates this field.\n */\n marketingCampaignCookieId?: string | null;\n};\n\n/**\n * Event types supported by the persisted analytics event queue.\n */\nexport type AnalyticsQueuedEventType = 'track' | 'identify' | 'view';\n\n/**\n * Base persisted event queue entry.\n */\nexport type AnalyticsQueuedEventBase = {\n /**\n * Event type used to replay the payload with the platform adapter.\n */\n type: AnalyticsQueuedEventType;\n\n /**\n * Stable identifier for the analytics payload.\n */\n messageId: string;\n\n /**\n * Original payload timestamp serialized for persistence.\n */\n timestamp: string;\n\n /**\n * Event purposes captured with the payload, before user consent is applied.\n */\n eventPurposes?: AnalyticsPurpose[];\n\n /**\n * Events config version used to classify the payload.\n */\n eventsConfigVersion?: string;\n};\n\n/**\n * Persisted track event queue entry.\n */\nexport type AnalyticsQueuedTrackEvent = AnalyticsQueuedEventBase & {\n type: 'track';\n eventName: string;\n properties?: AnalyticsEventProperties;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted identify event queue entry.\n */\nexport type AnalyticsQueuedIdentifyEvent = AnalyticsQueuedEventBase & {\n type: 'identify';\n userId: string;\n traits?: AnalyticsUserTraits;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted view event queue entry.\n */\nexport type AnalyticsQueuedViewEvent = AnalyticsQueuedEventBase & {\n type: 'view';\n name: string;\n properties?: AnalyticsEventProperties;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted analytics event queue entry.\n */\nexport type AnalyticsQueuedEvent =\n | AnalyticsQueuedTrackEvent\n | AnalyticsQueuedIdentifyEvent\n | AnalyticsQueuedViewEvent;\n\n/**\n * Persisted analytics event queue keyed by message ID.\n */\nexport type AnalyticsEventQueue = Record<string, AnalyticsQueuedEvent>;\n\n/**\n * Returns default values for AnalyticsController state.\n *\n * Note: analyticsId is NOT included - it's an identity that must be\n * provided by the platform (generated once on first run, then persisted).\n *\n * @returns Default state without analyticsId\n */\nexport function getDefaultAnalyticsControllerState(): Omit<\n AnalyticsControllerState,\n 'analyticsId'\n> {\n return {\n optedIn: false,\n consentDecisionMade: false,\n optedInToMarketing: false,\n marketingConsentDecisionMade: false,\n marketingCampaignCookieId: null,\n };\n}\n\n/**\n * The metadata for each property in {@link AnalyticsControllerState}.\n *\n * Both `optedIn` and `analyticsId` are persisted (`persist: true`).\n * The platform must supply a valid UUIDv4 `analyticsId` on first run.\n */\nconst analyticsControllerMetadata = {\n optedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n optedInToMarketing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n marketingConsentDecisionMade: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n eventsConfig: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n analyticsId: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n eventQueue: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n consentDecisionMade: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n preConsentEventQueue: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n eventFragments: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n marketingCampaignCookieId: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'trackErrorAsAnalytics',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\n 'optInToMarketing',\n 'optOutOfMarketing',\n 'resetMarketingConsentDecision',\n 'setMarketingCampaignCookieId',\n 'createEventFragment',\n 'upsertEventFragment',\n 'updateEventFragment',\n 'getEventFragmentById',\n 'deleteEventFragment',\n 'finalizeEventFragment',\n] as const;\n\n/**\n * Returns the state of the {@link AnalyticsController}.\n */\nexport type AnalyticsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AnalyticsControllerState\n>;\n\n/**\n * Actions that {@link AnalyticsControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsControllerActions =\n | AnalyticsControllerGetStateAction\n | AnalyticsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link AnalyticsControllerMessenger} calls.\n */\ntype AllowedActions =\n | GeolocationControllerGetGeolocationDataAction\n | ConfigRegistryControllerGetStateAction;\n\n/**\n * Event emitted when the state of the {@link AnalyticsController} changes.\n */\nexport type AnalyticsControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AnalyticsControllerState\n>;\n\n/**\n * Events that {@link AnalyticsControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsControllerEvents = AnalyticsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link AnalyticsControllerMessenger} subscribes to.\n */\ntype AllowedEvents = ConfigRegistryControllerStateChangedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link AnalyticsController}.\n */\nexport type AnalyticsControllerMessenger = Messenger<\n typeof controllerName,\n AnalyticsControllerActions | AllowedActions,\n AnalyticsControllerEvents | AllowedEvents\n>;\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * Initial state accepted by {@link AnalyticsController}.\n *\n * `analyticsId` is required. All other fields are optional and filled from\n * {@link getDefaultAnalyticsControllerState} when omitted.\n */\nexport type AnalyticsControllerInitialState =\n Partial<AnalyticsControllerState> &\n Pick<AnalyticsControllerState, 'analyticsId'>;\n\n/**\n * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Other fields are optional; defaults come from\n * {@link getDefaultAnalyticsControllerState}. The platform is responsible\n * for generating the ID on first run. It is then persisted with controller\n * state when using a persisted store.\n */\n state: AnalyticsControllerInitialState;\n /**\n * Messenger used to communicate with BaseController and other controllers.\n */\n messenger: AnalyticsControllerMessenger;\n /**\n * Platform adapter implementation for tracking events.\n */\n platformAdapter: AnalyticsPlatformAdapter;\n\n /**\n * Whether the anonymous events feature is enabled.\n *\n * @default false\n */\n isAnonymousEventsFeatureEnabled?: boolean;\n\n /**\n * Whether analytics event queue persistence is enabled.\n *\n * When enabled, AnalyticsController persists each platform adapter payload\n * until the adapter reports successful delivery.\n *\n * @default false\n */\n isEventQueuePersistenceEnabled?: boolean;\n\n /**\n * Whether the pre-consent event queue is enabled.\n *\n * When enabled, track events received while the user is undecided\n * (no consent decision made yet) are persisted and replayed on opt-in,\n * or dropped on opt-out. When disabled, such events are dropped immediately,\n * preserving the legacy behavior.\n *\n * @default false\n */\n isPreConsentQueueEnabled?: boolean;\n\n /**\n * Whether geolocation enrichment is enabled.\n *\n * When enabled, {@link AnalyticsController.init} resolves the user's\n * country, region, and timezone via `GeolocationController:getGeolocationData`\n * and attaches them to `context.location` on non-anonymous payloads.\n * Compositions must register that action when this is enabled. When disabled,\n * the controller never calls the geolocation action and events are delivered\n * without location.\n *\n * @default false\n */\n isGeolocationEnabled?: boolean;\n\n /**\n * Whether the event fragments feature is enabled.\n *\n * When enabled, clients can accumulate analytics properties across a user\n * journey with {@link AnalyticsController.createEventFragment} and friends,\n * as long as the consent state allows analytics to be captured. When\n * disabled, every fragment method is a logged no-op and no fragment is ever\n * written to state.\n *\n * @default false\n */\n isEventFragmentsEnabled?: boolean;\n};\n\n/**\n * Returns whether a value is a non-array object.\n *\n * @param value - The value to check.\n * @returns True if the value is a record.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isAnalyticsPurpose(value: unknown): value is AnalyticsPurpose {\n return (\n value === AnalyticsPurpose.Product || value === AnalyticsPurpose.Marketing\n );\n}\n\nfunction isEventPurposesRecord(\n value: unknown,\n): value is Record<string, AnalyticsPurpose[]> {\n return (\n isRecord(value) &&\n Object.values(value).every(\n (purposes) =>\n Array.isArray(purposes) &&\n purposes.length > 0 &&\n purposes.every(isAnalyticsPurpose),\n )\n );\n}\n\nfunction isAnalyticsEventsConfig(\n value: unknown,\n): value is AnalyticsEventsConfig {\n return (\n isRecord(value) &&\n typeof value.schemaVersion === 'string' &&\n typeof value.version === 'string' &&\n typeof value.timestamp === 'number' &&\n isEventPurposesRecord(value.events)\n );\n}\n\n/**\n * Returns whether a JSON value is a non-array object.\n *\n * @param value - The value to check.\n * @returns True if the value is a JSON record.\n */\nfunction isJsonRecord(value: Json | undefined): value is Record<string, Json> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Builds the analytics location context from geolocation data, keeping only\n * the fields the geolocation API was able to determine.\n *\n * @param geolocation - The geolocation data to convert.\n * @returns The location context, or `undefined` when no field is known.\n */\nfunction buildLocationContext(\n geolocation: GeolocationData,\n): AnalyticsLocationContext | undefined {\n const locationContext: AnalyticsLocationContext = {\n ...(geolocation.country === null\n ? {}\n : { country_code: geolocation.country }),\n ...(geolocation.region === null ? {} : { region: geolocation.region }),\n ...(geolocation.timezone === null\n ? {}\n : { timezone: geolocation.timezone }),\n };\n\n return Object.keys(locationContext).length === 0\n ? undefined\n : locationContext;\n}\n\n/**\n * Returns whether a value is a valid persisted analytics event.\n *\n * @param value - The value to check.\n * @returns True if the value is a queued analytics event.\n */\nfunction isAnalyticsQueuedEvent(value: unknown): value is AnalyticsQueuedEvent {\n if (!isRecord(value)) {\n return false;\n }\n\n if (\n typeof value.messageId !== 'string' ||\n typeof value.timestamp !== 'string' ||\n (value.eventPurposes !== undefined &&\n (!Array.isArray(value.eventPurposes) ||\n !value.eventPurposes.every(isAnalyticsPurpose))) ||\n (value.eventsConfigVersion !== undefined &&\n typeof value.eventsConfigVersion !== 'string')\n ) {\n return false;\n }\n\n if (value.type === 'track') {\n return (\n typeof value.eventName === 'string' &&\n (value.properties === undefined || isRecord(value.properties)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n if (value.type === 'identify') {\n return (\n typeof value.userId === 'string' &&\n (value.traits === undefined || isRecord(value.traits)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n if (value.type === 'view') {\n return (\n typeof value.name === 'string' &&\n (value.properties === undefined || isRecord(value.properties)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n return false;\n}\n\n/**\n * Returns whether a value is a valid persisted event fragment.\n *\n * @param value - The value to check.\n * @returns True if the value is an event fragment.\n */\nfunction isAnalyticsEventFragment(\n value: unknown,\n): value is AnalyticsEventFragment {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n typeof value.id === 'string' &&\n typeof value.createdAt === 'number' &&\n typeof value.lastUpdated === 'number' &&\n isRecord(value.properties) &&\n isRecord(value.sensitiveProperties) &&\n (value.initialEvent === undefined ||\n typeof value.initialEvent === 'string') &&\n (value.successEvent === undefined ||\n typeof value.successEvent === 'string') &&\n (value.failureEvent === undefined ||\n typeof value.failureEvent === 'string') &&\n (value.eventPurposes === undefined ||\n isEventPurposesRecord(value.eventPurposes)) &&\n (value.eventsConfigVersion === undefined ||\n typeof value.eventsConfigVersion === 'string') &&\n (value.context === undefined || isRecord(value.context)) &&\n (value.persist === undefined || typeof value.persist === 'boolean')\n );\n}\n\n/**\n * Merges a payload into an event fragment.\n *\n * `properties`, `sensitiveProperties` and `context` are merged one level deep,\n * so a key written twice is replaced rather than combined. This keeps array\n * values predictable: writing a shorter array replaces the longer one instead\n * of leaving stale trailing entries behind.\n *\n * @param fragment - The fragment to merge into.\n * @param payload - The payload to merge.\n * @returns A new fragment with the payload applied.\n */\nfunction mergeEventFragment(\n fragment: AnalyticsEventFragment,\n payload: AnalyticsEventFragmentPayload,\n): AnalyticsEventFragment {\n const context = mergeEventFragmentContext(fragment.context, payload.context);\n\n return {\n ...fragment,\n properties: { ...fragment.properties, ...(payload.properties ?? {}) },\n sensitiveProperties: {\n ...fragment.sensitiveProperties,\n ...(payload.sensitiveProperties ?? {}),\n },\n ...(context === undefined ? {} : { context }),\n lastUpdated: Date.now(),\n };\n}\n\n/**\n * Merges two optional analytics contexts.\n *\n * @param base - The context to merge into.\n * @param override - The context whose fields win. When omitted, `base` is kept.\n * @returns The merged context, or `undefined` when both sides are unset.\n */\nfunction mergeEventFragmentContext(\n base: AnalyticsContext | undefined,\n override: AnalyticsContext | undefined,\n): AnalyticsContext | undefined {\n if (override === undefined) {\n return base;\n }\n\n return { ...(base ?? {}), ...override };\n}\n\n/**\n * The AnalyticsController manages analytics tracking across platforms (Mobile/Extension).\n * It provides a unified interface for tracking events, identifying users, and managing\n * analytics preferences while delegating platform-specific implementation to an\n * {@link AnalyticsPlatformAdapter}.\n *\n * This controller follows the MetaMask controller pattern and integrates with the\n * messenger system to allow other controllers and components to track analytics events.\n * It delegates platform-specific implementation to an {@link AnalyticsPlatformAdapter}.\n *\n * The controller persists `optedIn` and `analyticsId` when composed with a persisted\n * store. The platform must supply a valid `analyticsId` on first launch.\n */\nexport class AnalyticsController extends BaseController<\n 'AnalyticsController',\n AnalyticsControllerState,\n AnalyticsControllerMessenger\n> {\n readonly #platformAdapter: AnalyticsPlatformAdapter;\n\n readonly #isAnonymousEventsFeatureEnabled: boolean;\n\n readonly #isEventQueuePersistenceEnabled: boolean;\n\n readonly #isPreConsentQueueEnabled: boolean;\n\n readonly #isGeolocationEnabled: boolean;\n\n readonly #isEventFragmentsEnabled: boolean;\n\n /**\n * In-memory event-purpose lookup from persisted state.\n */\n #eventPurposes: Map<string, AnalyticsPurpose[]>;\n\n #eventsConfigVersion: string | undefined;\n\n /**\n * The in-flight (or settled) initialization promise. Set on the first\n * {@link init} call and returned by subsequent calls so overlapping callers\n * await the same work rather than observing a premature completion.\n */\n #initPromise: Promise<void> | undefined;\n\n /**\n * The in-flight (or settled) geolocation resolution, if any. Its presence\n * marks that resolution has been started, so it runs at most once.\n */\n #locationResolvePromise: Promise<void> | undefined;\n\n #locationContext: AnalyticsLocationContext | undefined;\n\n /**\n * In-memory sampling, deduplication, and session-cap state for\n * {@link trackErrorAsAnalytics}. Not persisted.\n */\n readonly #errorTrackingSession: ErrorTrackingSession = {\n emittedCount: 0,\n lastEmittedAtBySignature: new Map(),\n };\n\n /**\n * Constructs an AnalyticsController instance.\n *\n * @param options - Controller options\n * @param options.state - Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Other fields are optional and default via `getDefaultAnalyticsControllerState()`.\n * @param options.messenger - Messenger used to communicate with BaseController\n * @param options.platformAdapter - Platform adapter implementation for tracking\n * @param options.isAnonymousEventsFeatureEnabled - Whether the anonymous events feature is enabled\n * @param options.isEventQueuePersistenceEnabled - Whether analytics event queue persistence is enabled\n * @param options.isPreConsentQueueEnabled - Whether the pre-consent event queue is enabled\n * @param options.isGeolocationEnabled - Whether geolocation enrichment is enabled\n * @param options.isEventFragmentsEnabled - Whether the event fragments feature is enabled\n * @throws Error if state.analyticsId is missing or not a valid UUIDv4\n * @remarks After construction, call {@link AnalyticsController.init} to complete initialization.\n */\n constructor({\n state,\n messenger,\n platformAdapter,\n isAnonymousEventsFeatureEnabled = false,\n isEventQueuePersistenceEnabled = false,\n isPreConsentQueueEnabled = false,\n isGeolocationEnabled = false,\n isEventFragmentsEnabled = false,\n }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\n };\n const eventsConfig = isAnalyticsEventsConfig(initialState.eventsConfig)\n ? initialState.eventsConfig\n : undefined;\n if (eventsConfig === undefined) {\n delete initialState.eventsConfig;\n } else {\n initialState.eventsConfig = eventsConfig;\n }\n\n validateAnalyticsControllerState(\n initialState,\n platformAdapter.skipUUIDv4Check === true,\n );\n\n super({\n name: controllerName,\n metadata: analyticsControllerMetadata,\n state: initialState,\n messenger,\n });\n\n this.#isAnonymousEventsFeatureEnabled = isAnonymousEventsFeatureEnabled;\n this.#isEventQueuePersistenceEnabled = isEventQueuePersistenceEnabled;\n this.#isPreConsentQueueEnabled = isPreConsentQueueEnabled;\n this.#isGeolocationEnabled = isGeolocationEnabled;\n this.#isEventFragmentsEnabled = isEventFragmentsEnabled;\n this.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\n this.#locationResolvePromise = undefined;\n this.#eventPurposes = new Map(Object.entries(eventsConfig?.events ?? {}));\n this.#eventsConfigVersion = eventsConfig?.version;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n log('AnalyticsController initialized and ready', {\n enabled: analyticsControllerSelectors.selectEnabled(this.state),\n optedIn: this.state.optedIn,\n optedInToMarketing: this.state.optedInToMarketing === true,\n marketingConsentDecisionMade:\n this.state.marketingConsentDecisionMade === true,\n consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\n eventFragmentsEnabled: this.#isEventFragmentsEnabled,\n });\n }\n\n /**\n * Initialize the controller by calling the platform adapter's\n * onSetupCompleted lifecycle hook and replaying any queued events. This\n * method must be called after construction to complete the setup process.\n *\n * When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation\n * is resolved only for a user who is already opted in to product or marketing\n * analytics. For users undecided or opted out of both purposes, it is\n * deferred until they opt in to either one (see {@link optIn} and\n * {@link optInToMarketing}), so a user's location is never requested before\n * they consent to analytics. In either case the `GeolocationController` and its\n * `GeolocationController:getGeolocationData` action must be registered before\n * resolution occurs, or enrichment is skipped for the session (a message is\n * logged, see {@link #resolveLocationContext}).\n *\n * Safe to call more than once: the first call performs initialization and\n * subsequent calls return the same in-flight (or settled) promise.\n *\n * @returns A promise that resolves once initialization has completed.\n */\n init(): Promise<void> {\n // Cache the in-flight promise so repeated or overlapping calls share a\n // single initialization and all await the same completion (rather than an\n // early call observing a finished init while work is still pending).\n this.#initPromise ??= this.#performInit();\n return this.#initPromise;\n }\n\n /**\n * Performs the one-time initialization work: resolve geolocation, run the\n * platform adapter's onSetupCompleted lifecycle hook, then replay any queued\n * and pre-consent events.\n */\n async #performInit(): Promise<void> {\n // Snapshot fragment IDs and createdAt before any awaited init work so\n // reconciliation can tell previous-session leftovers from fragments\n // created or replaced while init runs.\n const initEventFragmentSnapshot = new Map<string, number>();\n for (const [id, fragment] of Object.entries(\n this.state.eventFragments ?? {},\n )) {\n if (\n isAnalyticsEventFragment(fragment) &&\n fragment.id === id &&\n typeof fragment.createdAt === 'number'\n ) {\n initEventFragmentSnapshot.set(id, fragment.createdAt);\n }\n }\n\n await this.#fetchEventsConfig();\n\n this.messenger.subscribe(\n 'ConfigRegistryController:stateChanged',\n () => {\n this.#fetchEventsConfig().catch(\n /* istanbul ignore next */ () => undefined,\n );\n },\n (state) => state.configs.eventsConfig?.version,\n );\n\n // Resolve geolocation only when the user is already opted in to product or\n // marketing analytics. For undecided or opted-out users it is deferred to\n // {@link optIn} / {@link optInToMarketing}. Awaited so that an already-opted-in\n // session has location available before events replay.\n await this.#maybeResolveLocation();\n\n // Call onSetupCompleted lifecycle hook after initialization\n // State is already validated, so analyticsId is guaranteed to be a valid UUIDv4\n try {\n this.#platformAdapter.onSetupCompleted(this.state.analyticsId);\n } catch (error) {\n // Log error but don't throw - adapter setup failure shouldn't break controller\n log('Error calling platformAdapter.onSetupCompleted', error);\n }\n\n this.#replayQueuedEvents();\n this.#replayPreConsentEvents();\n this.#reconcileEventFragments(initEventFragmentSnapshot);\n }\n\n /**\n * Start resolving the geolocation context if warranted, and return the\n * in-flight (or settled) resolution so callers can await it. No-op unless\n * enrichment is enabled, the user is opted in to product or marketing\n * analytics, and a resolution has not already been started. Deferring\n * resolution until consent ensures a user's location is never requested before\n * they consent to analytics (for example, during onboarding).\n *\n * Resolution runs at most once per controller session: the settled promise\n * is retained, so the outcome, including a failure (see\n * {@link #resolveLocationContext}) is not retried, and events are delivered\n * without location for the rest of the session.\n *\n * @returns The geolocation resolution promise, or `undefined` when no\n * resolution is warranted.\n */\n #maybeResolveLocation(): Promise<void> | undefined {\n if (\n this.#isGeolocationEnabled &&\n this.#locationResolvePromise === undefined &&\n (this.state.optedIn || this.state.optedInToMarketing === true)\n ) {\n this.#locationResolvePromise = this.#resolveLocationContext();\n }\n\n return this.#locationResolvePromise;\n }\n\n async #resolveLocationContext(): Promise<void> {\n try {\n const geolocation = await this.messenger.call(\n 'GeolocationController:getGeolocationData',\n );\n\n this.#locationContext = buildLocationContext(geolocation);\n } catch (error) {\n // A common cause is the GeolocationController not being registered before\n // resolution runs (at init for an opted-in user, otherwise at opt-in).\n // Name it here so the failure is diagnosable, since enrichment is\n // otherwise skipped silently for the session.\n log(\n 'Failed to resolve geolocation for analytics enrichment; events will be sent without location. Ensure the GeolocationController is registered and initialized before the user opts in when geolocation is enabled.',\n error,\n );\n }\n }\n\n /**\n * Merge the resolved location context into a caller-provided context.\n *\n * Caller-provided `location` fields are preserved, but the fields the\n * controller resolves take precedence over them.\n *\n * @param context - Optional caller-provided context.\n * @returns The context enriched with location, or the original context when\n * no location is known.\n */\n #withLocationContext(\n context?: AnalyticsContext,\n ): AnalyticsContext | undefined {\n if (!this.#locationContext) {\n return context;\n }\n\n const callerLocation = context?.location;\n\n return {\n ...context,\n location: {\n ...(isJsonRecord(callerLocation) ? callerLocation : {}),\n ...this.#locationContext,\n },\n };\n }\n\n /**\n * Stamp the purposes currently allowed for a payload using Segment's consent\n * context shape.\n *\n * @param purposes - Purposes for which the payload is eligible.\n * @param context - Optional caller-provided context.\n * @param version - Config version captured with the payload.\n * @returns Context with allowed purpose preferences.\n */\n #withConsentContext(\n purposes: AnalyticsPurpose[],\n context?: AnalyticsContext,\n version?: string,\n ): AnalyticsContext {\n const preferences = this.#allowedPurposePreferences(purposes);\n const {\n consent: existingConsent,\n eventsConfigVersion: _ignoredVersion,\n ...unmanagedContext\n } = context ?? {};\n const consent: Record<string, Json> = isJsonRecord(existingConsent)\n ? existingConsent\n : {};\n const existingCategoryPreferences = isJsonRecord(\n consent.categoryPreferences,\n )\n ? consent.categoryPreferences\n : {};\n\n return {\n ...unmanagedContext,\n consent: {\n ...consent,\n categoryPreferences: {\n ...existingCategoryPreferences,\n ...preferences,\n },\n },\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n }\n\n /**\n * Load event-purpose configuration from ConfigRegistryController state.\n * Updates in-memory purposes map and persisted state when the version differs.\n */\n async #fetchEventsConfig(): Promise<void> {\n const eventsConfigState = this.messenger.call(\n 'ConfigRegistryController:getState',\n );\n\n const remoteEventsConfig = eventsConfigState.configs.eventsConfig;\n if (!remoteEventsConfig) {\n return;\n }\n\n const candidate: AnalyticsEventsConfig = {\n schemaVersion: remoteEventsConfig.schemaVersion,\n version: remoteEventsConfig.version,\n timestamp: remoteEventsConfig.timestamp,\n events: remoteEventsConfig.events as Record<string, AnalyticsPurpose[]>,\n };\n\n if (\n !isAnalyticsEventsConfig(candidate) ||\n candidate.version === this.#eventsConfigVersion\n ) {\n return;\n }\n\n this.#eventPurposes = new Map(Object.entries(candidate.events));\n this.#eventsConfigVersion = candidate.version;\n this.update((state) => {\n state.eventsConfig = candidate;\n });\n }\n\n #purposesFromName(name: string): AnalyticsPurpose[] {\n return [...(this.#eventPurposes.get(name) ?? [AnalyticsPurpose.Product])];\n }\n\n #purposesFromQueuedEvent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsPurpose[] {\n if (queuedEvent.type === 'identify') {\n return [AnalyticsPurpose.Product];\n }\n\n if (queuedEvent.eventPurposes !== undefined) {\n return [...queuedEvent.eventPurposes];\n }\n\n return this.#purposesFromName(\n queuedEvent.type === 'view' ? queuedEvent.name : queuedEvent.eventName,\n );\n }\n\n #eventNamesFromFragment(\n fragment: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent'\n >,\n ): string[] {\n return [\n fragment.initialEvent,\n fragment.successEvent,\n fragment.failureEvent,\n ].filter((name): name is string => typeof name === 'string');\n }\n\n #purposesFromFragmentEvent(\n fragment: Pick<AnalyticsEventFragment, 'eventPurposes'>,\n name: string,\n ): AnalyticsPurpose[] {\n return [\n ...(fragment.eventPurposes?.[name] ?? this.#purposesFromName(name)),\n ];\n }\n\n #purposesFromFragment(\n fragment: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent' | 'eventPurposes'\n >,\n ): AnalyticsPurpose[] {\n const names = this.#eventNamesFromFragment(fragment);\n if (names.length === 0) {\n // Nameless property bags have no event to classify. Treat them as\n // eligible for any purpose so they can accumulate when either consent\n // allows capture.\n return Object.values(AnalyticsPurpose);\n }\n\n return [\n ...new Set(\n names.flatMap((name) =>\n this.#purposesFromFragmentEvent(fragment, name),\n ),\n ),\n ];\n }\n\n #consent(purpose: AnalyticsPurpose): {\n optedIn: boolean;\n decisionMade: boolean;\n } {\n return purpose === AnalyticsPurpose.Marketing\n ? {\n optedIn: this.state.optedInToMarketing === true,\n decisionMade: this.state.marketingConsentDecisionMade === true,\n }\n : {\n optedIn: this.state.optedIn,\n decisionMade: this.state.consentDecisionMade === true,\n };\n }\n\n #allowedPurposePreferences(purposes: AnalyticsPurpose[]): {\n product: boolean;\n marketing: boolean;\n } {\n return {\n product:\n purposes.includes(AnalyticsPurpose.Product) &&\n this.#consent(AnalyticsPurpose.Product).optedIn,\n marketing:\n purposes.includes(AnalyticsPurpose.Marketing) &&\n this.#consent(AnalyticsPurpose.Marketing).optedIn,\n };\n }\n\n #hasAllowedPurpose(purposes: AnalyticsPurpose[]): boolean {\n const { product, marketing } = this.#allowedPurposePreferences(purposes);\n return product || marketing;\n }\n\n #hasUndecidedPurpose(purposes: AnalyticsPurpose[]): boolean {\n return purposes.some((purpose) => !this.#consent(purpose).decisionMade);\n }\n\n #isCaptureAllowed(purposes: AnalyticsPurpose[]): boolean {\n return (\n this.#hasAllowedPurpose(purposes) ||\n (this.#isPreConsentQueueEnabled && this.#hasUndecidedPurpose(purposes))\n );\n }\n\n #replaceQueue(field: AnalyticsQueue, nextQueue: Record<string, Json>): void {\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state[field] = nextQueue as never;\n });\n }\n\n /**\n * Send final track payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param eventName - The name of the event.\n * @param properties - Optional event properties.\n * @param context - Optional platform-specific context.\n * @param purposes - Capture-time purposes for the event.\n * @param version - Capture-time events config version.\n */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties: AnalyticsEventProperties | undefined,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n const isAllowed = this.#hasAllowedPurpose(purposes);\n const contextWithConsent = this.#withConsentContext(\n purposes,\n context,\n version,\n );\n\n // Direct delivery: enabled and not persisting.\n if (isAllowed && !this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.track(eventName, properties, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedTrackEvent = {\n type: 'track',\n eventName,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(properties === undefined ? {} : { properties }),\n context: contextWithConsent,\n eventPurposes: purposes,\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n\n if (!isAllowed) {\n this.#enqueuePreConsentEvent(queuedEvent);\n return;\n }\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Send final identify payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param userId - The user ID.\n * @param traits - Optional user traits.\n * @param context - Optional platform-specific context.\n */\n #sendOrQueueIdentifyEvent(\n userId: string,\n traits?: AnalyticsUserTraits,\n context?: AnalyticsContext,\n ): void {\n const purposes = [AnalyticsPurpose.Product];\n const contextWithConsent = this.#withConsentContext(purposes, context);\n\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedIdentifyEvent = {\n type: 'identify',\n userId,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(traits === undefined ? {} : { traits }),\n context: contextWithConsent,\n eventPurposes: purposes,\n };\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Send final view payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param name - The view name.\n * @param properties - Optional view properties.\n * @param context - Optional platform-specific context.\n * @param purposes - Capture-time purposes for the view.\n * @param version - Capture-time events config version.\n */\n #sendOrQueueViewEvent(\n name: string,\n properties: AnalyticsEventProperties | undefined,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n const isAllowed = this.#hasAllowedPurpose(purposes);\n const contextWithConsent = this.#withConsentContext(\n purposes,\n context,\n version,\n );\n\n if (isAllowed && !this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedViewEvent = {\n type: 'view',\n name,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(properties === undefined ? {} : { properties }),\n context: contextWithConsent,\n eventPurposes: purposes,\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n\n if (!isAllowed) {\n this.#enqueuePreConsentEvent(queuedEvent);\n return;\n }\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Add an analytics event to the queue and send it.\n *\n * @param queuedEvent - The event to enqueue and deliver.\n */\n #enqueueEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const eventQueue: Record<string, Json> = {\n ...(this.state.eventQueue ?? {}),\n [queuedEvent.messageId]: queuedEvent,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventQueue = eventQueue as never;\n });\n\n this.#sendQueuedEvent(queuedEvent);\n }\n\n /**\n * Send a queued event through the platform adapter.\n *\n * @param queuedEvent - The queued event to deliver.\n */\n #sendQueuedEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const timestamp = new Date(queuedEvent.timestamp);\n\n if (Number.isNaN(timestamp.getTime())) {\n log('Dropping queued analytics event with invalid timestamp', {\n messageId: queuedEvent.messageId,\n });\n this.#removeQueuedEvent(queuedEvent.messageId);\n return;\n }\n\n const options: AnalyticsDeliveryOptions = {\n messageId: queuedEvent.messageId,\n timestamp,\n callback: (error?: unknown) => {\n if (error) {\n log('Queued analytics event delivery failed', {\n messageId: queuedEvent.messageId,\n error,\n });\n }\n\n this.#removeQueuedEvent(queuedEvent.messageId);\n },\n };\n\n try {\n if (queuedEvent.type === 'track') {\n this.#platformAdapter.track(\n queuedEvent.eventName,\n cloneDeep(queuedEvent.properties),\n cloneDeep(queuedEvent.context),\n options,\n );\n } else if (queuedEvent.type === 'identify') {\n this.#platformAdapter.identify(\n queuedEvent.userId,\n cloneDeep(queuedEvent.traits),\n cloneDeep(queuedEvent.context),\n options,\n );\n } else {\n this.#platformAdapter.view(\n queuedEvent.name,\n cloneDeep(queuedEvent.properties),\n cloneDeep(queuedEvent.context),\n options,\n );\n }\n } catch (error) {\n log('Error sending queued analytics event', {\n messageId: queuedEvent.messageId,\n error,\n });\n }\n }\n\n /**\n * Replay persisted analytics events.\n */\n #replayQueuedEvents(): void {\n if (!this.#isEventQueuePersistenceEnabled || !this.state.eventQueue) {\n return;\n }\n\n const remainingQueue: Record<string, Json> = {};\n const eventsToSend: AnalyticsQueuedEvent[] = [];\n\n for (const [messageId, queuedEvent] of Object.entries(\n this.state.eventQueue,\n )) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued analytics event', { messageId });\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n\n if (this.#hasAllowedPurpose(purposes)) {\n const refreshedEvent = this.#refreshQueuedEventConsent(queuedEvent);\n remainingQueue[messageId] = refreshedEvent;\n eventsToSend.push(refreshedEvent);\n }\n }\n\n this.#replaceQueue(AnalyticsQueue.EventQueue, remainingQueue);\n\n for (const queuedEvent of eventsToSend) {\n this.#sendQueuedEvent(queuedEvent);\n }\n }\n\n /**\n * Remove a queued analytics event.\n *\n * @param messageId - The queued event message ID.\n */\n #removeQueuedEvent(messageId: string): void {\n const currentEventQueue = this.state.eventQueue;\n\n if (\n !currentEventQueue ||\n !Object.prototype.hasOwnProperty.call(currentEventQueue, messageId)\n ) {\n return;\n }\n\n const { [messageId]: _deletedEvent, ...eventQueue } = currentEventQueue;\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventQueue = eventQueue as never;\n });\n }\n\n #refreshQueuedEventConsent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsQueuedEvent {\n // Refresh only the consent stamp. Capture-time `eventPurposes` and\n // `eventsConfigVersion` stay as a pair and are never rewritten here.\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n const preferences = this.#allowedPurposePreferences(purposes);\n const existingPreferences = isJsonRecord(queuedEvent.context?.consent)\n ? queuedEvent.context.consent.categoryPreferences\n : undefined;\n\n if (\n isJsonRecord(existingPreferences) &&\n existingPreferences.product === preferences.product &&\n existingPreferences.marketing === preferences.marketing\n ) {\n return queuedEvent;\n }\n\n return {\n ...queuedEvent,\n context: this.#withConsentContext(\n purposes,\n queuedEvent.context,\n queuedEvent.eventsConfigVersion,\n ),\n };\n }\n\n /**\n * Prune a queue after a consent change.\n *\n * For {@link AnalyticsQueue.EventQueue}, entries are kept only while at least\n * one capture-time purpose is opted in, and their consent stamp is refreshed.\n * For {@link AnalyticsQueue.PreConsentEventQueue}, entries are kept while at\n * least one purpose is still allowed or undecided.\n *\n * @param field - The queue to prune.\n */\n #pruneQueueForConsent(field: AnalyticsQueue): void {\n const queue = this.state[field];\n if (!queue) {\n return;\n }\n\n const nextQueue: Record<string, Json> = {};\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n const isAllowed = this.#hasAllowedPurpose(purposes);\n\n if (field === AnalyticsQueue.EventQueue) {\n if (isAllowed) {\n nextQueue[messageId] = this.#refreshQueuedEventConsent(queuedEvent);\n }\n }\n\n if (field === AnalyticsQueue.PreConsentEventQueue) {\n if (isAllowed || this.#hasUndecidedPurpose(purposes)) {\n nextQueue[messageId] = queuedEvent;\n }\n }\n }\n\n this.#replaceQueue(field, nextQueue);\n }\n\n /**\n * Add an event to the pre-consent queue without delivering it.\n *\n * @param queuedEvent - The event to hold until the user opts in.\n */\n #enqueuePreConsentEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const preConsentEventQueue: Record<string, Json> = {\n ...(this.state.preConsentEventQueue ?? {}),\n [queuedEvent.messageId]: queuedEvent,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Enrich a pre-consent event with the geolocation resolved on opt-in.\n *\n * Pre-consent events are captured while geolocation is not yet resolved, so\n * they are re-enriched here as they replay. Anonymous track payloads are left\n * untouched, since they must never carry location.\n *\n * @param queuedEvent - The queued pre-consent event.\n * @returns The event with its context enriched, or the event unchanged when\n * enrichment does not apply.\n */\n #enrichPreConsentEvent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsQueuedEvent {\n if (\n queuedEvent.type === 'track' &&\n queuedEvent.properties?.anonymous === true\n ) {\n return queuedEvent;\n }\n\n const context = this.#withLocationContext(queuedEvent.context);\n\n return {\n ...queuedEvent,\n ...(context === undefined ? {} : { context }),\n };\n }\n\n /**\n * Replay eligible pre-consent events against current consent.\n *\n * Allowed entries are replayed, undecided entries are kept for later, and\n * entries with no remaining allowed or undecided purpose are dropped. The\n * keep set is written before replay so events cannot be re-queued or\n * replayed twice.\n *\n * If the pre-consent queue is disabled, any stale persisted entries (e.g. from\n * a previous session where it was enabled) are dropped so they can never be\n * replayed.\n */\n #replayPreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.preConsentEventQueue = {} as never;\n });\n return;\n }\n\n const keep: Record<string, Json> = {};\n const replay: AnalyticsQueuedEvent[] = [];\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n\n if (this.#hasAllowedPurpose(purposes)) {\n replay.push(queuedEvent);\n } else if (this.#hasUndecidedPurpose(purposes)) {\n keep[messageId] = queuedEvent;\n }\n }\n\n this.#replaceQueue(AnalyticsQueue.PreConsentEventQueue, keep);\n\n for (const queuedEvent of replay) {\n const eventToReplay = this.#refreshQueuedEventConsent(\n this.#enrichPreConsentEvent(queuedEvent),\n );\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(eventToReplay);\n } else {\n this.#sendQueuedEvent(eventToReplay);\n }\n }\n }\n\n /**\n * Reconcile persisted event fragments on initialization.\n *\n * A fragment describes a journey that was in progress when the previous\n * session ended. Only fragments that opted into `persist` and are younger\n * than {@link EVENT_FRAGMENT_MAX_AGE} can be resumed, so the rest are\n * discarded. Nothing is emitted: a journey that never reached its own\n * finalization is not a failure, just an unfinished one.\n *\n * If the feature is disabled (e.g. a previous session had it enabled), or the\n * consent state no longer allows capture for any of a fragment's purposes,\n * those fragments are dropped so none of them can linger.\n *\n * Non-persistent fragments are dropped only when their ID and `createdAt`\n * match a fragment present at the start of {@link init}. Fragments created\n * or replaced while init is in flight are kept so a slow startup path cannot\n * discard an in-progress journey, as long as they have not expired.\n *\n * @param initEventFragmentSnapshot - Fragment IDs and `createdAt` values\n * present when {@link init} began.\n */\n #reconcileEventFragments(\n initEventFragmentSnapshot: Map<string, number>,\n ): void {\n const fragments = this.state.eventFragments;\n\n if (!fragments) {\n return;\n }\n\n if (!this.#isEventFragmentsEnabled) {\n this.#clearEventFragments();\n return;\n }\n\n const eventFragments: AnalyticsEventFragments = {};\n const now = Date.now();\n\n for (const [id, fragment] of Object.entries(fragments)) {\n if (!isAnalyticsEventFragment(fragment) || fragment.id !== id) {\n log('Dropping invalid persisted event fragment', { id });\n continue;\n }\n\n if (now - fragment.lastUpdated > EVENT_FRAGMENT_MAX_AGE) {\n log('Dropping expired persisted event fragment', { id });\n continue;\n }\n\n if (!this.#isCaptureAllowed(this.#purposesFromFragment(fragment))) {\n continue;\n }\n\n const snapshotCreatedAt = initEventFragmentSnapshot.get(id);\n\n if (\n fragment.persist === true ||\n snapshotCreatedAt === undefined ||\n fragment.createdAt !== snapshotCreatedAt\n ) {\n eventFragments[id] = fragment;\n }\n }\n\n if (Object.keys(eventFragments).length === 0) {\n this.#clearEventFragments();\n return;\n }\n\n if (Object.keys(eventFragments).length === Object.keys(fragments).length) {\n return;\n }\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Read an event fragment from state without the feature guard.\n *\n * @param id - The fragment ID.\n * @returns The fragment, or `undefined` when no fragment has that ID.\n */\n #getEventFragment(id: string): AnalyticsEventFragment | undefined {\n return this.state.eventFragments?.[id];\n }\n\n /**\n * Write an event fragment to state, replacing any fragment with the same ID.\n *\n * @param fragment - The fragment to store.\n * @returns The stored fragment with capture-time purpose metadata.\n */\n #setEventFragment(fragment: AnalyticsEventFragment): AnalyticsEventFragment {\n // Snapshot classification only. Consent is stamped at emit time by\n // {@link #trackEvent}, so fragment.context stays caller metadata.\n let fragmentWithPurposeSnapshot = fragment;\n if (fragment.eventPurposes === undefined) {\n const names = this.#eventNamesFromFragment(fragment);\n const eventPurposes = Object.fromEntries(\n names.map((name) => [name, this.#purposesFromName(name)]),\n );\n fragmentWithPurposeSnapshot = {\n ...fragment,\n ...(names.length === 0 ? {} : { eventPurposes }),\n ...(this.#eventsConfigVersion === undefined\n ? {}\n : { eventsConfigVersion: this.#eventsConfigVersion }),\n };\n }\n\n const eventFragments: AnalyticsEventFragments = {\n ...this.state.eventFragments,\n [fragmentWithPurposeSnapshot.id]: fragmentWithPurposeSnapshot,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n\n return fragmentWithPurposeSnapshot;\n }\n\n /**\n * Remove an event fragment from state.\n *\n * @param id - The fragment ID.\n */\n #removeEventFragment(id: string): void {\n const currentEventFragments = this.state.eventFragments;\n\n if (\n !currentEventFragments ||\n !Object.prototype.hasOwnProperty.call(currentEventFragments, id)\n ) {\n return;\n }\n\n const { [id]: _deletedFragment, ...eventFragments } = currentEventFragments;\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Drop event fragments that the current consent state no longer allows to\n * accumulate.\n */\n #pruneEventFragmentsForConsent(): void {\n const fragments = this.state.eventFragments;\n\n if (!fragments || Object.keys(fragments).length === 0) {\n return;\n }\n\n const eventFragments: AnalyticsEventFragments = {};\n for (const [id, fragment] of Object.entries(fragments)) {\n if (\n isAnalyticsEventFragment(fragment) &&\n this.#isCaptureAllowed(this.#purposesFromFragment(fragment))\n ) {\n eventFragments[id] = fragment;\n }\n }\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Drop queued events and fragments that the current consent state no longer\n * allows to keep.\n */\n #pruneAllForConsent(): void {\n this.#pruneQueueForConsent(AnalyticsQueue.EventQueue);\n this.#pruneQueueForConsent(AnalyticsQueue.PreConsentEventQueue);\n this.#pruneEventFragmentsForConsent();\n }\n\n /**\n * Clear all event fragments.\n */\n #clearEventFragments(): void {\n if (\n !this.state.eventFragments ||\n Object.keys(this.state.eventFragments).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventFragments = {};\n });\n }\n\n /**\n * Returns whether an event fragment call should be ignored, either because\n * the feature is disabled or because the consent state does not allow\n * capture. The ignored call is logged so a missing `isEventFragmentsEnabled`\n * or an unexpected consent state is diagnosable rather than silent.\n *\n * Consent is checked on every call, not just on the ones that emit, so a\n * fragment never accumulates data for an event that could not be delivered.\n *\n * @param method - The name of the method that was called.\n * @param fragment - The fragment being read or written, when one is known.\n * @returns True when the call should be ignored.\n */\n #shouldIgnoreEventFragmentCall(\n method: string,\n fragment?: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent' | 'eventPurposes'\n >,\n ): boolean {\n if (!this.#isEventFragmentsEnabled) {\n log(\n 'Ignoring event fragment call because the event fragments feature is disabled',\n { method },\n );\n\n return true;\n }\n\n const captureAllowed = fragment\n ? this.#isCaptureAllowed(this.#purposesFromFragment(fragment))\n : this.#isCaptureAllowed(Object.values(AnalyticsPurpose));\n\n if (!captureAllowed) {\n log(\n 'Ignoring event fragment call because the consent state does not allow capturing analytics',\n { method },\n );\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Emit one of an event fragment's events, carrying the properties the\n * fragment has accumulated.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the anonymous\n * payload split, the pre-consent queue and geolocation enrichment all apply.\n *\n * @param fragment - The fragment supplying the properties.\n * @param name - The name of the event to emit.\n * @param context - The context to send with the event.\n */\n #emitEventFragment(\n fragment: AnalyticsEventFragment,\n name: string,\n context: AnalyticsContext | undefined,\n ): void {\n const properties = { ...fragment.properties };\n const sensitiveProperties = { ...fragment.sensitiveProperties };\n\n this.#trackEvent(\n {\n name,\n properties,\n sensitiveProperties,\n saveDataRecording: false,\n hasProperties:\n Object.keys(properties).length > 0 ||\n Object.keys(sensitiveProperties).length > 0,\n },\n context,\n this.#purposesFromFragmentEvent(fragment, name),\n fragment.eventsConfigVersion,\n );\n }\n\n /**\n * Track an analytics event.\n *\n * Events are only tracked if analytics is enabled.\n *\n * @param event - Analytics event with properties and sensitive properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n trackEvent(event: AnalyticsTrackingEvent, context?: AnalyticsContext): void {\n this.#trackEvent(\n event,\n context,\n this.#purposesFromName(event.name),\n this.#eventsConfigVersion,\n );\n }\n\n /**\n * Report a caught error as an `Error Occurred` analytics event.\n *\n * Properties are `source` and a sanitized `error_message`. Whitespace is\n * collapsed and the message is capped at 500 characters. Stack traces are\n * never included.\n *\n * `source` must uniquely identify the error case (for example\n * `swap-quote-fetch`). Do not reuse one value for every error in a file\n * or package.\n *\n * Sampling, a deduplication window, and a per-session cap bound volume.\n * Those controls default to always emitting, 10 seconds, and 50 events.\n * A caller can only lower the session cap. The budget belongs to this\n * controller instance and is not persisted.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the\n * pre-consent queue, and geolocation enrichment all apply. Opted-out\n * calls return before the volume controls run, so they do not consume\n * the session budget.\n *\n * Fail-safe: this method never throws. If delivery throws, the session\n * reservation is released.\n *\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param options.source - Unique discriminator for this error case.\n * @param options.volumeControls - Optional sampling, dedup, and session cap.\n */\n trackErrorAsAnalytics(\n error: unknown,\n options: TrackErrorAsAnalyticsOptions,\n ): void {\n try {\n if (\n !this.#isCaptureAllowed(\n this.#purposesFromName(ERROR_OCCURRED_EVENT_NAME),\n )\n ) {\n return;\n }\n\n const reserved = reserveErrorOccurredEvent(\n this.#errorTrackingSession,\n error,\n options,\n );\n if (reserved === undefined) {\n return;\n }\n\n try {\n this.trackEvent(reserved.event);\n } catch {\n releaseErrorTrackingReservation(\n this.#errorTrackingSession,\n reserved.reservation,\n );\n }\n } catch {\n // Fail-safe: telemetry must never break the calling flow.\n }\n }\n\n #trackEvent(\n event: AnalyticsTrackingEvent,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n // An event captured while the user is still undecided is held in the\n // pre-consent queue (see #sendOrQueueTrackEvent) instead of being\n // delivered, and replayed if they later opt in.\n if (!this.#isCaptureAllowed(purposes)) {\n return;\n }\n\n // if event does not have properties, send event without properties\n // and return to prevent any additional processing\n if (!event.hasProperties) {\n this.#sendOrQueueTrackEvent(\n event.name,\n undefined,\n this.#withLocationContext(context),\n purposes,\n version,\n );\n return;\n }\n\n // Track regular properties first if anonymous events feature is enabled\n if (this.#isAnonymousEventsFeatureEnabled) {\n // Note: Even if regular properties object is empty, we still send it to ensure\n // an event with user ID is tracked.\n this.#sendOrQueueTrackEvent(\n event.name,\n {\n ...event.properties,\n },\n this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n\n const hasSensitiveProperties =\n Object.keys(event.sensitiveProperties).length > 0;\n\n if (!this.#isAnonymousEventsFeatureEnabled || hasSensitiveProperties) {\n this.#sendOrQueueTrackEvent(\n event.name,\n {\n ...event.properties,\n ...event.sensitiveProperties,\n ...(hasSensitiveProperties && { anonymous: true }),\n },\n // When the anonymous events feature is enabled, this payload is the\n // anonymous one and must carry no geolocation. When the feature is\n // disabled, this is the single identified payload, so it is enriched.\n this.#isAnonymousEventsFeatureEnabled\n ? context\n : this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n }\n\n /**\n * Identify a user for analytics.\n *\n * @param traits - User traits/properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n identify(traits?: AnalyticsUserTraits, context?: AnalyticsContext): void {\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n this.#sendOrQueueIdentifyEvent(\n this.state.analyticsId,\n traits,\n this.#withLocationContext(context),\n );\n }\n\n /**\n * Track a page or screen view.\n *\n * @param name - The identifier/name of the page or screen being viewed (e.g., \"home\", \"settings\", \"wallet\")\n * @param properties - Optional properties associated with the view\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n trackView(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n const purposes = this.#purposesFromName(name);\n const version = this.#eventsConfigVersion;\n if (!this.#isCaptureAllowed(purposes)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n\n /**\n * Create an event fragment.\n *\n * A fragment accumulates properties across a user journey so that several\n * parts of a client can contribute to the same set of events without\n * re-deriving them. Declaring `successEvent` and `failureEvent` turns the\n * fragment into a funnel that {@link finalizeEventFragment} closes. Declaring\n * none of the event names makes it a pure property bag that the client reads\n * back with {@link getEventFragmentById} when it emits its own events.\n *\n * Any existing fragment with the same ID is replaced, so a new journey never\n * inherits properties from a stale one.\n *\n * Nothing is created unless the user is opted in, or undecided with the\n * pre-consent queue enabled, so an opted-out user accumulates no fragment\n * data.\n *\n * @param options - The fragment definition. An ID is generated when one is\n * not supplied.\n * @returns A read-only copy of the created fragment, or `undefined` when the\n * event fragments feature is disabled or the consent state does not allow\n * capture. Mutating the returned object does not change controller state.\n * Use {@link updateEventFragment} or {@link upsertEventFragment} to write.\n */\n createEventFragment(\n options: AnalyticsEventFragmentOptions = {},\n ): ReadonlyAnalyticsEventFragment | undefined {\n // Classify create from event names only. Caller consent context must not\n // affect the event-purpose snapshot.\n if (\n this.#shouldIgnoreEventFragmentCall('createEventFragment', {\n initialEvent: options.initialEvent,\n successEvent: options.successEvent,\n failureEvent: options.failureEvent,\n })\n ) {\n return undefined;\n }\n\n const now = Date.now();\n\n const fragment = this.#setEventFragment({\n id: options.id ?? uuid(),\n properties: { ...(options.properties ?? {}) },\n sensitiveProperties: { ...(options.sensitiveProperties ?? {}) },\n createdAt: now,\n lastUpdated: now,\n ...(options.initialEvent === undefined\n ? {}\n : { initialEvent: options.initialEvent }),\n ...(options.successEvent === undefined\n ? {}\n : { successEvent: options.successEvent }),\n ...(options.failureEvent === undefined\n ? {}\n : { failureEvent: options.failureEvent }),\n ...(options.context === undefined\n ? {}\n : { context: { ...options.context } }),\n ...(options.persist === undefined ? {} : { persist: options.persist }),\n });\n\n if (fragment.initialEvent) {\n this.#emitEventFragment(\n fragment,\n fragment.initialEvent,\n fragment.context,\n );\n }\n\n return cloneDeep(fragment);\n }\n\n /**\n * Write to an event fragment, creating a property bag if none exists.\n *\n * This is the ergonomic entry point for contributors that do not know\n * whether the journey has been started yet, and it avoids the read then\n * write race a caller would otherwise have to implement itself.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n */\n upsertEventFragment(\n id: string,\n payload: AnalyticsEventFragmentPayload = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (\n this.#shouldIgnoreEventFragmentCall('upsertEventFragment', fragment ?? {})\n ) {\n return;\n }\n\n if (!fragment) {\n this.createEventFragment({ id, ...payload });\n return;\n }\n\n this.#setEventFragment(mergeEventFragment(fragment, payload));\n }\n\n /**\n * Write to an existing event fragment.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * Use {@link upsertEventFragment} when the fragment may not exist yet.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\n updateEventFragment(\n id: string,\n payload: AnalyticsEventFragmentPayload = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('updateEventFragment', fragment)) {\n return;\n }\n\n if (!fragment) {\n throw new Error(`Event fragment with id ${id} does not exist.`);\n }\n\n this.#setEventFragment(mergeEventFragment(fragment, payload));\n }\n\n /**\n * Read an event fragment.\n *\n * @param id - The fragment ID.\n * @returns A read-only copy of the fragment, or `undefined` when no fragment\n * has that ID, the event fragments feature is disabled, or the consent state\n * does not allow capture. Mutating the returned object does not change\n * controller state. Use {@link updateEventFragment} or\n * {@link upsertEventFragment} to write.\n */\n getEventFragmentById(id: string): ReadonlyAnalyticsEventFragment | undefined {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('getEventFragmentById', fragment)) {\n return undefined;\n }\n\n return fragment === undefined ? undefined : cloneDeep(fragment);\n }\n\n /**\n * Discard an event fragment without emitting anything.\n *\n * @param id - The fragment ID.\n */\n deleteEventFragment(id: string): void {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('deleteEventFragment', fragment)) {\n return;\n }\n\n this.#removeEventFragment(id);\n }\n\n /**\n * Close an event fragment, emitting its closing event and discarding it.\n *\n * The event emitted is `failureEvent` when the journey was abandoned and\n * `successEvent` otherwise. A fragment that does not declare the relevant\n * event name is discarded silently, which is what makes a pure property bag\n * possible.\n *\n * @param id - The fragment ID.\n * @param options - Finalization options.\n * @param options.abandoned - Whether the journey was abandoned.\n * @param options.context - Context merged over the fragment's own context.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\n finalizeEventFragment(\n id: string,\n { abandoned = false, context }: AnalyticsEventFragmentFinalizeOptions = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (\n this.#shouldIgnoreEventFragmentCall('finalizeEventFragment', fragment)\n ) {\n return;\n }\n\n if (!fragment) {\n throw new Error(`Event fragment with id ${id} does not exist.`);\n }\n\n const eventName = abandoned ? fragment.failureEvent : fragment.successEvent;\n\n if (eventName) {\n this.#emitEventFragment(\n fragment,\n eventName,\n mergeEventFragmentContext(fragment.context, context),\n );\n }\n\n this.#removeEventFragment(id);\n }\n\n /**\n * Opt in to analytics.\n *\n * Records that a consent decision has been made and replays any events that\n * were queued while the user was undecided.\n *\n * When geolocation enrichment is enabled, geolocation is resolved here (once\n * the user has consented) and awaited before the queued events are replayed,\n * so those events are enriched with the resolved location as they are sent.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\n async optIn(): Promise<void> {\n this.update((state) => {\n state.optedIn = true;\n state.consentDecisionMade = true;\n });\n\n // Now that the user has consented, resolve geolocation (once) and wait for\n // it so the queued pre-consent events can be enriched as they replay.\n await this.#maybeResolveLocation();\n\n // Reconcile against the current state rather than replaying blindly: the\n // consent decision may have changed while geolocation was resolving (e.g.\n // resetConsentDecision ran during the await), and preserved pre-consent\n // events must not be delivered once the user is no longer opted in.\n this.#replayPreConsentEvents();\n }\n\n /**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events and in-progress event fragments so nothing captured before the\n * decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Reset the consent decision back to undecided.\n *\n * Intended for client flows that restart onboarding. Clears the opt-in\n * preference and discards the delivery queue, but preserves any pre-consent\n * events so they can still be replayed if the user opts in again. The user is\n * treated as undecided again.\n *\n * In-progress event fragments are kept only while the undecided user can\n * still accumulate them, and discarded otherwise, so no fragment outlives the\n * consent state that allowed it.\n */\n resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Opt in to marketing analytics.\n *\n * Independent of {@link optIn}. Replays queued marketing events.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\n async optInToMarketing(): Promise<void> {\n this.update((state) => {\n state.optedInToMarketing = true;\n state.marketingConsentDecisionMade = true;\n });\n\n await this.#maybeResolveLocation();\n this.#replayPreConsentEvents();\n }\n\n /**\n * Opt out of marketing analytics.\n *\n * Independent of {@link optOut}. Discards queued marketing events and\n * marketing event fragments.\n */\n optOutOfMarketing(): void {\n this.update((state) => {\n state.optedInToMarketing = false;\n state.marketingConsentDecisionMade = true;\n state.marketingCampaignCookieId = null;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Reset the marketing consent decision back to undecided.\n *\n * Independent of {@link resetConsentDecision}.\n */\n resetMarketingConsentDecision(): void {\n this.update((state) => {\n state.optedInToMarketing = false;\n state.marketingConsentDecisionMade = false;\n state.marketingCampaignCookieId = null;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Set the marketing campaign cookie ID.\n *\n * Stores the ID of the marketing campaign cookie (e.g. a Google Analytics\n * client ID) that was active when the user arrived. Pass `null` to clear it.\n * The value is automatically cleared by {@link optOutOfMarketing} and\n * {@link resetMarketingConsentDecision}.\n *\n * @param marketingCampaignCookieId - The marketing campaign cookie ID, or\n * `null` to clear it.\n */\n setMarketingCampaignCookieId(marketingCampaignCookieId: string | null): void {\n this.update((state) => {\n state.marketingCampaignCookieId = marketingCampaignCookieId;\n });\n }\n}\n"]}
1
+ {"version":3,"file":"AnalyticsController.js","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ConfigRegistryControllerGetStateAction,\n ConfigRegistryControllerStateChangedEvent,\n} from '@metamask/config-registry-controller';\nimport type {\n GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash-es';\nimport { v4 as uuid } from 'uuid';\n\nimport type { AnalyticsControllerMethodActions } from './AnalyticsController-method-action-types.js';\nimport { validateAnalyticsControllerState } from './analyticsControllerStateValidator.js';\nimport { projectLogger as log } from './AnalyticsLogger.js';\nimport type {\n AnalyticsPlatformAdapter,\n AnalyticsDeliveryOptions,\n AnalyticsContext,\n AnalyticsEventProperties,\n AnalyticsLocationContext,\n AnalyticsUserTraits,\n AnalyticsTrackingEvent,\n} from './AnalyticsPlatformAdapter.types.js';\nimport type {\n AnalyticsEventFragment,\n AnalyticsEventFragmentFinalizeOptions,\n AnalyticsEventFragmentOptions,\n AnalyticsEventFragmentPayload,\n AnalyticsEventFragments,\n ReadonlyAnalyticsEventFragment,\n} from './EventFragment.types.js';\nimport { analyticsControllerSelectors } from './selectors.js';\nimport type {\n ErrorTrackingSession,\n TrackErrorAsAnalyticsOptions,\n} from './track-error-as-analytics.js';\nimport {\n ERROR_OCCURRED_EVENT_NAME,\n releaseErrorTrackingReservation,\n reserveErrorOccurredEvent,\n} from './track-error-as-analytics.js';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link AnalyticsController}, 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 = 'AnalyticsController';\n\n/**\n * Maximum age of a persisted event fragment, measured from\n * {@link AnalyticsEventFragment.lastUpdated}.\n *\n * Fragments older than this are discarded during {@link AnalyticsController.init}\n * without emitting a success or failure event. Confirmation journeys that span a\n * restart are expected to resume within this window; abandoned ones must not keep\n * `properties` or `sensitiveProperties` in storage indefinitely.\n */\nexport const EVENT_FRAGMENT_MAX_AGE = 24 * 60 * 60 * 1000;\n\n/**\n * Purposes for which an analytics payload can be used.\n */\nexport const AnalyticsPurpose = {\n Product: 'product',\n Marketing: 'marketing',\n} as const;\n\n/**\n * A purpose for which an analytics payload can be used.\n */\nexport type AnalyticsPurpose =\n (typeof AnalyticsPurpose)[keyof typeof AnalyticsPurpose];\n\n/**\n * Persisted event-purpose classification fetched from config registry.\n */\nexport type AnalyticsEventsConfig = {\n schemaVersion: string;\n version: string;\n timestamp: number;\n events: Record<string, AnalyticsPurpose[]>;\n};\n\n/**\n * Persisted queues on {@link AnalyticsControllerState}.\n */\nconst AnalyticsQueue = {\n EventQueue: 'eventQueue',\n PreConsentEventQueue: 'preConsentEventQueue',\n} as const;\n\ntype AnalyticsQueue = (typeof AnalyticsQueue)[keyof typeof AnalyticsQueue];\n\n// === STATE ===\n\n/**\n * Describes the shape of the state object for {@link AnalyticsController}.\n */\nexport type AnalyticsControllerState = {\n /**\n * Whether the user has opted in to analytics.\n */\n optedIn: boolean;\n\n /**\n * Whether the user has opted in to marketing analytics.\n *\n * Independent of {@link optedIn}. Named events in the remote marketing list\n * are governed only by this flag. Optional for backward compatibility with\n * persisted state that predates this field. Missing values are treated as\n * `false`.\n */\n optedInToMarketing?: boolean;\n\n /**\n * Whether the user has made a marketing consent decision (opted in or opted\n * out). Mirrors {@link consentDecisionMade} for the marketing purpose.\n * Optional for backward compatibility. Missing values are treated as `false`.\n */\n marketingConsentDecisionMade?: boolean;\n\n /**\n * Cached event-purpose configuration. Optional for backward compatibility.\n * Loaded from ConfigRegistryController state during init. Unlisted events\n * default to product-only.\n */\n eventsConfig?: AnalyticsEventsConfig;\n\n /**\n * User's UUIDv4 analytics identifier.\n * This is an identity (unique per user), not a preference.\n * Must be provided by the platform - the controller does not generate it.\n */\n analyticsId: string;\n\n /**\n * Persisted queue of analytics events waiting for delivery acknowledgement.\n * This is only used when event queue persistence is enabled.\n */\n eventQueue?: Record<string, Json>;\n\n /**\n * Whether the user has made a consent decision (opted in or opted out).\n *\n * This distinguishes the \"undecided\" state (e.g. during onboarding, before\n * the user has answered the analytics prompt) from an explicit opt-out.\n * Defaults to `false` and is set to `true` by {@link AnalyticsController.optIn}\n * or {@link AnalyticsController.optOut}, and back to `false` by\n * {@link AnalyticsController.resetConsentDecision}. Optional for backward\n * compatibility with persisted state that predates this field.\n */\n consentDecisionMade?: boolean;\n\n /**\n * Persisted queue of track events ({@link AnalyticsQueuedTrackEvent}) captured\n * while the user is undecided (no consent decision made yet). Replayed on\n * opt-in and cleared on opt-out.\n * Preserved across {@link AnalyticsController.resetConsentDecision} so onboarding\n * restarts do not drop install-time events.\n * This is only used when the pre-consent queue is enabled.\n */\n preConsentEventQueue?: Record<string, Json>;\n\n /**\n * Persisted event fragments ({@link AnalyticsEventFragment}) keyed by\n * fragment ID. Fragments accumulate properties across a user journey and are\n * removed when the journey is finalized or deleted. Fragments that set\n * `persist: true` can survive {@link AnalyticsController.init}, but only\n * while younger than {@link EVENT_FRAGMENT_MAX_AGE}.\n * This is only used when the event fragments feature is enabled.\n */\n eventFragments?: AnalyticsEventFragments;\n\n /**\n * Marketing campaign cookie ID set when the user arrives via a marketing\n * campaign. Cleared automatically when the user calls\n * {@link AnalyticsController.optOutOfMarketing} or\n * {@link AnalyticsController.resetMarketingConsentDecision}. Optional for backward\n * compatibility with persisted state that predates this field.\n */\n marketingCampaignCookieId?: string | null;\n};\n\n/**\n * Event types supported by the persisted analytics event queue.\n */\nexport type AnalyticsQueuedEventType = 'track' | 'identify' | 'view';\n\n/**\n * Base persisted event queue entry.\n */\nexport type AnalyticsQueuedEventBase = {\n /**\n * Event type used to replay the payload with the platform adapter.\n */\n type: AnalyticsQueuedEventType;\n\n /**\n * Stable identifier for the analytics payload.\n */\n messageId: string;\n\n /**\n * Original payload timestamp serialized for persistence.\n */\n timestamp: string;\n\n /**\n * Event purposes captured with the payload, before user consent is applied.\n */\n eventPurposes?: AnalyticsPurpose[];\n\n /**\n * Events config version used to classify the payload.\n */\n eventsConfigVersion?: string;\n};\n\n/**\n * Persisted track event queue entry.\n */\nexport type AnalyticsQueuedTrackEvent = AnalyticsQueuedEventBase & {\n type: 'track';\n eventName: string;\n properties?: AnalyticsEventProperties;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted identify event queue entry.\n */\nexport type AnalyticsQueuedIdentifyEvent = AnalyticsQueuedEventBase & {\n type: 'identify';\n userId: string;\n traits?: AnalyticsUserTraits;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted view event queue entry.\n */\nexport type AnalyticsQueuedViewEvent = AnalyticsQueuedEventBase & {\n type: 'view';\n name: string;\n properties?: AnalyticsEventProperties;\n context?: AnalyticsContext;\n};\n\n/**\n * Persisted analytics event queue entry.\n */\nexport type AnalyticsQueuedEvent =\n | AnalyticsQueuedTrackEvent\n | AnalyticsQueuedIdentifyEvent\n | AnalyticsQueuedViewEvent;\n\n/**\n * Persisted analytics event queue keyed by message ID.\n */\nexport type AnalyticsEventQueue = Record<string, AnalyticsQueuedEvent>;\n\n/**\n * Returns default values for AnalyticsController state.\n *\n * Note: analyticsId is NOT included - it's an identity that must be\n * provided by the platform (generated once on first run, then persisted).\n *\n * @returns Default state without analyticsId\n */\nexport function getDefaultAnalyticsControllerState(): Omit<\n AnalyticsControllerState,\n 'analyticsId'\n> {\n return {\n optedIn: false,\n consentDecisionMade: false,\n optedInToMarketing: false,\n marketingConsentDecisionMade: false,\n marketingCampaignCookieId: null,\n };\n}\n\n/**\n * The metadata for each property in {@link AnalyticsControllerState}.\n *\n * Both `optedIn` and `analyticsId` are persisted (`persist: true`).\n * The platform must supply a valid UUIDv4 `analyticsId` on first run.\n */\nconst analyticsControllerMetadata = {\n optedIn: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n optedInToMarketing: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n marketingConsentDecisionMade: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n eventsConfig: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n analyticsId: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n eventQueue: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n consentDecisionMade: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n preConsentEventQueue: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n eventFragments: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: false,\n },\n marketingCampaignCookieId: {\n includeInStateLogs: true,\n persist: true,\n includeInDebugSnapshot: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'trackErrorAsAnalytics',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\n 'optInToMarketing',\n 'optOutOfMarketing',\n 'resetMarketingConsentDecision',\n 'setMarketingCampaignCookieId',\n 'createEventFragment',\n 'upsertEventFragment',\n 'updateEventFragment',\n 'getEventFragmentById',\n 'deleteEventFragment',\n 'finalizeEventFragment',\n] as const;\n\n/**\n * Returns the state of the {@link AnalyticsController}.\n */\nexport type AnalyticsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AnalyticsControllerState\n>;\n\n/**\n * Actions that {@link AnalyticsControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsControllerActions =\n | AnalyticsControllerGetStateAction\n | AnalyticsControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link AnalyticsControllerMessenger} calls.\n */\ntype AllowedActions =\n | GeolocationControllerGetGeolocationDataAction\n | ConfigRegistryControllerGetStateAction;\n\n/**\n * Event emitted when the state of the {@link AnalyticsController} changes.\n */\nexport type AnalyticsControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AnalyticsControllerState\n>;\n\n/**\n * Events that {@link AnalyticsControllerMessenger} exposes to other consumers.\n */\nexport type AnalyticsControllerEvents = AnalyticsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link AnalyticsControllerMessenger} subscribes to.\n */\ntype AllowedEvents = ConfigRegistryControllerStateChangedEvent;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link AnalyticsController}.\n */\nexport type AnalyticsControllerMessenger = Messenger<\n typeof controllerName,\n AnalyticsControllerActions | AllowedActions,\n AnalyticsControllerEvents | AllowedEvents\n>;\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * Initial state accepted by {@link AnalyticsController}.\n *\n * `analyticsId` is required. All other fields are optional and filled from\n * {@link getDefaultAnalyticsControllerState} when omitted.\n */\nexport type AnalyticsControllerInitialState =\n Partial<AnalyticsControllerState> &\n Pick<AnalyticsControllerState, 'analyticsId'>;\n\n/**\n * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Other fields are optional; defaults come from\n * {@link getDefaultAnalyticsControllerState}. The platform is responsible\n * for generating the ID on first run. It is then persisted with controller\n * state when using a persisted store.\n */\n state: AnalyticsControllerInitialState;\n /**\n * Messenger used to communicate with BaseController and other controllers.\n */\n messenger: AnalyticsControllerMessenger;\n /**\n * Platform adapter implementation for tracking events.\n */\n platformAdapter: AnalyticsPlatformAdapter;\n\n /**\n * Whether the anonymous events feature is enabled.\n *\n * @default false\n */\n isAnonymousEventsFeatureEnabled?: boolean;\n\n /**\n * Whether analytics event queue persistence is enabled.\n *\n * When enabled, AnalyticsController persists each platform adapter payload\n * until the adapter reports successful delivery.\n *\n * @default false\n */\n isEventQueuePersistenceEnabled?: boolean;\n\n /**\n * Whether the pre-consent event queue is enabled.\n *\n * When enabled, track events received while the user is undecided\n * (no consent decision made yet) are persisted and replayed on opt-in,\n * or dropped on opt-out. When disabled, such events are dropped immediately,\n * preserving the legacy behavior.\n *\n * @default false\n */\n isPreConsentQueueEnabled?: boolean;\n\n /**\n * Whether geolocation enrichment is enabled.\n *\n * When enabled, {@link AnalyticsController.init} resolves the user's\n * country, region, and timezone via `GeolocationController:getGeolocationData`\n * and attaches them to `context.location` on non-anonymous payloads.\n * Compositions must register that action when this is enabled. When disabled,\n * the controller never calls the geolocation action and events are delivered\n * without location.\n *\n * @default false\n */\n isGeolocationEnabled?: boolean;\n\n /**\n * Whether the event fragments feature is enabled.\n *\n * When enabled, clients can accumulate analytics properties across a user\n * journey with {@link AnalyticsController.createEventFragment} and friends,\n * as long as the consent state allows analytics to be captured. When\n * disabled, every fragment method is a logged no-op and no fragment is ever\n * written to state.\n *\n * @default false\n */\n isEventFragmentsEnabled?: boolean;\n};\n\n/**\n * Returns whether a value is a non-array object.\n *\n * @param value - The value to check.\n * @returns True if the value is a record.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isAnalyticsPurpose(value: unknown): value is AnalyticsPurpose {\n return (\n value === AnalyticsPurpose.Product || value === AnalyticsPurpose.Marketing\n );\n}\n\nfunction isEventPurposesRecord(\n value: unknown,\n): value is Record<string, AnalyticsPurpose[]> {\n return (\n isRecord(value) &&\n Object.values(value).every(\n (purposes) =>\n Array.isArray(purposes) &&\n purposes.length > 0 &&\n purposes.every(isAnalyticsPurpose),\n )\n );\n}\n\nfunction isAnalyticsEventsConfig(\n value: unknown,\n): value is AnalyticsEventsConfig {\n return (\n isRecord(value) &&\n typeof value.schemaVersion === 'string' &&\n typeof value.version === 'string' &&\n typeof value.timestamp === 'number' &&\n isEventPurposesRecord(value.events)\n );\n}\n\n/**\n * Returns whether a JSON value is a non-array object.\n *\n * @param value - The value to check.\n * @returns True if the value is a JSON record.\n */\nfunction isJsonRecord(value: Json | undefined): value is Record<string, Json> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Builds the analytics location context from geolocation data, keeping only\n * the fields the geolocation API was able to determine.\n *\n * @param geolocation - The geolocation data to convert.\n * @returns The location context, or `undefined` when no field is known.\n */\nfunction buildLocationContext(\n geolocation: GeolocationData,\n): AnalyticsLocationContext | undefined {\n const locationContext: AnalyticsLocationContext = {\n ...(geolocation.country === null\n ? {}\n : { country_code: geolocation.country }),\n ...(geolocation.region === null ? {} : { region: geolocation.region }),\n ...(geolocation.timezone === null\n ? {}\n : { timezone: geolocation.timezone }),\n };\n\n return Object.keys(locationContext).length === 0\n ? undefined\n : locationContext;\n}\n\n/**\n * Returns whether a value is a valid persisted analytics event.\n *\n * @param value - The value to check.\n * @returns True if the value is a queued analytics event.\n */\nfunction isAnalyticsQueuedEvent(value: unknown): value is AnalyticsQueuedEvent {\n if (!isRecord(value)) {\n return false;\n }\n\n if (\n typeof value.messageId !== 'string' ||\n typeof value.timestamp !== 'string' ||\n (value.eventPurposes !== undefined &&\n (!Array.isArray(value.eventPurposes) ||\n !value.eventPurposes.every(isAnalyticsPurpose))) ||\n (value.eventsConfigVersion !== undefined &&\n typeof value.eventsConfigVersion !== 'string')\n ) {\n return false;\n }\n\n if (value.type === 'track') {\n return (\n typeof value.eventName === 'string' &&\n (value.properties === undefined || isRecord(value.properties)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n if (value.type === 'identify') {\n return (\n typeof value.userId === 'string' &&\n (value.traits === undefined || isRecord(value.traits)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n if (value.type === 'view') {\n return (\n typeof value.name === 'string' &&\n (value.properties === undefined || isRecord(value.properties)) &&\n (value.context === undefined || isRecord(value.context))\n );\n }\n\n return false;\n}\n\n/**\n * Returns whether a value is a valid persisted event fragment.\n *\n * @param value - The value to check.\n * @returns True if the value is an event fragment.\n */\nfunction isAnalyticsEventFragment(\n value: unknown,\n): value is AnalyticsEventFragment {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n typeof value.id === 'string' &&\n typeof value.createdAt === 'number' &&\n typeof value.lastUpdated === 'number' &&\n isRecord(value.properties) &&\n isRecord(value.sensitiveProperties) &&\n (value.initialEvent === undefined ||\n typeof value.initialEvent === 'string') &&\n (value.successEvent === undefined ||\n typeof value.successEvent === 'string') &&\n (value.failureEvent === undefined ||\n typeof value.failureEvent === 'string') &&\n (value.eventPurposes === undefined ||\n isEventPurposesRecord(value.eventPurposes)) &&\n (value.eventsConfigVersion === undefined ||\n typeof value.eventsConfigVersion === 'string') &&\n (value.context === undefined || isRecord(value.context)) &&\n (value.persist === undefined || typeof value.persist === 'boolean')\n );\n}\n\n/**\n * Merges a payload into an event fragment.\n *\n * `properties`, `sensitiveProperties` and `context` are merged one level deep,\n * so a key written twice is replaced rather than combined. This keeps array\n * values predictable: writing a shorter array replaces the longer one instead\n * of leaving stale trailing entries behind.\n *\n * @param fragment - The fragment to merge into.\n * @param payload - The payload to merge.\n * @returns A new fragment with the payload applied.\n */\nfunction mergeEventFragment(\n fragment: AnalyticsEventFragment,\n payload: AnalyticsEventFragmentPayload,\n): AnalyticsEventFragment {\n const context = mergeEventFragmentContext(fragment.context, payload.context);\n\n return {\n ...fragment,\n properties: { ...fragment.properties, ...(payload.properties ?? {}) },\n sensitiveProperties: {\n ...fragment.sensitiveProperties,\n ...(payload.sensitiveProperties ?? {}),\n },\n ...(context === undefined ? {} : { context }),\n lastUpdated: Date.now(),\n };\n}\n\n/**\n * Merges two optional analytics contexts.\n *\n * @param base - The context to merge into.\n * @param override - The context whose fields win. When omitted, `base` is kept.\n * @returns The merged context, or `undefined` when both sides are unset.\n */\nfunction mergeEventFragmentContext(\n base: AnalyticsContext | undefined,\n override: AnalyticsContext | undefined,\n): AnalyticsContext | undefined {\n if (override === undefined) {\n return base;\n }\n\n return { ...(base ?? {}), ...override };\n}\n\n/**\n * The AnalyticsController manages analytics tracking across platforms (Mobile/Extension).\n * It provides a unified interface for tracking events, identifying users, and managing\n * analytics preferences while delegating platform-specific implementation to an\n * {@link AnalyticsPlatformAdapter}.\n *\n * This controller follows the MetaMask controller pattern and integrates with the\n * messenger system to allow other controllers and components to track analytics events.\n * It delegates platform-specific implementation to an {@link AnalyticsPlatformAdapter}.\n *\n * The controller persists `optedIn` and `analyticsId` when composed with a persisted\n * store. The platform must supply a valid `analyticsId` on first launch.\n */\nexport class AnalyticsController extends BaseController<\n 'AnalyticsController',\n AnalyticsControllerState,\n AnalyticsControllerMessenger\n> {\n readonly #platformAdapter: AnalyticsPlatformAdapter;\n\n readonly #isAnonymousEventsFeatureEnabled: boolean;\n\n readonly #isEventQueuePersistenceEnabled: boolean;\n\n readonly #isPreConsentQueueEnabled: boolean;\n\n readonly #isGeolocationEnabled: boolean;\n\n readonly #isEventFragmentsEnabled: boolean;\n\n /**\n * In-memory event-purpose lookup from persisted state.\n */\n #eventPurposes: Map<string, AnalyticsPurpose[]>;\n\n #eventsConfigVersion: string | undefined;\n\n /**\n * The in-flight (or settled) initialization promise. Set on the first\n * {@link init} call and returned by subsequent calls so overlapping callers\n * await the same work rather than observing a premature completion.\n */\n #initPromise: Promise<void> | undefined;\n\n /**\n * The in-flight (or settled) geolocation resolution, if any. Its presence\n * marks that resolution has been started, so it runs at most once.\n */\n #locationResolvePromise: Promise<void> | undefined;\n\n #locationContext: AnalyticsLocationContext | undefined;\n\n /**\n * In-memory sampling, deduplication, and session-cap state for\n * {@link trackErrorAsAnalytics}. Not persisted.\n */\n readonly #errorTrackingSession: ErrorTrackingSession = {\n emittedCount: 0,\n lastEmittedAtBySignature: new Map(),\n };\n\n /**\n * Constructs an AnalyticsController instance.\n *\n * @param options - Controller options\n * @param options.state - Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Other fields are optional and default via `getDefaultAnalyticsControllerState()`.\n * @param options.messenger - Messenger used to communicate with BaseController\n * @param options.platformAdapter - Platform adapter implementation for tracking\n * @param options.isAnonymousEventsFeatureEnabled - Whether the anonymous events feature is enabled\n * @param options.isEventQueuePersistenceEnabled - Whether analytics event queue persistence is enabled\n * @param options.isPreConsentQueueEnabled - Whether the pre-consent event queue is enabled\n * @param options.isGeolocationEnabled - Whether geolocation enrichment is enabled\n * @param options.isEventFragmentsEnabled - Whether the event fragments feature is enabled\n * @throws Error if state.analyticsId is missing or not a valid UUIDv4\n * @remarks After construction, call {@link AnalyticsController.init} to complete initialization.\n */\n constructor({\n state,\n messenger,\n platformAdapter,\n isAnonymousEventsFeatureEnabled = false,\n isEventQueuePersistenceEnabled = false,\n isPreConsentQueueEnabled = false,\n isGeolocationEnabled = false,\n isEventFragmentsEnabled = false,\n }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\n };\n const eventsConfig = isAnalyticsEventsConfig(initialState.eventsConfig)\n ? initialState.eventsConfig\n : undefined;\n if (eventsConfig === undefined) {\n delete initialState.eventsConfig;\n } else {\n initialState.eventsConfig = eventsConfig;\n }\n\n validateAnalyticsControllerState(\n initialState,\n platformAdapter.skipUUIDv4Check === true,\n );\n\n super({\n name: controllerName,\n metadata: analyticsControllerMetadata,\n state: initialState,\n messenger,\n });\n\n this.#isAnonymousEventsFeatureEnabled = isAnonymousEventsFeatureEnabled;\n this.#isEventQueuePersistenceEnabled = isEventQueuePersistenceEnabled;\n this.#isPreConsentQueueEnabled = isPreConsentQueueEnabled;\n this.#isGeolocationEnabled = isGeolocationEnabled;\n this.#isEventFragmentsEnabled = isEventFragmentsEnabled;\n this.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\n this.#locationResolvePromise = undefined;\n this.#eventPurposes = new Map(Object.entries(eventsConfig?.events ?? {}));\n this.#eventsConfigVersion = eventsConfig?.version;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n log('AnalyticsController initialized and ready', {\n enabled: analyticsControllerSelectors.selectEnabled(this.state),\n optedIn: this.state.optedIn,\n optedInToMarketing: this.state.optedInToMarketing === true,\n marketingConsentDecisionMade:\n this.state.marketingConsentDecisionMade === true,\n consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\n eventFragmentsEnabled: this.#isEventFragmentsEnabled,\n });\n }\n\n /**\n * Initialize the controller by calling the platform adapter's\n * onSetupCompleted lifecycle hook and replaying any queued events. This\n * method must be called after construction to complete the setup process.\n *\n * When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation\n * is resolved only for a user who is already opted in to product or marketing\n * analytics. For users undecided or opted out of both purposes, it is\n * deferred until they opt in to either one (see {@link optIn} and\n * {@link optInToMarketing}), so a user's location is never requested before\n * they consent to analytics. In either case the `GeolocationController` and its\n * `GeolocationController:getGeolocationData` action must be registered before\n * resolution occurs, or enrichment is skipped for the session (a message is\n * logged, see {@link #resolveLocationContext}).\n *\n * Safe to call more than once: the first call performs initialization and\n * subsequent calls return the same in-flight (or settled) promise.\n *\n * @returns A promise that resolves once initialization has completed.\n */\n init(): Promise<void> {\n // Cache the in-flight promise so repeated or overlapping calls share a\n // single initialization and all await the same completion (rather than an\n // early call observing a finished init while work is still pending).\n this.#initPromise ??= this.#performInit();\n return this.#initPromise;\n }\n\n /**\n * Performs the one-time initialization work: resolve geolocation, run the\n * platform adapter's onSetupCompleted lifecycle hook, then replay any queued\n * and pre-consent events.\n */\n async #performInit(): Promise<void> {\n // Snapshot fragment IDs and createdAt before any awaited init work so\n // reconciliation can tell previous-session leftovers from fragments\n // created or replaced while init runs.\n const initEventFragmentSnapshot = new Map<string, number>();\n for (const [id, fragment] of Object.entries(\n this.state.eventFragments ?? {},\n )) {\n if (\n isAnalyticsEventFragment(fragment) &&\n fragment.id === id &&\n typeof fragment.createdAt === 'number'\n ) {\n initEventFragmentSnapshot.set(id, fragment.createdAt);\n }\n }\n\n await this.#fetchEventsConfig();\n\n this.messenger.subscribe(\n 'ConfigRegistryController:stateChanged',\n () => {\n this.#fetchEventsConfig().catch(\n /* istanbul ignore next */ () => undefined,\n );\n },\n (state) => state.configs.eventsConfig?.version,\n );\n\n // Resolve geolocation only when the user is already opted in to product or\n // marketing analytics. For undecided or opted-out users it is deferred to\n // {@link optIn} / {@link optInToMarketing}. Awaited so that an already-opted-in\n // session has location available before events replay.\n await this.#maybeResolveLocation();\n\n // Call onSetupCompleted lifecycle hook after initialization\n // State is already validated, so analyticsId is guaranteed to be a valid UUIDv4\n try {\n this.#platformAdapter.onSetupCompleted(this.state.analyticsId);\n } catch (error) {\n // Log error but don't throw - adapter setup failure shouldn't break controller\n log('Error calling platformAdapter.onSetupCompleted', error);\n }\n\n this.#replayQueuedEvents();\n this.#replayPreConsentEvents();\n this.#reconcileEventFragments(initEventFragmentSnapshot);\n }\n\n /**\n * Start resolving the geolocation context if warranted, and return the\n * in-flight (or settled) resolution so callers can await it. No-op unless\n * enrichment is enabled, the user is opted in to product or marketing\n * analytics, and a resolution has not already been started. Deferring\n * resolution until consent ensures a user's location is never requested before\n * they consent to analytics (for example, during onboarding).\n *\n * Resolution runs at most once per controller session: the settled promise\n * is retained, so the outcome, including a failure (see\n * {@link #resolveLocationContext}) is not retried, and events are delivered\n * without location for the rest of the session.\n *\n * @returns The geolocation resolution promise, or `undefined` when no\n * resolution is warranted.\n */\n #maybeResolveLocation(): Promise<void> | undefined {\n if (\n this.#isGeolocationEnabled &&\n this.#locationResolvePromise === undefined &&\n (this.state.optedIn || this.state.optedInToMarketing === true)\n ) {\n this.#locationResolvePromise = this.#resolveLocationContext();\n }\n\n return this.#locationResolvePromise;\n }\n\n async #resolveLocationContext(): Promise<void> {\n try {\n const geolocation = await this.messenger.call(\n 'GeolocationController:getGeolocationData',\n );\n\n this.#locationContext = buildLocationContext(geolocation);\n } catch (error) {\n // A common cause is the GeolocationController not being registered before\n // resolution runs (at init for an opted-in user, otherwise at opt-in).\n // Name it here so the failure is diagnosable, since enrichment is\n // otherwise skipped silently for the session.\n log(\n 'Failed to resolve geolocation for analytics enrichment; events will be sent without location. Ensure the GeolocationController is registered and initialized before the user opts in when geolocation is enabled.',\n error,\n );\n }\n }\n\n /**\n * Merge the resolved location context into a caller-provided context.\n *\n * Caller-provided `location` fields are preserved, but the fields the\n * controller resolves take precedence over them.\n *\n * @param context - Optional caller-provided context.\n * @returns The context enriched with location, or the original context when\n * no location is known.\n */\n #withLocationContext(\n context?: AnalyticsContext,\n ): AnalyticsContext | undefined {\n if (!this.#locationContext) {\n return context;\n }\n\n const callerLocation = context?.location;\n\n return {\n ...context,\n location: {\n ...(isJsonRecord(callerLocation) ? callerLocation : {}),\n ...this.#locationContext,\n },\n };\n }\n\n /**\n * Stamp the purposes currently allowed for a payload using Segment's consent\n * context shape.\n *\n * @param purposes - Purposes for which the payload is eligible.\n * @param context - Optional caller-provided context.\n * @param version - Config version captured with the payload.\n * @returns Context with allowed purpose preferences.\n */\n #withConsentContext(\n purposes: AnalyticsPurpose[],\n context?: AnalyticsContext,\n version?: string,\n ): AnalyticsContext {\n const preferences = this.#allowedPurposePreferences(purposes);\n const {\n consent: existingConsent,\n eventsConfigVersion: _ignoredVersion,\n ...unmanagedContext\n } = context ?? {};\n const consent: Record<string, Json> = isJsonRecord(existingConsent)\n ? existingConsent\n : {};\n const existingCategoryPreferences = isJsonRecord(\n consent.categoryPreferences,\n )\n ? consent.categoryPreferences\n : {};\n\n return {\n ...unmanagedContext,\n consent: {\n ...consent,\n categoryPreferences: {\n ...existingCategoryPreferences,\n ...preferences,\n },\n },\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n }\n\n /**\n * Load event-purpose configuration from ConfigRegistryController state.\n * Updates in-memory purposes map and persisted state when the version differs.\n */\n async #fetchEventsConfig(): Promise<void> {\n const eventsConfigState = this.messenger.call(\n 'ConfigRegistryController:getState',\n );\n\n const remoteEventsConfig = eventsConfigState.configs.eventsConfig;\n if (!remoteEventsConfig) {\n return;\n }\n\n const candidate: AnalyticsEventsConfig = {\n schemaVersion: remoteEventsConfig.schemaVersion,\n version: remoteEventsConfig.version,\n timestamp: remoteEventsConfig.timestamp,\n events: remoteEventsConfig.events as Record<string, AnalyticsPurpose[]>,\n };\n\n if (\n !isAnalyticsEventsConfig(candidate) ||\n candidate.version === this.#eventsConfigVersion\n ) {\n return;\n }\n\n this.#eventPurposes = new Map(Object.entries(candidate.events));\n this.#eventsConfigVersion = candidate.version;\n this.update((state) => {\n state.eventsConfig = candidate;\n });\n }\n\n #purposesFromName(name: string): AnalyticsPurpose[] {\n return [...(this.#eventPurposes.get(name) ?? [AnalyticsPurpose.Product])];\n }\n\n #purposesFromQueuedEvent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsPurpose[] {\n if (queuedEvent.type === 'identify') {\n return [AnalyticsPurpose.Product];\n }\n\n if (queuedEvent.eventPurposes !== undefined) {\n return [...queuedEvent.eventPurposes];\n }\n\n return this.#purposesFromName(\n queuedEvent.type === 'view' ? queuedEvent.name : queuedEvent.eventName,\n );\n }\n\n #eventNamesFromFragment(\n fragment: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent'\n >,\n ): string[] {\n return [\n fragment.initialEvent,\n fragment.successEvent,\n fragment.failureEvent,\n ].filter((name): name is string => typeof name === 'string');\n }\n\n #purposesFromFragmentEvent(\n fragment: Pick<AnalyticsEventFragment, 'eventPurposes'>,\n name: string,\n ): AnalyticsPurpose[] {\n return [\n ...(fragment.eventPurposes?.[name] ?? this.#purposesFromName(name)),\n ];\n }\n\n #purposesFromFragment(\n fragment: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent' | 'eventPurposes'\n >,\n ): AnalyticsPurpose[] {\n const names = this.#eventNamesFromFragment(fragment);\n if (names.length === 0) {\n // Nameless property bags have no event to classify. Treat them as\n // eligible for any purpose so they can accumulate when either consent\n // allows capture.\n return Object.values(AnalyticsPurpose);\n }\n\n return [\n ...new Set(\n names.flatMap((name) =>\n this.#purposesFromFragmentEvent(fragment, name),\n ),\n ),\n ];\n }\n\n #consent(purpose: AnalyticsPurpose): {\n optedIn: boolean;\n decisionMade: boolean;\n } {\n return purpose === AnalyticsPurpose.Marketing\n ? {\n optedIn: this.state.optedInToMarketing === true,\n decisionMade: this.state.marketingConsentDecisionMade === true,\n }\n : {\n optedIn: this.state.optedIn,\n decisionMade: this.state.consentDecisionMade === true,\n };\n }\n\n #allowedPurposePreferences(purposes: AnalyticsPurpose[]): {\n product: boolean;\n marketing: boolean;\n } {\n return {\n product:\n purposes.includes(AnalyticsPurpose.Product) &&\n this.#consent(AnalyticsPurpose.Product).optedIn,\n marketing:\n purposes.includes(AnalyticsPurpose.Marketing) &&\n this.#consent(AnalyticsPurpose.Marketing).optedIn,\n };\n }\n\n #hasAllowedPurpose(purposes: AnalyticsPurpose[]): boolean {\n const { product, marketing } = this.#allowedPurposePreferences(purposes);\n return product || marketing;\n }\n\n #hasUndecidedPurpose(purposes: AnalyticsPurpose[]): boolean {\n return purposes.some((purpose) => !this.#consent(purpose).decisionMade);\n }\n\n #isCaptureAllowed(purposes: AnalyticsPurpose[]): boolean {\n return (\n this.#hasAllowedPurpose(purposes) ||\n (this.#isPreConsentQueueEnabled && this.#hasUndecidedPurpose(purposes))\n );\n }\n\n #replaceQueue(field: AnalyticsQueue, nextQueue: Record<string, Json>): void {\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state[field] = nextQueue as never;\n });\n }\n\n /**\n * Send final track payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param eventName - The name of the event.\n * @param properties - Optional event properties.\n * @param context - Optional platform-specific context.\n * @param purposes - Capture-time purposes for the event.\n * @param version - Capture-time events config version.\n */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties: AnalyticsEventProperties | undefined,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n const isAllowed = this.#hasAllowedPurpose(purposes);\n const contextWithConsent = this.#withConsentContext(\n purposes,\n context,\n version,\n );\n\n // Direct delivery: enabled and not persisting.\n if (isAllowed && !this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.track(eventName, properties, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedTrackEvent = {\n type: 'track',\n eventName,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(properties === undefined ? {} : { properties }),\n context: contextWithConsent,\n eventPurposes: purposes,\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n\n if (!isAllowed) {\n this.#enqueuePreConsentEvent(queuedEvent);\n return;\n }\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Send final identify payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param userId - The user ID.\n * @param traits - Optional user traits.\n * @param context - Optional platform-specific context.\n */\n #sendOrQueueIdentifyEvent(\n userId: string,\n traits?: AnalyticsUserTraits,\n context?: AnalyticsContext,\n ): void {\n const purposes = [AnalyticsPurpose.Product];\n const contextWithConsent = this.#withConsentContext(purposes, context);\n\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedIdentifyEvent = {\n type: 'identify',\n userId,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(traits === undefined ? {} : { traits }),\n context: contextWithConsent,\n eventPurposes: purposes,\n };\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Send final view payload through the platform adapter or queue it if persistence is enabled.\n *\n * @param name - The view name.\n * @param properties - Optional view properties.\n * @param context - Optional platform-specific context.\n * @param purposes - Capture-time purposes for the view.\n * @param version - Capture-time events config version.\n */\n #sendOrQueueViewEvent(\n name: string,\n properties: AnalyticsEventProperties | undefined,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n const isAllowed = this.#hasAllowedPurpose(purposes);\n const contextWithConsent = this.#withConsentContext(\n purposes,\n context,\n version,\n );\n\n if (isAllowed && !this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, contextWithConsent);\n return;\n }\n\n const queuedEvent: AnalyticsQueuedViewEvent = {\n type: 'view',\n name,\n messageId: uuid(),\n timestamp: new Date().toISOString(),\n ...(properties === undefined ? {} : { properties }),\n context: contextWithConsent,\n eventPurposes: purposes,\n ...(version === undefined ? {} : { eventsConfigVersion: version }),\n };\n\n if (!isAllowed) {\n this.#enqueuePreConsentEvent(queuedEvent);\n return;\n }\n\n this.#enqueueEvent(queuedEvent);\n }\n\n /**\n * Add an analytics event to the queue and send it.\n *\n * @param queuedEvent - The event to enqueue and deliver.\n */\n #enqueueEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const eventQueue: Record<string, Json> = {\n ...(this.state.eventQueue ?? {}),\n [queuedEvent.messageId]: queuedEvent,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventQueue = eventQueue as never;\n });\n\n this.#sendQueuedEvent(queuedEvent);\n }\n\n /**\n * Send a queued event through the platform adapter.\n *\n * @param queuedEvent - The queued event to deliver.\n */\n #sendQueuedEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const timestamp = new Date(queuedEvent.timestamp);\n\n if (Number.isNaN(timestamp.getTime())) {\n log('Dropping queued analytics event with invalid timestamp', {\n messageId: queuedEvent.messageId,\n });\n this.#removeQueuedEvent(queuedEvent.messageId);\n return;\n }\n\n const options: AnalyticsDeliveryOptions = {\n messageId: queuedEvent.messageId,\n timestamp,\n callback: (error?: unknown) => {\n if (error) {\n log('Queued analytics event delivery failed', {\n messageId: queuedEvent.messageId,\n error,\n });\n }\n\n this.#removeQueuedEvent(queuedEvent.messageId);\n },\n };\n\n try {\n if (queuedEvent.type === 'track') {\n this.#platformAdapter.track(\n queuedEvent.eventName,\n cloneDeep(queuedEvent.properties),\n cloneDeep(queuedEvent.context),\n options,\n );\n } else if (queuedEvent.type === 'identify') {\n this.#platformAdapter.identify(\n queuedEvent.userId,\n cloneDeep(queuedEvent.traits),\n cloneDeep(queuedEvent.context),\n options,\n );\n } else {\n this.#platformAdapter.view(\n queuedEvent.name,\n cloneDeep(queuedEvent.properties),\n cloneDeep(queuedEvent.context),\n options,\n );\n }\n } catch (error) {\n log('Error sending queued analytics event', {\n messageId: queuedEvent.messageId,\n error,\n });\n }\n }\n\n /**\n * Replay persisted analytics events.\n */\n #replayQueuedEvents(): void {\n if (!this.#isEventQueuePersistenceEnabled || !this.state.eventQueue) {\n return;\n }\n\n const remainingQueue: Record<string, Json> = {};\n const eventsToSend: AnalyticsQueuedEvent[] = [];\n\n for (const [messageId, queuedEvent] of Object.entries(\n this.state.eventQueue,\n )) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued analytics event', { messageId });\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n\n if (this.#hasAllowedPurpose(purposes)) {\n const refreshedEvent = this.#refreshQueuedEventConsent(queuedEvent);\n remainingQueue[messageId] = refreshedEvent;\n eventsToSend.push(refreshedEvent);\n }\n }\n\n this.#replaceQueue(AnalyticsQueue.EventQueue, remainingQueue);\n\n for (const queuedEvent of eventsToSend) {\n this.#sendQueuedEvent(queuedEvent);\n }\n }\n\n /**\n * Remove a queued analytics event.\n *\n * @param messageId - The queued event message ID.\n */\n #removeQueuedEvent(messageId: string): void {\n const currentEventQueue = this.state.eventQueue;\n\n if (\n !currentEventQueue ||\n !Object.prototype.hasOwnProperty.call(currentEventQueue, messageId)\n ) {\n return;\n }\n\n const { [messageId]: _deletedEvent, ...eventQueue } = currentEventQueue;\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventQueue = eventQueue as never;\n });\n }\n\n #refreshQueuedEventConsent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsQueuedEvent {\n // Refresh only the consent stamp. Capture-time `eventPurposes` and\n // `eventsConfigVersion` stay as a pair and are never rewritten here.\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n const preferences = this.#allowedPurposePreferences(purposes);\n const existingPreferences = isJsonRecord(queuedEvent.context?.consent)\n ? queuedEvent.context.consent.categoryPreferences\n : undefined;\n\n if (\n isJsonRecord(existingPreferences) &&\n existingPreferences.product === preferences.product &&\n existingPreferences.marketing === preferences.marketing\n ) {\n return queuedEvent;\n }\n\n return {\n ...queuedEvent,\n context: this.#withConsentContext(\n purposes,\n queuedEvent.context,\n queuedEvent.eventsConfigVersion,\n ),\n };\n }\n\n /**\n * Prune a queue after a consent change.\n *\n * For {@link AnalyticsQueue.EventQueue}, entries are kept only while at least\n * one capture-time purpose is opted in, and their consent stamp is refreshed.\n * For {@link AnalyticsQueue.PreConsentEventQueue}, entries are kept while at\n * least one purpose is still allowed or undecided.\n *\n * @param field - The queue to prune.\n */\n #pruneQueueForConsent(field: AnalyticsQueue): void {\n const queue = this.state[field];\n if (!queue) {\n return;\n }\n\n const nextQueue: Record<string, Json> = {};\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n const isAllowed = this.#hasAllowedPurpose(purposes);\n\n if (field === AnalyticsQueue.EventQueue) {\n if (isAllowed) {\n nextQueue[messageId] = this.#refreshQueuedEventConsent(queuedEvent);\n }\n }\n\n if (field === AnalyticsQueue.PreConsentEventQueue) {\n if (isAllowed || this.#hasUndecidedPurpose(purposes)) {\n nextQueue[messageId] = queuedEvent;\n }\n }\n }\n\n this.#replaceQueue(field, nextQueue);\n }\n\n /**\n * Add an event to the pre-consent queue without delivering it.\n *\n * @param queuedEvent - The event to hold until the user opts in.\n */\n #enqueuePreConsentEvent(queuedEvent: AnalyticsQueuedEvent): void {\n const preConsentEventQueue: Record<string, Json> = {\n ...(this.state.preConsentEventQueue ?? {}),\n [queuedEvent.messageId]: queuedEvent,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Enrich a pre-consent event with the geolocation resolved on opt-in.\n *\n * Pre-consent events are captured while geolocation is not yet resolved, so\n * they are re-enriched here as they replay. Anonymous track payloads are left\n * untouched, since they must never carry location.\n *\n * @param queuedEvent - The queued pre-consent event.\n * @returns The event with its context enriched, or the event unchanged when\n * enrichment does not apply.\n */\n #enrichPreConsentEvent(\n queuedEvent: AnalyticsQueuedEvent,\n ): AnalyticsQueuedEvent {\n if (\n queuedEvent.type === 'track' &&\n queuedEvent.properties?.anonymous === true\n ) {\n return queuedEvent;\n }\n\n const context = this.#withLocationContext(queuedEvent.context);\n\n return {\n ...queuedEvent,\n ...(context === undefined ? {} : { context }),\n };\n }\n\n /**\n * Replay eligible pre-consent events against current consent.\n *\n * Allowed entries are replayed, undecided entries are kept for later, and\n * entries with no remaining allowed or undecided purpose are dropped. The\n * keep set is written before replay so events cannot be re-queued or\n * replayed twice.\n *\n * If the pre-consent queue is disabled, any stale persisted entries (e.g. from\n * a previous session where it was enabled) are dropped so they can never be\n * replayed.\n */\n #replayPreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.preConsentEventQueue = {} as never;\n });\n return;\n }\n\n const keep: Record<string, Json> = {};\n const replay: AnalyticsQueuedEvent[] = [];\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n continue;\n }\n\n const purposes = this.#purposesFromQueuedEvent(queuedEvent);\n\n if (this.#hasAllowedPurpose(purposes)) {\n replay.push(queuedEvent);\n } else if (this.#hasUndecidedPurpose(purposes)) {\n keep[messageId] = queuedEvent;\n }\n }\n\n this.#replaceQueue(AnalyticsQueue.PreConsentEventQueue, keep);\n\n for (const queuedEvent of replay) {\n const eventToReplay = this.#refreshQueuedEventConsent(\n this.#enrichPreConsentEvent(queuedEvent),\n );\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(eventToReplay);\n } else {\n this.#sendQueuedEvent(eventToReplay);\n }\n }\n }\n\n /**\n * Reconcile persisted event fragments on initialization.\n *\n * A fragment describes a journey that was in progress when the previous\n * session ended. Only fragments that opted into `persist` and are younger\n * than {@link EVENT_FRAGMENT_MAX_AGE} can be resumed, so the rest are\n * discarded. Nothing is emitted: a journey that never reached its own\n * finalization is not a failure, just an unfinished one.\n *\n * If the feature is disabled (e.g. a previous session had it enabled), or the\n * consent state no longer allows capture for any of a fragment's purposes,\n * those fragments are dropped so none of them can linger.\n *\n * Non-persistent fragments are dropped only when their ID and `createdAt`\n * match a fragment present at the start of {@link init}. Fragments created\n * or replaced while init is in flight are kept so a slow startup path cannot\n * discard an in-progress journey, as long as they have not expired.\n *\n * @param initEventFragmentSnapshot - Fragment IDs and `createdAt` values\n * present when {@link init} began.\n */\n #reconcileEventFragments(\n initEventFragmentSnapshot: Map<string, number>,\n ): void {\n const fragments = this.state.eventFragments;\n\n if (!fragments) {\n return;\n }\n\n if (!this.#isEventFragmentsEnabled) {\n this.#clearEventFragments();\n return;\n }\n\n const eventFragments: AnalyticsEventFragments = {};\n const now = Date.now();\n\n for (const [id, fragment] of Object.entries(fragments)) {\n if (!isAnalyticsEventFragment(fragment) || fragment.id !== id) {\n log('Dropping invalid persisted event fragment', { id });\n continue;\n }\n\n if (now - fragment.lastUpdated > EVENT_FRAGMENT_MAX_AGE) {\n log('Dropping expired persisted event fragment', { id });\n continue;\n }\n\n if (!this.#isCaptureAllowed(this.#purposesFromFragment(fragment))) {\n continue;\n }\n\n const snapshotCreatedAt = initEventFragmentSnapshot.get(id);\n\n if (\n fragment.persist === true ||\n snapshotCreatedAt === undefined ||\n fragment.createdAt !== snapshotCreatedAt\n ) {\n eventFragments[id] = fragment;\n }\n }\n\n if (Object.keys(eventFragments).length === 0) {\n this.#clearEventFragments();\n return;\n }\n\n if (Object.keys(eventFragments).length === Object.keys(fragments).length) {\n return;\n }\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Read an event fragment from state without the feature guard.\n *\n * @param id - The fragment ID.\n * @returns The fragment, or `undefined` when no fragment has that ID.\n */\n #getEventFragment(id: string): AnalyticsEventFragment | undefined {\n return this.state.eventFragments?.[id];\n }\n\n /**\n * Write an event fragment to state, replacing any fragment with the same ID.\n *\n * @param fragment - The fragment to store.\n * @returns The stored fragment with capture-time purpose metadata.\n */\n #setEventFragment(fragment: AnalyticsEventFragment): AnalyticsEventFragment {\n // Snapshot classification only. Consent is stamped at emit time by\n // {@link #trackEvent}, so fragment.context stays caller metadata.\n let fragmentWithPurposeSnapshot = fragment;\n if (fragment.eventPurposes === undefined) {\n const names = this.#eventNamesFromFragment(fragment);\n const eventPurposes = Object.fromEntries(\n names.map((name) => [name, this.#purposesFromName(name)]),\n );\n fragmentWithPurposeSnapshot = {\n ...fragment,\n ...(names.length === 0 ? {} : { eventPurposes }),\n ...(this.#eventsConfigVersion === undefined\n ? {}\n : { eventsConfigVersion: this.#eventsConfigVersion }),\n };\n }\n\n const eventFragments: AnalyticsEventFragments = {\n ...this.state.eventFragments,\n [fragmentWithPurposeSnapshot.id]: fragmentWithPurposeSnapshot,\n };\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n\n return fragmentWithPurposeSnapshot;\n }\n\n /**\n * Remove an event fragment from state.\n *\n * @param id - The fragment ID.\n */\n #removeEventFragment(id: string): void {\n const currentEventFragments = this.state.eventFragments;\n\n if (\n !currentEventFragments ||\n !Object.prototype.hasOwnProperty.call(currentEventFragments, id)\n ) {\n return;\n }\n\n const { [id]: _deletedFragment, ...eventFragments } = currentEventFragments;\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Drop event fragments that the current consent state no longer allows to\n * accumulate.\n */\n #pruneEventFragmentsForConsent(): void {\n const fragments = this.state.eventFragments;\n\n if (!fragments || Object.keys(fragments).length === 0) {\n return;\n }\n\n const eventFragments: AnalyticsEventFragments = {};\n for (const [id, fragment] of Object.entries(fragments)) {\n if (\n isAnalyticsEventFragment(fragment) &&\n this.#isCaptureAllowed(this.#purposesFromFragment(fragment))\n ) {\n eventFragments[id] = fragment;\n }\n }\n\n this.update((state) => {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion\n state.eventFragments = eventFragments as never;\n });\n }\n\n /**\n * Drop queued events and fragments that the current consent state no longer\n * allows to keep.\n */\n #pruneAllForConsent(): void {\n this.#pruneQueueForConsent(AnalyticsQueue.EventQueue);\n this.#pruneQueueForConsent(AnalyticsQueue.PreConsentEventQueue);\n this.#pruneEventFragmentsForConsent();\n }\n\n /**\n * Clear all event fragments.\n */\n #clearEventFragments(): void {\n if (\n !this.state.eventFragments ||\n Object.keys(this.state.eventFragments).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventFragments = {};\n });\n }\n\n /**\n * Returns whether an event fragment call should be ignored, either because\n * the feature is disabled or because the consent state does not allow\n * capture. The ignored call is logged so a missing `isEventFragmentsEnabled`\n * or an unexpected consent state is diagnosable rather than silent.\n *\n * Consent is checked on every call, not just on the ones that emit, so a\n * fragment never accumulates data for an event that could not be delivered.\n *\n * @param method - The name of the method that was called.\n * @param fragment - The fragment being read or written, when one is known.\n * @returns True when the call should be ignored.\n */\n #shouldIgnoreEventFragmentCall(\n method: string,\n fragment?: Pick<\n AnalyticsEventFragment,\n 'initialEvent' | 'successEvent' | 'failureEvent' | 'eventPurposes'\n >,\n ): boolean {\n if (!this.#isEventFragmentsEnabled) {\n log(\n 'Ignoring event fragment call because the event fragments feature is disabled',\n { method },\n );\n\n return true;\n }\n\n const captureAllowed = fragment\n ? this.#isCaptureAllowed(this.#purposesFromFragment(fragment))\n : this.#isCaptureAllowed(Object.values(AnalyticsPurpose));\n\n if (!captureAllowed) {\n log(\n 'Ignoring event fragment call because the consent state does not allow capturing analytics',\n { method },\n );\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Emit one of an event fragment's events, carrying the properties the\n * fragment has accumulated.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the anonymous\n * payload split, the pre-consent queue and geolocation enrichment all apply.\n *\n * @param fragment - The fragment supplying the properties.\n * @param name - The name of the event to emit.\n * @param context - The context to send with the event.\n */\n #emitEventFragment(\n fragment: AnalyticsEventFragment,\n name: string,\n context: AnalyticsContext | undefined,\n ): void {\n const properties = { ...fragment.properties };\n const sensitiveProperties = { ...fragment.sensitiveProperties };\n\n this.#trackEvent(\n {\n name,\n properties,\n sensitiveProperties,\n saveDataRecording: false,\n hasProperties:\n Object.keys(properties).length > 0 ||\n Object.keys(sensitiveProperties).length > 0,\n },\n context,\n this.#purposesFromFragmentEvent(fragment, name),\n fragment.eventsConfigVersion,\n );\n }\n\n /**\n * Track an analytics event.\n *\n * Events are only tracked if analytics is enabled.\n *\n * @param event - Analytics event with properties and sensitive properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n trackEvent(event: AnalyticsTrackingEvent, context?: AnalyticsContext): void {\n this.#trackEvent(\n event,\n context,\n this.#purposesFromName(event.name),\n this.#eventsConfigVersion,\n );\n }\n\n /**\n * Report a caught error as an `Error Occurred` analytics event.\n *\n * Properties always include `source` and a sanitized `error_message`.\n * Whitespace is collapsed and the message is capped at 500 characters.\n * Stack traces are never included. Optional `properties` are merged in\n * as well.\n *\n * `source` must uniquely identify the error case (for example\n * `swap-quote-fetch`). Do not reuse one value for every error in a file\n * or package.\n *\n * Sampling, a deduplication window, and a per-session cap bound volume.\n * Those controls default to always emitting, 10 seconds, and 50 events.\n * A caller can only lower the session cap. The budget belongs to this\n * controller instance and is not persisted.\n *\n * Delivery goes through {@link trackEvent}, so consent gating, the\n * pre-consent queue, and geolocation enrichment all apply. Opted-out\n * calls return before the volume controls run, so they do not consume\n * the session budget.\n *\n * Fail-safe: this method never throws. If delivery throws, the session\n * reservation is released.\n *\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param options.source - Unique discriminator for this error case.\n * @param options.volumeControls - Optional sampling, dedup, and session cap.\n * @param properties - Optional extra properties.\n */\n trackErrorAsAnalytics(\n error: unknown,\n options: TrackErrorAsAnalyticsOptions,\n properties?: AnalyticsEventProperties,\n ): void {\n try {\n if (\n !this.#isCaptureAllowed(\n this.#purposesFromName(ERROR_OCCURRED_EVENT_NAME),\n )\n ) {\n return;\n }\n\n const reserved = reserveErrorOccurredEvent(\n this.#errorTrackingSession,\n error,\n options,\n properties,\n );\n if (reserved === undefined) {\n return;\n }\n\n try {\n this.trackEvent(reserved.event);\n } catch {\n releaseErrorTrackingReservation(\n this.#errorTrackingSession,\n reserved.reservation,\n );\n }\n } catch {\n // Fail-safe: telemetry must never break the calling flow.\n }\n }\n\n #trackEvent(\n event: AnalyticsTrackingEvent,\n context: AnalyticsContext | undefined,\n purposes: AnalyticsPurpose[],\n version: string | undefined,\n ): void {\n // An event captured while the user is still undecided is held in the\n // pre-consent queue (see #sendOrQueueTrackEvent) instead of being\n // delivered, and replayed if they later opt in.\n if (!this.#isCaptureAllowed(purposes)) {\n return;\n }\n\n // if event does not have properties, send event without properties\n // and return to prevent any additional processing\n if (!event.hasProperties) {\n this.#sendOrQueueTrackEvent(\n event.name,\n undefined,\n this.#withLocationContext(context),\n purposes,\n version,\n );\n return;\n }\n\n // Track regular properties first if anonymous events feature is enabled\n if (this.#isAnonymousEventsFeatureEnabled) {\n // Note: Even if regular properties object is empty, we still send it to ensure\n // an event with user ID is tracked.\n this.#sendOrQueueTrackEvent(\n event.name,\n {\n ...event.properties,\n },\n this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n\n const hasSensitiveProperties =\n Object.keys(event.sensitiveProperties).length > 0;\n\n if (!this.#isAnonymousEventsFeatureEnabled || hasSensitiveProperties) {\n this.#sendOrQueueTrackEvent(\n event.name,\n {\n ...event.properties,\n ...event.sensitiveProperties,\n ...(hasSensitiveProperties && { anonymous: true }),\n },\n // When the anonymous events feature is enabled, this payload is the\n // anonymous one and must carry no geolocation. When the feature is\n // disabled, this is the single identified payload, so it is enriched.\n this.#isAnonymousEventsFeatureEnabled\n ? context\n : this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n }\n\n /**\n * Identify a user for analytics.\n *\n * @param traits - User traits/properties\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n identify(traits?: AnalyticsUserTraits, context?: AnalyticsContext): void {\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n this.#sendOrQueueIdentifyEvent(\n this.state.analyticsId,\n traits,\n this.#withLocationContext(context),\n );\n }\n\n /**\n * Track a page or screen view.\n *\n * @param name - The identifier/name of the page or screen being viewed (e.g., \"home\", \"settings\", \"wallet\")\n * @param properties - Optional properties associated with the view\n * @param context - Optional platform-specific context forwarded to the platform adapter.\n */\n trackView(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n const purposes = this.#purposesFromName(name);\n const version = this.#eventsConfigVersion;\n if (!this.#isCaptureAllowed(purposes)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n purposes,\n version,\n );\n }\n\n /**\n * Create an event fragment.\n *\n * A fragment accumulates properties across a user journey so that several\n * parts of a client can contribute to the same set of events without\n * re-deriving them. Declaring `successEvent` and `failureEvent` turns the\n * fragment into a funnel that {@link finalizeEventFragment} closes. Declaring\n * none of the event names makes it a pure property bag that the client reads\n * back with {@link getEventFragmentById} when it emits its own events.\n *\n * Any existing fragment with the same ID is replaced, so a new journey never\n * inherits properties from a stale one.\n *\n * Nothing is created unless the user is opted in, or undecided with the\n * pre-consent queue enabled, so an opted-out user accumulates no fragment\n * data.\n *\n * @param options - The fragment definition. An ID is generated when one is\n * not supplied.\n * @returns A read-only copy of the created fragment, or `undefined` when the\n * event fragments feature is disabled or the consent state does not allow\n * capture. Mutating the returned object does not change controller state.\n * Use {@link updateEventFragment} or {@link upsertEventFragment} to write.\n */\n createEventFragment(\n options: AnalyticsEventFragmentOptions = {},\n ): ReadonlyAnalyticsEventFragment | undefined {\n // Classify create from event names only. Caller consent context must not\n // affect the event-purpose snapshot.\n if (\n this.#shouldIgnoreEventFragmentCall('createEventFragment', {\n initialEvent: options.initialEvent,\n successEvent: options.successEvent,\n failureEvent: options.failureEvent,\n })\n ) {\n return undefined;\n }\n\n const now = Date.now();\n\n const fragment = this.#setEventFragment({\n id: options.id ?? uuid(),\n properties: { ...(options.properties ?? {}) },\n sensitiveProperties: { ...(options.sensitiveProperties ?? {}) },\n createdAt: now,\n lastUpdated: now,\n ...(options.initialEvent === undefined\n ? {}\n : { initialEvent: options.initialEvent }),\n ...(options.successEvent === undefined\n ? {}\n : { successEvent: options.successEvent }),\n ...(options.failureEvent === undefined\n ? {}\n : { failureEvent: options.failureEvent }),\n ...(options.context === undefined\n ? {}\n : { context: { ...options.context } }),\n ...(options.persist === undefined ? {} : { persist: options.persist }),\n });\n\n if (fragment.initialEvent) {\n this.#emitEventFragment(\n fragment,\n fragment.initialEvent,\n fragment.context,\n );\n }\n\n return cloneDeep(fragment);\n }\n\n /**\n * Write to an event fragment, creating a property bag if none exists.\n *\n * This is the ergonomic entry point for contributors that do not know\n * whether the journey has been started yet, and it avoids the read then\n * write race a caller would otherwise have to implement itself.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n */\n upsertEventFragment(\n id: string,\n payload: AnalyticsEventFragmentPayload = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (\n this.#shouldIgnoreEventFragmentCall('upsertEventFragment', fragment ?? {})\n ) {\n return;\n }\n\n if (!fragment) {\n this.createEventFragment({ id, ...payload });\n return;\n }\n\n this.#setEventFragment(mergeEventFragment(fragment, payload));\n }\n\n /**\n * Write to an existing event fragment.\n *\n * @param id - The fragment ID.\n * @param payload - The properties and context to merge in.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * Use {@link upsertEventFragment} when the fragment may not exist yet.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\n updateEventFragment(\n id: string,\n payload: AnalyticsEventFragmentPayload = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('updateEventFragment', fragment)) {\n return;\n }\n\n if (!fragment) {\n throw new Error(`Event fragment with id ${id} does not exist.`);\n }\n\n this.#setEventFragment(mergeEventFragment(fragment, payload));\n }\n\n /**\n * Read an event fragment.\n *\n * @param id - The fragment ID.\n * @returns A read-only copy of the fragment, or `undefined` when no fragment\n * has that ID, the event fragments feature is disabled, or the consent state\n * does not allow capture. Mutating the returned object does not change\n * controller state. Use {@link updateEventFragment} or\n * {@link upsertEventFragment} to write.\n */\n getEventFragmentById(id: string): ReadonlyAnalyticsEventFragment | undefined {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('getEventFragmentById', fragment)) {\n return undefined;\n }\n\n return fragment === undefined ? undefined : cloneDeep(fragment);\n }\n\n /**\n * Discard an event fragment without emitting anything.\n *\n * @param id - The fragment ID.\n */\n deleteEventFragment(id: string): void {\n const fragment = this.#getEventFragment(id);\n if (this.#shouldIgnoreEventFragmentCall('deleteEventFragment', fragment)) {\n return;\n }\n\n this.#removeEventFragment(id);\n }\n\n /**\n * Close an event fragment, emitting its closing event and discarding it.\n *\n * The event emitted is `failureEvent` when the journey was abandoned and\n * `successEvent` otherwise. A fragment that does not declare the relevant\n * event name is discarded silently, which is what makes a pure property bag\n * possible.\n *\n * @param id - The fragment ID.\n * @param options - Finalization options.\n * @param options.abandoned - Whether the journey was abandoned.\n * @param options.context - Context merged over the fragment's own context.\n * @throws Error if no fragment has that ID when the call is not ignored.\n * When the event fragments feature is disabled or the consent state does not\n * allow capture, the call is a logged no-op and does not throw.\n */\n finalizeEventFragment(\n id: string,\n { abandoned = false, context }: AnalyticsEventFragmentFinalizeOptions = {},\n ): void {\n const fragment = this.#getEventFragment(id);\n if (\n this.#shouldIgnoreEventFragmentCall('finalizeEventFragment', fragment)\n ) {\n return;\n }\n\n if (!fragment) {\n throw new Error(`Event fragment with id ${id} does not exist.`);\n }\n\n const eventName = abandoned ? fragment.failureEvent : fragment.successEvent;\n\n if (eventName) {\n this.#emitEventFragment(\n fragment,\n eventName,\n mergeEventFragmentContext(fragment.context, context),\n );\n }\n\n this.#removeEventFragment(id);\n }\n\n /**\n * Opt in to analytics.\n *\n * Records that a consent decision has been made and replays any events that\n * were queued while the user was undecided.\n *\n * When geolocation enrichment is enabled, geolocation is resolved here (once\n * the user has consented) and awaited before the queued events are replayed,\n * so those events are enriched with the resolved location as they are sent.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\n async optIn(): Promise<void> {\n this.update((state) => {\n state.optedIn = true;\n state.consentDecisionMade = true;\n });\n\n // Now that the user has consented, resolve geolocation (once) and wait for\n // it so the queued pre-consent events can be enriched as they replay.\n await this.#maybeResolveLocation();\n\n // Reconcile against the current state rather than replaying blindly: the\n // consent decision may have changed while geolocation was resolving (e.g.\n // resetConsentDecision ran during the await), and preserved pre-consent\n // events must not be delivered once the user is no longer opted in.\n this.#replayPreConsentEvents();\n }\n\n /**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events and in-progress event fragments so nothing captured before the\n * decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Reset the consent decision back to undecided.\n *\n * Intended for client flows that restart onboarding. Clears the opt-in\n * preference and discards the delivery queue, but preserves any pre-consent\n * events so they can still be replayed if the user opts in again. The user is\n * treated as undecided again.\n *\n * In-progress event fragments are kept only while the undecided user can\n * still accumulate them, and discarded otherwise, so no fragment outlives the\n * consent state that allowed it.\n */\n resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Opt in to marketing analytics.\n *\n * Independent of {@link optIn}. Replays queued marketing events.\n *\n * @returns A promise that resolves once opt-in processing has completed.\n */\n async optInToMarketing(): Promise<void> {\n this.update((state) => {\n state.optedInToMarketing = true;\n state.marketingConsentDecisionMade = true;\n });\n\n await this.#maybeResolveLocation();\n this.#replayPreConsentEvents();\n }\n\n /**\n * Opt out of marketing analytics.\n *\n * Independent of {@link optOut}. Discards queued marketing events and\n * marketing event fragments.\n */\n optOutOfMarketing(): void {\n this.update((state) => {\n state.optedInToMarketing = false;\n state.marketingConsentDecisionMade = true;\n state.marketingCampaignCookieId = null;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Reset the marketing consent decision back to undecided.\n *\n * Independent of {@link resetConsentDecision}.\n */\n resetMarketingConsentDecision(): void {\n this.update((state) => {\n state.optedInToMarketing = false;\n state.marketingConsentDecisionMade = false;\n state.marketingCampaignCookieId = null;\n });\n\n this.#pruneAllForConsent();\n }\n\n /**\n * Set the marketing campaign cookie ID.\n *\n * Stores the ID of the marketing campaign cookie (e.g. a Google Analytics\n * client ID) that was active when the user arrived. Pass `null` to clear it.\n * The value is automatically cleared by {@link optOutOfMarketing} and\n * {@link resetMarketingConsentDecision}.\n *\n * @param marketingCampaignCookieId - The marketing campaign cookie ID, or\n * `null` to clear it.\n */\n setMarketingCampaignCookieId(marketingCampaignCookieId: string | null): void {\n this.update((state) => {\n state.marketingCampaignCookieId = marketingCampaignCookieId;\n });\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { AnalyticsTrackingEvent } from './AnalyticsPlatformAdapter.types.js';
1
+ import type { AnalyticsEventProperties, AnalyticsTrackingEvent } from './AnalyticsPlatformAdapter.types.js';
2
2
  /**
3
3
  * Analytics event emitted for a caught error.
4
4
  */
@@ -89,12 +89,13 @@ export type ReservedErrorOccurredEvent = {
89
89
  * @param session - Mutable per-controller volume-control state.
90
90
  * @param error - The caught value to report.
91
91
  * @param options - Source and optional volume controls.
92
+ * @param properties - Optional extra properties merged into the payload.
92
93
  * @param now - Current time in milliseconds. Defaults to `Date.now()`.
93
94
  * @param randomSample - Sample in [0, 1). Defaults to `Math.random()`.
94
95
  * @returns The event to send and its reservation, or `undefined` when the
95
96
  * error is suppressed.
96
97
  */
97
- export declare function reserveErrorOccurredEvent(session: ErrorTrackingSession, error: unknown, options: TrackErrorAsAnalyticsOptions, now?: number, randomSample?: number): ReservedErrorOccurredEvent | undefined;
98
+ export declare function reserveErrorOccurredEvent(session: ErrorTrackingSession, error: unknown, options: TrackErrorAsAnalyticsOptions, properties?: AnalyticsEventProperties, now?: number, randomSample?: number): ReservedErrorOccurredEvent | undefined;
98
99
  /**
99
100
  * Releases a reservation after delivery throws.
100
101
  *
@@ -1 +1 @@
1
- {"version":3,"file":"track-error-as-analytics.d.ts","sourceRoot":"","sources":["../src/track-error-as-analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAElF;;GAEG;AACH,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAS1D;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B;aACzC,YAAY,EAAE,CAAC;aACf,aAAa,EAAE,KAAM;aACrB,UAAU,EAAE,EAAE;CACN,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC3C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,WAAW,EAAE,wBAAwB,CAAC;CACvC,CAAC;AAyFF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,4BAA4B,EACrC,GAAG,GAAE,MAAmB,EACxB,YAAY,GAAE,MAAsB,GACnC,0BAA0B,GAAG,SAAS,CA0DxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,oBAAoB,EAC7B,WAAW,EAAE,wBAAwB,GACpC,IAAI,CAQN"}
1
+ {"version":3,"file":"track-error-as-analytics.d.ts","sourceRoot":"","sources":["../src/track-error-as-analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,qCAAqC,CAAC;AAE7C;;GAEG;AACH,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAS1D;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B;aACzC,YAAY,EAAE,CAAC;aACf,aAAa,EAAE,KAAM;aACrB,UAAU,EAAE,EAAE;CACN,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC3C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,WAAW,EAAE,wBAAwB,CAAC;CACvC,CAAC;AAyFF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,4BAA4B,EACrC,UAAU,CAAC,EAAE,wBAAwB,EACrC,GAAG,GAAE,MAAmB,EACxB,YAAY,GAAE,MAAsB,GACnC,0BAA0B,GAAG,SAAS,CA2DxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,oBAAoB,EAC7B,WAAW,EAAE,wBAAwB,GACpC,IAAI,CAQN"}
@@ -102,12 +102,13 @@ function resolveNonNegativeNumber(value, fallback) {
102
102
  * @param session - Mutable per-controller volume-control state.
103
103
  * @param error - The caught value to report.
104
104
  * @param options - Source and optional volume controls.
105
+ * @param properties - Optional extra properties merged into the payload.
105
106
  * @param now - Current time in milliseconds. Defaults to `Date.now()`.
106
107
  * @param randomSample - Sample in [0, 1). Defaults to `Math.random()`.
107
108
  * @returns The event to send and its reservation, or `undefined` when the
108
109
  * error is suppressed.
109
110
  */
110
- export function reserveErrorOccurredEvent(session, error, options, now = Date.now(), randomSample = Math.random()) {
111
+ export function reserveErrorOccurredEvent(session, error, options, properties, now = Date.now(), randomSample = Math.random()) {
111
112
  const { source } = options;
112
113
  const volumeControls = options.volumeControls ?? {};
113
114
  const samplingRate = resolveSamplingRate(volumeControls.samplingRate);
@@ -139,6 +140,7 @@ export function reserveErrorOccurredEvent(session, error, options, now = Date.no
139
140
  event: {
140
141
  name: ERROR_OCCURRED_EVENT_NAME,
141
142
  properties: {
143
+ ...(properties ?? {}),
142
144
  source,
143
145
  error_message: errorMessage,
144
146
  },
@@ -1 +1 @@
1
- {"version":3,"file":"track-error-as-analytics.js","sourceRoot":"","sources":["../src/track-error-as-analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnalyticsTrackingEvent } from './AnalyticsPlatformAdapter.types.js';\n\n/**\n * Analytics event emitted for a caught error.\n */\nexport const ERROR_OCCURRED_EVENT_NAME = 'Error Occurred';\n\n/**\n * Segment-safe cap for `error_message`, including the truncation ellipsis.\n */\nconst MAX_ERROR_MESSAGE_LENGTH = 500;\n\nconst ELLIPSIS = '…';\n\n/**\n * Default volume-control configuration for {@link reserveErrorOccurredEvent}.\n *\n * `samplingRate` is the probability from 0 to 1 that an individual event is emitted.\n * `dedupWindowMs` suppresses a repeated signature inside this window.\n * `sessionCap` is the maximum number of events emitted per controller instance.\n * A caller-supplied cap above this default is clamped down to it.\n */\nexport const DEFAULT_ERROR_TRACKING_OPTIONS = {\n samplingRate: 1,\n dedupWindowMs: 10_000,\n sessionCap: 50,\n} as const;\n\n/**\n * Volume-control knobs for {@link reserveErrorOccurredEvent}.\n */\nexport type TrackErrorVolumeControls = {\n /**\n * Per-event sampling rate between 0 and 1. 1 always emits.\n */\n samplingRate?: number;\n\n /**\n * Deduplication window in milliseconds. Events that share a signature\n * inside this window are suppressed.\n */\n dedupWindowMs?: number;\n\n /**\n * Maximum number of events emitted for this controller instance.\n * May only lower {@link DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap}.\n * Larger values are clamped so a caller cannot raise the session bound.\n */\n sessionCap?: number;\n};\n\n/**\n * Options for reporting a caught error as an `Error Occurred` event.\n */\nexport type TrackErrorAsAnalyticsOptions = {\n /**\n * Unique discriminator for this error case.\n *\n * Use one value per distinct failure circumstance (for example\n * `swap-quote-fetch` or `clear-metametrics-data`), not a shared label for\n * every error in a file or package.\n */\n source: string;\n\n /**\n * Optional sampling, deduplication, and session-cap overrides.\n */\n volumeControls?: TrackErrorVolumeControls;\n};\n\n/**\n * In-memory volume-control state for one controller instance.\n *\n * This is not persisted. A new controller starts with a fresh budget.\n */\nexport type ErrorTrackingSession = {\n emittedCount: number;\n lastEmittedAtBySignature: Map<string, number>;\n};\n\n/**\n * Budget held for an event that has not yet been delivered.\n */\nexport type ErrorTrackingReservation = {\n signature: string;\n reservedAt: number;\n};\n\n/**\n * An event that passed volume controls, plus the reservation to release if\n * delivery throws.\n */\nexport type ReservedErrorOccurredEvent = {\n event: AnalyticsTrackingEvent;\n reservation: ErrorTrackingReservation;\n};\n\n/**\n * Normalizes whitespace and caps length for an analytics `error_message`.\n *\n * @param message - Raw error text.\n * @returns Sanitized message safe to send in analytics.\n */\nfunction sanitizeErrorMessage(message: string): string {\n const trimmed = message.replace(/\\s+/gu, ' ').trim();\n if (trimmed.length <= MAX_ERROR_MESSAGE_LENGTH) {\n return trimmed;\n }\n\n return `${trimmed.slice(0, MAX_ERROR_MESSAGE_LENGTH - ELLIPSIS.length)}${ELLIPSIS}`;\n}\n\n/**\n * Normalizes any thrown value into a sanitized message.\n *\n * Stack traces are dropped. `null` and `undefined` become `Unknown error`.\n *\n * @param error - The caught value to normalize.\n * @returns The sanitized error message.\n */\nfunction normalizeError(error: unknown): string {\n if (error instanceof Error) {\n return sanitizeErrorMessage(error.message);\n }\n\n if (error === null || error === undefined) {\n return 'Unknown error';\n }\n\n if (typeof error === 'string') {\n return sanitizeErrorMessage(error);\n }\n\n if (\n typeof error === 'object' &&\n 'message' in error &&\n typeof error.message === 'string'\n ) {\n return sanitizeErrorMessage(error.message);\n }\n\n if (typeof error === 'number') {\n return sanitizeErrorMessage(error.toString());\n }\n\n return 'Unknown error';\n}\n\n/**\n * Resolves the sampling rate, clamping to [0, 1] and falling back to the\n * default for non-finite values. A caller bug must not disable sampling:\n * NaN comparisons are always false and would otherwise emit every event.\n *\n * @param samplingRate - Caller-supplied rate, or undefined for the default.\n * @returns A finite sampling rate between 0 and 1.\n */\nfunction resolveSamplingRate(samplingRate?: number): number {\n const fallback = DEFAULT_ERROR_TRACKING_OPTIONS.samplingRate;\n if (typeof samplingRate !== 'number' || !Number.isFinite(samplingRate)) {\n return fallback;\n }\n\n return Math.min(Math.max(samplingRate, 0), 1);\n}\n\n/**\n * Resolves a volume-control number, falling back to the default for\n * non-finite or negative values.\n *\n * @param value - Caller-supplied value, or undefined for the default.\n * @param fallback - Default used when the input is invalid.\n * @returns A finite, non-negative number.\n */\nfunction resolveNonNegativeNumber(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n return fallback;\n }\n\n return value;\n}\n\n/**\n * Applies volume controls and reserves session budget when the error should\n * be emitted.\n *\n * The signature is an unambiguous JSON encoding of the event name, source,\n * and sanitized message, so values that contain `:` cannot collide.\n * Call {@link releaseErrorTrackingReservation} if delivery throws, so a\n * failed report does not consume the cap or suppress the next attempt.\n *\n * @param session - Mutable per-controller volume-control state.\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param now - Current time in milliseconds. Defaults to `Date.now()`.\n * @param randomSample - Sample in [0, 1). Defaults to `Math.random()`.\n * @returns The event to send and its reservation, or `undefined` when the\n * error is suppressed.\n */\nexport function reserveErrorOccurredEvent(\n session: ErrorTrackingSession,\n error: unknown,\n options: TrackErrorAsAnalyticsOptions,\n now: number = Date.now(),\n randomSample: number = Math.random(),\n): ReservedErrorOccurredEvent | undefined {\n const { source } = options;\n const volumeControls = options.volumeControls ?? {};\n const samplingRate = resolveSamplingRate(volumeControls.samplingRate);\n const dedupWindowMs = resolveNonNegativeNumber(\n volumeControls.dedupWindowMs,\n DEFAULT_ERROR_TRACKING_OPTIONS.dedupWindowMs,\n );\n // The global cap is a hard ceiling. A caller-supplied sessionCap may\n // only lower it, never raise it.\n const sessionCap = Math.min(\n resolveNonNegativeNumber(\n volumeControls.sessionCap,\n DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap,\n ),\n DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap,\n );\n\n if (session.emittedCount >= sessionCap) {\n return undefined;\n }\n\n // Inclusive comparison so a rate of 0 never emits, including on a 0 draw.\n if (randomSample >= samplingRate) {\n return undefined;\n }\n\n const errorMessage = normalizeError(error);\n const signature = JSON.stringify([\n ERROR_OCCURRED_EVENT_NAME,\n source,\n errorMessage,\n ]);\n const lastEmittedAt = session.lastEmittedAtBySignature.get(signature);\n if (lastEmittedAt !== undefined && now - lastEmittedAt < dedupWindowMs) {\n return undefined;\n }\n\n const reservedAt = now;\n session.emittedCount += 1;\n session.lastEmittedAtBySignature.set(signature, reservedAt);\n\n return {\n event: {\n name: ERROR_OCCURRED_EVENT_NAME,\n properties: {\n source,\n error_message: errorMessage,\n },\n sensitiveProperties: {},\n saveDataRecording: false,\n hasProperties: true,\n },\n reservation: {\n signature,\n reservedAt,\n },\n };\n}\n\n/**\n * Releases a reservation after delivery throws.\n *\n * The session budget is not consumed, and the next identical error is not\n * suppressed by deduplication.\n *\n * @param session - Mutable per-controller volume-control state.\n * @param reservation - The reservation returned with the event.\n */\nexport function releaseErrorTrackingReservation(\n session: ErrorTrackingSession,\n reservation: ErrorTrackingReservation,\n): void {\n session.emittedCount = Math.max(0, session.emittedCount - 1);\n if (\n session.lastEmittedAtBySignature.get(reservation.signature) ===\n reservation.reservedAt\n ) {\n session.lastEmittedAtBySignature.delete(reservation.signature);\n }\n}\n"]}
1
+ {"version":3,"file":"track-error-as-analytics.js","sourceRoot":"","sources":["../src/track-error-as-analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AnalyticsEventProperties,\n AnalyticsTrackingEvent,\n} from './AnalyticsPlatformAdapter.types.js';\n\n/**\n * Analytics event emitted for a caught error.\n */\nexport const ERROR_OCCURRED_EVENT_NAME = 'Error Occurred';\n\n/**\n * Segment-safe cap for `error_message`, including the truncation ellipsis.\n */\nconst MAX_ERROR_MESSAGE_LENGTH = 500;\n\nconst ELLIPSIS = '…';\n\n/**\n * Default volume-control configuration for {@link reserveErrorOccurredEvent}.\n *\n * `samplingRate` is the probability from 0 to 1 that an individual event is emitted.\n * `dedupWindowMs` suppresses a repeated signature inside this window.\n * `sessionCap` is the maximum number of events emitted per controller instance.\n * A caller-supplied cap above this default is clamped down to it.\n */\nexport const DEFAULT_ERROR_TRACKING_OPTIONS = {\n samplingRate: 1,\n dedupWindowMs: 10_000,\n sessionCap: 50,\n} as const;\n\n/**\n * Volume-control knobs for {@link reserveErrorOccurredEvent}.\n */\nexport type TrackErrorVolumeControls = {\n /**\n * Per-event sampling rate between 0 and 1. 1 always emits.\n */\n samplingRate?: number;\n\n /**\n * Deduplication window in milliseconds. Events that share a signature\n * inside this window are suppressed.\n */\n dedupWindowMs?: number;\n\n /**\n * Maximum number of events emitted for this controller instance.\n * May only lower {@link DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap}.\n * Larger values are clamped so a caller cannot raise the session bound.\n */\n sessionCap?: number;\n};\n\n/**\n * Options for reporting a caught error as an `Error Occurred` event.\n */\nexport type TrackErrorAsAnalyticsOptions = {\n /**\n * Unique discriminator for this error case.\n *\n * Use one value per distinct failure circumstance (for example\n * `swap-quote-fetch` or `clear-metametrics-data`), not a shared label for\n * every error in a file or package.\n */\n source: string;\n\n /**\n * Optional sampling, deduplication, and session-cap overrides.\n */\n volumeControls?: TrackErrorVolumeControls;\n};\n\n/**\n * In-memory volume-control state for one controller instance.\n *\n * This is not persisted. A new controller starts with a fresh budget.\n */\nexport type ErrorTrackingSession = {\n emittedCount: number;\n lastEmittedAtBySignature: Map<string, number>;\n};\n\n/**\n * Budget held for an event that has not yet been delivered.\n */\nexport type ErrorTrackingReservation = {\n signature: string;\n reservedAt: number;\n};\n\n/**\n * An event that passed volume controls, plus the reservation to release if\n * delivery throws.\n */\nexport type ReservedErrorOccurredEvent = {\n event: AnalyticsTrackingEvent;\n reservation: ErrorTrackingReservation;\n};\n\n/**\n * Normalizes whitespace and caps length for an analytics `error_message`.\n *\n * @param message - Raw error text.\n * @returns Sanitized message safe to send in analytics.\n */\nfunction sanitizeErrorMessage(message: string): string {\n const trimmed = message.replace(/\\s+/gu, ' ').trim();\n if (trimmed.length <= MAX_ERROR_MESSAGE_LENGTH) {\n return trimmed;\n }\n\n return `${trimmed.slice(0, MAX_ERROR_MESSAGE_LENGTH - ELLIPSIS.length)}${ELLIPSIS}`;\n}\n\n/**\n * Normalizes any thrown value into a sanitized message.\n *\n * Stack traces are dropped. `null` and `undefined` become `Unknown error`.\n *\n * @param error - The caught value to normalize.\n * @returns The sanitized error message.\n */\nfunction normalizeError(error: unknown): string {\n if (error instanceof Error) {\n return sanitizeErrorMessage(error.message);\n }\n\n if (error === null || error === undefined) {\n return 'Unknown error';\n }\n\n if (typeof error === 'string') {\n return sanitizeErrorMessage(error);\n }\n\n if (\n typeof error === 'object' &&\n 'message' in error &&\n typeof error.message === 'string'\n ) {\n return sanitizeErrorMessage(error.message);\n }\n\n if (typeof error === 'number') {\n return sanitizeErrorMessage(error.toString());\n }\n\n return 'Unknown error';\n}\n\n/**\n * Resolves the sampling rate, clamping to [0, 1] and falling back to the\n * default for non-finite values. A caller bug must not disable sampling:\n * NaN comparisons are always false and would otherwise emit every event.\n *\n * @param samplingRate - Caller-supplied rate, or undefined for the default.\n * @returns A finite sampling rate between 0 and 1.\n */\nfunction resolveSamplingRate(samplingRate?: number): number {\n const fallback = DEFAULT_ERROR_TRACKING_OPTIONS.samplingRate;\n if (typeof samplingRate !== 'number' || !Number.isFinite(samplingRate)) {\n return fallback;\n }\n\n return Math.min(Math.max(samplingRate, 0), 1);\n}\n\n/**\n * Resolves a volume-control number, falling back to the default for\n * non-finite or negative values.\n *\n * @param value - Caller-supplied value, or undefined for the default.\n * @param fallback - Default used when the input is invalid.\n * @returns A finite, non-negative number.\n */\nfunction resolveNonNegativeNumber(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n return fallback;\n }\n\n return value;\n}\n\n/**\n * Applies volume controls and reserves session budget when the error should\n * be emitted.\n *\n * The signature is an unambiguous JSON encoding of the event name, source,\n * and sanitized message, so values that contain `:` cannot collide.\n * Call {@link releaseErrorTrackingReservation} if delivery throws, so a\n * failed report does not consume the cap or suppress the next attempt.\n *\n * @param session - Mutable per-controller volume-control state.\n * @param error - The caught value to report.\n * @param options - Source and optional volume controls.\n * @param properties - Optional extra properties merged into the payload.\n * @param now - Current time in milliseconds. Defaults to `Date.now()`.\n * @param randomSample - Sample in [0, 1). Defaults to `Math.random()`.\n * @returns The event to send and its reservation, or `undefined` when the\n * error is suppressed.\n */\nexport function reserveErrorOccurredEvent(\n session: ErrorTrackingSession,\n error: unknown,\n options: TrackErrorAsAnalyticsOptions,\n properties?: AnalyticsEventProperties,\n now: number = Date.now(),\n randomSample: number = Math.random(),\n): ReservedErrorOccurredEvent | undefined {\n const { source } = options;\n const volumeControls = options.volumeControls ?? {};\n const samplingRate = resolveSamplingRate(volumeControls.samplingRate);\n const dedupWindowMs = resolveNonNegativeNumber(\n volumeControls.dedupWindowMs,\n DEFAULT_ERROR_TRACKING_OPTIONS.dedupWindowMs,\n );\n // The global cap is a hard ceiling. A caller-supplied sessionCap may\n // only lower it, never raise it.\n const sessionCap = Math.min(\n resolveNonNegativeNumber(\n volumeControls.sessionCap,\n DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap,\n ),\n DEFAULT_ERROR_TRACKING_OPTIONS.sessionCap,\n );\n\n if (session.emittedCount >= sessionCap) {\n return undefined;\n }\n\n // Inclusive comparison so a rate of 0 never emits, including on a 0 draw.\n if (randomSample >= samplingRate) {\n return undefined;\n }\n\n const errorMessage = normalizeError(error);\n const signature = JSON.stringify([\n ERROR_OCCURRED_EVENT_NAME,\n source,\n errorMessage,\n ]);\n const lastEmittedAt = session.lastEmittedAtBySignature.get(signature);\n if (lastEmittedAt !== undefined && now - lastEmittedAt < dedupWindowMs) {\n return undefined;\n }\n\n const reservedAt = now;\n session.emittedCount += 1;\n session.lastEmittedAtBySignature.set(signature, reservedAt);\n\n return {\n event: {\n name: ERROR_OCCURRED_EVENT_NAME,\n properties: {\n ...(properties ?? {}),\n source,\n error_message: errorMessage,\n },\n sensitiveProperties: {},\n saveDataRecording: false,\n hasProperties: true,\n },\n reservation: {\n signature,\n reservedAt,\n },\n };\n}\n\n/**\n * Releases a reservation after delivery throws.\n *\n * The session budget is not consumed, and the next identical error is not\n * suppressed by deduplication.\n *\n * @param session - Mutable per-controller volume-control state.\n * @param reservation - The reservation returned with the event.\n */\nexport function releaseErrorTrackingReservation(\n session: ErrorTrackingSession,\n reservation: ErrorTrackingReservation,\n): void {\n session.emittedCount = Math.max(0, session.emittedCount - 1);\n if (\n session.lastEmittedAtBySignature.get(reservation.signature) ===\n reservation.reservedAt\n ) {\n session.lastEmittedAtBySignature.delete(reservation.signature);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/analytics-controller",
3
- "version": "4.2.0-preview-9dfb538be",
3
+ "version": "4.3.0-preview-04cf473",
4
4
  "description": "Common Analytics controller for event tracking",
5
5
  "keywords": [
6
6
  "Ethereum",