@ninetailed/experience.js 7.11.0 → 7.12.0-beta.0
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/index.cjs.js +226 -116
- package/index.esm.js +232 -117
- package/package.json +3 -3
- package/src/lib/Ninetailed.d.ts +18 -2
- package/src/lib/NinetailedCorePlugin/NinetailedCorePlugin.d.ts +21 -5
- package/src/lib/NinetailedCorePlugin/actions.d.ts +59 -0
- package/src/lib/NinetailedCorePlugin/constants.d.ts +1 -0
- package/src/lib/experience/makeExperienceSelectMiddleware.d.ts +15 -10
- package/src/lib/types/index.d.ts +34 -10
- package/src/lib/utils/EventBuilder.d.ts +10 -4
package/index.cjs.js
CHANGED
|
@@ -98,6 +98,7 @@ const ANONYMOUS_ID = '__nt_anonymous_id__';
|
|
|
98
98
|
const DEBUG_FLAG = '__nt_debug__';
|
|
99
99
|
const PROFILE_FALLBACK_CACHE = '__nt_profile__';
|
|
100
100
|
const EXPERIENCES_FALLBACK_CACHE = '__nt_experiences__';
|
|
101
|
+
const CHANGES_FALLBACK_CACHE = '__nt_changes__';
|
|
101
102
|
const PROFILE_CHANGE = 'profile-change';
|
|
102
103
|
const PROFILE_RESET = 'profile-reset';
|
|
103
104
|
const CONSENT = '__nt-consent__';
|
|
@@ -148,14 +149,12 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
148
149
|
instance.dispatch({
|
|
149
150
|
type: PROFILE_RESET
|
|
150
151
|
});
|
|
151
|
-
|
|
152
|
-
instance.storage.removeItem(PROFILE_FALLBACK_CACHE);
|
|
153
|
-
instance.storage.removeItem(EXPERIENCES_FALLBACK_CACHE);
|
|
152
|
+
this.clearCaches();
|
|
154
153
|
experience_jsShared.logger.debug('Removed old profile data from localstorage.');
|
|
155
154
|
if (typeof this.onInitProfileId === 'function') {
|
|
156
155
|
const profileId = yield this.onInitProfileId(undefined);
|
|
157
156
|
if (typeof profileId === 'string') {
|
|
158
|
-
|
|
157
|
+
this.setAnonymousId(profileId);
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
yield this.ninetailed.track('nt_reset');
|
|
@@ -201,13 +200,13 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
201
200
|
const legacyAnonymousId = instance.storage.getItem(LEGACY_ANONYMOUS_ID);
|
|
202
201
|
if (legacyAnonymousId) {
|
|
203
202
|
experience_jsShared.logger.debug('Found legacy anonymousId, migrating to new one.', legacyAnonymousId);
|
|
204
|
-
|
|
203
|
+
this.setAnonymousId(legacyAnonymousId);
|
|
205
204
|
instance.storage.removeItem(LEGACY_ANONYMOUS_ID);
|
|
206
205
|
}
|
|
207
206
|
if (typeof this.onInitProfileId === 'function') {
|
|
208
|
-
const profileId = yield this.onInitProfileId(
|
|
207
|
+
const profileId = yield this.onInitProfileId(this.getAnonymousId());
|
|
209
208
|
if (typeof profileId === 'string') {
|
|
210
|
-
|
|
209
|
+
this.setAnonymousId(profileId);
|
|
211
210
|
}
|
|
212
211
|
}
|
|
213
212
|
experience_jsShared.logger.debug('Ninetailed Core plugin initialized.');
|
|
@@ -316,6 +315,61 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
316
315
|
}
|
|
317
316
|
return this._instance;
|
|
318
317
|
}
|
|
318
|
+
getAnonymousId() {
|
|
319
|
+
var _c;
|
|
320
|
+
return (_c = this.instance.storage.getItem(ANONYMOUS_ID)) !== null && _c !== void 0 ? _c : undefined;
|
|
321
|
+
}
|
|
322
|
+
setAnonymousId(id) {
|
|
323
|
+
this.instance.storage.setItem(ANONYMOUS_ID, id);
|
|
324
|
+
}
|
|
325
|
+
clearAnonymousId() {
|
|
326
|
+
this.instance.storage.removeItem(ANONYMOUS_ID);
|
|
327
|
+
}
|
|
328
|
+
setFallbackProfile(profile) {
|
|
329
|
+
this.instance.storage.setItem(PROFILE_FALLBACK_CACHE, profile);
|
|
330
|
+
}
|
|
331
|
+
getFallbackProfile() {
|
|
332
|
+
var _c;
|
|
333
|
+
return (_c = this.instance.storage.getItem(PROFILE_FALLBACK_CACHE)) !== null && _c !== void 0 ? _c : undefined;
|
|
334
|
+
}
|
|
335
|
+
clearFallbackProfile() {
|
|
336
|
+
this.instance.storage.removeItem(PROFILE_FALLBACK_CACHE);
|
|
337
|
+
}
|
|
338
|
+
setFallbackExperiences(experiences) {
|
|
339
|
+
this.instance.storage.setItem(EXPERIENCES_FALLBACK_CACHE, experiences);
|
|
340
|
+
}
|
|
341
|
+
getFallbackExperiences() {
|
|
342
|
+
return this.instance.storage.getItem(EXPERIENCES_FALLBACK_CACHE) || [];
|
|
343
|
+
}
|
|
344
|
+
clearFallbackExperiences() {
|
|
345
|
+
this.instance.storage.removeItem(EXPERIENCES_FALLBACK_CACHE);
|
|
346
|
+
}
|
|
347
|
+
setFallbackChanges(changes) {
|
|
348
|
+
this.instance.storage.setItem(CHANGES_FALLBACK_CACHE, changes);
|
|
349
|
+
}
|
|
350
|
+
getFallbackChanges() {
|
|
351
|
+
return this.instance.storage.getItem(CHANGES_FALLBACK_CACHE) || [];
|
|
352
|
+
}
|
|
353
|
+
clearFallbackChanges() {
|
|
354
|
+
this.instance.storage.removeItem(CHANGES_FALLBACK_CACHE);
|
|
355
|
+
}
|
|
356
|
+
clearCaches() {
|
|
357
|
+
this.clearAnonymousId();
|
|
358
|
+
this.clearFallbackProfile();
|
|
359
|
+
this.clearFallbackExperiences();
|
|
360
|
+
this.clearFallbackChanges();
|
|
361
|
+
}
|
|
362
|
+
populateCaches({
|
|
363
|
+
experiences,
|
|
364
|
+
profile,
|
|
365
|
+
anonymousId,
|
|
366
|
+
changes
|
|
367
|
+
}) {
|
|
368
|
+
this.setAnonymousId(anonymousId);
|
|
369
|
+
this.setFallbackProfile(profile);
|
|
370
|
+
this.setFallbackExperiences(experiences);
|
|
371
|
+
this.setFallbackChanges(changes);
|
|
372
|
+
}
|
|
319
373
|
_flush() {
|
|
320
374
|
return __awaiter(this, void 0, void 0, function* () {
|
|
321
375
|
const events = Object.assign([], this.queue);
|
|
@@ -327,10 +381,11 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
327
381
|
};
|
|
328
382
|
}
|
|
329
383
|
try {
|
|
330
|
-
const anonymousId = this.
|
|
384
|
+
const anonymousId = this.getAnonymousId();
|
|
331
385
|
const {
|
|
332
386
|
profile,
|
|
333
|
-
experiences
|
|
387
|
+
experiences,
|
|
388
|
+
changes
|
|
334
389
|
} = yield this.apiClient.upsertProfile({
|
|
335
390
|
profileId: anonymousId,
|
|
336
391
|
events
|
|
@@ -338,15 +393,20 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
338
393
|
locale: this.locale,
|
|
339
394
|
enabledFeatures: this.enabledFeatures
|
|
340
395
|
});
|
|
341
|
-
this.
|
|
342
|
-
|
|
343
|
-
|
|
396
|
+
this.populateCaches({
|
|
397
|
+
anonymousId: profile.id,
|
|
398
|
+
profile,
|
|
399
|
+
experiences,
|
|
400
|
+
changes
|
|
401
|
+
});
|
|
344
402
|
experience_jsShared.logger.debug('Profile from api: ', profile);
|
|
345
403
|
experience_jsShared.logger.debug('Experiences from api: ', experiences);
|
|
346
404
|
this.instance.dispatch({
|
|
347
405
|
type: PROFILE_CHANGE,
|
|
348
406
|
profile,
|
|
349
|
-
experiences
|
|
407
|
+
experiences,
|
|
408
|
+
changes,
|
|
409
|
+
error: undefined
|
|
350
410
|
});
|
|
351
411
|
yield delay(20);
|
|
352
412
|
return {
|
|
@@ -354,22 +414,26 @@ class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyt
|
|
|
354
414
|
};
|
|
355
415
|
} catch (error) {
|
|
356
416
|
experience_jsShared.logger.debug('An error occurred during flushing the events: ', error);
|
|
357
|
-
const fallbackProfile = this.
|
|
358
|
-
const fallbackExperiences = this.
|
|
417
|
+
const fallbackProfile = this.getFallbackProfile();
|
|
418
|
+
const fallbackExperiences = this.getFallbackExperiences();
|
|
419
|
+
const fallbackChanges = this.getFallbackChanges();
|
|
359
420
|
if (fallbackProfile) {
|
|
360
421
|
experience_jsShared.logger.debug('Found a fallback profile - will use this.');
|
|
361
422
|
this.instance.dispatch({
|
|
362
423
|
type: PROFILE_CHANGE,
|
|
363
424
|
profile: fallbackProfile,
|
|
364
|
-
experiences: fallbackExperiences
|
|
425
|
+
experiences: fallbackExperiences,
|
|
426
|
+
changes: fallbackChanges,
|
|
427
|
+
error: undefined
|
|
365
428
|
});
|
|
366
429
|
} else {
|
|
367
430
|
experience_jsShared.logger.debug('No fallback profile found - setting profile to null.');
|
|
368
431
|
this.instance.dispatch({
|
|
369
432
|
type: PROFILE_CHANGE,
|
|
370
433
|
profile: null,
|
|
434
|
+
changes: fallbackChanges,
|
|
371
435
|
experiences: fallbackExperiences,
|
|
372
|
-
error
|
|
436
|
+
error: error
|
|
373
437
|
});
|
|
374
438
|
}
|
|
375
439
|
return {
|
|
@@ -436,19 +500,17 @@ const isInterestedInHiddenPage = arg => {
|
|
|
436
500
|
};
|
|
437
501
|
|
|
438
502
|
const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
503
|
+
const experientVariantsAssignments = encodedExperienceVariantsMap.split(',');
|
|
504
|
+
const experienceVariantsMap = {};
|
|
505
|
+
for (const experienceVariantAssignment of experientVariantsAssignments) {
|
|
506
|
+
const [experienceId, variantIndexString] = experienceVariantAssignment.split('=');
|
|
507
|
+
const variantIndex = parseInt(variantIndexString);
|
|
442
508
|
if (!experienceId || !variantIndex) {
|
|
443
|
-
|
|
509
|
+
continue;
|
|
444
510
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
};
|
|
449
|
-
}).filter(x => !!x).reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
450
|
-
[curr.experienceId]: curr.variantIndex
|
|
451
|
-
}), {});
|
|
511
|
+
experienceVariantsMap[experienceId] = variantIndex;
|
|
512
|
+
}
|
|
513
|
+
return experienceVariantsMap;
|
|
452
514
|
};
|
|
453
515
|
|
|
454
516
|
class OnChangeEmitter {
|
|
@@ -510,6 +572,28 @@ const createPassThroughMiddleware = () => {
|
|
|
510
572
|
};
|
|
511
573
|
};
|
|
512
574
|
};
|
|
575
|
+
function createExperienceSelectionMiddleware({
|
|
576
|
+
plugins,
|
|
577
|
+
experiences,
|
|
578
|
+
baseline,
|
|
579
|
+
profile
|
|
580
|
+
}) {
|
|
581
|
+
if (profile === null) {
|
|
582
|
+
return createPassThroughMiddleware();
|
|
583
|
+
}
|
|
584
|
+
const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
|
|
585
|
+
const middlewareFunctions = [];
|
|
586
|
+
for (const plugin of pluginsWithMiddleware) {
|
|
587
|
+
const middleware = plugin.getExperienceSelectionMiddleware({
|
|
588
|
+
experiences,
|
|
589
|
+
baseline
|
|
590
|
+
});
|
|
591
|
+
if (middleware !== undefined) {
|
|
592
|
+
middlewareFunctions.push(middleware);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return experience_jsShared.pipe(...middlewareFunctions);
|
|
596
|
+
}
|
|
513
597
|
const makeExperienceSelectMiddleware = ({
|
|
514
598
|
plugins,
|
|
515
599
|
onChange,
|
|
@@ -519,18 +603,12 @@ const makeExperienceSelectMiddleware = ({
|
|
|
519
603
|
}) => {
|
|
520
604
|
let removeChangeListeners = [];
|
|
521
605
|
const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
experiences,
|
|
529
|
-
baseline
|
|
530
|
-
})).filter(result => typeof result !== 'undefined');
|
|
531
|
-
return experience_jsShared.pipe(...middlewareFunctions);
|
|
532
|
-
};
|
|
533
|
-
const middleware = prepareMiddleware();
|
|
606
|
+
const middleware = createExperienceSelectionMiddleware({
|
|
607
|
+
plugins,
|
|
608
|
+
experiences,
|
|
609
|
+
baseline,
|
|
610
|
+
profile
|
|
611
|
+
});
|
|
534
612
|
const addListeners = () => {
|
|
535
613
|
removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
|
|
536
614
|
const listener = () => {
|
|
@@ -539,8 +617,12 @@ const makeExperienceSelectMiddleware = ({
|
|
|
539
617
|
return plugin.onChangeEmitter.addListener(listener);
|
|
540
618
|
});
|
|
541
619
|
};
|
|
620
|
+
// WARNING: This specific implementation using forEach is required.
|
|
621
|
+
// DO NOT replace with for...of or other loop constructs as they will break functionality.
|
|
622
|
+
// The exact reason is uncertain but appears related to the transplier.
|
|
623
|
+
// TODO: Come back and find out why this is the case, maybe a version bump is in order.
|
|
542
624
|
const removeListeners = () => {
|
|
543
|
-
removeChangeListeners.forEach(
|
|
625
|
+
removeChangeListeners.forEach(removeListener => removeListener());
|
|
544
626
|
};
|
|
545
627
|
return {
|
|
546
628
|
addListeners,
|
|
@@ -553,44 +635,36 @@ class EventBuilder {
|
|
|
553
635
|
constructor(buildRequestContext) {
|
|
554
636
|
this.buildRequestContext = buildRequestContext || buildClientNinetailedRequestContext;
|
|
555
637
|
}
|
|
556
|
-
|
|
557
|
-
return
|
|
638
|
+
buildEventBase(data) {
|
|
639
|
+
return Object.assign(Object.assign({
|
|
558
640
|
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4()
|
|
559
641
|
}, data), {
|
|
560
642
|
timestamp: Date.now(),
|
|
561
|
-
properties: properties || {},
|
|
562
643
|
ctx: this.buildRequestContext()
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
page(properties, data) {
|
|
647
|
+
return experience_jsShared.buildPageEvent(Object.assign(Object.assign({}, this.buildEventBase(data)), {
|
|
648
|
+
properties: properties || {}
|
|
563
649
|
}));
|
|
564
650
|
}
|
|
565
651
|
track(event, properties, data) {
|
|
566
|
-
return experience_jsShared.buildTrackEvent(Object.assign(Object.assign({
|
|
567
|
-
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
568
|
-
timestamp: Date.now()
|
|
569
|
-
}, data), {
|
|
652
|
+
return experience_jsShared.buildTrackEvent(Object.assign(Object.assign({}, this.buildEventBase(data)), {
|
|
570
653
|
event,
|
|
571
|
-
properties: properties || {}
|
|
572
|
-
ctx: this.buildRequestContext()
|
|
654
|
+
properties: properties || {}
|
|
573
655
|
}));
|
|
574
656
|
}
|
|
575
657
|
identify(userId, traits, data) {
|
|
576
|
-
return experience_jsShared.buildIdentifyEvent(Object.assign(Object.assign({
|
|
577
|
-
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
578
|
-
timestamp: Date.now()
|
|
579
|
-
}, data), {
|
|
658
|
+
return experience_jsShared.buildIdentifyEvent(Object.assign(Object.assign({}, this.buildEventBase(data)), {
|
|
580
659
|
traits: traits || {},
|
|
581
|
-
userId: userId || ''
|
|
582
|
-
ctx: this.buildRequestContext()
|
|
660
|
+
userId: userId || ''
|
|
583
661
|
}));
|
|
584
662
|
}
|
|
585
663
|
component(componentId, experienceId, variantIndex, data) {
|
|
586
|
-
return experience_jsShared.buildComponentViewEvent(Object.assign(Object.assign({
|
|
587
|
-
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
588
|
-
timestamp: Date.now()
|
|
589
|
-
}, data), {
|
|
664
|
+
return experience_jsShared.buildComponentViewEvent(Object.assign(Object.assign({}, this.buildEventBase(data)), {
|
|
590
665
|
componentId,
|
|
591
666
|
experienceId: experienceId || '',
|
|
592
|
-
variantIndex: variantIndex || 0
|
|
593
|
-
ctx: this.buildRequestContext()
|
|
667
|
+
variantIndex: variantIndex || 0
|
|
594
668
|
}));
|
|
595
669
|
}
|
|
596
670
|
}
|
|
@@ -647,11 +721,7 @@ class Ninetailed {
|
|
|
647
721
|
yield this.instance.page(data, this.buildOptions(options));
|
|
648
722
|
return this.ninetailedCorePlugin.flush();
|
|
649
723
|
} catch (error) {
|
|
650
|
-
|
|
651
|
-
if (error instanceof RangeError) {
|
|
652
|
-
throw new Error(`[Validation Error] "page" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
653
|
-
}
|
|
654
|
-
throw error;
|
|
724
|
+
return this.handleMethodError(error, 'page');
|
|
655
725
|
}
|
|
656
726
|
});
|
|
657
727
|
this.track = (event, properties, options) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -664,11 +734,7 @@ class Ninetailed {
|
|
|
664
734
|
yield this.instance.track(event.toString(), result.data, this.buildOptions(options));
|
|
665
735
|
return this.ninetailedCorePlugin.flush();
|
|
666
736
|
} catch (error) {
|
|
667
|
-
|
|
668
|
-
if (error instanceof RangeError) {
|
|
669
|
-
throw new Error(`[Validation Error] "track" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
670
|
-
}
|
|
671
|
-
throw error;
|
|
737
|
+
this.handleMethodError(error, 'track');
|
|
672
738
|
}
|
|
673
739
|
});
|
|
674
740
|
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -681,11 +747,7 @@ class Ninetailed {
|
|
|
681
747
|
yield this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, this.buildOptions(options));
|
|
682
748
|
return this.ninetailedCorePlugin.flush();
|
|
683
749
|
} catch (error) {
|
|
684
|
-
|
|
685
|
-
if (error instanceof RangeError) {
|
|
686
|
-
throw new Error(`[Validation Error] "identify" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
687
|
-
}
|
|
688
|
-
throw error;
|
|
750
|
+
this.handleMethodError(error, 'identify');
|
|
689
751
|
}
|
|
690
752
|
});
|
|
691
753
|
this.batch = events => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -714,11 +776,7 @@ class Ninetailed {
|
|
|
714
776
|
yield Promise.all(promises);
|
|
715
777
|
return this.ninetailedCorePlugin.flush();
|
|
716
778
|
} catch (error) {
|
|
717
|
-
|
|
718
|
-
if (error instanceof RangeError) {
|
|
719
|
-
throw new Error(`[Validation Error] "batch" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
720
|
-
}
|
|
721
|
-
throw error;
|
|
779
|
+
this.handleMethodError(error, 'batch');
|
|
722
780
|
}
|
|
723
781
|
});
|
|
724
782
|
this.trackStickyComponentView = ({
|
|
@@ -736,11 +794,7 @@ class Ninetailed {
|
|
|
736
794
|
});
|
|
737
795
|
return this.ninetailedCorePlugin.flush();
|
|
738
796
|
} catch (error) {
|
|
739
|
-
|
|
740
|
-
if (error instanceof RangeError) {
|
|
741
|
-
throw new Error(`[Validation Error] "trackStickyComponentView" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
742
|
-
}
|
|
743
|
-
throw error;
|
|
797
|
+
this.handleMethodError(error, 'trackStickyComponentView');
|
|
744
798
|
}
|
|
745
799
|
});
|
|
746
800
|
/**
|
|
@@ -763,33 +817,13 @@ class Ninetailed {
|
|
|
763
817
|
const {
|
|
764
818
|
element
|
|
765
819
|
} = payload,
|
|
766
|
-
|
|
820
|
+
remainingPayload = __rest(payload, ["element"]);
|
|
767
821
|
if (!(element instanceof Element)) {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const isConstructorNameNotObject = constructorName && constructorName !== 'Object';
|
|
771
|
-
experience_jsShared.logger.warn(`ElementSeenObserver.observeElement was called with an invalid element. Expected an Element but got ${typeof element}${isConstructorNameNotObject ? ` of type ${constructorName}` : ''}. This call will be ignored.`);
|
|
772
|
-
} else {
|
|
773
|
-
const existingPayloads = this.observedElements.get(element);
|
|
774
|
-
const delays = this.pluginsWithCustomComponentViewThreshold.map(plugin => plugin.getComponentViewTrackingThreshold());
|
|
775
|
-
const uniqueDelays = Array.from(new Set([...delays, options === null || options === void 0 ? void 0 : options.delay]));
|
|
776
|
-
if (!existingPayloads) {
|
|
777
|
-
this.observedElements.set(element, [remaingPayload]);
|
|
778
|
-
} else {
|
|
779
|
-
const isPayloadAlreadyObserved = existingPayloads.some(payload => {
|
|
780
|
-
return JSON.stringify(payload) === JSON.stringify(remaingPayload);
|
|
781
|
-
});
|
|
782
|
-
if (isPayloadAlreadyObserved) {
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
this.observedElements.set(element, [...existingPayloads, remaingPayload]);
|
|
786
|
-
}
|
|
787
|
-
uniqueDelays.forEach(delay => {
|
|
788
|
-
this.elementSeenObserver.observe(element, {
|
|
789
|
-
delay
|
|
790
|
-
});
|
|
791
|
-
});
|
|
822
|
+
this.logInvalidElement(element);
|
|
823
|
+
return;
|
|
792
824
|
}
|
|
825
|
+
this.storeElementPayload(element, remainingPayload);
|
|
826
|
+
this.setupElementObservation(element, options === null || options === void 0 ? void 0 : options.delay);
|
|
793
827
|
};
|
|
794
828
|
this.unobserveElement = element => {
|
|
795
829
|
this.observedElements.delete(element);
|
|
@@ -835,6 +869,7 @@ class Ninetailed {
|
|
|
835
869
|
cb(Object.assign(Object.assign({}, this._profileState), {
|
|
836
870
|
status: 'success',
|
|
837
871
|
profile: payload.profile,
|
|
872
|
+
changes: payload.changes,
|
|
838
873
|
experiences: payload.experiences,
|
|
839
874
|
error: null
|
|
840
875
|
}));
|
|
@@ -1003,14 +1038,26 @@ class Ninetailed {
|
|
|
1003
1038
|
removeMiddlewareChangeListeners();
|
|
1004
1039
|
};
|
|
1005
1040
|
};
|
|
1006
|
-
|
|
1007
|
-
|
|
1041
|
+
/**
|
|
1042
|
+
* Registers a callback to be notified when changes occur in the profile state.
|
|
1043
|
+
*
|
|
1044
|
+
* @param cb - Callback function that receives the changes state
|
|
1045
|
+
* @returns Function to unsubscribe from changes updates
|
|
1046
|
+
*/
|
|
1047
|
+
this.onChangesChange = cb => {
|
|
1048
|
+
this.notifyChangesCallback(cb, this._profileState);
|
|
1049
|
+
return this.onProfileChange(profileState => {
|
|
1050
|
+
this.notifyChangesCallback(cb, profileState);
|
|
1051
|
+
});
|
|
1052
|
+
};
|
|
1053
|
+
this.onIsInitialized = onIsInitializedCallback => {
|
|
1054
|
+
if (typeof onIsInitializedCallback === 'function') {
|
|
1008
1055
|
if (this.isInitialized) {
|
|
1009
|
-
|
|
1056
|
+
onIsInitializedCallback();
|
|
1010
1057
|
} else {
|
|
1011
1058
|
const detachOnReadyListener = this.instance.on('ready', () => {
|
|
1012
1059
|
this.isInitialized = true;
|
|
1013
|
-
|
|
1060
|
+
onIsInitializedCallback();
|
|
1014
1061
|
detachOnReadyListener();
|
|
1015
1062
|
});
|
|
1016
1063
|
}
|
|
@@ -1090,6 +1137,7 @@ class Ninetailed {
|
|
|
1090
1137
|
status: 'loading',
|
|
1091
1138
|
profile: null,
|
|
1092
1139
|
experiences: null,
|
|
1140
|
+
changes: null,
|
|
1093
1141
|
error: null,
|
|
1094
1142
|
from: 'api'
|
|
1095
1143
|
};
|
|
@@ -1136,6 +1184,67 @@ class Ninetailed {
|
|
|
1136
1184
|
get pluginsWithCustomComponentViewThreshold() {
|
|
1137
1185
|
return [this.ninetailedCorePlugin, ...this.plugins].filter(plugin => experience_jsPluginAnalytics.hasComponentViewTrackingThreshold(plugin));
|
|
1138
1186
|
}
|
|
1187
|
+
logInvalidElement(element) {
|
|
1188
|
+
const isObject = typeof element === 'object' && element !== null;
|
|
1189
|
+
const constructorName = isObject ? element.constructor.name : '';
|
|
1190
|
+
const isConstructorNameNotObject = constructorName && constructorName !== 'Object';
|
|
1191
|
+
experience_jsShared.logger.warn(`ElementSeenObserver.observeElement was called with an invalid element. Expected an Element but got ${typeof element}${isConstructorNameNotObject ? ` of type ${constructorName}` : ''}. This call will be ignored.`);
|
|
1192
|
+
}
|
|
1193
|
+
storeElementPayload(element, payload) {
|
|
1194
|
+
const existingPayloads = this.observedElements.get(element) || [];
|
|
1195
|
+
// Check if the payload is already being observed for this element
|
|
1196
|
+
const isPayloadAlreadyObserved = existingPayloads.some(existingPayload => JSON.stringify(existingPayload) === JSON.stringify(payload));
|
|
1197
|
+
if (isPayloadAlreadyObserved) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
// Store the new or updated payloads
|
|
1201
|
+
this.observedElements.set(element, [...existingPayloads, payload]);
|
|
1202
|
+
}
|
|
1203
|
+
setupElementObservation(element, delay) {
|
|
1204
|
+
// Get all relevant delays from plugins and the custom delay
|
|
1205
|
+
const pluginDelays = this.pluginsWithCustomComponentViewThreshold.map(plugin => plugin.getComponentViewTrackingThreshold());
|
|
1206
|
+
// Ensure we only observe each delay once
|
|
1207
|
+
const uniqueDelays = Array.from(new Set([...pluginDelays, delay]));
|
|
1208
|
+
// Set up observation for each delay
|
|
1209
|
+
uniqueDelays.forEach(delay => {
|
|
1210
|
+
this.elementSeenObserver.observe(element, {
|
|
1211
|
+
delay
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Helper method to extract changes state from profile state and notify callback
|
|
1217
|
+
* @private
|
|
1218
|
+
*/
|
|
1219
|
+
notifyChangesCallback(cb, profileState) {
|
|
1220
|
+
if (profileState.status === 'loading') {
|
|
1221
|
+
cb({
|
|
1222
|
+
status: 'loading',
|
|
1223
|
+
changes: null,
|
|
1224
|
+
error: null
|
|
1225
|
+
});
|
|
1226
|
+
} else if (profileState.status === 'error') {
|
|
1227
|
+
cb({
|
|
1228
|
+
status: 'error',
|
|
1229
|
+
changes: null,
|
|
1230
|
+
error: profileState.error
|
|
1231
|
+
});
|
|
1232
|
+
} else {
|
|
1233
|
+
cb({
|
|
1234
|
+
status: 'success',
|
|
1235
|
+
changes: profileState.changes,
|
|
1236
|
+
error: null
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
// Always throws, never returns
|
|
1241
|
+
handleMethodError(error, method) {
|
|
1242
|
+
experience_jsShared.logger.error(error);
|
|
1243
|
+
if (error instanceof RangeError) {
|
|
1244
|
+
throw new Error(`[Validation Error] "${method}" was called with invalid params. Could not validate due to "RangeError: Maximum call stack size exceeded". This can be caused by passing a cyclic data structure as a parameter. Refrain from passing a cyclic data structure or sanitize it beforehand.`);
|
|
1245
|
+
}
|
|
1246
|
+
throw error;
|
|
1247
|
+
}
|
|
1139
1248
|
get profileState() {
|
|
1140
1249
|
return this._profileState;
|
|
1141
1250
|
}
|
|
@@ -1281,6 +1390,7 @@ Object.defineProperty(exports, 'selectHasExperienceVariants', {
|
|
|
1281
1390
|
get: function () { return experience_jsShared.selectHasVariants; }
|
|
1282
1391
|
});
|
|
1283
1392
|
exports.ANONYMOUS_ID = ANONYMOUS_ID;
|
|
1393
|
+
exports.CHANGES_FALLBACK_CACHE = CHANGES_FALLBACK_CACHE;
|
|
1284
1394
|
exports.COMPONENT = COMPONENT;
|
|
1285
1395
|
exports.COMPONENT_START = COMPONENT_START;
|
|
1286
1396
|
exports.CONSENT = CONSENT;
|