@ninetailed/experience.js 7.5.3 → 7.6.0-beta.2

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.
Files changed (35) hide show
  1. package/index.cjs.d.ts +1 -0
  2. package/{index.cjs → index.cjs.js} +697 -375
  3. package/index.esm.js +1203 -0
  4. package/package.json +6 -9
  5. package/src/index.d.ts +1 -1
  6. package/src/lib/ElementSeenObserver.d.ts +1 -1
  7. package/src/lib/Ninetailed.d.ts +19 -5
  8. package/src/lib/NinetailedCorePlugin/NinetailedCorePlugin.d.ts +69 -0
  9. package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/index.d.ts +1 -1
  10. package/src/lib/constants.d.ts +1 -2
  11. package/src/lib/experience/makeExperienceSelectMiddleware.d.ts +9 -9
  12. package/src/lib/guards/hasComponentViewTrackingThreshold.d.ts +3 -0
  13. package/src/lib/guards/hasExperienceSelectionMiddleware.d.ts +1 -1
  14. package/src/lib/plugins/selectPluginsHavingExperienceSelectionMiddleware.d.ts +2 -2
  15. package/src/lib/plugins/selectPluginsHavingOnChangeEmitter.d.ts +1 -1
  16. package/src/lib/types/OnSelectVariant.d.ts +51 -0
  17. package/src/lib/types/index.d.ts +9 -9
  18. package/src/lib/types/interfaces/HasComponentViewTrackingThreshold.d.ts +3 -0
  19. package/src/lib/types/interfaces/HasExperienceSelectionMiddleware.d.ts +10 -10
  20. package/src/lib/types/interfaces/InterestedInHiddenPage.d.ts +1 -1
  21. package/src/lib/types/interfaces/InterestedInProfileChange.d.ts +1 -1
  22. package/src/lib/types/interfaces/InterestedInSeenElements.d.ts +1 -3
  23. package/src/lib/utils/EventBuilder.d.ts +221 -0
  24. package/src/lib/utils/noop.d.ts +1 -0
  25. package/index.js +0 -886
  26. package/src/lib/ninetailedCorePlugin/ninetailedCorePlugin.d.ts +0 -17
  27. package/src/lib/test-helpers/intersection-observer-test-helper.d.ts +0 -2
  28. package/src/lib/types/ElementSeenPayload.d.ts +0 -82
  29. package/src/lib/types/EventHandler.d.ts +0 -6
  30. package/src/lib/types/NinetailedPlugin.d.ts +0 -11
  31. package/src/lib/types/TrackingProperties.d.ts +0 -35
  32. /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/build-context.d.ts +0 -0
  33. /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/build-locale.d.ts +0 -0
  34. /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/index.d.ts +0 -0
  35. /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/constants.d.ts +0 -0
package/index.esm.js ADDED
@@ -0,0 +1,1203 @@
1
+ import { FEATURES, buildComponentViewEvent, logger, ConsoleLogSink, buildPageEvent, buildTrackEvent, buildIdentifyEvent, unionBy, pipe, PageviewProperties, Properties, Traits, isPageViewEvent, isTrackEvent, isIdentifyEvent, isComponentViewEvent, selectHasVariants, selectExperience, selectVariant as selectVariant$1, selectBaselineWithVariants, NinetailedApiClient, OnLogLogSink, OnErrorLogSink } from '@ninetailed/experience.js-shared';
2
+ export { EXPERIENCE_TRAIT_PREFIX, isExperienceMatch, selectActiveExperiments, selectDistribution, selectExperience, selectBaselineWithVariants as selectExperienceBaselineWithVariants, selectVariant as selectExperienceVariant, selectVariants as selectExperienceVariants, selectHasVariants as selectHasExperienceVariants } from '@ninetailed/experience.js-shared';
3
+ import { NinetailedAnalyticsPlugin, HAS_SEEN_COMPONENT, HAS_SEEN_ELEMENT } from '@ninetailed/experience.js-plugin-analytics';
4
+ import Analytics from 'analytics';
5
+ import { v4 } from 'uuid';
6
+
7
+ const COMPONENT = 'component';
8
+ const COMPONENT_START = 'componentStart';
9
+ const PAGE_HIDDEN = 'page_hidden';
10
+ const HAS_SEEN_STICKY_COMPONENT = 'sticky_component_view';
11
+
12
+ const buildClientLocale = () => navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language;
13
+
14
+ const buildClientNinetailedRequestContext = () => ({
15
+ url: window.location.href,
16
+ referrer: document.referrer,
17
+ locale: buildClientLocale(),
18
+ userAgent: navigator.userAgent,
19
+ document: {
20
+ title: document.title
21
+ }
22
+ });
23
+
24
+ /**
25
+ * Similar to _.throttle but waits for the promise to resolve.
26
+ * There is no "wait time" because you can simply await `Promise.timeout` inside `fn` to wait some time before the next call.
27
+ */
28
+ function asyncThrottle(fn) {
29
+ let runningPromise;
30
+ let queuedPromise;
31
+ let nextArgs;
32
+ return async args => {
33
+ if (runningPromise) {
34
+ nextArgs = args;
35
+ if (queuedPromise) {
36
+ return queuedPromise;
37
+ } else {
38
+ queuedPromise = runningPromise.then(() => {
39
+ queuedPromise = undefined;
40
+ runningPromise = fn(nextArgs);
41
+ return runningPromise;
42
+ });
43
+ return queuedPromise;
44
+ }
45
+ } else {
46
+ runningPromise = fn(args);
47
+ return runningPromise;
48
+ }
49
+ };
50
+ }
51
+
52
+ const LEGACY_ANONYMOUS_ID = '__anon_id';
53
+ const ANONYMOUS_ID = '__nt_anonymous_id__';
54
+ const DEBUG_FLAG = '__nt_debug__';
55
+ const PROFILE_FALLBACK_CACHE = '__nt_profile__';
56
+ const EXPERIENCES_FALLBACK_CACHE = '__nt_experiences__';
57
+ const PROFILE_CHANGE = 'profile-change';
58
+ const PROFILE_RESET = 'profile-reset';
59
+ const CONSENT = '__nt-consent__';
60
+ const SET_ENABLED_FEATURES = 'set-enabled-features';
61
+ const EMPTY_MERGE_ID = 'nt:empty-merge-id';
62
+
63
+ const PLUGIN_NAME = 'ninetailed:core';
64
+ const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
65
+ class NinetailedCorePlugin extends NinetailedAnalyticsPlugin {
66
+ constructor({
67
+ apiClient,
68
+ locale,
69
+ ninetailed,
70
+ onInitProfileId,
71
+ buildClientContext
72
+ }) {
73
+ var _this;
74
+ super();
75
+ _this = this;
76
+ this.name = PLUGIN_NAME;
77
+ this._instance = void 0;
78
+ this.queue = [];
79
+ this.enabledFeatures = Object.values(FEATURES);
80
+ this.buildContext = buildClientNinetailedRequestContext;
81
+ this.onInitProfileId = void 0;
82
+ this.apiClient = void 0;
83
+ this.locale = void 0;
84
+ this.ninetailed = void 0;
85
+ this[HAS_SEEN_STICKY_COMPONENT] = async function ({
86
+ payload
87
+ }) {
88
+ const ctx = _this.buildContext();
89
+ return _this.enqueueEvent(buildComponentViewEvent({
90
+ messageId: payload.meta.rid,
91
+ timestamp: payload.meta.ts,
92
+ componentId: payload.componentId,
93
+ experienceId: payload.experienceId,
94
+ variantIndex: payload.variantIndex,
95
+ ctx
96
+ }));
97
+ };
98
+ this.methods = {
99
+ reset: async function (...args) {
100
+ logger.debug('Resetting profile.');
101
+ const instance = args[args.length - 1];
102
+ instance.dispatch({
103
+ type: PROFILE_RESET
104
+ });
105
+ instance.storage.removeItem(ANONYMOUS_ID);
106
+ instance.storage.removeItem(PROFILE_FALLBACK_CACHE);
107
+ instance.storage.removeItem(EXPERIENCES_FALLBACK_CACHE);
108
+ logger.debug('Removed old profile data from localstorage.');
109
+ if (typeof _this.onInitProfileId === 'function') {
110
+ const profileId = await _this.onInitProfileId(undefined);
111
+ if (typeof profileId === 'string') {
112
+ instance.storage.setItem(ANONYMOUS_ID, profileId);
113
+ }
114
+ }
115
+ await _this.ninetailed.track('nt_reset');
116
+ logger.info('Profile reset successful.');
117
+ await delay(10);
118
+ },
119
+ debug: async function (...args) {
120
+ const enabled = args[0];
121
+ const instance = args[args.length - 1];
122
+ const consoleLogSink = new ConsoleLogSink();
123
+ if (enabled) {
124
+ instance.storage.setItem(DEBUG_FLAG, true);
125
+ logger.addSink(consoleLogSink);
126
+ logger.info('Debug mode enabled.');
127
+ } else {
128
+ instance.storage.removeItem(DEBUG_FLAG);
129
+ logger.info('Debug mode disabled.');
130
+ logger.removeSink(consoleLogSink.name);
131
+ }
132
+ }
133
+ };
134
+ this.flush = asyncThrottle(this._flush.bind(this));
135
+ if (onInitProfileId) {
136
+ this.onInitProfileId = onInitProfileId;
137
+ }
138
+ if (buildClientContext) {
139
+ this.buildContext = buildClientContext;
140
+ }
141
+ this.apiClient = apiClient;
142
+ this.ninetailed = ninetailed;
143
+ this.locale = locale;
144
+ }
145
+ async initialize({
146
+ instance
147
+ }) {
148
+ this._instance = instance;
149
+ if (instance.storage.getItem(DEBUG_FLAG)) {
150
+ logger.addSink(new ConsoleLogSink());
151
+ logger.info('Ninetailed Debug Mode is enabled.');
152
+ }
153
+
154
+ // legacy support for the old anonymousId
155
+ const legacyAnonymousId = instance.storage.getItem(LEGACY_ANONYMOUS_ID);
156
+ if (legacyAnonymousId) {
157
+ logger.debug('Found legacy anonymousId, migrating to new one.', legacyAnonymousId);
158
+ instance.storage.setItem(ANONYMOUS_ID, legacyAnonymousId);
159
+ instance.storage.removeItem(LEGACY_ANONYMOUS_ID);
160
+ }
161
+ if (typeof this.onInitProfileId === 'function') {
162
+ const profileId = await this.onInitProfileId(instance.storage.getItem(ANONYMOUS_ID));
163
+ if (typeof profileId === 'string') {
164
+ instance.storage.setItem(ANONYMOUS_ID, profileId);
165
+ }
166
+ }
167
+ instance.on(SET_ENABLED_FEATURES, ({
168
+ payload
169
+ }) => {
170
+ this.enabledFeatures = payload.features || [];
171
+ });
172
+ logger.debug('Ninetailed Core plugin initialized.');
173
+ }
174
+ pageStart(params) {
175
+ return this.abortNonClientEvents(params);
176
+ }
177
+ async page({
178
+ payload
179
+ }) {
180
+ logger.info('Sending Page event.');
181
+ const ctx = this.buildContext();
182
+ return this.enqueueEvent(buildPageEvent({
183
+ messageId: payload.meta.rid,
184
+ timestamp: payload.meta.ts,
185
+ properties: payload.properties,
186
+ ctx
187
+ }));
188
+ }
189
+ trackStart(params) {
190
+ return this.abortNonClientEvents(params);
191
+ }
192
+ async track({
193
+ payload
194
+ }) {
195
+ logger.info('Sending Track event.');
196
+ const ctx = this.buildContext();
197
+ return this.enqueueEvent(buildTrackEvent({
198
+ messageId: payload.meta.rid,
199
+ timestamp: payload.meta.ts,
200
+ event: payload.event,
201
+ properties: payload.properties,
202
+ ctx
203
+ }));
204
+ }
205
+ identifyStart(params) {
206
+ return this.abortNonClientEvents(params);
207
+ }
208
+ async identify({
209
+ payload
210
+ }) {
211
+ logger.info('Sending Identify event.');
212
+ const ctx = this.buildContext();
213
+ if (payload.userId === EMPTY_MERGE_ID && (!payload.traits || typeof payload.traits === 'object' && Object.keys(payload.traits).length === 0)) {
214
+ logger.info('Skipping Identify event as no userId and no traits are set.');
215
+ return;
216
+ }
217
+ return this.enqueueEvent(buildIdentifyEvent({
218
+ messageId: payload.meta.rid,
219
+ timestamp: payload.meta.ts,
220
+ traits: payload.traits,
221
+ userId: payload.userId === EMPTY_MERGE_ID ? '' : payload.userId,
222
+ ctx
223
+ }));
224
+ }
225
+ getComponentViewTrackingThreshold() {
226
+ return 0;
227
+ }
228
+ async onTrackExperience(properties) {
229
+ if (properties.experience.sticky) {
230
+ await this.instance.dispatch({
231
+ type: HAS_SEEN_STICKY_COMPONENT,
232
+ componentId: properties.selectedVariant.id,
233
+ experienceId: properties.experience.id,
234
+ variantIndex: properties.selectedVariantIndex
235
+ });
236
+ this.flush();
237
+ }
238
+ return Promise.resolve();
239
+ }
240
+ onTrackComponent() {
241
+ return Promise.resolve();
242
+ }
243
+ setItemStart({
244
+ abort,
245
+ payload
246
+ }) {
247
+ if (![ANONYMOUS_ID, DEBUG_FLAG, PROFILE_FALLBACK_CACHE, EXPERIENCES_FALLBACK_CACHE, CONSENT].includes(payload.key)) {
248
+ return abort();
249
+ }
250
+ return payload;
251
+ }
252
+ async enqueueEvent(event) {
253
+ this.queue = unionBy([event], this.queue, 'messageId');
254
+ }
255
+ abortNonClientEvents({
256
+ abort,
257
+ payload
258
+ }) {
259
+ if (typeof window !== 'object') {
260
+ return abort();
261
+ }
262
+ return payload;
263
+ }
264
+ get instance() {
265
+ if (!this._instance) {
266
+ throw new Error('Ninetailed Core plugin not initialized.');
267
+ }
268
+ return this._instance;
269
+ }
270
+ async _flush() {
271
+ const events = Object.assign([], this.queue);
272
+ logger.info('Start flushing events.');
273
+ this.queue = [];
274
+ if (!events.length) {
275
+ return {
276
+ success: true
277
+ };
278
+ }
279
+ try {
280
+ const anonymousId = this.instance.storage.getItem(ANONYMOUS_ID);
281
+ const {
282
+ profile,
283
+ experiences
284
+ } = await this.apiClient.upsertProfile({
285
+ profileId: anonymousId,
286
+ events
287
+ }, {
288
+ locale: this.locale,
289
+ enabledFeatures: this.enabledFeatures
290
+ });
291
+ this.instance.storage.setItem(ANONYMOUS_ID, profile.id);
292
+ this.instance.storage.setItem(PROFILE_FALLBACK_CACHE, profile);
293
+ this.instance.storage.setItem(EXPERIENCES_FALLBACK_CACHE, experiences);
294
+ logger.debug('Profile from api: ', profile);
295
+ logger.debug('Experiences from api: ', experiences);
296
+ this.instance.dispatch({
297
+ type: PROFILE_CHANGE,
298
+ profile,
299
+ experiences
300
+ });
301
+ await delay(20);
302
+ return {
303
+ success: true
304
+ };
305
+ } catch (error) {
306
+ logger.debug('An error occurred during flushing the events: ', error);
307
+ const fallbackProfile = this.instance.storage.getItem(PROFILE_FALLBACK_CACHE);
308
+ const fallbackExperiences = this.instance.storage.getItem(EXPERIENCES_FALLBACK_CACHE) || [];
309
+ if (fallbackProfile) {
310
+ logger.debug('Found a fallback profile - will use this.');
311
+ this.instance.dispatch({
312
+ type: PROFILE_CHANGE,
313
+ profile: fallbackProfile,
314
+ experiences: fallbackExperiences
315
+ });
316
+ } else {
317
+ logger.debug('No fallback profile found - setting profile to null.');
318
+ this.instance.dispatch({
319
+ type: PROFILE_CHANGE,
320
+ profile: null,
321
+ experiences: fallbackExperiences,
322
+ error
323
+ });
324
+ }
325
+ return {
326
+ success: false
327
+ };
328
+ }
329
+ }
330
+ }
331
+
332
+ function _objectWithoutPropertiesLoose(source, excluded) {
333
+ if (source == null) return {};
334
+ var target = {};
335
+ var sourceKeys = Object.keys(source);
336
+ var key, i;
337
+ for (i = 0; i < sourceKeys.length; i++) {
338
+ key = sourceKeys[i];
339
+ if (excluded.indexOf(key) >= 0) continue;
340
+ target[key] = source[key];
341
+ }
342
+ return target;
343
+ }
344
+
345
+ class ElementSeenObserver {
346
+ constructor(_options) {
347
+ this._intersectionObserver = void 0;
348
+ this._elementDelays = void 0;
349
+ this._intersectionTimers = void 0;
350
+ this._options = _options;
351
+ this._elementDelays = new WeakMap();
352
+ this._intersectionTimers = new WeakMap();
353
+ if (typeof IntersectionObserver !== 'undefined') {
354
+ this._intersectionObserver = new IntersectionObserver(this.onIntersection.bind(this));
355
+ }
356
+ }
357
+ onIntersection(entries) {
358
+ entries.forEach(entry => {
359
+ const {
360
+ isIntersecting,
361
+ target
362
+ } = entry;
363
+ if (isIntersecting) {
364
+ const delays = this._elementDelays.get(target);
365
+ delays == null || delays.forEach(delay => {
366
+ const timeOut = window.setTimeout(() => {
367
+ this._options.onElementSeen(target, delay);
368
+ }, delay);
369
+ const currentTimers = this._intersectionTimers.get(target) || [];
370
+ this._intersectionTimers.set(target, [...currentTimers, timeOut]);
371
+ });
372
+ } else {
373
+ const timeOuts = this._intersectionTimers.get(target);
374
+ timeOuts == null || timeOuts.forEach(timeOut => {
375
+ if (typeof timeOut !== 'undefined') {
376
+ window.clearTimeout(timeOut);
377
+ }
378
+ });
379
+ }
380
+ });
381
+ }
382
+ observe(element, options) {
383
+ var _this$_intersectionOb;
384
+ const delays = this._elementDelays.get(element) || [];
385
+ this._elementDelays.set(element, Array.from(new Set([...delays, (options == null ? void 0 : options.delay) || 0])));
386
+ (_this$_intersectionOb = this._intersectionObserver) == null || _this$_intersectionOb.observe(element);
387
+ }
388
+ unobserve(element) {
389
+ var _this$_intersectionOb2;
390
+ (_this$_intersectionOb2 = this._intersectionObserver) == null || _this$_intersectionOb2.unobserve(element);
391
+ }
392
+ }
393
+
394
+ const acceptsCredentials = plugin => {
395
+ return typeof plugin === 'object' && plugin !== null && 'setCredentials' in plugin && typeof plugin.setCredentials === 'function';
396
+ };
397
+
398
+ const isInterestedInHiddenPage = arg => {
399
+ return typeof arg === 'object' && arg !== null && PAGE_HIDDEN in arg && typeof arg[PAGE_HIDDEN] === 'function';
400
+ };
401
+
402
+ const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
403
+ return encodedExperienceVariantsMap.split(',').map(experienceIdWithVariant => {
404
+ const [experienceId, _variantIndex] = experienceIdWithVariant.split('=');
405
+ const variantIndex = parseInt(_variantIndex);
406
+ if (!experienceId || !variantIndex) {
407
+ return null;
408
+ }
409
+ return {
410
+ experienceId,
411
+ variantIndex
412
+ };
413
+ }).filter(x => !!x).reduce((acc, curr) => Object.assign({}, acc, {
414
+ [curr.experienceId]: curr.variantIndex
415
+ }), {});
416
+ };
417
+
418
+ class OnChangeEmitter {
419
+ constructor() {
420
+ this.onChangeListeners = [];
421
+ }
422
+ addListener(listener) {
423
+ this.onChangeListeners.push(listener);
424
+ return () => {
425
+ this.removeOnChangeListener(listener);
426
+ };
427
+ }
428
+ invokeListeners() {
429
+ this.onChangeListeners.forEach(listener => listener());
430
+ }
431
+ removeOnChangeListener(listener) {
432
+ this.onChangeListeners = this.onChangeListeners.filter(l => l !== listener);
433
+ }
434
+ }
435
+
436
+ const hasOnChangeEmitter = arg => {
437
+ return typeof arg === 'object' && arg !== null && 'onChangeEmitter' in arg && typeof arg.onChangeEmitter === 'object' && arg.onChangeEmitter !== null && arg.onChangeEmitter.constructor === OnChangeEmitter;
438
+ };
439
+
440
+ const selectPluginsHavingOnChangeEmitter = plugins => {
441
+ const filteredPlugins = [];
442
+ for (const plugin of plugins) {
443
+ if (hasOnChangeEmitter(plugin)) {
444
+ filteredPlugins.push(plugin);
445
+ }
446
+ }
447
+ return filteredPlugins;
448
+ };
449
+
450
+ const hasExperienceSelectionMiddleware = arg => {
451
+ return typeof arg === 'object' && arg !== null && 'getExperienceSelectionMiddleware' in arg && typeof arg.getExperienceSelectionMiddleware === 'function';
452
+ };
453
+
454
+ const selectPluginsHavingExperienceSelectionMiddleware = plugins => {
455
+ const filteredPlugins = [];
456
+ for (const plugin of plugins) {
457
+ if (hasExperienceSelectionMiddleware(plugin)) {
458
+ filteredPlugins.push(plugin);
459
+ }
460
+ }
461
+ return filteredPlugins;
462
+ };
463
+
464
+ const createPassThroughMiddleware = () => {
465
+ return ({
466
+ experience,
467
+ variant,
468
+ variantIndex
469
+ }) => {
470
+ return {
471
+ experience,
472
+ variant,
473
+ variantIndex
474
+ };
475
+ };
476
+ };
477
+ const makeExperienceSelectMiddleware = ({
478
+ plugins,
479
+ onChange,
480
+ experiences,
481
+ baseline,
482
+ profile
483
+ }) => {
484
+ let removeChangeListeners = [];
485
+ const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
486
+ const prepareMiddleware = () => {
487
+ if (profile === null) {
488
+ return createPassThroughMiddleware();
489
+ }
490
+ const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
491
+ const middlewareFunctions = pluginsWithMiddleware.map(plugin => plugin.getExperienceSelectionMiddleware({
492
+ experiences,
493
+ baseline
494
+ }));
495
+ return pipe(...middlewareFunctions);
496
+ };
497
+ const middleware = prepareMiddleware();
498
+ const addListeners = () => {
499
+ removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
500
+ const listener = () => {
501
+ onChange(middleware);
502
+ };
503
+ return plugin.onChangeEmitter.addListener(listener);
504
+ });
505
+ };
506
+ const removeListeners = () => {
507
+ removeChangeListeners.forEach(listener => listener());
508
+ };
509
+ return {
510
+ addListeners,
511
+ removeListeners,
512
+ middleware
513
+ };
514
+ };
515
+
516
+ class EventBuilder {
517
+ constructor(buildRequestContext) {
518
+ this.buildRequestContext = void 0;
519
+ this.buildRequestContext = buildRequestContext || buildClientNinetailedRequestContext;
520
+ }
521
+ page(properties, data) {
522
+ return buildPageEvent(Object.assign({
523
+ messageId: (data == null ? void 0 : data.messageId) || v4()
524
+ }, data, {
525
+ timestamp: Date.now(),
526
+ properties: properties || {},
527
+ ctx: this.buildRequestContext()
528
+ }));
529
+ }
530
+ track(event, properties, data) {
531
+ return buildTrackEvent(Object.assign({
532
+ messageId: (data == null ? void 0 : data.messageId) || v4(),
533
+ timestamp: Date.now()
534
+ }, data, {
535
+ event,
536
+ properties: properties || {},
537
+ ctx: this.buildRequestContext()
538
+ }));
539
+ }
540
+ identify(userId, traits, data) {
541
+ return buildIdentifyEvent(Object.assign({
542
+ messageId: (data == null ? void 0 : data.messageId) || v4(),
543
+ timestamp: Date.now()
544
+ }, data, {
545
+ traits: traits || {},
546
+ userId: userId || '',
547
+ ctx: this.buildRequestContext()
548
+ }));
549
+ }
550
+ component(componentId, experienceId, variantIndex, data) {
551
+ return buildComponentViewEvent(Object.assign({
552
+ messageId: (data == null ? void 0 : data.messageId) || v4(),
553
+ timestamp: Date.now()
554
+ }, data, {
555
+ componentId,
556
+ experienceId: experienceId || '',
557
+ variantIndex: variantIndex || 0,
558
+ ctx: this.buildRequestContext()
559
+ }));
560
+ }
561
+ }
562
+
563
+ const hasComponentViewTrackingThreshold = arg => {
564
+ return typeof arg === 'object' && arg !== null && 'getComponentViewTrackingThreshold' in arg && typeof arg['getComponentViewTrackingThreshold'] === 'function';
565
+ };
566
+
567
+ const _excluded = ["experience", "variant", "variantIndex"],
568
+ _excluded2 = ["element"];
569
+ const buildOverrideMiddleware = experienceSelectionMiddleware => _ref => {
570
+ let {
571
+ experience: originalExperience,
572
+ variant: originalVariant,
573
+ variantIndex: originalVariantIndex
574
+ } = _ref,
575
+ other = _objectWithoutPropertiesLoose(_ref, _excluded);
576
+ const {
577
+ experience,
578
+ variant,
579
+ variantIndex
580
+ } = experienceSelectionMiddleware({
581
+ experience: originalExperience,
582
+ variant: originalVariant,
583
+ variantIndex: originalVariantIndex
584
+ });
585
+ return Object.assign({}, other, {
586
+ audience: experience != null && experience.audience ? experience.audience : null,
587
+ experience,
588
+ variant,
589
+ variantIndex
590
+ });
591
+ };
592
+ class Ninetailed {
593
+ constructor(ninetailedApiClientInstanceOrOptions, {
594
+ plugins,
595
+ url,
596
+ locale,
597
+ requestTimeout,
598
+ onLog,
599
+ onError,
600
+ buildClientContext,
601
+ onInitProfileId,
602
+ componentViewTrackingThreshold = 2000,
603
+ storageImpl,
604
+ useClientSideEvaluation = false
605
+ } = {}) {
606
+ var _this = this;
607
+ this.instance = void 0;
608
+ this._profileState = void 0;
609
+ this.isInitialized = false;
610
+ this.apiClient = void 0;
611
+ this.ninetailedCorePlugin = void 0;
612
+ this.elementSeenObserver = void 0;
613
+ this.observedElements = void 0;
614
+ this.clientId = void 0;
615
+ this.environment = void 0;
616
+ this.plugins = void 0;
617
+ this.logger = void 0;
618
+ this.componentViewTrackingThreshold = void 0;
619
+ this.useClientSideEvaluation = void 0;
620
+ this.eventBuilder = void 0;
621
+ this.page = async function (data, options) {
622
+ try {
623
+ const result = PageviewProperties.partial().default({}).safeParse(data);
624
+ if (!result.success) {
625
+ throw new Error(`[Validation Error] "page" was called with invalid params. Page data is not valid: ${result.error.format()}`);
626
+ }
627
+ await _this.waitUntilInitialized();
628
+ await _this.instance.page(data, _this.buildOptions(options));
629
+ return _this.ninetailedCorePlugin.flush();
630
+ } catch (error) {
631
+ logger.error(error);
632
+ if (error instanceof RangeError) {
633
+ 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.`);
634
+ }
635
+ throw error;
636
+ }
637
+ };
638
+ this.track = async function (event, properties, options) {
639
+ try {
640
+ const result = Properties.default({}).safeParse(properties);
641
+ if (!result.success) {
642
+ throw new Error(`[Validation Error] "track" was called with invalid params. Properties are no valid json object: ${result.error.format()}`);
643
+ }
644
+ await _this.waitUntilInitialized();
645
+ await _this.instance.track(event.toString(), result.data, _this.buildOptions(options));
646
+ return _this.ninetailedCorePlugin.flush();
647
+ } catch (error) {
648
+ logger.error(error);
649
+ if (error instanceof RangeError) {
650
+ 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.`);
651
+ }
652
+ throw error;
653
+ }
654
+ };
655
+ this.identify = async function (uid, traits, options) {
656
+ try {
657
+ const result = Traits.default({}).safeParse(traits);
658
+ if (!result.success) {
659
+ throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
660
+ }
661
+ await _this.waitUntilInitialized();
662
+ await _this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, _this.buildOptions(options));
663
+ return _this.ninetailedCorePlugin.flush();
664
+ } catch (error) {
665
+ logger.error(error);
666
+ if (error instanceof RangeError) {
667
+ 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.`);
668
+ }
669
+ throw error;
670
+ }
671
+ };
672
+ this.batch = async function (events) {
673
+ try {
674
+ await _this.waitUntilInitialized();
675
+ const promises = events.map(event => {
676
+ if (isPageViewEvent(event)) {
677
+ return _this.instance.page(event.properties);
678
+ }
679
+ if (isTrackEvent(event)) {
680
+ return _this.instance.track(event.event, event.properties);
681
+ }
682
+ if (isIdentifyEvent(event)) {
683
+ return _this.instance.identify(event.userId || EMPTY_MERGE_ID, event.traits);
684
+ }
685
+ if (isComponentViewEvent(event)) {
686
+ return _this.instance.dispatch({
687
+ experienceId: event.experienceId,
688
+ componentId: event.componentId,
689
+ variantIndex: event.variantIndex,
690
+ type: HAS_SEEN_STICKY_COMPONENT
691
+ });
692
+ }
693
+ return Promise.resolve();
694
+ });
695
+ await Promise.all(promises);
696
+ return _this.ninetailedCorePlugin.flush();
697
+ } catch (error) {
698
+ logger.error(error);
699
+ if (error instanceof RangeError) {
700
+ 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.`);
701
+ }
702
+ throw error;
703
+ }
704
+ };
705
+ this.trackStickyComponentView = async function ({
706
+ experienceId,
707
+ componentId,
708
+ variantIndex
709
+ }) {
710
+ try {
711
+ await _this.waitUntilInitialized();
712
+ await _this.instance.dispatch({
713
+ experienceId,
714
+ componentId,
715
+ variantIndex,
716
+ type: HAS_SEEN_STICKY_COMPONENT
717
+ });
718
+ return _this.ninetailedCorePlugin.flush();
719
+ } catch (error) {
720
+ logger.error(error);
721
+ if (error instanceof RangeError) {
722
+ 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.`);
723
+ }
724
+ throw error;
725
+ }
726
+ };
727
+ /**
728
+ * @deprecated The legacy datamodel is not recommended anymore
729
+ * Will be removed in the next version of the SDK
730
+ */
731
+ this.trackHasSeenComponent = async function (properties) {
732
+ return _this.instance.dispatch(Object.assign({}, properties, {
733
+ type: HAS_SEEN_COMPONENT
734
+ }));
735
+ };
736
+ this.trackComponentView = properties => {
737
+ return this.instance.dispatch(Object.assign({}, properties, {
738
+ type: HAS_SEEN_ELEMENT
739
+ }));
740
+ };
741
+ this.observeElement = (payload, options) => {
742
+ const {
743
+ element
744
+ } = payload,
745
+ remaingPayload = _objectWithoutPropertiesLoose(payload, _excluded2);
746
+ if (!(element instanceof Element)) {
747
+ const isObject = typeof element === 'object' && element !== null;
748
+ const constructorName = isObject ? element.constructor.name : '';
749
+ const isConstructorNameNotObject = constructorName && constructorName !== 'Object';
750
+ 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.`);
751
+ } else {
752
+ this.observedElements.set(element, remaingPayload);
753
+ const delays = this.pluginsWithCustomComponentViewThreshold.map(plugin => plugin.getComponentViewTrackingThreshold());
754
+ const uniqueDelays = Array.from(new Set([...delays, (options == null ? void 0 : options.delay) || this.componentViewTrackingThreshold]));
755
+ uniqueDelays.forEach(delay => {
756
+ this.elementSeenObserver.observe(element, {
757
+ delay
758
+ });
759
+ });
760
+ }
761
+ };
762
+ this.unobserveElement = element => {
763
+ this.observedElements.delete(element);
764
+ this.elementSeenObserver.unobserve(element);
765
+ };
766
+ this.onElementSeen = (element, delay) => {
767
+ const payload = this.observedElements.get(element);
768
+ if (typeof payload !== 'undefined') {
769
+ const pluginNamesInterestedInSeenElementMessage = [...this.pluginsWithCustomComponentViewThreshold.filter(plugin => plugin.getComponentViewTrackingThreshold() === delay), ...this.plugins.filter(plugin => !hasComponentViewTrackingThreshold(plugin))].map(plugin => plugin.name);
770
+ if (pluginNamesInterestedInSeenElementMessage.length === 0) {
771
+ return;
772
+ }
773
+ this.instance.dispatch(Object.assign({}, payload, {
774
+ element,
775
+ type: HAS_SEEN_ELEMENT,
776
+ plugins: Object.assign({
777
+ all: false
778
+ }, pluginNamesInterestedInSeenElementMessage.reduce((acc, curr) => Object.assign({}, acc, {
779
+ [curr]: true
780
+ }), {}))
781
+ }));
782
+ }
783
+ };
784
+ this.reset = async function () {
785
+ await _this.waitUntilInitialized();
786
+
787
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
788
+ // @ts-ignore
789
+ _this.instance.plugins[PLUGIN_NAME].reset();
790
+ };
791
+ this.debug = async function (enabled) {
792
+ await _this.waitUntilInitialized();
793
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
794
+ // @ts-ignore
795
+ _this.instance.plugins[PLUGIN_NAME].debug(enabled);
796
+ };
797
+ this.onProfileChange = cb => {
798
+ cb(this.profileState);
799
+ return this.instance.on(PROFILE_CHANGE, ({
800
+ payload
801
+ }) => {
802
+ if (payload.error) {
803
+ cb(Object.assign({}, this._profileState, {
804
+ status: 'error',
805
+ profile: payload.profile,
806
+ experiences: payload.experiences,
807
+ error: payload.error
808
+ }));
809
+ } else {
810
+ cb(Object.assign({}, this._profileState, {
811
+ status: 'success',
812
+ profile: payload.profile,
813
+ experiences: payload.experiences,
814
+ error: null
815
+ }));
816
+ }
817
+ });
818
+ };
819
+ this.onSelectVariant = ({
820
+ baseline,
821
+ experiences
822
+ }, cb) => {
823
+ let middlewareChangeListeners = [];
824
+ let state = null;
825
+ const removeMiddlewareChangeListeners = () => {
826
+ middlewareChangeListeners.forEach(removeListener => removeListener());
827
+ middlewareChangeListeners = [];
828
+ };
829
+ const setSelectedVariant = newState => {
830
+ state = newState;
831
+ cb(state);
832
+ };
833
+ const removeProfileChangeListener = this.onProfileChange(profileState => {
834
+ const {
835
+ addListeners,
836
+ removeListeners,
837
+ middleware: experienceSelectionMiddleware
838
+ } = makeExperienceSelectMiddleware({
839
+ plugins: this.plugins,
840
+ experiences,
841
+ baseline,
842
+ profile: profileState.profile,
843
+ onChange: middleware => {
844
+ const overrideResult = buildOverrideMiddleware(middleware);
845
+ if (state !== null) {
846
+ setSelectedVariant(overrideResult(state));
847
+ }
848
+ }
849
+ });
850
+ addListeners();
851
+ middlewareChangeListeners.push(removeListeners);
852
+ const overrideResult = buildOverrideMiddleware(experienceSelectionMiddleware);
853
+ const hasVariants = experiences.map(experience => selectHasVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
854
+ const baseReturn = Object.assign({}, profileState, {
855
+ hasVariants,
856
+ baseline
857
+ });
858
+ const emptyReturn = Object.assign({}, baseReturn, {
859
+ experience: null,
860
+ variant: baseline,
861
+ variantIndex: 0,
862
+ audience: null,
863
+ isPersonalized: false,
864
+ profile: null,
865
+ error: null
866
+ });
867
+ if (profileState.status === 'loading') {
868
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
869
+ loading: true,
870
+ status: 'loading'
871
+ })));
872
+ return;
873
+ }
874
+ if (profileState.status === 'error') {
875
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
876
+ loading: false,
877
+ status: 'error',
878
+ error: profileState.error
879
+ })));
880
+ return;
881
+ }
882
+ const {
883
+ profile,
884
+ experiences: selectedExperiences
885
+ } = profileState;
886
+ if (!profile || !selectedExperiences) {
887
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
888
+ loading: false,
889
+ status: 'error',
890
+ error: new Error('No Profile or Selected Experiences were returned by the API')
891
+ })));
892
+ return;
893
+ }
894
+ if (this.useClientSideEvaluation) {
895
+ const _experience = selectExperience({
896
+ experiences,
897
+ profile
898
+ });
899
+ if (!_experience) {
900
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
901
+ loading: false,
902
+ status: 'success',
903
+ profile
904
+ })));
905
+ return;
906
+ }
907
+ const {
908
+ variant: _variant,
909
+ index
910
+ } = selectVariant$1({
911
+ baseline,
912
+ experience: _experience,
913
+ profile
914
+ });
915
+ setSelectedVariant(overrideResult(Object.assign({}, baseReturn, {
916
+ status: 'success',
917
+ loading: false,
918
+ error: null,
919
+ experience: _experience,
920
+ variant: _variant,
921
+ variantIndex: index,
922
+ audience: _experience.audience ? _experience.audience : null,
923
+ profile,
924
+ isPersonalized: true
925
+ })));
926
+ return;
927
+ }
928
+ const experience = experiences.find(experience => selectedExperiences.some(selectedExperience => selectedExperience.experienceId === experience.id));
929
+ const selectedExperience = selectedExperiences.find(({
930
+ experienceId
931
+ }) => experienceId === (experience == null ? void 0 : experience.id));
932
+ if (!experience || !selectedExperience) {
933
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
934
+ loading: false,
935
+ status: 'success',
936
+ profile
937
+ })));
938
+ return;
939
+ }
940
+ const baselineVariants = selectBaselineWithVariants(experience, baseline);
941
+ if (!baselineVariants) {
942
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
943
+ loading: false,
944
+ status: 'success',
945
+ profile
946
+ })));
947
+ return;
948
+ }
949
+ const {
950
+ variants
951
+ } = baselineVariants;
952
+ const variant = variants[selectedExperience.variantIndex - 1];
953
+ if (!variant) {
954
+ setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
955
+ loading: false,
956
+ status: 'success',
957
+ profile
958
+ })));
959
+ return;
960
+ }
961
+ setSelectedVariant(overrideResult(Object.assign({}, baseReturn, {
962
+ status: 'success',
963
+ loading: false,
964
+ error: null,
965
+ experience,
966
+ variant,
967
+ variantIndex: selectedExperience.variantIndex,
968
+ audience: experience.audience ? experience.audience : null,
969
+ profile,
970
+ isPersonalized: true
971
+ })));
972
+ });
973
+ return () => {
974
+ removeProfileChangeListener();
975
+ removeMiddlewareChangeListeners();
976
+ };
977
+ };
978
+ this.onIsInitialized = onIsInitialized => {
979
+ if (typeof onIsInitialized === 'function') {
980
+ if (this.isInitialized) {
981
+ onIsInitialized();
982
+ } else {
983
+ const detachOnReadyListener = this.instance.on('ready', () => {
984
+ this.isInitialized = true;
985
+ onIsInitialized();
986
+ detachOnReadyListener();
987
+ });
988
+ }
989
+ }
990
+ };
991
+ this.waitUntilInitialized = () => {
992
+ return new Promise(resolve => {
993
+ this.onIsInitialized(resolve);
994
+ });
995
+ };
996
+ this.onVisibilityChange = () => {
997
+ if (typeof document === 'undefined') {
998
+ return;
999
+ }
1000
+ document.addEventListener('visibilitychange', () => {
1001
+ if (document.visibilityState === 'hidden') {
1002
+ this.instance.dispatch({
1003
+ type: PAGE_HIDDEN
1004
+ });
1005
+ }
1006
+ });
1007
+ };
1008
+ this.useClientSideEvaluation = useClientSideEvaluation;
1009
+ if (ninetailedApiClientInstanceOrOptions instanceof NinetailedApiClient) {
1010
+ this.apiClient = ninetailedApiClientInstanceOrOptions;
1011
+ } else {
1012
+ const {
1013
+ clientId,
1014
+ environment,
1015
+ preview
1016
+ } = ninetailedApiClientInstanceOrOptions;
1017
+ this.clientId = clientId;
1018
+ this.environment = environment || 'main';
1019
+ this.apiClient = new NinetailedApiClient({
1020
+ clientId,
1021
+ environment,
1022
+ url,
1023
+ preview
1024
+ });
1025
+ }
1026
+ this.plugins = (plugins != null ? plugins : []).flat();
1027
+ this.plugins.forEach(plugin => {
1028
+ if (acceptsCredentials(plugin) && this.clientId && this.environment) {
1029
+ plugin.setCredentials({
1030
+ clientId: this.clientId,
1031
+ environment: this.environment
1032
+ });
1033
+ }
1034
+ });
1035
+ this._profileState = {
1036
+ status: 'loading',
1037
+ profile: null,
1038
+ experiences: null,
1039
+ error: null,
1040
+ from: 'api'
1041
+ };
1042
+ if (typeof onLog === 'function') {
1043
+ logger.addSink(new OnLogLogSink(onLog));
1044
+ }
1045
+ if (typeof onError === 'function') {
1046
+ logger.addSink(new OnErrorLogSink(onError));
1047
+ }
1048
+ this.eventBuilder = new EventBuilder(buildClientContext);
1049
+ this.logger = logger;
1050
+ this.ninetailedCorePlugin = new NinetailedCorePlugin({
1051
+ apiClient: this.apiClient,
1052
+ locale,
1053
+ requestTimeout,
1054
+ buildClientContext,
1055
+ onInitProfileId,
1056
+ ninetailed: this
1057
+ });
1058
+ this.instance = Analytics(Object.assign({
1059
+ app: 'ninetailed',
1060
+ plugins: [...this.plugins, this.ninetailedCorePlugin]
1061
+ }, storageImpl ? {
1062
+ storage: storageImpl
1063
+ } : {}));
1064
+ const _detachOnReadyListener = this.instance.on('ready', () => {
1065
+ this.isInitialized = true;
1066
+ logger.info('Ninetailed Experience.js SDK is completely initialized.');
1067
+ _detachOnReadyListener();
1068
+ });
1069
+
1070
+ // put in private method
1071
+ this.onProfileChange(profileState => {
1072
+ this._profileState = profileState;
1073
+ if (typeof window !== 'undefined') {
1074
+ window.ninetailed = Object.assign({}, window.ninetailed, {
1075
+ profile: this.profileState.profile,
1076
+ experiences: this.profileState.experiences
1077
+ });
1078
+ }
1079
+ });
1080
+ this.observedElements = new WeakMap();
1081
+ this.elementSeenObserver = new ElementSeenObserver({
1082
+ onElementSeen: this.onElementSeen.bind(this)
1083
+ });
1084
+ this.componentViewTrackingThreshold = componentViewTrackingThreshold;
1085
+ const hasPluginsInterestedInHiddenPage = this.plugins.some(isInterestedInHiddenPage);
1086
+ if (hasPluginsInterestedInHiddenPage) {
1087
+ this.onVisibilityChange();
1088
+ }
1089
+ this.registerWindowHandlers();
1090
+ }
1091
+ get pluginsWithCustomComponentViewThreshold() {
1092
+ return [this.ninetailedCorePlugin, ...this.plugins].filter(plugin => hasComponentViewTrackingThreshold(plugin));
1093
+ }
1094
+ get profileState() {
1095
+ return this._profileState;
1096
+ }
1097
+ buildOptions(options = {}) {
1098
+ return Object.assign({}, options);
1099
+ }
1100
+ registerWindowHandlers() {
1101
+ if (typeof window !== 'undefined') {
1102
+ window.ninetailed = Object.assign({}, window.ninetailed, {
1103
+ page: this.page.bind(this),
1104
+ track: this.track.bind(this),
1105
+ identify: this.identify.bind(this),
1106
+ reset: this.reset.bind(this),
1107
+ debug: this.debug.bind(this),
1108
+ profile: this.profileState.profile
1109
+ });
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ const selectVariant = (baseline, variants, {
1115
+ status,
1116
+ profile,
1117
+ error
1118
+ }, options = {
1119
+ holdout: -1
1120
+ }) => {
1121
+ if (status === 'loading') {
1122
+ return {
1123
+ loading: true,
1124
+ variant: Object.assign({}, baseline, {
1125
+ id: 'baseline',
1126
+ audience: {
1127
+ id: 'baseline'
1128
+ }
1129
+ }),
1130
+ audience: {
1131
+ id: 'baseline'
1132
+ },
1133
+ isPersonalized: false,
1134
+ error: null
1135
+ };
1136
+ }
1137
+ if (status === 'error') {
1138
+ return {
1139
+ loading: false,
1140
+ variant: Object.assign({}, baseline, {
1141
+ id: 'baseline',
1142
+ audience: {
1143
+ id: 'baseline'
1144
+ }
1145
+ }),
1146
+ audience: {
1147
+ id: 'baseline'
1148
+ },
1149
+ isPersonalized: false,
1150
+ error: error
1151
+ };
1152
+ }
1153
+ const variant = variants.find(variant => {
1154
+ var _profile$audiences, _variant$audience;
1155
+ return profile == null || (_profile$audiences = profile.audiences) == null ? void 0 : _profile$audiences.includes((_variant$audience = variant.audience) == null ? void 0 : _variant$audience.id);
1156
+ });
1157
+ if (variant) {
1158
+ if (options != null && options.holdout || -1 > ((profile == null ? void 0 : profile.random) || 0)) {
1159
+ return {
1160
+ loading: false,
1161
+ variant: Object.assign({}, baseline, {
1162
+ audience: {
1163
+ id: 'baseline'
1164
+ }
1165
+ }),
1166
+ audience: Object.assign({}, variant.audience, {
1167
+ id: variant.audience.id
1168
+ }),
1169
+ isPersonalized: false,
1170
+ error: null
1171
+ };
1172
+ }
1173
+ return {
1174
+ loading: false,
1175
+ variant,
1176
+ audience: Object.assign({}, variant.audience, {
1177
+ id: variant.audience.id
1178
+ }),
1179
+ isPersonalized: true,
1180
+ error: null
1181
+ };
1182
+ }
1183
+
1184
+ /**
1185
+ * There was no matching audience found.
1186
+ */
1187
+ return {
1188
+ loading: false,
1189
+ variant: Object.assign({}, baseline, {
1190
+ id: 'baseline',
1191
+ audience: {
1192
+ id: 'baseline'
1193
+ }
1194
+ }),
1195
+ audience: {
1196
+ id: 'baseline'
1197
+ },
1198
+ isPersonalized: false,
1199
+ error: null
1200
+ };
1201
+ };
1202
+
1203
+ export { ANONYMOUS_ID, COMPONENT, COMPONENT_START, CONSENT, DEBUG_FLAG, EMPTY_MERGE_ID, EXPERIENCES_FALLBACK_CACHE, HAS_SEEN_STICKY_COMPONENT, LEGACY_ANONYMOUS_ID, Ninetailed, NinetailedCorePlugin, OnChangeEmitter, PAGE_HIDDEN, PLUGIN_NAME, PROFILE_CHANGE, PROFILE_FALLBACK_CACHE, PROFILE_RESET, SET_ENABLED_FEATURES, buildClientNinetailedRequestContext, decodeExperienceVariantsMap, makeExperienceSelectMiddleware, selectPluginsHavingExperienceSelectionMiddleware, selectPluginsHavingOnChangeEmitter, selectVariant };