@metamask-previews/analytics-controller 2.0.0-preview-597a80865 → 2.0.0-preview-0d04fb622
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 +4 -0
- package/README.md +46 -5
- package/dist/AnalyticsController-method-action-types.cjs.map +1 -1
- package/dist/AnalyticsController-method-action-types.d.cts +102 -2
- package/dist/AnalyticsController-method-action-types.d.cts.map +1 -1
- package/dist/AnalyticsController-method-action-types.d.mts +102 -2
- 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 +341 -13
- package/dist/AnalyticsController.cjs.map +1 -1
- package/dist/AnalyticsController.d.cts +105 -2
- package/dist/AnalyticsController.d.cts.map +1 -1
- package/dist/AnalyticsController.d.mts +105 -2
- package/dist/AnalyticsController.d.mts.map +1 -1
- package/dist/AnalyticsController.mjs +341 -13
- package/dist/AnalyticsController.mjs.map +1 -1
- package/dist/EventFragment.types.cjs +3 -0
- package/dist/EventFragment.types.cjs.map +1 -0
- package/dist/EventFragment.types.d.cts +88 -0
- package/dist/EventFragment.types.d.cts.map +1 -0
- package/dist/EventFragment.types.d.mts +88 -0
- package/dist/EventFragment.types.d.mts.map +1 -0
- package/dist/EventFragment.types.mjs +2 -0
- package/dist/EventFragment.types.mjs.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/selectors.cjs +19 -0
- package/dist/selectors.cjs.map +1 -1
- package/dist/selectors.d.cts +3 -0
- package/dist/selectors.d.cts.map +1 -1
- package/dist/selectors.d.mts +3 -0
- package/dist/selectors.d.mts.map +1 -1
- package/dist/selectors.mjs +19 -0
- package/dist/selectors.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import type { Messenger } from "@metamask/messenger";
|
|
|
5
5
|
import type { Json } from "@metamask/utils";
|
|
6
6
|
import type { AnalyticsControllerMethodActions } from "./AnalyticsController-method-action-types.mjs";
|
|
7
7
|
import type { AnalyticsPlatformAdapter, AnalyticsContext, AnalyticsEventProperties, AnalyticsUserTraits, AnalyticsTrackingEvent } from "./AnalyticsPlatformAdapter.types.mjs";
|
|
8
|
+
import type { AnalyticsEventFragment, AnalyticsEventFragmentFinalizeOptions, AnalyticsEventFragmentOptions, AnalyticsEventFragmentPayload, AnalyticsEventFragments } from "./EventFragment.types.mjs";
|
|
8
9
|
/**
|
|
9
10
|
* The name of the {@link AnalyticsController}, used to namespace the
|
|
10
11
|
* controller's actions and events and to namespace the controller's state data
|
|
@@ -50,6 +51,13 @@ export type AnalyticsControllerState = {
|
|
|
50
51
|
* This is only used when the pre-consent queue is enabled.
|
|
51
52
|
*/
|
|
52
53
|
preConsentEventQueue?: Record<string, Json>;
|
|
54
|
+
/**
|
|
55
|
+
* Persisted event fragments ({@link AnalyticsEventFragment}) keyed by
|
|
56
|
+
* fragment ID. Fragments accumulate properties across a user journey and are
|
|
57
|
+
* removed when the journey is finalized or deleted.
|
|
58
|
+
* This is only used when the event fragments feature is enabled.
|
|
59
|
+
*/
|
|
60
|
+
eventFragments?: AnalyticsEventFragments;
|
|
53
61
|
};
|
|
54
62
|
/**
|
|
55
63
|
* Event types supported by the persisted analytics event queue.
|
|
@@ -202,6 +210,18 @@ export type AnalyticsControllerOptions = {
|
|
|
202
210
|
* @default false
|
|
203
211
|
*/
|
|
204
212
|
isGeolocationEnabled?: boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Whether the event fragments feature is enabled.
|
|
215
|
+
*
|
|
216
|
+
* When enabled, clients can accumulate analytics properties across a user
|
|
217
|
+
* journey with {@link AnalyticsController.createEventFragment} and friends,
|
|
218
|
+
* as long as the consent state allows analytics to be captured. When
|
|
219
|
+
* disabled, every fragment method is a logged no-op and no fragment is ever
|
|
220
|
+
* written to state.
|
|
221
|
+
*
|
|
222
|
+
* @default false
|
|
223
|
+
*/
|
|
224
|
+
isEventFragmentsEnabled?: boolean;
|
|
205
225
|
};
|
|
206
226
|
/**
|
|
207
227
|
* The AnalyticsController manages analytics tracking across platforms (Mobile/Extension).
|
|
@@ -230,10 +250,11 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
230
250
|
* @param options.isEventQueuePersistenceEnabled - Whether analytics event queue persistence is enabled
|
|
231
251
|
* @param options.isPreConsentQueueEnabled - Whether the pre-consent event queue is enabled
|
|
232
252
|
* @param options.isGeolocationEnabled - Whether geolocation enrichment is enabled
|
|
253
|
+
* @param options.isEventFragmentsEnabled - Whether the event fragments feature is enabled
|
|
233
254
|
* @throws Error if state.analyticsId is missing or not a valid UUIDv4
|
|
234
255
|
* @remarks After construction, call {@link AnalyticsController.init} to complete initialization.
|
|
235
256
|
*/
|
|
236
|
-
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled, isEventQueuePersistenceEnabled, isPreConsentQueueEnabled, isGeolocationEnabled, }: AnalyticsControllerOptions);
|
|
257
|
+
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled, isEventQueuePersistenceEnabled, isPreConsentQueueEnabled, isGeolocationEnabled, isEventFragmentsEnabled, }: AnalyticsControllerOptions);
|
|
237
258
|
/**
|
|
238
259
|
* Initialize the controller by calling the platform adapter's
|
|
239
260
|
* onSetupCompleted lifecycle hook and replaying any queued events. This
|
|
@@ -278,6 +299,83 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
278
299
|
* @param context - Optional platform-specific context forwarded to the platform adapter.
|
|
279
300
|
*/
|
|
280
301
|
trackView(name: string, properties?: AnalyticsEventProperties, context?: AnalyticsContext): void;
|
|
302
|
+
/**
|
|
303
|
+
* Create an event fragment.
|
|
304
|
+
*
|
|
305
|
+
* A fragment accumulates properties across a user journey so that several
|
|
306
|
+
* parts of a client can contribute to the same set of events without
|
|
307
|
+
* re-deriving them. Declaring `successEvent` and `failureEvent` turns the
|
|
308
|
+
* fragment into a funnel that {@link finalizeEventFragment} closes. Declaring
|
|
309
|
+
* none of the event names makes it a pure property bag that the client reads
|
|
310
|
+
* back with {@link getEventFragmentById} when it emits its own events.
|
|
311
|
+
*
|
|
312
|
+
* Any existing fragment with the same ID is replaced, so a new journey never
|
|
313
|
+
* inherits properties from a stale one.
|
|
314
|
+
*
|
|
315
|
+
* Nothing is created unless the user is opted in, or undecided with the
|
|
316
|
+
* pre-consent queue enabled, so an opted-out user accumulates no fragment
|
|
317
|
+
* data.
|
|
318
|
+
*
|
|
319
|
+
* @param options - The fragment definition. An ID is generated when one is
|
|
320
|
+
* not supplied.
|
|
321
|
+
* @returns The created fragment, or `undefined` when the event fragments
|
|
322
|
+
* feature is disabled or the consent state does not allow capture.
|
|
323
|
+
*/
|
|
324
|
+
createEventFragment(options?: AnalyticsEventFragmentOptions): AnalyticsEventFragment | undefined;
|
|
325
|
+
/**
|
|
326
|
+
* Write to an event fragment, creating a property bag if none exists.
|
|
327
|
+
*
|
|
328
|
+
* This is the ergonomic entry point for contributors that do not know
|
|
329
|
+
* whether the journey has been started yet, and it avoids the read then
|
|
330
|
+
* write race a caller would otherwise have to implement itself.
|
|
331
|
+
*
|
|
332
|
+
* @param id - The fragment ID.
|
|
333
|
+
* @param payload - The properties and context to merge in.
|
|
334
|
+
*/
|
|
335
|
+
upsertEventFragment(id: string, payload?: AnalyticsEventFragmentPayload): void;
|
|
336
|
+
/**
|
|
337
|
+
* Write to an existing event fragment.
|
|
338
|
+
*
|
|
339
|
+
* @param id - The fragment ID.
|
|
340
|
+
* @param payload - The properties and context to merge in.
|
|
341
|
+
* @throws Error if no fragment has that ID when the call is not ignored.
|
|
342
|
+
* Use {@link upsertEventFragment} when the fragment may not exist yet.
|
|
343
|
+
* When the event fragments feature is disabled or the consent state does not
|
|
344
|
+
* allow capture, the call is a logged no-op and does not throw.
|
|
345
|
+
*/
|
|
346
|
+
updateEventFragment(id: string, payload?: AnalyticsEventFragmentPayload): void;
|
|
347
|
+
/**
|
|
348
|
+
* Read an event fragment.
|
|
349
|
+
*
|
|
350
|
+
* @param id - The fragment ID.
|
|
351
|
+
* @returns The fragment, or `undefined` when no fragment has that ID, the
|
|
352
|
+
* event fragments feature is disabled, or the consent state does not allow
|
|
353
|
+
* capture.
|
|
354
|
+
*/
|
|
355
|
+
getEventFragmentById(id: string): AnalyticsEventFragment | undefined;
|
|
356
|
+
/**
|
|
357
|
+
* Discard an event fragment without emitting anything.
|
|
358
|
+
*
|
|
359
|
+
* @param id - The fragment ID.
|
|
360
|
+
*/
|
|
361
|
+
deleteEventFragment(id: string): void;
|
|
362
|
+
/**
|
|
363
|
+
* Close an event fragment, emitting its closing event and discarding it.
|
|
364
|
+
*
|
|
365
|
+
* The event emitted is `failureEvent` when the journey was abandoned and
|
|
366
|
+
* `successEvent` otherwise. A fragment that does not declare the relevant
|
|
367
|
+
* event name is discarded silently, which is what makes a pure property bag
|
|
368
|
+
* possible.
|
|
369
|
+
*
|
|
370
|
+
* @param id - The fragment ID.
|
|
371
|
+
* @param options - Finalization options.
|
|
372
|
+
* @param options.abandoned - Whether the journey was abandoned.
|
|
373
|
+
* @param options.context - Context merged over the fragment's own context.
|
|
374
|
+
* @throws Error if no fragment has that ID when the call is not ignored.
|
|
375
|
+
* When the event fragments feature is disabled or the consent state does not
|
|
376
|
+
* allow capture, the call is a logged no-op and does not throw.
|
|
377
|
+
*/
|
|
378
|
+
finalizeEventFragment(id: string, { abandoned, context }?: AnalyticsEventFragmentFinalizeOptions): void;
|
|
281
379
|
/**
|
|
282
380
|
* Opt in to analytics.
|
|
283
381
|
*
|
|
@@ -295,7 +393,8 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
295
393
|
* Opt out of analytics.
|
|
296
394
|
*
|
|
297
395
|
* Records that a consent decision has been made and discards any persisted
|
|
298
|
-
* events so nothing captured before the
|
|
396
|
+
* events and in-progress event fragments so nothing captured before the
|
|
397
|
+
* decision is ever delivered.
|
|
299
398
|
*/
|
|
300
399
|
optOut(): void;
|
|
301
400
|
/**
|
|
@@ -305,6 +404,10 @@ export declare class AnalyticsController extends BaseController<'AnalyticsContro
|
|
|
305
404
|
* preference and discards the delivery queue, but preserves any pre-consent
|
|
306
405
|
* events so they can still be replayed if the user opts in again. The user is
|
|
307
406
|
* treated as undecided again.
|
|
407
|
+
*
|
|
408
|
+
* In-progress event fragments are kept only while the undecided user can
|
|
409
|
+
* still accumulate them, and discarded otherwise, so no fragment outlives the
|
|
410
|
+
* consent state that allowed it.
|
|
308
411
|
*/
|
|
309
412
|
resetConsentDecision(): void;
|
|
310
413
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsController.d.mts","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;
|
|
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_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;
|
|
12
|
+
var _AnalyticsController_instances, _AnalyticsController_platformAdapter, _AnalyticsController_isAnonymousEventsFeatureEnabled, _AnalyticsController_isEventQueuePersistenceEnabled, _AnalyticsController_isPreConsentQueueEnabled, _AnalyticsController_isGeolocationEnabled, _AnalyticsController_isEventFragmentsEnabled, _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, _AnalyticsController_reconcileEventFragments, _AnalyticsController_purgeNonPersistentEventFragments, _AnalyticsController_getEventFragment, _AnalyticsController_setEventFragment, _AnalyticsController_removeEventFragment, _AnalyticsController_clearEventFragments, _AnalyticsController_shouldIgnoreEventFragmentCall, _AnalyticsController_emitEventFragment, _AnalyticsController_isAnalyticsCaptureAllowed;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import $lodash from "lodash";
|
|
15
15
|
const { cloneDeep } = $lodash;
|
|
@@ -75,6 +75,12 @@ const analyticsControllerMetadata = {
|
|
|
75
75
|
includeInDebugSnapshot: false,
|
|
76
76
|
usedInUi: false,
|
|
77
77
|
},
|
|
78
|
+
eventFragments: {
|
|
79
|
+
includeInStateLogs: false,
|
|
80
|
+
persist: true,
|
|
81
|
+
includeInDebugSnapshot: false,
|
|
82
|
+
usedInUi: false,
|
|
83
|
+
},
|
|
78
84
|
};
|
|
79
85
|
// === MESSENGER ===
|
|
80
86
|
const MESSENGER_EXPOSED_METHODS = [
|
|
@@ -84,6 +90,12 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
84
90
|
'optIn',
|
|
85
91
|
'optOut',
|
|
86
92
|
'resetConsentDecision',
|
|
93
|
+
'createEventFragment',
|
|
94
|
+
'upsertEventFragment',
|
|
95
|
+
'updateEventFragment',
|
|
96
|
+
'getEventFragmentById',
|
|
97
|
+
'deleteEventFragment',
|
|
98
|
+
'finalizeEventFragment',
|
|
87
99
|
];
|
|
88
100
|
/**
|
|
89
101
|
* Returns whether a value is a non-array object.
|
|
@@ -155,6 +167,69 @@ function isAnalyticsQueuedEvent(value) {
|
|
|
155
167
|
}
|
|
156
168
|
return false;
|
|
157
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Returns whether a value is a valid persisted event fragment.
|
|
172
|
+
*
|
|
173
|
+
* @param value - The value to check.
|
|
174
|
+
* @returns True if the value is an event fragment.
|
|
175
|
+
*/
|
|
176
|
+
function isAnalyticsEventFragment(value) {
|
|
177
|
+
if (!isRecord(value)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return (typeof value.id === 'string' &&
|
|
181
|
+
typeof value.createdAt === 'number' &&
|
|
182
|
+
typeof value.lastUpdated === 'number' &&
|
|
183
|
+
isRecord(value.properties) &&
|
|
184
|
+
isRecord(value.sensitiveProperties) &&
|
|
185
|
+
(value.initialEvent === undefined ||
|
|
186
|
+
typeof value.initialEvent === 'string') &&
|
|
187
|
+
(value.successEvent === undefined ||
|
|
188
|
+
typeof value.successEvent === 'string') &&
|
|
189
|
+
(value.failureEvent === undefined ||
|
|
190
|
+
typeof value.failureEvent === 'string') &&
|
|
191
|
+
(value.context === undefined || isRecord(value.context)) &&
|
|
192
|
+
(value.persist === undefined || typeof value.persist === 'boolean'));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Merges a payload into an event fragment.
|
|
196
|
+
*
|
|
197
|
+
* `properties`, `sensitiveProperties` and `context` are merged one level deep,
|
|
198
|
+
* so a key written twice is replaced rather than combined. This keeps array
|
|
199
|
+
* values predictable: writing a shorter array replaces the longer one instead
|
|
200
|
+
* of leaving stale trailing entries behind.
|
|
201
|
+
*
|
|
202
|
+
* @param fragment - The fragment to merge into.
|
|
203
|
+
* @param payload - The payload to merge.
|
|
204
|
+
* @returns A new fragment with the payload applied.
|
|
205
|
+
*/
|
|
206
|
+
function mergeEventFragment(fragment, payload) {
|
|
207
|
+
const context = mergeEventFragmentContext(fragment.context, payload.context);
|
|
208
|
+
return {
|
|
209
|
+
...fragment,
|
|
210
|
+
properties: { ...fragment.properties, ...(payload.properties ?? {}) },
|
|
211
|
+
sensitiveProperties: {
|
|
212
|
+
...fragment.sensitiveProperties,
|
|
213
|
+
...(payload.sensitiveProperties ?? {}),
|
|
214
|
+
},
|
|
215
|
+
...(context === undefined ? {} : { context }),
|
|
216
|
+
lastUpdated: Date.now(),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Merges two optional analytics contexts, preserving `undefined` when neither
|
|
221
|
+
* side has one so an empty context is never sent.
|
|
222
|
+
*
|
|
223
|
+
* @param base - The context to merge into.
|
|
224
|
+
* @param override - The context whose fields win.
|
|
225
|
+
* @returns The merged context, or `undefined` when both sides are unset.
|
|
226
|
+
*/
|
|
227
|
+
function mergeEventFragmentContext(base, override) {
|
|
228
|
+
if (base === undefined && override === undefined) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return { ...(base ?? {}), ...(override ?? {}) };
|
|
232
|
+
}
|
|
158
233
|
/**
|
|
159
234
|
* The AnalyticsController manages analytics tracking across platforms (Mobile/Extension).
|
|
160
235
|
* It provides a unified interface for tracking events, identifying users, and managing
|
|
@@ -181,10 +256,11 @@ export class AnalyticsController extends BaseController {
|
|
|
181
256
|
* @param options.isEventQueuePersistenceEnabled - Whether analytics event queue persistence is enabled
|
|
182
257
|
* @param options.isPreConsentQueueEnabled - Whether the pre-consent event queue is enabled
|
|
183
258
|
* @param options.isGeolocationEnabled - Whether geolocation enrichment is enabled
|
|
259
|
+
* @param options.isEventFragmentsEnabled - Whether the event fragments feature is enabled
|
|
184
260
|
* @throws Error if state.analyticsId is missing or not a valid UUIDv4
|
|
185
261
|
* @remarks After construction, call {@link AnalyticsController.init} to complete initialization.
|
|
186
262
|
*/
|
|
187
|
-
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled = false, isEventQueuePersistenceEnabled = false, isPreConsentQueueEnabled = false, isGeolocationEnabled = false, }) {
|
|
263
|
+
constructor({ state, messenger, platformAdapter, isAnonymousEventsFeatureEnabled = false, isEventQueuePersistenceEnabled = false, isPreConsentQueueEnabled = false, isGeolocationEnabled = false, isEventFragmentsEnabled = false, }) {
|
|
188
264
|
const initialState = {
|
|
189
265
|
...getDefaultAnalyticsControllerState(),
|
|
190
266
|
...state,
|
|
@@ -202,6 +278,7 @@ export class AnalyticsController extends BaseController {
|
|
|
202
278
|
_AnalyticsController_isEventQueuePersistenceEnabled.set(this, void 0);
|
|
203
279
|
_AnalyticsController_isPreConsentQueueEnabled.set(this, void 0);
|
|
204
280
|
_AnalyticsController_isGeolocationEnabled.set(this, void 0);
|
|
281
|
+
_AnalyticsController_isEventFragmentsEnabled.set(this, void 0);
|
|
205
282
|
/**
|
|
206
283
|
* The in-flight (or settled) initialization promise. Set on the first
|
|
207
284
|
* {@link init} call and returned by subsequent calls so overlapping callers
|
|
@@ -218,6 +295,7 @@ export class AnalyticsController extends BaseController {
|
|
|
218
295
|
__classPrivateFieldSet(this, _AnalyticsController_isEventQueuePersistenceEnabled, isEventQueuePersistenceEnabled, "f");
|
|
219
296
|
__classPrivateFieldSet(this, _AnalyticsController_isPreConsentQueueEnabled, isPreConsentQueueEnabled, "f");
|
|
220
297
|
__classPrivateFieldSet(this, _AnalyticsController_isGeolocationEnabled, isGeolocationEnabled, "f");
|
|
298
|
+
__classPrivateFieldSet(this, _AnalyticsController_isEventFragmentsEnabled, isEventFragmentsEnabled, "f");
|
|
221
299
|
__classPrivateFieldSet(this, _AnalyticsController_platformAdapter, platformAdapter, "f");
|
|
222
300
|
__classPrivateFieldSet(this, _AnalyticsController_initPromise, undefined, "f");
|
|
223
301
|
__classPrivateFieldSet(this, _AnalyticsController_locationResolvePromise, undefined, "f");
|
|
@@ -230,6 +308,7 @@ export class AnalyticsController extends BaseController {
|
|
|
230
308
|
eventQueuePersistenceEnabled: __classPrivateFieldGet(this, _AnalyticsController_isEventQueuePersistenceEnabled, "f"),
|
|
231
309
|
preConsentQueueEnabled: __classPrivateFieldGet(this, _AnalyticsController_isPreConsentQueueEnabled, "f"),
|
|
232
310
|
geolocationEnabled: __classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f"),
|
|
311
|
+
eventFragmentsEnabled: __classPrivateFieldGet(this, _AnalyticsController_isEventFragmentsEnabled, "f"),
|
|
233
312
|
});
|
|
234
313
|
}
|
|
235
314
|
/**
|
|
@@ -267,15 +346,11 @@ export class AnalyticsController extends BaseController {
|
|
|
267
346
|
* @param context - Optional platform-specific context forwarded to the platform adapter.
|
|
268
347
|
*/
|
|
269
348
|
trackEvent(event, context) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const shouldQueuePreConsent = __classPrivateFieldGet(this, _AnalyticsController_isPreConsentQueueEnabled, "f") && !this.state.consentDecisionMade;
|
|
276
|
-
if (!shouldQueuePreConsent) {
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
349
|
+
// An event captured while the user is still undecided is held in the
|
|
350
|
+
// pre-consent queue (see #sendOrQueueTrackEvent) instead of being
|
|
351
|
+
// delivered, and replayed if they later opt in.
|
|
352
|
+
if (!__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_isAnalyticsCaptureAllowed).call(this)) {
|
|
353
|
+
return;
|
|
279
354
|
}
|
|
280
355
|
// if event does not have properties, send event without properties
|
|
281
356
|
// and return to prevent any additional processing
|
|
@@ -333,6 +408,153 @@ export class AnalyticsController extends BaseController {
|
|
|
333
408
|
// Delegate to platform adapter
|
|
334
409
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_sendOrQueueViewEvent).call(this, name, properties, __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_withLocationContext).call(this, context));
|
|
335
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Create an event fragment.
|
|
413
|
+
*
|
|
414
|
+
* A fragment accumulates properties across a user journey so that several
|
|
415
|
+
* parts of a client can contribute to the same set of events without
|
|
416
|
+
* re-deriving them. Declaring `successEvent` and `failureEvent` turns the
|
|
417
|
+
* fragment into a funnel that {@link finalizeEventFragment} closes. Declaring
|
|
418
|
+
* none of the event names makes it a pure property bag that the client reads
|
|
419
|
+
* back with {@link getEventFragmentById} when it emits its own events.
|
|
420
|
+
*
|
|
421
|
+
* Any existing fragment with the same ID is replaced, so a new journey never
|
|
422
|
+
* inherits properties from a stale one.
|
|
423
|
+
*
|
|
424
|
+
* Nothing is created unless the user is opted in, or undecided with the
|
|
425
|
+
* pre-consent queue enabled, so an opted-out user accumulates no fragment
|
|
426
|
+
* data.
|
|
427
|
+
*
|
|
428
|
+
* @param options - The fragment definition. An ID is generated when one is
|
|
429
|
+
* not supplied.
|
|
430
|
+
* @returns The created fragment, or `undefined` when the event fragments
|
|
431
|
+
* feature is disabled or the consent state does not allow capture.
|
|
432
|
+
*/
|
|
433
|
+
createEventFragment(options = {}) {
|
|
434
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'createEventFragment')) {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
const now = Date.now();
|
|
438
|
+
const fragment = {
|
|
439
|
+
id: options.id ?? uuid(),
|
|
440
|
+
properties: { ...(options.properties ?? {}) },
|
|
441
|
+
sensitiveProperties: { ...(options.sensitiveProperties ?? {}) },
|
|
442
|
+
createdAt: now,
|
|
443
|
+
lastUpdated: now,
|
|
444
|
+
...(options.initialEvent === undefined
|
|
445
|
+
? {}
|
|
446
|
+
: { initialEvent: options.initialEvent }),
|
|
447
|
+
...(options.successEvent === undefined
|
|
448
|
+
? {}
|
|
449
|
+
: { successEvent: options.successEvent }),
|
|
450
|
+
...(options.failureEvent === undefined
|
|
451
|
+
? {}
|
|
452
|
+
: { failureEvent: options.failureEvent }),
|
|
453
|
+
...(options.context === undefined ? {} : { context: options.context }),
|
|
454
|
+
...(options.persist === undefined ? {} : { persist: options.persist }),
|
|
455
|
+
};
|
|
456
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_setEventFragment).call(this, fragment);
|
|
457
|
+
if (fragment.initialEvent) {
|
|
458
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_emitEventFragment).call(this, fragment, fragment.initialEvent, fragment.context);
|
|
459
|
+
}
|
|
460
|
+
return fragment;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Write to an event fragment, creating a property bag if none exists.
|
|
464
|
+
*
|
|
465
|
+
* This is the ergonomic entry point for contributors that do not know
|
|
466
|
+
* whether the journey has been started yet, and it avoids the read then
|
|
467
|
+
* write race a caller would otherwise have to implement itself.
|
|
468
|
+
*
|
|
469
|
+
* @param id - The fragment ID.
|
|
470
|
+
* @param payload - The properties and context to merge in.
|
|
471
|
+
*/
|
|
472
|
+
upsertEventFragment(id, payload = {}) {
|
|
473
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'upsertEventFragment')) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const fragment = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_getEventFragment).call(this, id);
|
|
477
|
+
if (!fragment) {
|
|
478
|
+
this.createEventFragment({ id, ...payload });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_setEventFragment).call(this, mergeEventFragment(fragment, payload));
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Write to an existing event fragment.
|
|
485
|
+
*
|
|
486
|
+
* @param id - The fragment ID.
|
|
487
|
+
* @param payload - The properties and context to merge in.
|
|
488
|
+
* @throws Error if no fragment has that ID when the call is not ignored.
|
|
489
|
+
* Use {@link upsertEventFragment} when the fragment may not exist yet.
|
|
490
|
+
* When the event fragments feature is disabled or the consent state does not
|
|
491
|
+
* allow capture, the call is a logged no-op and does not throw.
|
|
492
|
+
*/
|
|
493
|
+
updateEventFragment(id, payload = {}) {
|
|
494
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'updateEventFragment')) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const fragment = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_getEventFragment).call(this, id);
|
|
498
|
+
if (!fragment) {
|
|
499
|
+
throw new Error(`Event fragment with id ${id} does not exist.`);
|
|
500
|
+
}
|
|
501
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_setEventFragment).call(this, mergeEventFragment(fragment, payload));
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Read an event fragment.
|
|
505
|
+
*
|
|
506
|
+
* @param id - The fragment ID.
|
|
507
|
+
* @returns The fragment, or `undefined` when no fragment has that ID, the
|
|
508
|
+
* event fragments feature is disabled, or the consent state does not allow
|
|
509
|
+
* capture.
|
|
510
|
+
*/
|
|
511
|
+
getEventFragmentById(id) {
|
|
512
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'getEventFragmentById')) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
return __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_getEventFragment).call(this, id);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Discard an event fragment without emitting anything.
|
|
519
|
+
*
|
|
520
|
+
* @param id - The fragment ID.
|
|
521
|
+
*/
|
|
522
|
+
deleteEventFragment(id) {
|
|
523
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'deleteEventFragment')) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_removeEventFragment).call(this, id);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Close an event fragment, emitting its closing event and discarding it.
|
|
530
|
+
*
|
|
531
|
+
* The event emitted is `failureEvent` when the journey was abandoned and
|
|
532
|
+
* `successEvent` otherwise. A fragment that does not declare the relevant
|
|
533
|
+
* event name is discarded silently, which is what makes a pure property bag
|
|
534
|
+
* possible.
|
|
535
|
+
*
|
|
536
|
+
* @param id - The fragment ID.
|
|
537
|
+
* @param options - Finalization options.
|
|
538
|
+
* @param options.abandoned - Whether the journey was abandoned.
|
|
539
|
+
* @param options.context - Context merged over the fragment's own context.
|
|
540
|
+
* @throws Error if no fragment has that ID when the call is not ignored.
|
|
541
|
+
* When the event fragments feature is disabled or the consent state does not
|
|
542
|
+
* allow capture, the call is a logged no-op and does not throw.
|
|
543
|
+
*/
|
|
544
|
+
finalizeEventFragment(id, { abandoned = false, context } = {}) {
|
|
545
|
+
if (__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_shouldIgnoreEventFragmentCall).call(this, 'finalizeEventFragment')) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const fragment = __classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_getEventFragment).call(this, id);
|
|
549
|
+
if (!fragment) {
|
|
550
|
+
throw new Error(`Event fragment with id ${id} does not exist.`);
|
|
551
|
+
}
|
|
552
|
+
const eventName = abandoned ? fragment.failureEvent : fragment.successEvent;
|
|
553
|
+
if (eventName) {
|
|
554
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_emitEventFragment).call(this, fragment, eventName, mergeEventFragmentContext(fragment.context, context));
|
|
555
|
+
}
|
|
556
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_removeEventFragment).call(this, id);
|
|
557
|
+
}
|
|
336
558
|
/**
|
|
337
559
|
* Opt in to analytics.
|
|
338
560
|
*
|
|
@@ -363,7 +585,8 @@ export class AnalyticsController extends BaseController {
|
|
|
363
585
|
* Opt out of analytics.
|
|
364
586
|
*
|
|
365
587
|
* Records that a consent decision has been made and discards any persisted
|
|
366
|
-
* events so nothing captured before the
|
|
588
|
+
* events and in-progress event fragments so nothing captured before the
|
|
589
|
+
* decision is ever delivered.
|
|
367
590
|
*/
|
|
368
591
|
optOut() {
|
|
369
592
|
this.update((state) => {
|
|
@@ -372,6 +595,7 @@ export class AnalyticsController extends BaseController {
|
|
|
372
595
|
});
|
|
373
596
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearQueuedEvents).call(this);
|
|
374
597
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
598
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearEventFragments).call(this);
|
|
375
599
|
}
|
|
376
600
|
/**
|
|
377
601
|
* Reset the consent decision back to undecided.
|
|
@@ -380,6 +604,10 @@ export class AnalyticsController extends BaseController {
|
|
|
380
604
|
* preference and discards the delivery queue, but preserves any pre-consent
|
|
381
605
|
* events so they can still be replayed if the user opts in again. The user is
|
|
382
606
|
* treated as undecided again.
|
|
607
|
+
*
|
|
608
|
+
* In-progress event fragments are kept only while the undecided user can
|
|
609
|
+
* still accumulate them, and discarded otherwise, so no fragment outlives the
|
|
610
|
+
* consent state that allowed it.
|
|
383
611
|
*/
|
|
384
612
|
resetConsentDecision() {
|
|
385
613
|
this.update((state) => {
|
|
@@ -387,15 +615,29 @@ export class AnalyticsController extends BaseController {
|
|
|
387
615
|
state.consentDecisionMade = false;
|
|
388
616
|
});
|
|
389
617
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearQueuedEvents).call(this);
|
|
618
|
+
if (!__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_isAnalyticsCaptureAllowed).call(this)) {
|
|
619
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearEventFragments).call(this);
|
|
620
|
+
}
|
|
390
621
|
}
|
|
391
622
|
}
|
|
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 =
|
|
623
|
+
_AnalyticsController_platformAdapter = new WeakMap(), _AnalyticsController_isAnonymousEventsFeatureEnabled = new WeakMap(), _AnalyticsController_isEventQueuePersistenceEnabled = new WeakMap(), _AnalyticsController_isPreConsentQueueEnabled = new WeakMap(), _AnalyticsController_isGeolocationEnabled = new WeakMap(), _AnalyticsController_isEventFragmentsEnabled = new WeakMap(), _AnalyticsController_initPromise = new WeakMap(), _AnalyticsController_locationResolvePromise = new WeakMap(), _AnalyticsController_locationContext = new WeakMap(), _AnalyticsController_instances = new WeakSet(), _AnalyticsController_performInit =
|
|
393
624
|
/**
|
|
394
625
|
* Performs the one-time initialization work: resolve geolocation, run the
|
|
395
626
|
* platform adapter's onSetupCompleted lifecycle hook, then replay any queued
|
|
396
627
|
* and pre-consent events.
|
|
397
628
|
*/
|
|
398
629
|
async function _AnalyticsController_performInit() {
|
|
630
|
+
// Snapshot fragment IDs and createdAt before any awaited init work so
|
|
631
|
+
// reconciliation can tell previous-session leftovers from fragments
|
|
632
|
+
// created or replaced while init runs.
|
|
633
|
+
const initEventFragmentSnapshot = new Map();
|
|
634
|
+
for (const [id, fragment] of Object.entries(this.state.eventFragments ?? {})) {
|
|
635
|
+
if (isAnalyticsEventFragment(fragment) &&
|
|
636
|
+
fragment.id === id &&
|
|
637
|
+
typeof fragment.createdAt === 'number') {
|
|
638
|
+
initEventFragmentSnapshot.set(id, fragment.createdAt);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
399
641
|
// Resolve geolocation only when the user is already opted in; for undecided
|
|
400
642
|
// or opted-out users it is deferred to {@link optIn}. Awaited so that an
|
|
401
643
|
// already-opted-in session has location available before events replay.
|
|
@@ -411,6 +653,7 @@ async function _AnalyticsController_performInit() {
|
|
|
411
653
|
}
|
|
412
654
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_replayQueuedEvents).call(this);
|
|
413
655
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcilePreConsentEvents).call(this);
|
|
656
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_reconcileEventFragments).call(this, initEventFragmentSnapshot);
|
|
414
657
|
}, _AnalyticsController_maybeResolveLocation = function _AnalyticsController_maybeResolveLocation() {
|
|
415
658
|
if (__classPrivateFieldGet(this, _AnalyticsController_isGeolocationEnabled, "f") &&
|
|
416
659
|
__classPrivateFieldGet(this, _AnalyticsController_locationResolvePromise, "f") === undefined &&
|
|
@@ -633,5 +876,90 @@ async function _AnalyticsController_performInit() {
|
|
|
633
876
|
else if (this.state.consentDecisionMade) {
|
|
634
877
|
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearPreConsentEvents).call(this);
|
|
635
878
|
}
|
|
879
|
+
}, _AnalyticsController_reconcileEventFragments = function _AnalyticsController_reconcileEventFragments(initEventFragmentSnapshot) {
|
|
880
|
+
const fragments = this.state.eventFragments;
|
|
881
|
+
if (!fragments) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!__classPrivateFieldGet(this, _AnalyticsController_isEventFragmentsEnabled, "f") || !__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_isAnalyticsCaptureAllowed).call(this)) {
|
|
885
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_clearEventFragments).call(this);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_purgeNonPersistentEventFragments).call(this, fragments, initEventFragmentSnapshot);
|
|
889
|
+
}, _AnalyticsController_purgeNonPersistentEventFragments = function _AnalyticsController_purgeNonPersistentEventFragments(currentEventFragments, initEventFragmentSnapshot) {
|
|
890
|
+
const eventFragments = {};
|
|
891
|
+
for (const [id, fragment] of Object.entries(currentEventFragments)) {
|
|
892
|
+
if (!isAnalyticsEventFragment(fragment) || fragment.id !== id) {
|
|
893
|
+
log('Dropping invalid persisted event fragment', { id });
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
const snapshotCreatedAt = initEventFragmentSnapshot.get(id);
|
|
897
|
+
if (fragment.persist === true ||
|
|
898
|
+
snapshotCreatedAt === undefined ||
|
|
899
|
+
fragment.createdAt !== snapshotCreatedAt) {
|
|
900
|
+
eventFragments[id] = fragment;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (Object.keys(eventFragments).length ===
|
|
904
|
+
Object.keys(currentEventFragments).length) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
this.update((state) => {
|
|
908
|
+
state.eventFragments = eventFragments;
|
|
909
|
+
});
|
|
910
|
+
}, _AnalyticsController_getEventFragment = function _AnalyticsController_getEventFragment(id) {
|
|
911
|
+
return this.state.eventFragments?.[id];
|
|
912
|
+
}, _AnalyticsController_setEventFragment = function _AnalyticsController_setEventFragment(fragment) {
|
|
913
|
+
const eventFragments = {
|
|
914
|
+
...this.state.eventFragments,
|
|
915
|
+
[fragment.id]: fragment,
|
|
916
|
+
};
|
|
917
|
+
this.update((state) => {
|
|
918
|
+
state.eventFragments = eventFragments;
|
|
919
|
+
});
|
|
920
|
+
}, _AnalyticsController_removeEventFragment = function _AnalyticsController_removeEventFragment(id) {
|
|
921
|
+
const currentEventFragments = this.state.eventFragments;
|
|
922
|
+
if (!currentEventFragments ||
|
|
923
|
+
!Object.prototype.hasOwnProperty.call(currentEventFragments, id)) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const { [id]: _deletedFragment, ...eventFragments } = currentEventFragments;
|
|
927
|
+
this.update((state) => {
|
|
928
|
+
state.eventFragments = eventFragments;
|
|
929
|
+
});
|
|
930
|
+
}, _AnalyticsController_clearEventFragments = function _AnalyticsController_clearEventFragments() {
|
|
931
|
+
if (!this.state.eventFragments ||
|
|
932
|
+
Object.keys(this.state.eventFragments).length === 0) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
this.update((state) => {
|
|
936
|
+
state.eventFragments = {};
|
|
937
|
+
});
|
|
938
|
+
}, _AnalyticsController_shouldIgnoreEventFragmentCall = function _AnalyticsController_shouldIgnoreEventFragmentCall(method) {
|
|
939
|
+
if (!__classPrivateFieldGet(this, _AnalyticsController_isEventFragmentsEnabled, "f")) {
|
|
940
|
+
log('Ignoring event fragment call because the event fragments feature is disabled', { method });
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
if (!__classPrivateFieldGet(this, _AnalyticsController_instances, "m", _AnalyticsController_isAnalyticsCaptureAllowed).call(this)) {
|
|
944
|
+
log('Ignoring event fragment call because the consent state does not allow capturing analytics', { method });
|
|
945
|
+
return true;
|
|
946
|
+
}
|
|
947
|
+
return false;
|
|
948
|
+
}, _AnalyticsController_emitEventFragment = function _AnalyticsController_emitEventFragment(fragment, name, context) {
|
|
949
|
+
const properties = { ...fragment.properties };
|
|
950
|
+
const sensitiveProperties = { ...fragment.sensitiveProperties };
|
|
951
|
+
this.trackEvent({
|
|
952
|
+
name,
|
|
953
|
+
properties,
|
|
954
|
+
sensitiveProperties,
|
|
955
|
+
saveDataRecording: false,
|
|
956
|
+
hasProperties: Object.keys(properties).length > 0 ||
|
|
957
|
+
Object.keys(sensitiveProperties).length > 0,
|
|
958
|
+
}, context);
|
|
959
|
+
}, _AnalyticsController_isAnalyticsCaptureAllowed = function _AnalyticsController_isAnalyticsCaptureAllowed() {
|
|
960
|
+
if (analyticsControllerSelectors.selectEnabled(this.state)) {
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
return __classPrivateFieldGet(this, _AnalyticsController_isPreConsentQueueEnabled, "f") && !this.state.consentDecisionMade;
|
|
636
964
|
};
|
|
637
965
|
//# sourceMappingURL=AnalyticsController.mjs.map
|