@metamask-previews/analytics-controller 1.2.1-preview-a42e8d0d2 → 2.0.0-preview-d2f661012
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 +8 -4
- package/dist/AnalyticsController-method-action-types.cjs.map +1 -1
- package/dist/AnalyticsController-method-action-types.d.cts +6 -0
- package/dist/AnalyticsController-method-action-types.d.cts.map +1 -1
- package/dist/AnalyticsController-method-action-types.d.mts +6 -0
- package/dist/AnalyticsController-method-action-types.d.mts.map +1 -1
- package/dist/AnalyticsController-method-action-types.mjs.map +1 -1
- package/dist/AnalyticsController.cjs +66 -45
- package/dist/AnalyticsController.cjs.map +1 -1
- package/dist/AnalyticsController.d.cts +18 -14
- package/dist/AnalyticsController.d.cts.map +1 -1
- package/dist/AnalyticsController.d.mts +18 -14
- package/dist/AnalyticsController.d.mts.map +1 -1
- package/dist/AnalyticsController.mjs +66 -45
- package/dist/AnalyticsController.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0]
|
|
11
|
+
|
|
10
12
|
### Changed
|
|
11
13
|
|
|
12
|
-
- **BREAKING:** Optionally enrich non-anonymous track, identify, and view payloads with the user's `country_code`, `region`, and `timezone` under `context.location`, gated behind the new `isGeolocationEnabled` constructor option (default `false`) ([#9691](https://github.com/MetaMask/core/pull/9691))
|
|
13
|
-
- `AnalyticsController.init`
|
|
14
|
-
- When
|
|
14
|
+
- **BREAKING:** Optionally enrich non-anonymous track, identify, and view payloads with the user's `country_code`, `region`, and `timezone` under `context.location`, gated behind the new `isGeolocationEnabled` constructor option (default `false`) ([#9691](https://github.com/MetaMask/core/pull/9691), [#9728](https://github.com/MetaMask/core/pull/9728))
|
|
15
|
+
- `AnalyticsController.init` and `AnalyticsController.optIn` are now asynchronous and return a `Promise<void>`, so await them before tracking events
|
|
16
|
+
- When enabled, geolocation is resolved via `GeolocationController:getGeolocationData` (which compositions must register) only after the user opts in, so location is never requested before they consent to analytics; queued pre-consent events are then enriched on replay (anonymous payloads excluded)
|
|
15
17
|
- Adds `@metamask/geolocation-controller` `^0.1.3` as a dependency
|
|
16
18
|
- Bump `@metamask/messenger` from `^1.2.0` to `^2.0.0` ([#9392](https://github.com/MetaMask/core/pull/9392))
|
|
19
|
+
- Bump `@metamask/geolocation-controller` from `^0.1.3` to `^1.0.0` ([#9735](https://github.com/MetaMask/core/pull/9735))
|
|
17
20
|
|
|
18
21
|
## [1.2.1]
|
|
19
22
|
|
|
@@ -65,7 +68,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
65
68
|
|
|
66
69
|
- Initial release of @metamask/analytics-controller. ([#7017](https://github.com/MetaMask/core/pull/7017), [#7202](https://github.com/MetaMask/core/pull/7202))
|
|
67
70
|
|
|
68
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@
|
|
71
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@2.0.0...HEAD
|
|
72
|
+
[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@1.2.1...@metamask/analytics-controller@2.0.0
|
|
69
73
|
[1.2.1]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@1.2.0...@metamask/analytics-controller@1.2.1
|
|
70
74
|
[1.2.0]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@1.1.1...@metamask/analytics-controller@1.2.0
|
|
71
75
|
[1.1.1]: https://github.com/MetaMask/core/compare/@metamask/analytics-controller@1.1.0...@metamask/analytics-controller@1.1.1
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController-method-action-types.cjs","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 * 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 * 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 */\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 so nothing captured before the 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 */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction;\n"]}
|
|
1
|
+
{"version":3,"file":"AnalyticsController-method-action-types.cjs","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 * 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 * 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 so nothing captured before the 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 */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction;\n"]}
|
|
@@ -41,6 +41,12 @@ export type AnalyticsControllerTrackViewAction = {
|
|
|
41
41
|
*
|
|
42
42
|
* Records that a consent decision has been made and replays any events that
|
|
43
43
|
* were queued while the user was undecided.
|
|
44
|
+
*
|
|
45
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
46
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
47
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
48
|
+
*
|
|
49
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
44
50
|
*/
|
|
45
51
|
export type AnalyticsControllerOptInAction = {
|
|
46
52
|
type: `AnalyticsController:optIn`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController-method-action-types.d.cts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAiC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,gCAAgC,CAAC;IACvC,OAAO,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,8BAA8B,CAAC;IACrC,OAAO,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,+BAA+B,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"AnalyticsController-method-action-types.d.cts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAiC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,gCAAgC,CAAC;IACvC,OAAO,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,8BAA8B,CAAC;IACrC,OAAO,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,+BAA+B,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,4BAA4B,CAAC;IACnC,OAAO,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;CACxC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,6CAA6C,GAAG;IAC1D,IAAI,EAAE,0CAA0C,CAAC;IACjD,OAAO,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;CACtD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GACxC,mCAAmC,GACnC,iCAAiC,GACjC,kCAAkC,GAClC,8BAA8B,GAC9B,+BAA+B,GAC/B,6CAA6C,CAAC"}
|
|
@@ -41,6 +41,12 @@ export type AnalyticsControllerTrackViewAction = {
|
|
|
41
41
|
*
|
|
42
42
|
* Records that a consent decision has been made and replays any events that
|
|
43
43
|
* were queued while the user was undecided.
|
|
44
|
+
*
|
|
45
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
46
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
47
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
48
|
+
*
|
|
49
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
44
50
|
*/
|
|
45
51
|
export type AnalyticsControllerOptInAction = {
|
|
46
52
|
type: `AnalyticsController:optIn`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController-method-action-types.d.mts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAiC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,gCAAgC,CAAC;IACvC,OAAO,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,8BAA8B,CAAC;IACrC,OAAO,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,+BAA+B,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"AnalyticsController-method-action-types.d.mts","sourceRoot":"","sources":["../src/AnalyticsController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAiC;AAEpE;;;;;;;GAOG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,gCAAgC,CAAC;IACvC,OAAO,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,8BAA8B,CAAC;IACrC,OAAO,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,+BAA+B,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,4BAA4B,CAAC;IACnC,OAAO,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;CACxC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,6CAA6C,GAAG;IAC1D,IAAI,EAAE,0CAA0C,CAAC;IACjD,OAAO,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;CACtD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gCAAgC,GACxC,mCAAmC,GACnC,iCAAiC,GACjC,kCAAkC,GAClC,8BAA8B,GAC9B,+BAA+B,GAC/B,6CAA6C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController-method-action-types.mjs","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 * 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 * 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 */\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 so nothing captured before the 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 */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction;\n"]}
|
|
1
|
+
{"version":3,"file":"AnalyticsController-method-action-types.mjs","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 * 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 * 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 so nothing captured before the 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 */\nexport type AnalyticsControllerResetConsentDecisionAction = {\n type: `AnalyticsController:resetConsentDecision`;\n handler: AnalyticsController['resetConsentDecision'];\n};\n\n/**\n * Union of all AnalyticsController action types.\n */\nexport type AnalyticsControllerMethodActions =\n | AnalyticsControllerTrackEventAction\n | AnalyticsControllerIdentifyAction\n | AnalyticsControllerTrackViewAction\n | AnalyticsControllerOptInAction\n | AnalyticsControllerOptOutAction\n | AnalyticsControllerResetConsentDecisionAction;\n"]}
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _AnalyticsController_instances, _AnalyticsController_platformAdapter, _AnalyticsController_isAnonymousEventsFeatureEnabled, _AnalyticsController_isEventQueuePersistenceEnabled, _AnalyticsController_isPreConsentQueueEnabled, _AnalyticsController_isGeolocationEnabled, _AnalyticsController_initPromise, _AnalyticsController_locationContext, _AnalyticsController_performInit, _AnalyticsController_resolveLocationContext, _AnalyticsController_withLocationContext, _AnalyticsController_sendOrQueueTrackEvent, _AnalyticsController_sendOrQueueIdentifyEvent, _AnalyticsController_sendOrQueueViewEvent, _AnalyticsController_enqueueEvent, _AnalyticsController_sendQueuedEvent, _AnalyticsController_replayQueuedEvents, _AnalyticsController_removeQueuedEvent, _AnalyticsController_clearQueuedEvents, _AnalyticsController_enqueuePreConsentEvent, _AnalyticsController_replayPreConsentEvents, _AnalyticsController_clearPreConsentEvents, _AnalyticsController_reconcilePreConsentEvents;
|
|
13
|
+
var _AnalyticsController_instances, _AnalyticsController_platformAdapter, _AnalyticsController_isAnonymousEventsFeatureEnabled, _AnalyticsController_isEventQueuePersistenceEnabled, _AnalyticsController_isPreConsentQueueEnabled, _AnalyticsController_isGeolocationEnabled, _AnalyticsController_initPromise, _AnalyticsController_locationResolvePromise, _AnalyticsController_locationContext, _AnalyticsController_performInit, _AnalyticsController_maybeResolveLocation, _AnalyticsController_resolveLocationContext, _AnalyticsController_withLocationContext, _AnalyticsController_sendOrQueueTrackEvent, _AnalyticsController_sendOrQueueIdentifyEvent, _AnalyticsController_sendOrQueueViewEvent, _AnalyticsController_enqueueEvent, _AnalyticsController_sendQueuedEvent, _AnalyticsController_replayQueuedEvents, _AnalyticsController_removeQueuedEvent, _AnalyticsController_clearQueuedEvents, _AnalyticsController_enqueuePreConsentEvent, _AnalyticsController_replayPreConsentEvents, _AnalyticsController_enrichPreConsentEvent, _AnalyticsController_clearPreConsentEvents, _AnalyticsController_reconcilePreConsentEvents;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.AnalyticsController = exports.getDefaultAnalyticsControllerState = exports.controllerName = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -211,6 +211,11 @@ class AnalyticsController extends base_controller_1.BaseController {
|
|
|
211
211
|
* await the same work rather than observing a premature completion.
|
|
212
212
|
*/
|
|
213
213
|
_AnalyticsController_initPromise.set(this, void 0);
|
|
214
|
+
/**
|
|
215
|
+
* The in-flight (or settled) geolocation resolution, if any. Its presence
|
|
216
|
+
* marks that resolution has been started, so it runs at most once.
|
|
217
|
+
*/
|
|
218
|
+
_AnalyticsController_locationResolvePromise.set(this, void 0);
|
|
214
219
|
_AnalyticsController_locationContext.set(this, void 0);
|
|
215
220
|
__classPrivateFieldSet(this, _AnalyticsController_isAnonymousEventsFeatureEnabled, isAnonymousEventsFeatureEnabled, "f");
|
|
216
221
|
__classPrivateFieldSet(this, _AnalyticsController_isEventQueuePersistenceEnabled, isEventQueuePersistenceEnabled, "f");
|
|
@@ -218,6 +223,7 @@ class AnalyticsController extends base_controller_1.BaseController {
|
|
|
218
223
|
__classPrivateFieldSet(this, _AnalyticsController_isGeolocationEnabled, isGeolocationEnabled, "f");
|
|
219
224
|
__classPrivateFieldSet(this, _AnalyticsController_platformAdapter, platformAdapter, "f");
|
|
220
225
|
__classPrivateFieldSet(this, _AnalyticsController_initPromise, undefined, "f");
|
|
226
|
+
__classPrivateFieldSet(this, _AnalyticsController_locationResolvePromise, undefined, "f");
|
|
221
227
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
222
228
|
(0, AnalyticsLogger_js_1.projectLogger)('AnalyticsController initialized and ready', {
|
|
223
229
|
enabled: selectors_js_1.analyticsControllerSelectors.selectEnabled(this.state),
|
|
@@ -230,20 +236,18 @@ class AnalyticsController extends base_controller_1.BaseController {
|
|
|
230
236
|
});
|
|
231
237
|
}
|
|
232
238
|
/**
|
|
233
|
-
* Initialize the controller by
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* process.
|
|
237
|
-
*
|
|
238
|
-
* Geolocation is resolved before any queued event is replayed so that
|
|
239
|
-
* replayed events carry the same location context as new ones.
|
|
239
|
+
* Initialize the controller by calling the platform adapter's
|
|
240
|
+
* onSetupCompleted lifecycle hook and replaying any queued events. This
|
|
241
|
+
* method must be called after construction to complete the setup process.
|
|
240
242
|
*
|
|
241
|
-
* When geolocation enrichment is enabled (`isGeolocationEnabled`),
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
243
|
+
* When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation
|
|
244
|
+
* is resolved only for a user who is already opted in; for undecided or
|
|
245
|
+
* opted-out users it is deferred until they opt in (see {@link optIn}), so a
|
|
246
|
+
* user's location is never requested before they consent to analytics. In
|
|
247
|
+
* either case the `GeolocationController` and its
|
|
248
|
+
* `GeolocationController:getGeolocationData` action must be registered before
|
|
249
|
+
* resolution occurs, or enrichment is skipped for the session (a message is
|
|
250
|
+
* logged, see {@link #resolveLocationContext}).
|
|
247
251
|
*
|
|
248
252
|
* Safe to call more than once: the first call performs initialization and
|
|
249
253
|
* subsequent calls return the same in-flight (or settled) promise.
|
|
@@ -337,13 +341,26 @@ class AnalyticsController extends base_controller_1.BaseController {
|
|
|
337
341
|
*
|
|
338
342
|
* Records that a consent decision has been made and replays any events that
|
|
339
343
|
* were queued while the user was undecided.
|
|
344
|
+
*
|
|
345
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
346
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
347
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
348
|
+
*
|
|
349
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
340
350
|
*/
|
|
341
|
-
optIn() {
|
|
351
|
+
async optIn() {
|
|
342
352
|
this.update((state) => {
|
|
343
353
|
state.optedIn = true;
|
|
344
354
|
state.consentDecisionMade = true;
|
|
345
355
|
});
|
|
346
|
-
|
|
356
|
+
// Now that the user has consented, resolve geolocation (once) and wait for
|
|
357
|
+
// it so the queued pre-consent events can be enriched as they replay.
|
|
358
|
+
await __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_maybeResolveLocation).call(this);
|
|
359
|
+
// Reconcile against the current state rather than replaying blindly: the
|
|
360
|
+
// consent decision may have changed while geolocation was resolving (e.g.
|
|
361
|
+
// resetConsentDecision ran during the await), and preserved pre-consent
|
|
362
|
+
// events must not be delivered once the user is no longer opted in.
|
|
363
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcilePreConsentEvents).call(this);
|
|
347
364
|
}
|
|
348
365
|
/**
|
|
349
366
|
* Opt out of analytics.
|
|
@@ -376,14 +393,17 @@ class AnalyticsController extends base_controller_1.BaseController {
|
|
|
376
393
|
}
|
|
377
394
|
}
|
|
378
395
|
exports.AnalyticsController = AnalyticsController;
|
|
379
|
-
_AnalyticsController_platformAdapter = new WeakMap(), _AnalyticsController_isAnonymousEventsFeatureEnabled = new WeakMap(), _AnalyticsController_isEventQueuePersistenceEnabled = new WeakMap(), _AnalyticsController_isPreConsentQueueEnabled = new WeakMap(), _AnalyticsController_isGeolocationEnabled = new WeakMap(), _AnalyticsController_initPromise = new WeakMap(), _AnalyticsController_locationContext = new WeakMap(), _AnalyticsController_instances = new WeakSet(), _AnalyticsController_performInit =
|
|
396
|
+
_AnalyticsController_platformAdapter = new WeakMap(), _AnalyticsController_isAnonymousEventsFeatureEnabled = new WeakMap(), _AnalyticsController_isEventQueuePersistenceEnabled = new WeakMap(), _AnalyticsController_isPreConsentQueueEnabled = new WeakMap(), _AnalyticsController_isGeolocationEnabled = new WeakMap(), _AnalyticsController_initPromise = new WeakMap(), _AnalyticsController_locationResolvePromise = new WeakMap(), _AnalyticsController_locationContext = new WeakMap(), _AnalyticsController_instances = new WeakSet(), _AnalyticsController_performInit =
|
|
380
397
|
/**
|
|
381
398
|
* Performs the one-time initialization work: resolve geolocation, run the
|
|
382
399
|
* platform adapter's onSetupCompleted lifecycle hook, then replay any queued
|
|
383
400
|
* and pre-consent events.
|
|
384
401
|
*/
|
|
385
402
|
async function _AnalyticsController_performInit() {
|
|
386
|
-
|
|
403
|
+
// Resolve geolocation only when the user is already opted in; for undecided
|
|
404
|
+
// or opted-out users it is deferred to {@link optIn}. Awaited so that an
|
|
405
|
+
// already-opted-in session has location available before events replay.
|
|
406
|
+
await __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_maybeResolveLocation).call(this);
|
|
387
407
|
// Call onSetupCompleted lifecycle hook after initialization
|
|
388
408
|
// State is already validated, so analyticsId is guaranteed to be a valid UUIDv4
|
|
389
409
|
try {
|
|
@@ -395,27 +415,24 @@ async function _AnalyticsController_performInit() {
|
|
|
395
415
|
}
|
|
396
416
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayQueuedEvents).call(this);
|
|
397
417
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcilePreConsentEvents).call(this);
|
|
398
|
-
},
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
async function _AnalyticsController_resolveLocationContext() {
|
|
407
|
-
if (!__classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f")) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
418
|
+
}, _AnalyticsController_maybeResolveLocation = function _AnalyticsController_maybeResolveLocation() {
|
|
419
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f") &&
|
|
420
|
+
__classPrivateFieldGet(this, _AnalyticsController_locationResolvePromise, "f") === undefined &&
|
|
421
|
+
selectors_js_1.analyticsControllerSelectors.selectEnabled(this.state)) {
|
|
422
|
+
__classPrivateFieldSet(this, _AnalyticsController_locationResolvePromise, __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_resolveLocationContext).call(this), "f");
|
|
423
|
+
}
|
|
424
|
+
return __classPrivateFieldGet(this, _AnalyticsController_locationResolvePromise, "f");
|
|
425
|
+
}, _AnalyticsController_resolveLocationContext = async function _AnalyticsController_resolveLocationContext() {
|
|
410
426
|
try {
|
|
411
427
|
const geolocation = await this.messenger.call('GeolocationController:getGeolocationData');
|
|
412
428
|
__classPrivateFieldSet(this, _AnalyticsController_locationContext, buildLocationContext(geolocation), "f");
|
|
413
429
|
}
|
|
414
430
|
catch (error) {
|
|
415
|
-
// A common cause is
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
|
|
431
|
+
// A common cause is the GeolocationController not being registered before
|
|
432
|
+
// resolution runs (at init for an opted-in user, otherwise at opt-in).
|
|
433
|
+
// Name it here so the failure is diagnosable, since enrichment is
|
|
434
|
+
// otherwise skipped silently for the session.
|
|
435
|
+
(0, AnalyticsLogger_js_1.projectLogger)('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.', error);
|
|
419
436
|
}
|
|
420
437
|
}, _AnalyticsController_withLocationContext = function _AnalyticsController_withLocationContext(context) {
|
|
421
438
|
if (!__classPrivateFieldGet(this, _AnalyticsController_locationContext, "f")) {
|
|
@@ -570,14 +587,7 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
570
587
|
this.update((state) => {
|
|
571
588
|
state.preConsentEventQueue = preConsentEventQueue;
|
|
572
589
|
});
|
|
573
|
-
}, _AnalyticsController_replayPreConsentEvents = function _AnalyticsController_replayPreConsentEvents() {
|
|
574
|
-
if (!__classPrivateFieldGet(this, _AnalyticsController_isPreConsentQueueEnabled, "f")) {
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
const queue = this.state.preConsentEventQueue;
|
|
578
|
-
if (!queue) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
590
|
+
}, _AnalyticsController_replayPreConsentEvents = function _AnalyticsController_replayPreConsentEvents(queue) {
|
|
581
591
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
582
592
|
for (const [messageId, queuedEvent] of Object.entries(queue)) {
|
|
583
593
|
if (!isAnalyticsQueuedEvent(queuedEvent) ||
|
|
@@ -587,13 +597,24 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
587
597
|
});
|
|
588
598
|
continue;
|
|
589
599
|
}
|
|
600
|
+
const eventToReplay = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enrichPreConsentEvent).call(this, queuedEvent);
|
|
590
601
|
if (__classPrivateFieldGet(this, _AnalyticsController_isEventQueuePersistenceEnabled, "f")) {
|
|
591
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enqueueEvent).call(this,
|
|
602
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enqueueEvent).call(this, eventToReplay);
|
|
592
603
|
}
|
|
593
604
|
else {
|
|
594
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_sendQueuedEvent).call(this,
|
|
605
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_sendQueuedEvent).call(this, eventToReplay);
|
|
595
606
|
}
|
|
596
607
|
}
|
|
608
|
+
}, _AnalyticsController_enrichPreConsentEvent = function _AnalyticsController_enrichPreConsentEvent(queuedEvent) {
|
|
609
|
+
if (queuedEvent.type === 'track' &&
|
|
610
|
+
queuedEvent.properties?.anonymous === true) {
|
|
611
|
+
return queuedEvent;
|
|
612
|
+
}
|
|
613
|
+
const context = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_withLocationContext).call(this, queuedEvent.context);
|
|
614
|
+
return {
|
|
615
|
+
...queuedEvent,
|
|
616
|
+
...(context === undefined ? {} : { context }),
|
|
617
|
+
};
|
|
597
618
|
}, _AnalyticsController_clearPreConsentEvents = function _AnalyticsController_clearPreConsentEvents() {
|
|
598
619
|
if (!this.state.preConsentEventQueue) {
|
|
599
620
|
return;
|
|
@@ -611,7 +632,7 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
611
632
|
return;
|
|
612
633
|
}
|
|
613
634
|
if (this.state.optedIn) {
|
|
614
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayPreConsentEvents).call(this);
|
|
635
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayPreConsentEvents).call(this, queue);
|
|
615
636
|
}
|
|
616
637
|
else if (this.state.consentDecisionMade) {
|
|
617
638
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController.cjs","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 GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash';\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';\nimport { analyticsControllerSelectors } from './selectors.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// === 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 * 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/**\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/**\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 };\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 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} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\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 = GeolocationControllerGetGeolocationDataAction;\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 = never;\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 * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * The platform is responsible for generating the ID on first run.\n * It is then persisted with controller state when using a persisted store.\n */\n state: AnalyticsControllerState;\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/**\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\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 ) {\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 * 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 /**\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 #locationContext: AnalyticsLocationContext | undefined;\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 * Use `getDefaultAnalyticsControllerState()` for default opt-in preferences.\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 * @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 }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\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.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\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 consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\n });\n }\n\n /**\n * Initialize the controller by resolving the geolocation used to enrich\n * events and then calling the platform adapter's onSetupCompleted lifecycle\n * hook. This method must be called after construction to complete the setup\n * process.\n *\n * Geolocation is resolved before any queued event is replayed so that\n * replayed events carry the same location context as new ones.\n *\n * When geolocation enrichment is enabled (`isGeolocationEnabled`), the\n * `GeolocationController` and its `GeolocationController:getGeolocationData`\n * action must be registered and initialized *before* this method is called.\n * Otherwise the resolution fails and events are delivered for the rest of the\n * session without location (a message is logged, see\n * {@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 await this.#resolveLocationContext();\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.#reconcilePreConsentEvents();\n }\n\n /**\n * Resolve the location context used to enrich analytics events.\n *\n * No-op unless geolocation enrichment is enabled. Otherwise geolocation is\n * best-effort: when the GeolocationController is unavailable or fails to\n * resolve, events are still delivered, just without location.\n */\n async #resolveLocationContext(): Promise<void> {\n if (!this.#isGeolocationEnabled) {\n return;\n }\n\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 calling `init()` before the GeolocationController is\n // registered/initialized. Name it here so the failure is diagnosable,\n // since enrichment is 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 AnalyticsController.init() 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 * 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 */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n // Direct delivery: enabled and not persisting.\n if (\n analyticsControllerSelectors.selectEnabled(this.state) &&\n !this.#isEventQueuePersistenceEnabled\n ) {\n this.#platformAdapter.track(eventName, properties, context);\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 === undefined ? {} : { context }),\n };\n\n // Not yet enabled (reached only while undecided with the pre-consent queue\n // enabled): hold the event until the user opts in.\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\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 if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, context);\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 === undefined ? {} : { context }),\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 */\n #sendOrQueueViewEvent(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, context);\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 === undefined ? {} : { context }),\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 as unknown as Json,\n };\n\n this.update((state) => {\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n this.#clearQueuedEvents();\n return;\n }\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 this.#removeQueuedEvent(messageId);\n continue;\n }\n\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 state.eventQueue = eventQueue as never;\n });\n }\n\n /**\n * Clear all queued analytics events.\n */\n #clearQueuedEvents(): void {\n if (\n !this.state.eventQueue ||\n Object.keys(this.state.eventQueue).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventQueue = {} as never;\n });\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 as unknown as Json,\n };\n\n this.update((state) => {\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Replay queued pre-consent events through the delivery path.\n *\n * Called on opt-in, once analytics is enabled. The queue is cleared before\n * replaying so events cannot be re-queued or replayed twice.\n */\n #replayPreConsentEvents(): void {\n if (!this.#isPreConsentQueueEnabled) {\n return;\n }\n\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n this.#clearPreConsentEvents();\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued pre-consent analytics event', {\n messageId,\n });\n continue;\n }\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(queuedEvent);\n } else {\n this.#sendQueuedEvent(queuedEvent);\n }\n }\n }\n\n /**\n * Clear all queued pre-consent events.\n */\n #clearPreConsentEvents(): void {\n if (!this.state.preConsentEventQueue) {\n return;\n }\n\n this.update((state) => {\n state.preConsentEventQueue = {} as never;\n });\n }\n\n /**\n * Reconcile the pre-consent queue on initialization.\n *\n * The queue should normally be empty unless the user is still undecided. This\n * handles the rare cases where a consent decision was persisted but the queue\n * was not flushed/cleared (e.g. an interrupted shutdown): replay it if the\n * user is opted in, or clear it if they opted out.\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 #reconcilePreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.#clearPreConsentEvents();\n return;\n }\n\n if (this.state.optedIn) {\n this.#replayPreConsentEvents();\n } else if (this.state.consentDecisionMade) {\n this.#clearPreConsentEvents();\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n // While the user is undecided, fall through so the event is processed and\n // captured in the pre-consent queue (see #sendOrQueueTrackEvent) to be\n // replayed if they later opt in. Otherwise (opted out, or pre-consent\n // queue disabled) drop it.\n const shouldQueuePreConsent =\n this.#isPreConsentQueueEnabled && !this.state.consentDecisionMade;\n if (!shouldQueuePreConsent) {\n return;\n }\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 );\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 );\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 );\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 // Delegate to platform adapter using the current analytics ID\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n );\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 optIn(): void {\n this.update((state) => {\n state.optedIn = true;\n state.consentDecisionMade = true;\n });\n\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 so nothing captured before the decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#clearQueuedEvents();\n this.#clearPreConsentEvents();\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 resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#clearQueuedEvents();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AnalyticsController.cjs","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 GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash';\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';\nimport { analyticsControllerSelectors } from './selectors.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// === 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 * 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/**\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/**\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 };\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 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} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\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 = GeolocationControllerGetGeolocationDataAction;\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 = never;\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 * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * The platform is responsible for generating the ID on first run.\n * It is then persisted with controller state when using a persisted store.\n */\n state: AnalyticsControllerState;\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/**\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\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 ) {\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 * 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 /**\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 * Constructs an AnalyticsController instance.\n *\n * @param options - Controller options\n * @param options.state - Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Use `getDefaultAnalyticsControllerState()` for default opt-in preferences.\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 * @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 }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\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.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\n this.#locationResolvePromise = undefined;\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 consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\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; for undecided or\n * opted-out users it is deferred until they opt in (see {@link optIn}), so a\n * user's location is never requested before they consent to analytics. In\n * 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 // Resolve geolocation only when the user is already opted in; for undecided\n // or opted-out users it is deferred to {@link optIn}. Awaited so that an\n // already-opted-in 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.#reconcilePreConsentEvents();\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, and a resolution has not\n * already been started. Deferring resolution until opt-in ensures a user's\n * location is never requested before they consent to analytics (for example,\n * 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 analyticsControllerSelectors.selectEnabled(this.state)\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 * 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 */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n // Direct delivery: enabled and not persisting.\n if (\n analyticsControllerSelectors.selectEnabled(this.state) &&\n !this.#isEventQueuePersistenceEnabled\n ) {\n this.#platformAdapter.track(eventName, properties, context);\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 === undefined ? {} : { context }),\n };\n\n // Not yet enabled (reached only while undecided with the pre-consent queue\n // enabled): hold the event until the user opts in.\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\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 if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, context);\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 === undefined ? {} : { context }),\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 */\n #sendOrQueueViewEvent(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, context);\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 === undefined ? {} : { context }),\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 as unknown as Json,\n };\n\n this.update((state) => {\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n this.#clearQueuedEvents();\n return;\n }\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 this.#removeQueuedEvent(messageId);\n continue;\n }\n\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 state.eventQueue = eventQueue as never;\n });\n }\n\n /**\n * Clear all queued analytics events.\n */\n #clearQueuedEvents(): void {\n if (\n !this.state.eventQueue ||\n Object.keys(this.state.eventQueue).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventQueue = {} as never;\n });\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 as unknown as Json,\n };\n\n this.update((state) => {\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Replay queued pre-consent events through the delivery path.\n *\n * Only called by {@link #reconcilePreConsentEvents}, which guarantees the\n * pre-consent queue is enabled and that the user is opted in. The queue is\n * cleared before replaying so events cannot be re-queued or replayed twice.\n *\n * @param queue - The pre-consent event queue to replay.\n */\n #replayPreConsentEvents(queue: Record<string, Json>): void {\n this.#clearPreConsentEvents();\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued pre-consent analytics event', {\n messageId,\n });\n continue;\n }\n\n const eventToReplay = this.#enrichPreConsentEvent(queuedEvent);\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(eventToReplay);\n } else {\n this.#sendQueuedEvent(eventToReplay);\n }\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 * Clear all queued pre-consent events.\n */\n #clearPreConsentEvents(): void {\n if (!this.state.preConsentEventQueue) {\n return;\n }\n\n this.update((state) => {\n state.preConsentEventQueue = {} as never;\n });\n }\n\n /**\n * Reconcile the pre-consent queue on initialization.\n *\n * The queue should normally be empty unless the user is still undecided. This\n * handles the rare cases where a consent decision was persisted but the queue\n * was not flushed/cleared (e.g. an interrupted shutdown): replay it if the\n * user is opted in, or clear it if they opted out.\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 #reconcilePreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.#clearPreConsentEvents();\n return;\n }\n\n if (this.state.optedIn) {\n this.#replayPreConsentEvents(queue);\n } else if (this.state.consentDecisionMade) {\n this.#clearPreConsentEvents();\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n // While the user is undecided, fall through so the event is processed and\n // captured in the pre-consent queue (see #sendOrQueueTrackEvent) to be\n // replayed if they later opt in. Otherwise (opted out, or pre-consent\n // queue disabled) drop it.\n const shouldQueuePreConsent =\n this.#isPreConsentQueueEnabled && !this.state.consentDecisionMade;\n if (!shouldQueuePreConsent) {\n return;\n }\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 );\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 );\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 );\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 // Delegate to platform adapter using the current analytics ID\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n );\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.#reconcilePreConsentEvents();\n }\n\n /**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events so nothing captured before the decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#clearQueuedEvents();\n this.#clearPreConsentEvents();\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 resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#clearQueuedEvents();\n }\n}\n"]}
|
|
@@ -235,20 +235,18 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
235
235
|
*/
|
|
236
236
|
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled, isEventQueuePersistenceEnabled, isPreConsentQueueEnabled, isGeolocationEnabled, }: AnalyticsControllerOptions);
|
|
237
237
|
/**
|
|
238
|
-
* Initialize the controller by
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* process.
|
|
238
|
+
* Initialize the controller by calling the platform adapter's
|
|
239
|
+
* onSetupCompleted lifecycle hook and replaying any queued events. This
|
|
240
|
+
* method must be called after construction to complete the setup process.
|
|
242
241
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* `GeolocationController` and its
|
|
248
|
-
* action must be registered
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* {@link #resolveLocationContext}).
|
|
242
|
+
* When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation
|
|
243
|
+
* is resolved only for a user who is already opted in; for undecided or
|
|
244
|
+
* opted-out users it is deferred until they opt in (see {@link optIn}), so a
|
|
245
|
+
* user's location is never requested before they consent to analytics. In
|
|
246
|
+
* either case the `GeolocationController` and its
|
|
247
|
+
* `GeolocationController:getGeolocationData` action must be registered before
|
|
248
|
+
* resolution occurs, or enrichment is skipped for the session (a message is
|
|
249
|
+
* logged, see {@link #resolveLocationContext}).
|
|
252
250
|
*
|
|
253
251
|
* Safe to call more than once: the first call performs initialization and
|
|
254
252
|
* subsequent calls return the same in-flight (or settled) promise.
|
|
@@ -285,8 +283,14 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
285
283
|
*
|
|
286
284
|
* Records that a consent decision has been made and replays any events that
|
|
287
285
|
* were queued while the user was undecided.
|
|
286
|
+
*
|
|
287
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
288
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
289
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
290
|
+
*
|
|
291
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
288
292
|
*/
|
|
289
|
-
optIn(): void
|
|
293
|
+
optIn(): Promise<void>;
|
|
290
294
|
/**
|
|
291
295
|
* Opt out of analytics.
|
|
292
296
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController.d.cts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,6CAA6C,EAE9C,yCAAyC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAI5C,OAAO,KAAK,EAAE,gCAAgC,EAAE,sDAAqD;AAGrG,OAAO,KAAK,EACV,wBAAwB,EAExB,gBAAgB,EAChB,wBAAwB,EAExB,mBAAmB,EACnB,sBAAsB,EACvB,6CAAyC;AAK1C;;;;GAIG;AACH,eAAO,MAAM,cAAc,wBAAwB,CAAC;AAIpD;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAElC;;;;;;;;;OASG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC7C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,IAAI,EAAE,wBAAwB,CAAC;IAE/B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,wBAAwB,GAAG;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,wBAAwB,GAAG;IACpE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,GAAG;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B,yBAAyB,GACzB,4BAA4B,GAC5B,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEvE;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,IAAI,IAAI,CACxD,wBAAwB,EACxB,aAAa,CACd,CAKA;AAoDD;;GAEG;AACH,MAAM,MAAM,iCAAiC,GAAG,wBAAwB,CACtE,OAAO,cAAc,EACrB,wBAAwB,CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAClC,iCAAiC,GACjC,gCAAgC,CAAC;AAErC;;GAEG;AACH,KAAK,cAAc,GAAG,6CAA6C,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,mCAAmC,GAAG,0BAA0B,CAC1E,OAAO,cAAc,EACrB,wBAAwB,CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,mCAAmC,CAAC;AAE5E;;GAEG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,SAAS,CAClD,OAAO,cAAc,EACrB,0BAA0B,GAAG,cAAc,EAC3C,yBAAyB,GAAG,aAAa,CAC1C,CAAC;AAIF;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC;;;;OAIG;IACH,KAAK,EAAE,wBAAwB,CAAC;IAChC;;OAEG;IACH,SAAS,EAAE,4BAA4B,CAAC;IACxC;;OAEG;IACH,eAAe,EAAE,wBAAwB,CAAC;IAE1C;;;;OAIG;IACH,+BAA+B,CAAC,EAAE,OAAO,CAAC;IAE1C;;;;;;;OAOG;IACH,8BAA8B,CAAC,EAAE,OAAO,CAAC;IAEzC;;;;;;;;;OASG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AA4FF;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAoB,SAAQ,cAAc,CACrD,qBAAqB,EACrB,wBAAwB,EACxB,4BAA4B,CAC7B;;
|
|
1
|
+
{"version":3,"file":"AnalyticsController.d.cts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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"}
|
|
@@ -235,20 +235,18 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
235
235
|
*/
|
|
236
236
|
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled, isEventQueuePersistenceEnabled, isPreConsentQueueEnabled, isGeolocationEnabled, }: AnalyticsControllerOptions);
|
|
237
237
|
/**
|
|
238
|
-
* Initialize the controller by
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* process.
|
|
238
|
+
* Initialize the controller by calling the platform adapter's
|
|
239
|
+
* onSetupCompleted lifecycle hook and replaying any queued events. This
|
|
240
|
+
* method must be called after construction to complete the setup process.
|
|
242
241
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* `GeolocationController` and its
|
|
248
|
-
* action must be registered
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* {@link #resolveLocationContext}).
|
|
242
|
+
* When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation
|
|
243
|
+
* is resolved only for a user who is already opted in; for undecided or
|
|
244
|
+
* opted-out users it is deferred until they opt in (see {@link optIn}), so a
|
|
245
|
+
* user's location is never requested before they consent to analytics. In
|
|
246
|
+
* either case the `GeolocationController` and its
|
|
247
|
+
* `GeolocationController:getGeolocationData` action must be registered before
|
|
248
|
+
* resolution occurs, or enrichment is skipped for the session (a message is
|
|
249
|
+
* logged, see {@link #resolveLocationContext}).
|
|
252
250
|
*
|
|
253
251
|
* Safe to call more than once: the first call performs initialization and
|
|
254
252
|
* subsequent calls return the same in-flight (or settled) promise.
|
|
@@ -285,8 +283,14 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
285
283
|
*
|
|
286
284
|
* Records that a consent decision has been made and replays any events that
|
|
287
285
|
* were queued while the user was undecided.
|
|
286
|
+
*
|
|
287
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
288
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
289
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
290
|
+
*
|
|
291
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
288
292
|
*/
|
|
289
|
-
optIn(): void
|
|
293
|
+
optIn(): Promise<void>;
|
|
290
294
|
/**
|
|
291
295
|
* Opt out of analytics.
|
|
292
296
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController.d.mts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AnalyticsController.d.mts","sourceRoot":"","sources":["../src/AnalyticsController.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _AnalyticsController_instances, _AnalyticsController_platformAdapter, _AnalyticsController_isAnonymousEventsFeatureEnabled, _AnalyticsController_isEventQueuePersistenceEnabled, _AnalyticsController_isPreConsentQueueEnabled, _AnalyticsController_isGeolocationEnabled, _AnalyticsController_initPromise, _AnalyticsController_locationContext, _AnalyticsController_performInit, _AnalyticsController_resolveLocationContext, _AnalyticsController_withLocationContext, _AnalyticsController_sendOrQueueTrackEvent, _AnalyticsController_sendOrQueueIdentifyEvent, _AnalyticsController_sendOrQueueViewEvent, _AnalyticsController_enqueueEvent, _AnalyticsController_sendQueuedEvent, _AnalyticsController_replayQueuedEvents, _AnalyticsController_removeQueuedEvent, _AnalyticsController_clearQueuedEvents, _AnalyticsController_enqueuePreConsentEvent, _AnalyticsController_replayPreConsentEvents, _AnalyticsController_clearPreConsentEvents, _AnalyticsController_reconcilePreConsentEvents;
|
|
12
|
+
var _AnalyticsController_instances, _AnalyticsController_platformAdapter, _AnalyticsController_isAnonymousEventsFeatureEnabled, _AnalyticsController_isEventQueuePersistenceEnabled, _AnalyticsController_isPreConsentQueueEnabled, _AnalyticsController_isGeolocationEnabled, _AnalyticsController_initPromise, _AnalyticsController_locationResolvePromise, _AnalyticsController_locationContext, _AnalyticsController_performInit, _AnalyticsController_maybeResolveLocation, _AnalyticsController_resolveLocationContext, _AnalyticsController_withLocationContext, _AnalyticsController_sendOrQueueTrackEvent, _AnalyticsController_sendOrQueueIdentifyEvent, _AnalyticsController_sendOrQueueViewEvent, _AnalyticsController_enqueueEvent, _AnalyticsController_sendQueuedEvent, _AnalyticsController_replayQueuedEvents, _AnalyticsController_removeQueuedEvent, _AnalyticsController_clearQueuedEvents, _AnalyticsController_enqueuePreConsentEvent, _AnalyticsController_replayPreConsentEvents, _AnalyticsController_enrichPreConsentEvent, _AnalyticsController_clearPreConsentEvents, _AnalyticsController_reconcilePreConsentEvents;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import $lodash from "lodash";
|
|
15
15
|
const { cloneDeep } = $lodash;
|
|
@@ -208,6 +208,11 @@ export class AnalyticsController extends BaseController {
|
|
|
208
208
|
* await the same work rather than observing a premature completion.
|
|
209
209
|
*/
|
|
210
210
|
_AnalyticsController_initPromise.set(this, void 0);
|
|
211
|
+
/**
|
|
212
|
+
* The in-flight (or settled) geolocation resolution, if any. Its presence
|
|
213
|
+
* marks that resolution has been started, so it runs at most once.
|
|
214
|
+
*/
|
|
215
|
+
_AnalyticsController_locationResolvePromise.set(this, void 0);
|
|
211
216
|
_AnalyticsController_locationContext.set(this, void 0);
|
|
212
217
|
__classPrivateFieldSet(this, _AnalyticsController_isAnonymousEventsFeatureEnabled, isAnonymousEventsFeatureEnabled, "f");
|
|
213
218
|
__classPrivateFieldSet(this, _AnalyticsController_isEventQueuePersistenceEnabled, isEventQueuePersistenceEnabled, "f");
|
|
@@ -215,6 +220,7 @@ export class AnalyticsController extends BaseController {
|
|
|
215
220
|
__classPrivateFieldSet(this, _AnalyticsController_isGeolocationEnabled, isGeolocationEnabled, "f");
|
|
216
221
|
__classPrivateFieldSet(this, _AnalyticsController_platformAdapter, platformAdapter, "f");
|
|
217
222
|
__classPrivateFieldSet(this, _AnalyticsController_initPromise, undefined, "f");
|
|
223
|
+
__classPrivateFieldSet(this, _AnalyticsController_locationResolvePromise, undefined, "f");
|
|
218
224
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
219
225
|
log('AnalyticsController initialized and ready', {
|
|
220
226
|
enabled: analyticsControllerSelectors.selectEnabled(this.state),
|
|
@@ -227,20 +233,18 @@ export class AnalyticsController extends BaseController {
|
|
|
227
233
|
});
|
|
228
234
|
}
|
|
229
235
|
/**
|
|
230
|
-
* Initialize the controller by
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* process.
|
|
234
|
-
*
|
|
235
|
-
* Geolocation is resolved before any queued event is replayed so that
|
|
236
|
-
* replayed events carry the same location context as new ones.
|
|
236
|
+
* Initialize the controller by calling the platform adapter's
|
|
237
|
+
* onSetupCompleted lifecycle hook and replaying any queued events. This
|
|
238
|
+
* method must be called after construction to complete the setup process.
|
|
237
239
|
*
|
|
238
|
-
* When geolocation enrichment is enabled (`isGeolocationEnabled`),
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
240
|
+
* When geolocation enrichment is enabled (`isGeolocationEnabled`), geolocation
|
|
241
|
+
* is resolved only for a user who is already opted in; for undecided or
|
|
242
|
+
* opted-out users it is deferred until they opt in (see {@link optIn}), so a
|
|
243
|
+
* user's location is never requested before they consent to analytics. In
|
|
244
|
+
* either case the `GeolocationController` and its
|
|
245
|
+
* `GeolocationController:getGeolocationData` action must be registered before
|
|
246
|
+
* resolution occurs, or enrichment is skipped for the session (a message is
|
|
247
|
+
* logged, see {@link #resolveLocationContext}).
|
|
244
248
|
*
|
|
245
249
|
* Safe to call more than once: the first call performs initialization and
|
|
246
250
|
* subsequent calls return the same in-flight (or settled) promise.
|
|
@@ -334,13 +338,26 @@ export class AnalyticsController extends BaseController {
|
|
|
334
338
|
*
|
|
335
339
|
* Records that a consent decision has been made and replays any events that
|
|
336
340
|
* were queued while the user was undecided.
|
|
341
|
+
*
|
|
342
|
+
* When geolocation enrichment is enabled, geolocation is resolved here (once
|
|
343
|
+
* the user has consented) and awaited before the queued events are replayed,
|
|
344
|
+
* so those events are enriched with the resolved location as they are sent.
|
|
345
|
+
*
|
|
346
|
+
* @returns A promise that resolves once opt-in processing has completed.
|
|
337
347
|
*/
|
|
338
|
-
optIn() {
|
|
348
|
+
async optIn() {
|
|
339
349
|
this.update((state) => {
|
|
340
350
|
state.optedIn = true;
|
|
341
351
|
state.consentDecisionMade = true;
|
|
342
352
|
});
|
|
343
|
-
|
|
353
|
+
// Now that the user has consented, resolve geolocation (once) and wait for
|
|
354
|
+
// it so the queued pre-consent events can be enriched as they replay.
|
|
355
|
+
await __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_maybeResolveLocation).call(this);
|
|
356
|
+
// Reconcile against the current state rather than replaying blindly: the
|
|
357
|
+
// consent decision may have changed while geolocation was resolving (e.g.
|
|
358
|
+
// resetConsentDecision ran during the await), and preserved pre-consent
|
|
359
|
+
// events must not be delivered once the user is no longer opted in.
|
|
360
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcilePreConsentEvents).call(this);
|
|
344
361
|
}
|
|
345
362
|
/**
|
|
346
363
|
* Opt out of analytics.
|
|
@@ -372,14 +389,17 @@ export class AnalyticsController extends BaseController {
|
|
|
372
389
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearQueuedEvents).call(this);
|
|
373
390
|
}
|
|
374
391
|
}
|
|
375
|
-
_AnalyticsController_platformAdapter = new WeakMap(), _AnalyticsController_isAnonymousEventsFeatureEnabled = new WeakMap(), _AnalyticsController_isEventQueuePersistenceEnabled = new WeakMap(), _AnalyticsController_isPreConsentQueueEnabled = new WeakMap(), _AnalyticsController_isGeolocationEnabled = new WeakMap(), _AnalyticsController_initPromise = new WeakMap(), _AnalyticsController_locationContext = new WeakMap(), _AnalyticsController_instances = new WeakSet(), _AnalyticsController_performInit =
|
|
392
|
+
_AnalyticsController_platformAdapter = new WeakMap(), _AnalyticsController_isAnonymousEventsFeatureEnabled = new WeakMap(), _AnalyticsController_isEventQueuePersistenceEnabled = new WeakMap(), _AnalyticsController_isPreConsentQueueEnabled = new WeakMap(), _AnalyticsController_isGeolocationEnabled = new WeakMap(), _AnalyticsController_initPromise = new WeakMap(), _AnalyticsController_locationResolvePromise = new WeakMap(), _AnalyticsController_locationContext = new WeakMap(), _AnalyticsController_instances = new WeakSet(), _AnalyticsController_performInit =
|
|
376
393
|
/**
|
|
377
394
|
* Performs the one-time initialization work: resolve geolocation, run the
|
|
378
395
|
* platform adapter's onSetupCompleted lifecycle hook, then replay any queued
|
|
379
396
|
* and pre-consent events.
|
|
380
397
|
*/
|
|
381
398
|
async function _AnalyticsController_performInit() {
|
|
382
|
-
|
|
399
|
+
// Resolve geolocation only when the user is already opted in; for undecided
|
|
400
|
+
// or opted-out users it is deferred to {@link optIn}. Awaited so that an
|
|
401
|
+
// already-opted-in session has location available before events replay.
|
|
402
|
+
await __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_maybeResolveLocation).call(this);
|
|
383
403
|
// Call onSetupCompleted lifecycle hook after initialization
|
|
384
404
|
// State is already validated, so analyticsId is guaranteed to be a valid UUIDv4
|
|
385
405
|
try {
|
|
@@ -391,27 +411,24 @@ async function _AnalyticsController_performInit() {
|
|
|
391
411
|
}
|
|
392
412
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayQueuedEvents).call(this);
|
|
393
413
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcilePreConsentEvents).call(this);
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
async function _AnalyticsController_resolveLocationContext() {
|
|
403
|
-
if (!__classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f")) {
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
414
|
+
}, _AnalyticsController_maybeResolveLocation = function _AnalyticsController_maybeResolveLocation() {
|
|
415
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f") &&
|
|
416
|
+
__classPrivateFieldGet(this, _AnalyticsController_locationResolvePromise, "f") === undefined &&
|
|
417
|
+
analyticsControllerSelectors.selectEnabled(this.state)) {
|
|
418
|
+
__classPrivateFieldSet(this, _AnalyticsController_locationResolvePromise, __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_resolveLocationContext).call(this), "f");
|
|
419
|
+
}
|
|
420
|
+
return __classPrivateFieldGet(this, _AnalyticsController_locationResolvePromise, "f");
|
|
421
|
+
}, _AnalyticsController_resolveLocationContext = async function _AnalyticsController_resolveLocationContext() {
|
|
406
422
|
try {
|
|
407
423
|
const geolocation = await this.messenger.call('GeolocationController:getGeolocationData');
|
|
408
424
|
__classPrivateFieldSet(this, _AnalyticsController_locationContext, buildLocationContext(geolocation), "f");
|
|
409
425
|
}
|
|
410
426
|
catch (error) {
|
|
411
|
-
// A common cause is
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
|
|
427
|
+
// A common cause is the GeolocationController not being registered before
|
|
428
|
+
// resolution runs (at init for an opted-in user, otherwise at opt-in).
|
|
429
|
+
// Name it here so the failure is diagnosable, since enrichment is
|
|
430
|
+
// otherwise skipped silently for the session.
|
|
431
|
+
log('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.', error);
|
|
415
432
|
}
|
|
416
433
|
}, _AnalyticsController_withLocationContext = function _AnalyticsController_withLocationContext(context) {
|
|
417
434
|
if (!__classPrivateFieldGet(this, _AnalyticsController_locationContext, "f")) {
|
|
@@ -566,14 +583,7 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
566
583
|
this.update((state) => {
|
|
567
584
|
state.preConsentEventQueue = preConsentEventQueue;
|
|
568
585
|
});
|
|
569
|
-
}, _AnalyticsController_replayPreConsentEvents = function _AnalyticsController_replayPreConsentEvents() {
|
|
570
|
-
if (!__classPrivateFieldGet(this, _AnalyticsController_isPreConsentQueueEnabled, "f")) {
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const queue = this.state.preConsentEventQueue;
|
|
574
|
-
if (!queue) {
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
586
|
+
}, _AnalyticsController_replayPreConsentEvents = function _AnalyticsController_replayPreConsentEvents(queue) {
|
|
577
587
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
578
588
|
for (const [messageId, queuedEvent] of Object.entries(queue)) {
|
|
579
589
|
if (!isAnalyticsQueuedEvent(queuedEvent) ||
|
|
@@ -583,13 +593,24 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
583
593
|
});
|
|
584
594
|
continue;
|
|
585
595
|
}
|
|
596
|
+
const eventToReplay = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enrichPreConsentEvent).call(this, queuedEvent);
|
|
586
597
|
if (__classPrivateFieldGet(this, _AnalyticsController_isEventQueuePersistenceEnabled, "f")) {
|
|
587
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enqueueEvent).call(this,
|
|
598
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_enqueueEvent).call(this, eventToReplay);
|
|
588
599
|
}
|
|
589
600
|
else {
|
|
590
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_sendQueuedEvent).call(this,
|
|
601
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_sendQueuedEvent).call(this, eventToReplay);
|
|
591
602
|
}
|
|
592
603
|
}
|
|
604
|
+
}, _AnalyticsController_enrichPreConsentEvent = function _AnalyticsController_enrichPreConsentEvent(queuedEvent) {
|
|
605
|
+
if (queuedEvent.type === 'track' &&
|
|
606
|
+
queuedEvent.properties?.anonymous === true) {
|
|
607
|
+
return queuedEvent;
|
|
608
|
+
}
|
|
609
|
+
const context = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_withLocationContext).call(this, queuedEvent.context);
|
|
610
|
+
return {
|
|
611
|
+
...queuedEvent,
|
|
612
|
+
...(context === undefined ? {} : { context }),
|
|
613
|
+
};
|
|
593
614
|
}, _AnalyticsController_clearPreConsentEvents = function _AnalyticsController_clearPreConsentEvents() {
|
|
594
615
|
if (!this.state.preConsentEventQueue) {
|
|
595
616
|
return;
|
|
@@ -607,7 +628,7 @@ async function _AnalyticsController_resolveLocationContext() {
|
|
|
607
628
|
return;
|
|
608
629
|
}
|
|
609
630
|
if (this.state.optedIn) {
|
|
610
|
-
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayPreConsentEvents).call(this);
|
|
631
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayPreConsentEvents).call(this, queue);
|
|
611
632
|
}
|
|
612
633
|
else if (this.state.consentDecisionMade) {
|
|
613
634
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController.mjs","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 GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash';\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';\nimport { analyticsControllerSelectors } from './selectors.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// === 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 * 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/**\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/**\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 };\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 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} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\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 = GeolocationControllerGetGeolocationDataAction;\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 = never;\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 * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * The platform is responsible for generating the ID on first run.\n * It is then persisted with controller state when using a persisted store.\n */\n state: AnalyticsControllerState;\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/**\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\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 ) {\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 * 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 /**\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 #locationContext: AnalyticsLocationContext | undefined;\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 * Use `getDefaultAnalyticsControllerState()` for default opt-in preferences.\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 * @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 }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\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.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\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 consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\n });\n }\n\n /**\n * Initialize the controller by resolving the geolocation used to enrich\n * events and then calling the platform adapter's onSetupCompleted lifecycle\n * hook. This method must be called after construction to complete the setup\n * process.\n *\n * Geolocation is resolved before any queued event is replayed so that\n * replayed events carry the same location context as new ones.\n *\n * When geolocation enrichment is enabled (`isGeolocationEnabled`), the\n * `GeolocationController` and its `GeolocationController:getGeolocationData`\n * action must be registered and initialized *before* this method is called.\n * Otherwise the resolution fails and events are delivered for the rest of the\n * session without location (a message is logged, see\n * {@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 await this.#resolveLocationContext();\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.#reconcilePreConsentEvents();\n }\n\n /**\n * Resolve the location context used to enrich analytics events.\n *\n * No-op unless geolocation enrichment is enabled. Otherwise geolocation is\n * best-effort: when the GeolocationController is unavailable or fails to\n * resolve, events are still delivered, just without location.\n */\n async #resolveLocationContext(): Promise<void> {\n if (!this.#isGeolocationEnabled) {\n return;\n }\n\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 calling `init()` before the GeolocationController is\n // registered/initialized. Name it here so the failure is diagnosable,\n // since enrichment is 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 AnalyticsController.init() 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 * 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 */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n // Direct delivery: enabled and not persisting.\n if (\n analyticsControllerSelectors.selectEnabled(this.state) &&\n !this.#isEventQueuePersistenceEnabled\n ) {\n this.#platformAdapter.track(eventName, properties, context);\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 === undefined ? {} : { context }),\n };\n\n // Not yet enabled (reached only while undecided with the pre-consent queue\n // enabled): hold the event until the user opts in.\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\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 if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, context);\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 === undefined ? {} : { context }),\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 */\n #sendOrQueueViewEvent(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, context);\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 === undefined ? {} : { context }),\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 as unknown as Json,\n };\n\n this.update((state) => {\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n this.#clearQueuedEvents();\n return;\n }\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 this.#removeQueuedEvent(messageId);\n continue;\n }\n\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 state.eventQueue = eventQueue as never;\n });\n }\n\n /**\n * Clear all queued analytics events.\n */\n #clearQueuedEvents(): void {\n if (\n !this.state.eventQueue ||\n Object.keys(this.state.eventQueue).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventQueue = {} as never;\n });\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 as unknown as Json,\n };\n\n this.update((state) => {\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Replay queued pre-consent events through the delivery path.\n *\n * Called on opt-in, once analytics is enabled. The queue is cleared before\n * replaying so events cannot be re-queued or replayed twice.\n */\n #replayPreConsentEvents(): void {\n if (!this.#isPreConsentQueueEnabled) {\n return;\n }\n\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n this.#clearPreConsentEvents();\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued pre-consent analytics event', {\n messageId,\n });\n continue;\n }\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(queuedEvent);\n } else {\n this.#sendQueuedEvent(queuedEvent);\n }\n }\n }\n\n /**\n * Clear all queued pre-consent events.\n */\n #clearPreConsentEvents(): void {\n if (!this.state.preConsentEventQueue) {\n return;\n }\n\n this.update((state) => {\n state.preConsentEventQueue = {} as never;\n });\n }\n\n /**\n * Reconcile the pre-consent queue on initialization.\n *\n * The queue should normally be empty unless the user is still undecided. This\n * handles the rare cases where a consent decision was persisted but the queue\n * was not flushed/cleared (e.g. an interrupted shutdown): replay it if the\n * user is opted in, or clear it if they opted out.\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 #reconcilePreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.#clearPreConsentEvents();\n return;\n }\n\n if (this.state.optedIn) {\n this.#replayPreConsentEvents();\n } else if (this.state.consentDecisionMade) {\n this.#clearPreConsentEvents();\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n // While the user is undecided, fall through so the event is processed and\n // captured in the pre-consent queue (see #sendOrQueueTrackEvent) to be\n // replayed if they later opt in. Otherwise (opted out, or pre-consent\n // queue disabled) drop it.\n const shouldQueuePreConsent =\n this.#isPreConsentQueueEnabled && !this.state.consentDecisionMade;\n if (!shouldQueuePreConsent) {\n return;\n }\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 );\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 );\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 );\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 // Delegate to platform adapter using the current analytics ID\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n );\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 optIn(): void {\n this.update((state) => {\n state.optedIn = true;\n state.consentDecisionMade = true;\n });\n\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 so nothing captured before the decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#clearQueuedEvents();\n this.#clearPreConsentEvents();\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 resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#clearQueuedEvents();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AnalyticsController.mjs","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 GeolocationControllerGetGeolocationDataAction,\n GeolocationData,\n} from '@metamask/geolocation-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport { cloneDeep } from 'lodash';\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';\nimport { analyticsControllerSelectors } from './selectors.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// === 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 * 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/**\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/**\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 };\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 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} satisfies StateMetadata<AnalyticsControllerState>;\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'trackEvent',\n 'identify',\n 'trackView',\n 'optIn',\n 'optOut',\n 'resetConsentDecision',\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 = GeolocationControllerGetGeolocationDataAction;\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 = never;\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 * The options that AnalyticsController takes.\n */\nexport type AnalyticsControllerOptions = {\n /**\n * Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * The platform is responsible for generating the ID on first run.\n * It is then persisted with controller state when using a persisted store.\n */\n state: AnalyticsControllerState;\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/**\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\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 ) {\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 * 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 /**\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 * Constructs an AnalyticsController instance.\n *\n * @param options - Controller options\n * @param options.state - Initial controller state. Must include a valid UUIDv4 `analyticsId`.\n * Use `getDefaultAnalyticsControllerState()` for default opt-in preferences.\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 * @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 }: AnalyticsControllerOptions) {\n const initialState: AnalyticsControllerState = {\n ...getDefaultAnalyticsControllerState(),\n ...state,\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.#platformAdapter = platformAdapter;\n this.#initPromise = undefined;\n this.#locationResolvePromise = undefined;\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 consentDecisionMade: this.state.consentDecisionMade,\n analyticsId: this.state.analyticsId,\n eventQueuePersistenceEnabled: this.#isEventQueuePersistenceEnabled,\n preConsentQueueEnabled: this.#isPreConsentQueueEnabled,\n geolocationEnabled: this.#isGeolocationEnabled,\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; for undecided or\n * opted-out users it is deferred until they opt in (see {@link optIn}), so a\n * user's location is never requested before they consent to analytics. In\n * 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 // Resolve geolocation only when the user is already opted in; for undecided\n // or opted-out users it is deferred to {@link optIn}. Awaited so that an\n // already-opted-in 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.#reconcilePreConsentEvents();\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, and a resolution has not\n * already been started. Deferring resolution until opt-in ensures a user's\n * location is never requested before they consent to analytics (for example,\n * 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 analyticsControllerSelectors.selectEnabled(this.state)\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 * 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 */\n #sendOrQueueTrackEvent(\n eventName: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n // Direct delivery: enabled and not persisting.\n if (\n analyticsControllerSelectors.selectEnabled(this.state) &&\n !this.#isEventQueuePersistenceEnabled\n ) {\n this.#platformAdapter.track(eventName, properties, context);\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 === undefined ? {} : { context }),\n };\n\n // Not yet enabled (reached only while undecided with the pre-consent queue\n // enabled): hold the event until the user opts in.\n if (!analyticsControllerSelectors.selectEnabled(this.state)) {\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 if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.identify(userId, traits, context);\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 === undefined ? {} : { context }),\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 */\n #sendOrQueueViewEvent(\n name: string,\n properties?: AnalyticsEventProperties,\n context?: AnalyticsContext,\n ): void {\n if (!this.#isEventQueuePersistenceEnabled) {\n this.#platformAdapter.view(name, properties, context);\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 === undefined ? {} : { context }),\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 as unknown as Json,\n };\n\n this.update((state) => {\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n this.#clearQueuedEvents();\n return;\n }\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 this.#removeQueuedEvent(messageId);\n continue;\n }\n\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 state.eventQueue = eventQueue as never;\n });\n }\n\n /**\n * Clear all queued analytics events.\n */\n #clearQueuedEvents(): void {\n if (\n !this.state.eventQueue ||\n Object.keys(this.state.eventQueue).length === 0\n ) {\n return;\n }\n\n this.update((state) => {\n state.eventQueue = {} as never;\n });\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 as unknown as Json,\n };\n\n this.update((state) => {\n state.preConsentEventQueue = preConsentEventQueue as never;\n });\n }\n\n /**\n * Replay queued pre-consent events through the delivery path.\n *\n * Only called by {@link #reconcilePreConsentEvents}, which guarantees the\n * pre-consent queue is enabled and that the user is opted in. The queue is\n * cleared before replaying so events cannot be re-queued or replayed twice.\n *\n * @param queue - The pre-consent event queue to replay.\n */\n #replayPreConsentEvents(queue: Record<string, Json>): void {\n this.#clearPreConsentEvents();\n\n for (const [messageId, queuedEvent] of Object.entries(queue)) {\n if (\n !isAnalyticsQueuedEvent(queuedEvent) ||\n queuedEvent.messageId !== messageId\n ) {\n log('Dropping invalid queued pre-consent analytics event', {\n messageId,\n });\n continue;\n }\n\n const eventToReplay = this.#enrichPreConsentEvent(queuedEvent);\n\n if (this.#isEventQueuePersistenceEnabled) {\n this.#enqueueEvent(eventToReplay);\n } else {\n this.#sendQueuedEvent(eventToReplay);\n }\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 * Clear all queued pre-consent events.\n */\n #clearPreConsentEvents(): void {\n if (!this.state.preConsentEventQueue) {\n return;\n }\n\n this.update((state) => {\n state.preConsentEventQueue = {} as never;\n });\n }\n\n /**\n * Reconcile the pre-consent queue on initialization.\n *\n * The queue should normally be empty unless the user is still undecided. This\n * handles the rare cases where a consent decision was persisted but the queue\n * was not flushed/cleared (e.g. an interrupted shutdown): replay it if the\n * user is opted in, or clear it if they opted out.\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 #reconcilePreConsentEvents(): void {\n const queue = this.state.preConsentEventQueue;\n\n if (!queue) {\n return;\n }\n\n if (!this.#isPreConsentQueueEnabled) {\n this.#clearPreConsentEvents();\n return;\n }\n\n if (this.state.optedIn) {\n this.#replayPreConsentEvents(queue);\n } else if (this.state.consentDecisionMade) {\n this.#clearPreConsentEvents();\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n // While the user is undecided, fall through so the event is processed and\n // captured in the pre-consent queue (see #sendOrQueueTrackEvent) to be\n // replayed if they later opt in. Otherwise (opted out, or pre-consent\n // queue disabled) drop it.\n const shouldQueuePreConsent =\n this.#isPreConsentQueueEnabled && !this.state.consentDecisionMade;\n if (!shouldQueuePreConsent) {\n return;\n }\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 );\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 );\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 );\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 // Delegate to platform adapter using the current analytics ID\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 if (!analyticsControllerSelectors.selectEnabled(this.state)) {\n return;\n }\n\n // Delegate to platform adapter\n this.#sendOrQueueViewEvent(\n name,\n properties,\n this.#withLocationContext(context),\n );\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.#reconcilePreConsentEvents();\n }\n\n /**\n * Opt out of analytics.\n *\n * Records that a consent decision has been made and discards any persisted\n * events so nothing captured before the decision is ever delivered.\n */\n optOut(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = true;\n });\n\n this.#clearQueuedEvents();\n this.#clearPreConsentEvents();\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 resetConsentDecision(): void {\n this.update((state) => {\n state.optedIn = false;\n state.consentDecisionMade = false;\n });\n\n this.#clearQueuedEvents();\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/analytics-controller",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-preview-d2f661012",
|
|
4
4
|
"description": "Common Analytics controller for event tracking",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@metamask/base-controller": "^9.1.0",
|
|
59
|
-
"@metamask/geolocation-controller": "^0.
|
|
59
|
+
"@metamask/geolocation-controller": "^1.0.0",
|
|
60
60
|
"@metamask/messenger": "^2.0.0",
|
|
61
61
|
"@metamask/utils": "^11.11.0",
|
|
62
62
|
"lodash": "^4.17.21",
|