@ninetailed/experience.js 7.5.0-beta.9 → 7.6.0-beta.1
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 +435 -132
- package/index.js +436 -134
- package/package.json +3 -2
- package/src/lib/Ninetailed.d.ts +15 -3
- package/src/lib/constants.d.ts +1 -0
- package/src/lib/experience/makeExperienceSelectMiddleware.d.ts +8 -8
- package/src/lib/guards/hasExperienceSelectionMiddleware.d.ts +1 -1
- package/src/lib/plugins/selectPluginsHavingExperienceSelectionMiddleware.d.ts +1 -1
- package/src/lib/types/OnSelectVariant.d.ts +51 -0
- package/src/lib/types/index.d.ts +8 -2
- package/src/lib/types/interfaces/HasExperienceSelectionMiddleware.d.ts +10 -10
- package/src/lib/utils/EventBuilder.d.ts +221 -0
- package/src/lib/utils/noop.d.ts +1 -0
package/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var experience_jsShared = require('@ninetailed/experience.js-shared');
|
|
6
6
|
var Analytics = require('analytics');
|
|
7
|
+
var uuid = require('uuid');
|
|
7
8
|
var zod = require('zod');
|
|
8
9
|
|
|
9
10
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -15,6 +16,7 @@ const HAS_SEEN_ELEMENT = 'has_seen_element';
|
|
|
15
16
|
const COMPONENT = 'component';
|
|
16
17
|
const COMPONENT_START = 'componentStart';
|
|
17
18
|
const PAGE_HIDDEN = 'page_hidden';
|
|
19
|
+
const HAS_SEEN_STICKY_COMPONENT = 'sticky_component_view';
|
|
18
20
|
|
|
19
21
|
/******************************************************************************
|
|
20
22
|
Copyright (c) Microsoft Corporation.
|
|
@@ -272,6 +274,20 @@ const ninetailedCorePlugin = ({
|
|
|
272
274
|
ctx
|
|
273
275
|
}));
|
|
274
276
|
}),
|
|
277
|
+
[HAS_SEEN_STICKY_COMPONENT]: ({
|
|
278
|
+
payload
|
|
279
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
280
|
+
experience_jsShared.logger.info('Sending Sticky Components event.');
|
|
281
|
+
const ctx = buildContext();
|
|
282
|
+
return enqueueEvent(experience_jsShared.buildComponentViewEvent({
|
|
283
|
+
messageId: payload.meta.rid,
|
|
284
|
+
timestamp: payload.meta.ts,
|
|
285
|
+
componentId: payload.componentId,
|
|
286
|
+
experienceId: payload.experienceId,
|
|
287
|
+
variantIndex: payload.variantIndex,
|
|
288
|
+
ctx
|
|
289
|
+
}));
|
|
290
|
+
}),
|
|
275
291
|
setItemStart: ({
|
|
276
292
|
abort,
|
|
277
293
|
payload
|
|
@@ -368,6 +384,189 @@ const isInterestedInHiddenPage = arg => {
|
|
|
368
384
|
return typeof arg === 'object' && arg !== null && PAGE_HIDDEN in arg && typeof arg[PAGE_HIDDEN] === 'function';
|
|
369
385
|
};
|
|
370
386
|
|
|
387
|
+
const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
388
|
+
return encodedExperienceVariantsMap.split(',').map(experienceIdWithVariant => {
|
|
389
|
+
const [experienceId, _variantIndex] = experienceIdWithVariant.split('=');
|
|
390
|
+
const variantIndex = parseInt(_variantIndex);
|
|
391
|
+
if (!experienceId || !variantIndex) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
experienceId,
|
|
396
|
+
variantIndex
|
|
397
|
+
};
|
|
398
|
+
}).filter(x => !!x).reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
399
|
+
[curr.experienceId]: curr.variantIndex
|
|
400
|
+
}), {});
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
class OnChangeEmitter {
|
|
404
|
+
constructor() {
|
|
405
|
+
this.onChangeListeners = [];
|
|
406
|
+
}
|
|
407
|
+
addListener(listener) {
|
|
408
|
+
this.onChangeListeners.push(listener);
|
|
409
|
+
return () => {
|
|
410
|
+
this.removeOnChangeListener(listener);
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
invokeListeners() {
|
|
414
|
+
this.onChangeListeners.forEach(listener => listener());
|
|
415
|
+
}
|
|
416
|
+
removeOnChangeListener(listener) {
|
|
417
|
+
this.onChangeListeners = this.onChangeListeners.filter(l => l !== listener);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const hasOnChangeEmitter = arg => {
|
|
422
|
+
return typeof arg === 'object' && arg !== null && 'onChangeEmitter' in arg && typeof arg.onChangeEmitter === 'object' && arg.onChangeEmitter !== null && arg.onChangeEmitter.constructor === OnChangeEmitter;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const selectPluginsHavingOnChangeEmitter = plugins => {
|
|
426
|
+
const filteredPlugins = [];
|
|
427
|
+
for (const plugin of plugins) {
|
|
428
|
+
if (hasOnChangeEmitter(plugin)) {
|
|
429
|
+
filteredPlugins.push(plugin);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return filteredPlugins;
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const hasExperienceSelectionMiddleware = arg => {
|
|
436
|
+
return typeof arg === 'object' && arg !== null && 'getExperienceSelectionMiddleware' in arg && typeof arg.getExperienceSelectionMiddleware === 'function';
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const selectPluginsHavingExperienceSelectionMiddleware = plugins => {
|
|
440
|
+
const filteredPlugins = [];
|
|
441
|
+
for (const plugin of plugins) {
|
|
442
|
+
if (hasExperienceSelectionMiddleware(plugin)) {
|
|
443
|
+
filteredPlugins.push(plugin);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return filteredPlugins;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const createPassThroughMiddleware = () => {
|
|
450
|
+
return ({
|
|
451
|
+
experience,
|
|
452
|
+
variant,
|
|
453
|
+
variantIndex
|
|
454
|
+
}) => {
|
|
455
|
+
return {
|
|
456
|
+
experience,
|
|
457
|
+
variant,
|
|
458
|
+
variantIndex
|
|
459
|
+
};
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
const makeExperienceSelectMiddleware = ({
|
|
463
|
+
plugins,
|
|
464
|
+
onChange,
|
|
465
|
+
experiences,
|
|
466
|
+
baseline,
|
|
467
|
+
profile
|
|
468
|
+
}) => {
|
|
469
|
+
let removeChangeListeners = [];
|
|
470
|
+
const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
|
|
471
|
+
const prepareMiddleware = () => {
|
|
472
|
+
if (profile === null) {
|
|
473
|
+
return createPassThroughMiddleware();
|
|
474
|
+
}
|
|
475
|
+
const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
|
|
476
|
+
const middlewareFunctions = pluginsWithMiddleware.map(plugin => plugin.getExperienceSelectionMiddleware({
|
|
477
|
+
experiences,
|
|
478
|
+
baseline
|
|
479
|
+
}));
|
|
480
|
+
return experience_jsShared.pipe(...middlewareFunctions);
|
|
481
|
+
};
|
|
482
|
+
const middleware = prepareMiddleware();
|
|
483
|
+
const addListeners = () => {
|
|
484
|
+
removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
|
|
485
|
+
const listener = () => {
|
|
486
|
+
onChange(middleware);
|
|
487
|
+
};
|
|
488
|
+
return plugin.onChangeEmitter.addListener(listener);
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
const removeListeners = () => {
|
|
492
|
+
removeChangeListeners.forEach(listener => listener());
|
|
493
|
+
};
|
|
494
|
+
return {
|
|
495
|
+
addListeners,
|
|
496
|
+
removeListeners,
|
|
497
|
+
middleware
|
|
498
|
+
};
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
class EventBuilder {
|
|
502
|
+
constructor(buildRequestContext) {
|
|
503
|
+
this.buildRequestContext = buildRequestContext || buildClientNinetailedRequestContext;
|
|
504
|
+
}
|
|
505
|
+
page(properties, data) {
|
|
506
|
+
return experience_jsShared.buildPageEvent(Object.assign(Object.assign({
|
|
507
|
+
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4()
|
|
508
|
+
}, data), {
|
|
509
|
+
timestamp: Date.now(),
|
|
510
|
+
properties: properties || {},
|
|
511
|
+
ctx: this.buildRequestContext()
|
|
512
|
+
}));
|
|
513
|
+
}
|
|
514
|
+
track(event, properties, data) {
|
|
515
|
+
return experience_jsShared.buildTrackEvent(Object.assign(Object.assign({
|
|
516
|
+
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
517
|
+
timestamp: Date.now()
|
|
518
|
+
}, data), {
|
|
519
|
+
event,
|
|
520
|
+
properties: properties || {},
|
|
521
|
+
ctx: this.buildRequestContext()
|
|
522
|
+
}));
|
|
523
|
+
}
|
|
524
|
+
identify(userId, traits, data) {
|
|
525
|
+
return experience_jsShared.buildIdentifyEvent(Object.assign(Object.assign({
|
|
526
|
+
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
527
|
+
timestamp: Date.now()
|
|
528
|
+
}, data), {
|
|
529
|
+
traits: traits || {},
|
|
530
|
+
userId: userId || '',
|
|
531
|
+
ctx: this.buildRequestContext()
|
|
532
|
+
}));
|
|
533
|
+
}
|
|
534
|
+
component(componentId, experienceId, variantIndex, data) {
|
|
535
|
+
return experience_jsShared.buildComponentViewEvent(Object.assign(Object.assign({
|
|
536
|
+
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4(),
|
|
537
|
+
timestamp: Date.now()
|
|
538
|
+
}, data), {
|
|
539
|
+
componentId,
|
|
540
|
+
experienceId: experienceId || '',
|
|
541
|
+
variantIndex: variantIndex || 0,
|
|
542
|
+
ctx: this.buildRequestContext()
|
|
543
|
+
}));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const buildOverrideMiddleware = experienceSelectionMiddleware => _a => {
|
|
548
|
+
var {
|
|
549
|
+
experience: originalExperience,
|
|
550
|
+
variant: originalVariant,
|
|
551
|
+
variantIndex: originalVariantIndex
|
|
552
|
+
} = _a,
|
|
553
|
+
other = __rest(_a, ["experience", "variant", "variantIndex"]);
|
|
554
|
+
const {
|
|
555
|
+
experience,
|
|
556
|
+
variant,
|
|
557
|
+
variantIndex
|
|
558
|
+
} = experienceSelectionMiddleware({
|
|
559
|
+
experience: originalExperience,
|
|
560
|
+
variant: originalVariant,
|
|
561
|
+
variantIndex: originalVariantIndex
|
|
562
|
+
});
|
|
563
|
+
return Object.assign(Object.assign({}, other), {
|
|
564
|
+
audience: (experience === null || experience === void 0 ? void 0 : experience.audience) ? experience.audience : null,
|
|
565
|
+
experience,
|
|
566
|
+
variant,
|
|
567
|
+
variantIndex
|
|
568
|
+
});
|
|
569
|
+
};
|
|
371
570
|
class Ninetailed {
|
|
372
571
|
constructor(ninetailedApiClientInstanceOrOptions, {
|
|
373
572
|
plugins,
|
|
@@ -379,7 +578,8 @@ class Ninetailed {
|
|
|
379
578
|
buildClientContext,
|
|
380
579
|
onInitProfileId,
|
|
381
580
|
componentViewTrackingThreshold = 2000,
|
|
382
|
-
storageImpl
|
|
581
|
+
storageImpl,
|
|
582
|
+
useClientSideEvaluation = false
|
|
383
583
|
} = {}) {
|
|
384
584
|
this.isInitialized = false;
|
|
385
585
|
this.page = (data, options) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -416,6 +616,78 @@ class Ninetailed {
|
|
|
416
616
|
throw error;
|
|
417
617
|
}
|
|
418
618
|
});
|
|
619
|
+
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
620
|
+
try {
|
|
621
|
+
const result = experience_jsShared.Traits.default({}).safeParse(traits);
|
|
622
|
+
if (!result.success) {
|
|
623
|
+
throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
|
|
624
|
+
}
|
|
625
|
+
yield this.waitUntilInitialized();
|
|
626
|
+
yield this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, this.buildOptions(options));
|
|
627
|
+
return this.ninetailedCorePlugin.flush();
|
|
628
|
+
} catch (error) {
|
|
629
|
+
experience_jsShared.logger.error(error);
|
|
630
|
+
if (error instanceof RangeError) {
|
|
631
|
+
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.`);
|
|
632
|
+
}
|
|
633
|
+
throw error;
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
this.batch = events => __awaiter(this, void 0, void 0, function* () {
|
|
637
|
+
try {
|
|
638
|
+
yield this.waitUntilInitialized();
|
|
639
|
+
const promises = events.map(event => {
|
|
640
|
+
if (experience_jsShared.isPageViewEvent(event)) {
|
|
641
|
+
return this.instance.page(event.properties);
|
|
642
|
+
}
|
|
643
|
+
if (experience_jsShared.isTrackEvent(event)) {
|
|
644
|
+
return this.instance.track(event.event, event.properties);
|
|
645
|
+
}
|
|
646
|
+
if (experience_jsShared.isIdentifyEvent(event)) {
|
|
647
|
+
return this.instance.identify(event.userId || EMPTY_MERGE_ID, event.traits);
|
|
648
|
+
}
|
|
649
|
+
if (experience_jsShared.isComponentViewEvent(event)) {
|
|
650
|
+
return this.instance.dispatch({
|
|
651
|
+
experienceId: event.experienceId,
|
|
652
|
+
componentId: event.componentId,
|
|
653
|
+
variantIndex: event.variantIndex,
|
|
654
|
+
type: HAS_SEEN_STICKY_COMPONENT
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
return Promise.resolve();
|
|
658
|
+
});
|
|
659
|
+
yield Promise.all(promises);
|
|
660
|
+
return this.ninetailedCorePlugin.flush();
|
|
661
|
+
} catch (error) {
|
|
662
|
+
experience_jsShared.logger.error(error);
|
|
663
|
+
if (error instanceof RangeError) {
|
|
664
|
+
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.`);
|
|
665
|
+
}
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
this.trackStickyComponentView = ({
|
|
670
|
+
experienceId,
|
|
671
|
+
componentId,
|
|
672
|
+
variantIndex
|
|
673
|
+
}) => __awaiter(this, void 0, void 0, function* () {
|
|
674
|
+
try {
|
|
675
|
+
yield this.waitUntilInitialized();
|
|
676
|
+
yield this.instance.dispatch({
|
|
677
|
+
experienceId,
|
|
678
|
+
componentId,
|
|
679
|
+
variantIndex,
|
|
680
|
+
type: HAS_SEEN_STICKY_COMPONENT
|
|
681
|
+
});
|
|
682
|
+
return this.ninetailedCorePlugin.flush();
|
|
683
|
+
} catch (error) {
|
|
684
|
+
experience_jsShared.logger.error(error);
|
|
685
|
+
if (error instanceof RangeError) {
|
|
686
|
+
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.`);
|
|
687
|
+
}
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
419
691
|
/**
|
|
420
692
|
* @deprecated The legacy datamodel is not recommended anymore
|
|
421
693
|
* Will be removed in the next version of the SDK
|
|
@@ -459,23 +731,6 @@ class Ninetailed {
|
|
|
459
731
|
}, payload));
|
|
460
732
|
}
|
|
461
733
|
};
|
|
462
|
-
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
463
|
-
try {
|
|
464
|
-
const result = experience_jsShared.Traits.default({}).safeParse(traits);
|
|
465
|
-
if (!result.success) {
|
|
466
|
-
throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
|
|
467
|
-
}
|
|
468
|
-
yield this.waitUntilInitialized();
|
|
469
|
-
yield this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, this.buildOptions(options));
|
|
470
|
-
return this.ninetailedCorePlugin.flush();
|
|
471
|
-
} catch (error) {
|
|
472
|
-
experience_jsShared.logger.error(error);
|
|
473
|
-
if (error instanceof RangeError) {
|
|
474
|
-
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.`);
|
|
475
|
-
}
|
|
476
|
-
throw error;
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
734
|
this.reset = () => __awaiter(this, void 0, void 0, function* () {
|
|
480
735
|
yield this.waitUntilInitialized();
|
|
481
736
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -510,6 +765,165 @@ class Ninetailed {
|
|
|
510
765
|
}
|
|
511
766
|
});
|
|
512
767
|
};
|
|
768
|
+
this.onSelectVariant = ({
|
|
769
|
+
baseline,
|
|
770
|
+
experiences
|
|
771
|
+
}, cb) => {
|
|
772
|
+
let middlewareChangeListeners = [];
|
|
773
|
+
let state = null;
|
|
774
|
+
const removeMiddlewareChangeListeners = () => {
|
|
775
|
+
middlewareChangeListeners.forEach(removeListener => removeListener());
|
|
776
|
+
middlewareChangeListeners = [];
|
|
777
|
+
};
|
|
778
|
+
const setSelectedVariant = newState => {
|
|
779
|
+
state = newState;
|
|
780
|
+
cb(state);
|
|
781
|
+
};
|
|
782
|
+
const removeProfileChangeListener = this.onProfileChange(profileState => {
|
|
783
|
+
const {
|
|
784
|
+
addListeners,
|
|
785
|
+
removeListeners,
|
|
786
|
+
middleware: experienceSelectionMiddleware
|
|
787
|
+
} = makeExperienceSelectMiddleware({
|
|
788
|
+
plugins: this.plugins,
|
|
789
|
+
experiences,
|
|
790
|
+
baseline,
|
|
791
|
+
profile: profileState.profile,
|
|
792
|
+
onChange: middleware => {
|
|
793
|
+
const overrideResult = buildOverrideMiddleware(middleware);
|
|
794
|
+
if (state !== null) {
|
|
795
|
+
setSelectedVariant(overrideResult(state));
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
addListeners();
|
|
800
|
+
middlewareChangeListeners.push(removeListeners);
|
|
801
|
+
const overrideResult = buildOverrideMiddleware(experienceSelectionMiddleware);
|
|
802
|
+
const hasVariants = experiences.map(experience => experience_jsShared.selectHasVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
|
|
803
|
+
const baseReturn = Object.assign(Object.assign({}, profileState), {
|
|
804
|
+
hasVariants,
|
|
805
|
+
baseline
|
|
806
|
+
});
|
|
807
|
+
const emptyReturn = Object.assign(Object.assign({}, baseReturn), {
|
|
808
|
+
experience: null,
|
|
809
|
+
variant: baseline,
|
|
810
|
+
variantIndex: 0,
|
|
811
|
+
audience: null,
|
|
812
|
+
isPersonalized: false,
|
|
813
|
+
profile: null,
|
|
814
|
+
error: null
|
|
815
|
+
});
|
|
816
|
+
if (profileState.status === 'loading') {
|
|
817
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
818
|
+
loading: true,
|
|
819
|
+
status: 'loading'
|
|
820
|
+
})));
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (profileState.status === 'error') {
|
|
824
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
825
|
+
loading: false,
|
|
826
|
+
status: 'error',
|
|
827
|
+
error: profileState.error
|
|
828
|
+
})));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const {
|
|
832
|
+
profile,
|
|
833
|
+
experiences: selectedExperiences
|
|
834
|
+
} = profileState;
|
|
835
|
+
if (!profile || !selectedExperiences) {
|
|
836
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
837
|
+
loading: false,
|
|
838
|
+
status: 'error',
|
|
839
|
+
error: new Error('No Profile or Selected Experiences were returned by the API')
|
|
840
|
+
})));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (this.useClientSideEvaluation) {
|
|
844
|
+
const _experience = experience_jsShared.selectExperience({
|
|
845
|
+
experiences,
|
|
846
|
+
profile
|
|
847
|
+
});
|
|
848
|
+
if (!_experience) {
|
|
849
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
850
|
+
loading: false,
|
|
851
|
+
status: 'success',
|
|
852
|
+
profile
|
|
853
|
+
})));
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const {
|
|
857
|
+
variant: _variant,
|
|
858
|
+
index
|
|
859
|
+
} = experience_jsShared.selectVariant({
|
|
860
|
+
baseline,
|
|
861
|
+
experience: _experience,
|
|
862
|
+
profile
|
|
863
|
+
});
|
|
864
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, baseReturn), {
|
|
865
|
+
status: 'success',
|
|
866
|
+
loading: false,
|
|
867
|
+
error: null,
|
|
868
|
+
experience: _experience,
|
|
869
|
+
variant: _variant,
|
|
870
|
+
variantIndex: index,
|
|
871
|
+
audience: _experience.audience ? _experience.audience : null,
|
|
872
|
+
profile,
|
|
873
|
+
isPersonalized: true
|
|
874
|
+
})));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const experience = experiences.find(experience => selectedExperiences.some(selectedExperience => selectedExperience.experienceId === experience.id));
|
|
878
|
+
const selectedExperience = selectedExperiences.find(({
|
|
879
|
+
experienceId
|
|
880
|
+
}) => experienceId === (experience === null || experience === void 0 ? void 0 : experience.id));
|
|
881
|
+
if (!experience || !selectedExperience) {
|
|
882
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
883
|
+
loading: false,
|
|
884
|
+
status: 'success',
|
|
885
|
+
profile
|
|
886
|
+
})));
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const baselineVariants = experience_jsShared.selectBaselineWithVariants(experience, baseline);
|
|
890
|
+
if (!baselineVariants) {
|
|
891
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
892
|
+
loading: false,
|
|
893
|
+
status: 'success',
|
|
894
|
+
profile
|
|
895
|
+
})));
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const {
|
|
899
|
+
variants
|
|
900
|
+
} = baselineVariants;
|
|
901
|
+
const variant = variants[selectedExperience.variantIndex - 1];
|
|
902
|
+
if (!variant) {
|
|
903
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
904
|
+
loading: false,
|
|
905
|
+
status: 'success',
|
|
906
|
+
profile
|
|
907
|
+
})));
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, baseReturn), {
|
|
911
|
+
status: 'success',
|
|
912
|
+
loading: false,
|
|
913
|
+
error: null,
|
|
914
|
+
experience,
|
|
915
|
+
variant,
|
|
916
|
+
variantIndex: selectedExperience.variantIndex,
|
|
917
|
+
audience: experience.audience ? experience.audience : null,
|
|
918
|
+
profile,
|
|
919
|
+
isPersonalized: true
|
|
920
|
+
})));
|
|
921
|
+
});
|
|
922
|
+
return () => {
|
|
923
|
+
removeProfileChangeListener();
|
|
924
|
+
removeMiddlewareChangeListeners();
|
|
925
|
+
};
|
|
926
|
+
};
|
|
513
927
|
this.onIsInitialized = onIsInitialized => {
|
|
514
928
|
if (typeof onIsInitialized === 'function') {
|
|
515
929
|
if (this.isInitialized) {
|
|
@@ -540,6 +954,7 @@ class Ninetailed {
|
|
|
540
954
|
}
|
|
541
955
|
});
|
|
542
956
|
};
|
|
957
|
+
this.useClientSideEvaluation = useClientSideEvaluation;
|
|
543
958
|
if (ninetailedApiClientInstanceOrOptions instanceof experience_jsShared.NinetailedApiClient) {
|
|
544
959
|
this.apiClient = ninetailedApiClientInstanceOrOptions;
|
|
545
960
|
} else {
|
|
@@ -579,6 +994,7 @@ class Ninetailed {
|
|
|
579
994
|
if (typeof onError === 'function') {
|
|
580
995
|
experience_jsShared.logger.addSink(new experience_jsShared.OnErrorLogSink(onError));
|
|
581
996
|
}
|
|
997
|
+
this.eventBuilder = new EventBuilder(buildClientContext);
|
|
582
998
|
this.logger = experience_jsShared.logger;
|
|
583
999
|
this.ninetailedCorePlugin = ninetailedCorePlugin({
|
|
584
1000
|
apiClient: this.apiClient,
|
|
@@ -763,120 +1179,6 @@ const ElementSeenPayloadSchema = zod.z.object({
|
|
|
763
1179
|
variantIndex: zod.z.number()
|
|
764
1180
|
});
|
|
765
1181
|
|
|
766
|
-
const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
767
|
-
return encodedExperienceVariantsMap.split(',').map(experienceIdWithVariant => {
|
|
768
|
-
const [experienceId, _variantIndex] = experienceIdWithVariant.split('=');
|
|
769
|
-
const variantIndex = parseInt(_variantIndex);
|
|
770
|
-
if (!experienceId || !variantIndex) {
|
|
771
|
-
return null;
|
|
772
|
-
}
|
|
773
|
-
return {
|
|
774
|
-
experienceId,
|
|
775
|
-
variantIndex
|
|
776
|
-
};
|
|
777
|
-
}).filter(x => !!x).reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
778
|
-
[curr.experienceId]: curr.variantIndex
|
|
779
|
-
}), {});
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
class OnChangeEmitter {
|
|
783
|
-
constructor() {
|
|
784
|
-
this.onChangeListeners = [];
|
|
785
|
-
}
|
|
786
|
-
addListener(listener) {
|
|
787
|
-
this.onChangeListeners.push(listener);
|
|
788
|
-
return () => {
|
|
789
|
-
this.removeOnChangeListener(listener);
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
invokeListeners() {
|
|
793
|
-
this.onChangeListeners.forEach(listener => listener());
|
|
794
|
-
}
|
|
795
|
-
removeOnChangeListener(listener) {
|
|
796
|
-
this.onChangeListeners = this.onChangeListeners.filter(l => l !== listener);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const hasOnChangeEmitter = arg => {
|
|
801
|
-
return typeof arg === 'object' && arg !== null && 'onChangeEmitter' in arg && typeof arg.onChangeEmitter === 'object' && arg.onChangeEmitter !== null && arg.onChangeEmitter.constructor === OnChangeEmitter;
|
|
802
|
-
};
|
|
803
|
-
|
|
804
|
-
const selectPluginsHavingOnChangeEmitter = plugins => {
|
|
805
|
-
const filteredPlugins = [];
|
|
806
|
-
for (const plugin of plugins) {
|
|
807
|
-
if (hasOnChangeEmitter(plugin)) {
|
|
808
|
-
filteredPlugins.push(plugin);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
return filteredPlugins;
|
|
812
|
-
};
|
|
813
|
-
|
|
814
|
-
const hasExperienceSelectionMiddleware = arg => {
|
|
815
|
-
return typeof arg === 'object' && arg !== null && 'getExperienceSelectionMiddleware' in arg && typeof arg.getExperienceSelectionMiddleware === 'function';
|
|
816
|
-
};
|
|
817
|
-
|
|
818
|
-
const selectPluginsHavingExperienceSelectionMiddleware = plugins => {
|
|
819
|
-
const filteredPlugins = [];
|
|
820
|
-
for (const plugin of plugins) {
|
|
821
|
-
if (hasExperienceSelectionMiddleware(plugin)) {
|
|
822
|
-
filteredPlugins.push(plugin);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
return filteredPlugins;
|
|
826
|
-
};
|
|
827
|
-
|
|
828
|
-
const createPassThroughMiddleware = () => {
|
|
829
|
-
return ({
|
|
830
|
-
experience,
|
|
831
|
-
variant,
|
|
832
|
-
variantIndex
|
|
833
|
-
}) => {
|
|
834
|
-
return {
|
|
835
|
-
experience,
|
|
836
|
-
variant,
|
|
837
|
-
variantIndex
|
|
838
|
-
};
|
|
839
|
-
};
|
|
840
|
-
};
|
|
841
|
-
const makeExperienceSelectMiddleware = ({
|
|
842
|
-
plugins,
|
|
843
|
-
onChange,
|
|
844
|
-
experiences,
|
|
845
|
-
baseline,
|
|
846
|
-
profile
|
|
847
|
-
}) => {
|
|
848
|
-
let removeChangeListeners = [];
|
|
849
|
-
const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
|
|
850
|
-
const prepareMiddleware = () => {
|
|
851
|
-
if (profile === null) {
|
|
852
|
-
return createPassThroughMiddleware();
|
|
853
|
-
}
|
|
854
|
-
const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
|
|
855
|
-
const middlewareFunctions = pluginsWithMiddleware.map(plugin => plugin.getExperienceSelectionMiddleware({
|
|
856
|
-
experiences,
|
|
857
|
-
baseline
|
|
858
|
-
}));
|
|
859
|
-
return experience_jsShared.pipe(...middlewareFunctions);
|
|
860
|
-
};
|
|
861
|
-
const addListeners = () => {
|
|
862
|
-
removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
|
|
863
|
-
const listener = () => {
|
|
864
|
-
onChange();
|
|
865
|
-
};
|
|
866
|
-
return plugin.onChangeEmitter.addListener(listener);
|
|
867
|
-
});
|
|
868
|
-
};
|
|
869
|
-
const removeListeners = () => {
|
|
870
|
-
removeChangeListeners.forEach(listener => listener());
|
|
871
|
-
};
|
|
872
|
-
const middleware = prepareMiddleware();
|
|
873
|
-
return {
|
|
874
|
-
addListeners,
|
|
875
|
-
removeListeners,
|
|
876
|
-
middleware
|
|
877
|
-
};
|
|
878
|
-
};
|
|
879
|
-
|
|
880
1182
|
Object.defineProperty(exports, 'EXPERIENCE_TRAIT_PREFIX', {
|
|
881
1183
|
enumerable: true,
|
|
882
1184
|
get: function () { return experience_jsShared.EXPERIENCE_TRAIT_PREFIX; }
|
|
@@ -923,6 +1225,7 @@ exports.EXPERIENCES_FALLBACK_CACHE = EXPERIENCES_FALLBACK_CACHE;
|
|
|
923
1225
|
exports.ElementSeenPayloadSchema = ElementSeenPayloadSchema;
|
|
924
1226
|
exports.HAS_SEEN_COMPONENT = HAS_SEEN_COMPONENT;
|
|
925
1227
|
exports.HAS_SEEN_ELEMENT = HAS_SEEN_ELEMENT;
|
|
1228
|
+
exports.HAS_SEEN_STICKY_COMPONENT = HAS_SEEN_STICKY_COMPONENT;
|
|
926
1229
|
exports.LEGACY_ANONYMOUS_ID = LEGACY_ANONYMOUS_ID;
|
|
927
1230
|
exports.Ninetailed = Ninetailed;
|
|
928
1231
|
exports.NinetailedPlugin = NinetailedPlugin;
|