@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.
- package/index.cjs.d.ts +1 -0
- package/{index.cjs → index.cjs.js} +697 -375
- package/index.esm.js +1203 -0
- package/package.json +6 -9
- package/src/index.d.ts +1 -1
- package/src/lib/ElementSeenObserver.d.ts +1 -1
- package/src/lib/Ninetailed.d.ts +19 -5
- package/src/lib/NinetailedCorePlugin/NinetailedCorePlugin.d.ts +69 -0
- package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/index.d.ts +1 -1
- package/src/lib/constants.d.ts +1 -2
- package/src/lib/experience/makeExperienceSelectMiddleware.d.ts +9 -9
- package/src/lib/guards/hasComponentViewTrackingThreshold.d.ts +3 -0
- package/src/lib/guards/hasExperienceSelectionMiddleware.d.ts +1 -1
- package/src/lib/plugins/selectPluginsHavingExperienceSelectionMiddleware.d.ts +2 -2
- package/src/lib/plugins/selectPluginsHavingOnChangeEmitter.d.ts +1 -1
- package/src/lib/types/OnSelectVariant.d.ts +51 -0
- package/src/lib/types/index.d.ts +9 -9
- package/src/lib/types/interfaces/HasComponentViewTrackingThreshold.d.ts +3 -0
- package/src/lib/types/interfaces/HasExperienceSelectionMiddleware.d.ts +10 -10
- package/src/lib/types/interfaces/InterestedInHiddenPage.d.ts +1 -1
- package/src/lib/types/interfaces/InterestedInProfileChange.d.ts +1 -1
- package/src/lib/types/interfaces/InterestedInSeenElements.d.ts +1 -3
- package/src/lib/utils/EventBuilder.d.ts +221 -0
- package/src/lib/utils/noop.d.ts +1 -0
- package/index.js +0 -886
- package/src/lib/ninetailedCorePlugin/ninetailedCorePlugin.d.ts +0 -17
- package/src/lib/test-helpers/intersection-observer-test-helper.d.ts +0 -2
- package/src/lib/types/ElementSeenPayload.d.ts +0 -82
- package/src/lib/types/EventHandler.d.ts +0 -6
- package/src/lib/types/NinetailedPlugin.d.ts +0 -11
- package/src/lib/types/TrackingProperties.d.ts +0 -35
- /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/build-context.d.ts +0 -0
- /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/build-locale.d.ts +0 -0
- /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/Events/index.d.ts +0 -0
- /package/src/lib/{ninetailedCorePlugin → NinetailedCorePlugin}/constants.d.ts +0 -0
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var experience_jsShared = require('@ninetailed/experience.js-shared');
|
|
6
|
+
var experience_jsPluginAnalytics = require('@ninetailed/experience.js-plugin-analytics');
|
|
6
7
|
var Analytics = require('analytics');
|
|
7
|
-
var
|
|
8
|
+
var uuid = require('uuid');
|
|
8
9
|
|
|
9
10
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
11
|
|
|
11
12
|
var Analytics__default = /*#__PURE__*/_interopDefaultLegacy(Analytics);
|
|
12
13
|
|
|
13
|
-
const HAS_SEEN_COMPONENT = 'has_seen_component';
|
|
14
|
-
const HAS_SEEN_ELEMENT = 'has_seen_element';
|
|
15
14
|
const COMPONENT = 'component';
|
|
16
15
|
const COMPONENT_START = 'componentStart';
|
|
17
16
|
const PAGE_HIDDEN = 'page_hidden';
|
|
17
|
+
const HAS_SEEN_STICKY_COMPONENT = 'sticky_component_view';
|
|
18
18
|
|
|
19
19
|
/******************************************************************************
|
|
20
20
|
Copyright (c) Microsoft Corporation.
|
|
@@ -104,98 +104,87 @@ const CONSENT = '__nt-consent__';
|
|
|
104
104
|
const SET_ENABLED_FEATURES = 'set-enabled-features';
|
|
105
105
|
const EMPTY_MERGE_ID = 'nt:empty-merge-id';
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
var _a;
|
|
108
|
+
const PLUGIN_NAME = 'ninetailed:core';
|
|
108
109
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
_instance.storage.setItem(ANONYMOUS_ID, profile.id);
|
|
142
|
-
_instance.storage.setItem(PROFILE_FALLBACK_CACHE, profile);
|
|
143
|
-
_instance.storage.setItem(EXPERIENCES_FALLBACK_CACHE, experiences);
|
|
144
|
-
experience_jsShared.logger.debug('Profile from api: ', profile);
|
|
145
|
-
experience_jsShared.logger.debug('Experiences from api: ', experiences);
|
|
146
|
-
_instance.dispatch({
|
|
147
|
-
type: PROFILE_CHANGE,
|
|
148
|
-
profile,
|
|
149
|
-
experiences
|
|
150
|
-
});
|
|
151
|
-
yield delay(20);
|
|
152
|
-
return {
|
|
153
|
-
success: true
|
|
154
|
-
};
|
|
155
|
-
} catch (error) {
|
|
156
|
-
experience_jsShared.logger.debug('An error occurred during flushing the events: ', error);
|
|
157
|
-
const fallbackProfile = _instance.storage.getItem(PROFILE_FALLBACK_CACHE);
|
|
158
|
-
const fallbackExperiences = _instance.storage.getItem(EXPERIENCES_FALLBACK_CACHE) || [];
|
|
159
|
-
if (fallbackProfile) {
|
|
160
|
-
experience_jsShared.logger.debug('Found a fallback profile - will use this.');
|
|
161
|
-
_instance.dispatch({
|
|
162
|
-
type: PROFILE_CHANGE,
|
|
163
|
-
profile: fallbackProfile,
|
|
164
|
-
experiences: fallbackExperiences
|
|
165
|
-
});
|
|
166
|
-
} else {
|
|
167
|
-
experience_jsShared.logger.debug('No fallback profile found - setting profile to null.');
|
|
168
|
-
_instance.dispatch({
|
|
169
|
-
type: PROFILE_CHANGE,
|
|
170
|
-
profile: null,
|
|
171
|
-
experiences: fallbackExperiences,
|
|
172
|
-
error
|
|
110
|
+
class NinetailedCorePlugin extends experience_jsPluginAnalytics.NinetailedAnalyticsPlugin {
|
|
111
|
+
constructor({
|
|
112
|
+
apiClient,
|
|
113
|
+
locale,
|
|
114
|
+
ninetailed,
|
|
115
|
+
onInitProfileId,
|
|
116
|
+
buildClientContext
|
|
117
|
+
}) {
|
|
118
|
+
super();
|
|
119
|
+
this.name = PLUGIN_NAME;
|
|
120
|
+
this.queue = [];
|
|
121
|
+
this.enabledFeatures = Object.values(experience_jsShared.FEATURES);
|
|
122
|
+
this.buildContext = buildClientNinetailedRequestContext;
|
|
123
|
+
this[_a] = ({
|
|
124
|
+
payload
|
|
125
|
+
}) => __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const ctx = this.buildContext();
|
|
127
|
+
return this.enqueueEvent(experience_jsShared.buildComponentViewEvent({
|
|
128
|
+
messageId: payload.meta.rid,
|
|
129
|
+
timestamp: payload.meta.ts,
|
|
130
|
+
componentId: payload.componentId,
|
|
131
|
+
experienceId: payload.experienceId,
|
|
132
|
+
variantIndex: payload.variantIndex,
|
|
133
|
+
ctx
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
this.methods = {
|
|
137
|
+
reset: (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
experience_jsShared.logger.debug('Resetting profile.');
|
|
139
|
+
const instance = args[args.length - 1];
|
|
140
|
+
instance.dispatch({
|
|
141
|
+
type: PROFILE_RESET
|
|
173
142
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
143
|
+
instance.storage.removeItem(ANONYMOUS_ID);
|
|
144
|
+
instance.storage.removeItem(PROFILE_FALLBACK_CACHE);
|
|
145
|
+
instance.storage.removeItem(EXPERIENCES_FALLBACK_CACHE);
|
|
146
|
+
experience_jsShared.logger.debug('Removed old profile data from localstorage.');
|
|
147
|
+
if (typeof this.onInitProfileId === 'function') {
|
|
148
|
+
const profileId = yield this.onInitProfileId(undefined);
|
|
149
|
+
if (typeof profileId === 'string') {
|
|
150
|
+
instance.storage.setItem(ANONYMOUS_ID, profileId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
yield this.ninetailed.track('nt_reset');
|
|
154
|
+
experience_jsShared.logger.info('Profile reset successful.');
|
|
155
|
+
yield delay(10);
|
|
156
|
+
}),
|
|
157
|
+
debug: (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
const enabled = args[0];
|
|
159
|
+
const instance = args[args.length - 1];
|
|
160
|
+
const consoleLogSink = new experience_jsShared.ConsoleLogSink();
|
|
161
|
+
if (enabled) {
|
|
162
|
+
instance.storage.setItem(DEBUG_FLAG, true);
|
|
163
|
+
experience_jsShared.logger.addSink(consoleLogSink);
|
|
164
|
+
experience_jsShared.logger.info('Debug mode enabled.');
|
|
165
|
+
} else {
|
|
166
|
+
instance.storage.removeItem(DEBUG_FLAG);
|
|
167
|
+
experience_jsShared.logger.info('Debug mode disabled.');
|
|
168
|
+
experience_jsShared.logger.removeSink(consoleLogSink.name);
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
this.flush = asyncThrottle(this._flush.bind(this));
|
|
173
|
+
if (onInitProfileId) {
|
|
174
|
+
this.onInitProfileId = onInitProfileId;
|
|
178
175
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
queue = experience_jsShared.unionBy([event], queue, 'messageId');
|
|
182
|
-
});
|
|
183
|
-
const abortNonClientEvents = ({
|
|
184
|
-
abort,
|
|
185
|
-
payload
|
|
186
|
-
}) => {
|
|
187
|
-
if (typeof window !== 'object') {
|
|
188
|
-
return abort();
|
|
176
|
+
if (buildClientContext) {
|
|
177
|
+
this.buildContext = buildClientContext;
|
|
189
178
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
_instance = instance;
|
|
179
|
+
this.apiClient = apiClient;
|
|
180
|
+
this.ninetailed = ninetailed;
|
|
181
|
+
this.locale = locale;
|
|
182
|
+
}
|
|
183
|
+
initialize({
|
|
184
|
+
instance
|
|
185
|
+
}) {
|
|
186
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
187
|
+
this._instance = instance;
|
|
199
188
|
if (instance.storage.getItem(DEBUG_FLAG)) {
|
|
200
189
|
experience_jsShared.logger.addSink(new experience_jsShared.ConsoleLogSink());
|
|
201
190
|
experience_jsShared.logger.info('Ninetailed Debug Mode is enabled.');
|
|
@@ -207,8 +196,8 @@ const ninetailedCorePlugin = ({
|
|
|
207
196
|
instance.storage.setItem(ANONYMOUS_ID, legacyAnonymousId);
|
|
208
197
|
instance.storage.removeItem(LEGACY_ANONYMOUS_ID);
|
|
209
198
|
}
|
|
210
|
-
if (typeof onInitProfileId === 'function') {
|
|
211
|
-
const profileId = yield onInitProfileId(instance.storage.getItem(ANONYMOUS_ID));
|
|
199
|
+
if (typeof this.onInitProfileId === 'function') {
|
|
200
|
+
const profileId = yield this.onInitProfileId(instance.storage.getItem(ANONYMOUS_ID));
|
|
212
201
|
if (typeof profileId === 'string') {
|
|
213
202
|
instance.storage.setItem(ANONYMOUS_ID, profileId);
|
|
214
203
|
}
|
|
@@ -216,109 +205,181 @@ const ninetailedCorePlugin = ({
|
|
|
216
205
|
instance.on(SET_ENABLED_FEATURES, ({
|
|
217
206
|
payload
|
|
218
207
|
}) => {
|
|
219
|
-
enabledFeatures = payload.features || [];
|
|
208
|
+
this.enabledFeatures = payload.features || [];
|
|
220
209
|
});
|
|
221
210
|
experience_jsShared.logger.debug('Ninetailed Core plugin initialized.');
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
pageStart(params) {
|
|
214
|
+
return this.abortNonClientEvents(params);
|
|
215
|
+
}
|
|
216
|
+
page({
|
|
217
|
+
payload
|
|
218
|
+
}) {
|
|
219
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
230
220
|
experience_jsShared.logger.info('Sending Page event.');
|
|
231
|
-
const ctx = buildContext();
|
|
232
|
-
return enqueueEvent(experience_jsShared.buildPageEvent({
|
|
221
|
+
const ctx = this.buildContext();
|
|
222
|
+
return this.enqueueEvent(experience_jsShared.buildPageEvent({
|
|
233
223
|
messageId: payload.meta.rid,
|
|
234
224
|
timestamp: payload.meta.ts,
|
|
235
225
|
properties: payload.properties,
|
|
236
226
|
ctx
|
|
237
227
|
}));
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
trackStart(params) {
|
|
231
|
+
return this.abortNonClientEvents(params);
|
|
232
|
+
}
|
|
233
|
+
track({
|
|
234
|
+
payload
|
|
235
|
+
}) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
245
237
|
experience_jsShared.logger.info('Sending Track event.');
|
|
246
|
-
const ctx = buildContext();
|
|
247
|
-
return enqueueEvent(experience_jsShared.buildTrackEvent({
|
|
238
|
+
const ctx = this.buildContext();
|
|
239
|
+
return this.enqueueEvent(experience_jsShared.buildTrackEvent({
|
|
248
240
|
messageId: payload.meta.rid,
|
|
249
241
|
timestamp: payload.meta.ts,
|
|
250
242
|
event: payload.event,
|
|
251
243
|
properties: payload.properties,
|
|
252
244
|
ctx
|
|
253
245
|
}));
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
identifyStart(params) {
|
|
249
|
+
return this.abortNonClientEvents(params);
|
|
250
|
+
}
|
|
251
|
+
identify({
|
|
252
|
+
payload
|
|
253
|
+
}) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
261
255
|
experience_jsShared.logger.info('Sending Identify event.');
|
|
262
|
-
const ctx = buildContext();
|
|
256
|
+
const ctx = this.buildContext();
|
|
263
257
|
if (payload.userId === EMPTY_MERGE_ID && (!payload.traits || typeof payload.traits === 'object' && Object.keys(payload.traits).length === 0)) {
|
|
264
258
|
experience_jsShared.logger.info('Skipping Identify event as no userId and no traits are set.');
|
|
265
259
|
return;
|
|
266
260
|
}
|
|
267
|
-
return enqueueEvent(experience_jsShared.buildIdentifyEvent({
|
|
261
|
+
return this.enqueueEvent(experience_jsShared.buildIdentifyEvent({
|
|
268
262
|
messageId: payload.meta.rid,
|
|
269
263
|
timestamp: payload.meta.ts,
|
|
270
264
|
traits: payload.traits,
|
|
271
265
|
userId: payload.userId === EMPTY_MERGE_ID ? '' : payload.userId,
|
|
272
266
|
ctx
|
|
273
267
|
}));
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
getComponentViewTrackingThreshold() {
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
onTrackExperience(properties) {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
if (properties.experience.sticky) {
|
|
276
|
+
yield this.instance.dispatch({
|
|
277
|
+
type: HAS_SEEN_STICKY_COMPONENT,
|
|
278
|
+
componentId: properties.selectedVariant.id,
|
|
279
|
+
experienceId: properties.experience.id,
|
|
280
|
+
variantIndex: properties.selectedVariantIndex
|
|
281
|
+
});
|
|
282
|
+
this.flush();
|
|
281
283
|
}
|
|
282
|
-
return
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
284
|
+
return Promise.resolve();
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
onTrackComponent() {
|
|
288
|
+
return Promise.resolve();
|
|
289
|
+
}
|
|
290
|
+
setItemStart({
|
|
291
|
+
abort,
|
|
292
|
+
payload
|
|
293
|
+
}) {
|
|
294
|
+
if (![ANONYMOUS_ID, DEBUG_FLAG, PROFILE_FALLBACK_CACHE, EXPERIENCES_FALLBACK_CACHE, CONSENT].includes(payload.key)) {
|
|
295
|
+
return abort();
|
|
296
|
+
}
|
|
297
|
+
return payload;
|
|
298
|
+
}
|
|
299
|
+
enqueueEvent(event) {
|
|
300
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
301
|
+
this.queue = experience_jsShared.unionBy([event], this.queue, 'messageId');
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
abortNonClientEvents({
|
|
305
|
+
abort,
|
|
306
|
+
payload
|
|
307
|
+
}) {
|
|
308
|
+
if (typeof window !== 'object') {
|
|
309
|
+
return abort();
|
|
310
|
+
}
|
|
311
|
+
return payload;
|
|
312
|
+
}
|
|
313
|
+
get instance() {
|
|
314
|
+
if (!this._instance) {
|
|
315
|
+
throw new Error('Ninetailed Core plugin not initialized.');
|
|
316
|
+
}
|
|
317
|
+
return this._instance;
|
|
318
|
+
}
|
|
319
|
+
_flush() {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
const events = Object.assign([], this.queue);
|
|
322
|
+
experience_jsShared.logger.info('Start flushing events.');
|
|
323
|
+
this.queue = [];
|
|
324
|
+
if (!events.length) {
|
|
325
|
+
return {
|
|
326
|
+
success: true
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const anonymousId = this.instance.storage.getItem(ANONYMOUS_ID);
|
|
331
|
+
const {
|
|
332
|
+
profile,
|
|
333
|
+
experiences
|
|
334
|
+
} = yield this.apiClient.upsertProfile({
|
|
335
|
+
profileId: anonymousId,
|
|
336
|
+
events
|
|
337
|
+
}, {
|
|
338
|
+
locale: this.locale,
|
|
339
|
+
enabledFeatures: this.enabledFeatures
|
|
290
340
|
});
|
|
291
|
-
instance.storage.
|
|
292
|
-
instance.storage.
|
|
293
|
-
instance.storage.
|
|
294
|
-
experience_jsShared.logger.debug('
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
yield
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
341
|
+
this.instance.storage.setItem(ANONYMOUS_ID, profile.id);
|
|
342
|
+
this.instance.storage.setItem(PROFILE_FALLBACK_CACHE, profile);
|
|
343
|
+
this.instance.storage.setItem(EXPERIENCES_FALLBACK_CACHE, experiences);
|
|
344
|
+
experience_jsShared.logger.debug('Profile from api: ', profile);
|
|
345
|
+
experience_jsShared.logger.debug('Experiences from api: ', experiences);
|
|
346
|
+
this.instance.dispatch({
|
|
347
|
+
type: PROFILE_CHANGE,
|
|
348
|
+
profile,
|
|
349
|
+
experiences
|
|
350
|
+
});
|
|
351
|
+
yield delay(20);
|
|
352
|
+
return {
|
|
353
|
+
success: true
|
|
354
|
+
};
|
|
355
|
+
} catch (error) {
|
|
356
|
+
experience_jsShared.logger.debug('An error occurred during flushing the events: ', error);
|
|
357
|
+
const fallbackProfile = this.instance.storage.getItem(PROFILE_FALLBACK_CACHE);
|
|
358
|
+
const fallbackExperiences = this.instance.storage.getItem(EXPERIENCES_FALLBACK_CACHE) || [];
|
|
359
|
+
if (fallbackProfile) {
|
|
360
|
+
experience_jsShared.logger.debug('Found a fallback profile - will use this.');
|
|
361
|
+
this.instance.dispatch({
|
|
362
|
+
type: PROFILE_CHANGE,
|
|
363
|
+
profile: fallbackProfile,
|
|
364
|
+
experiences: fallbackExperiences
|
|
365
|
+
});
|
|
313
366
|
} else {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
367
|
+
experience_jsShared.logger.debug('No fallback profile found - setting profile to null.');
|
|
368
|
+
this.instance.dispatch({
|
|
369
|
+
type: PROFILE_CHANGE,
|
|
370
|
+
profile: null,
|
|
371
|
+
experiences: fallbackExperiences,
|
|
372
|
+
error
|
|
373
|
+
});
|
|
317
374
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
375
|
+
return {
|
|
376
|
+
success: false
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
_a = HAS_SEEN_STICKY_COMPONENT;
|
|
322
383
|
|
|
323
384
|
class ElementSeenObserver {
|
|
324
385
|
constructor(_options) {
|
|
@@ -336,23 +397,29 @@ class ElementSeenObserver {
|
|
|
336
397
|
target
|
|
337
398
|
} = entry;
|
|
338
399
|
if (isIntersecting) {
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
400
|
+
const delays = this._elementDelays.get(target);
|
|
401
|
+
delays === null || delays === void 0 ? void 0 : delays.forEach(delay => {
|
|
402
|
+
const timeOut = window.setTimeout(() => {
|
|
403
|
+
this._options.onElementSeen(target, delay);
|
|
404
|
+
}, delay);
|
|
405
|
+
const currentTimers = this._intersectionTimers.get(target) || [];
|
|
406
|
+
this._intersectionTimers.set(target, [...currentTimers, timeOut]);
|
|
407
|
+
});
|
|
344
408
|
} else {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
409
|
+
const timeOuts = this._intersectionTimers.get(target);
|
|
410
|
+
timeOuts === null || timeOuts === void 0 ? void 0 : timeOuts.forEach(timeOut => {
|
|
411
|
+
if (typeof timeOut !== 'undefined') {
|
|
412
|
+
window.clearTimeout(timeOut);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
349
415
|
}
|
|
350
416
|
});
|
|
351
417
|
}
|
|
352
418
|
observe(element, options) {
|
|
353
|
-
var _a
|
|
354
|
-
this._elementDelays.
|
|
355
|
-
(
|
|
419
|
+
var _a;
|
|
420
|
+
const delays = this._elementDelays.get(element) || [];
|
|
421
|
+
this._elementDelays.set(element, Array.from(new Set([...delays, (options === null || options === void 0 ? void 0 : options.delay) || 0])));
|
|
422
|
+
(_a = this._intersectionObserver) === null || _a === void 0 ? void 0 : _a.observe(element);
|
|
356
423
|
}
|
|
357
424
|
unobserve(element) {
|
|
358
425
|
var _a;
|
|
@@ -368,6 +435,193 @@ const isInterestedInHiddenPage = arg => {
|
|
|
368
435
|
return typeof arg === 'object' && arg !== null && PAGE_HIDDEN in arg && typeof arg[PAGE_HIDDEN] === 'function';
|
|
369
436
|
};
|
|
370
437
|
|
|
438
|
+
const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
439
|
+
return encodedExperienceVariantsMap.split(',').map(experienceIdWithVariant => {
|
|
440
|
+
const [experienceId, _variantIndex] = experienceIdWithVariant.split('=');
|
|
441
|
+
const variantIndex = parseInt(_variantIndex);
|
|
442
|
+
if (!experienceId || !variantIndex) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
experienceId,
|
|
447
|
+
variantIndex
|
|
448
|
+
};
|
|
449
|
+
}).filter(x => !!x).reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
450
|
+
[curr.experienceId]: curr.variantIndex
|
|
451
|
+
}), {});
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
class OnChangeEmitter {
|
|
455
|
+
constructor() {
|
|
456
|
+
this.onChangeListeners = [];
|
|
457
|
+
}
|
|
458
|
+
addListener(listener) {
|
|
459
|
+
this.onChangeListeners.push(listener);
|
|
460
|
+
return () => {
|
|
461
|
+
this.removeOnChangeListener(listener);
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
invokeListeners() {
|
|
465
|
+
this.onChangeListeners.forEach(listener => listener());
|
|
466
|
+
}
|
|
467
|
+
removeOnChangeListener(listener) {
|
|
468
|
+
this.onChangeListeners = this.onChangeListeners.filter(l => l !== listener);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const hasOnChangeEmitter = arg => {
|
|
473
|
+
return typeof arg === 'object' && arg !== null && 'onChangeEmitter' in arg && typeof arg.onChangeEmitter === 'object' && arg.onChangeEmitter !== null && arg.onChangeEmitter.constructor === OnChangeEmitter;
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const selectPluginsHavingOnChangeEmitter = plugins => {
|
|
477
|
+
const filteredPlugins = [];
|
|
478
|
+
for (const plugin of plugins) {
|
|
479
|
+
if (hasOnChangeEmitter(plugin)) {
|
|
480
|
+
filteredPlugins.push(plugin);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return filteredPlugins;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const hasExperienceSelectionMiddleware = arg => {
|
|
487
|
+
return typeof arg === 'object' && arg !== null && 'getExperienceSelectionMiddleware' in arg && typeof arg.getExperienceSelectionMiddleware === 'function';
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const selectPluginsHavingExperienceSelectionMiddleware = plugins => {
|
|
491
|
+
const filteredPlugins = [];
|
|
492
|
+
for (const plugin of plugins) {
|
|
493
|
+
if (hasExperienceSelectionMiddleware(plugin)) {
|
|
494
|
+
filteredPlugins.push(plugin);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return filteredPlugins;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
const createPassThroughMiddleware = () => {
|
|
501
|
+
return ({
|
|
502
|
+
experience,
|
|
503
|
+
variant,
|
|
504
|
+
variantIndex
|
|
505
|
+
}) => {
|
|
506
|
+
return {
|
|
507
|
+
experience,
|
|
508
|
+
variant,
|
|
509
|
+
variantIndex
|
|
510
|
+
};
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
const makeExperienceSelectMiddleware = ({
|
|
514
|
+
plugins,
|
|
515
|
+
onChange,
|
|
516
|
+
experiences,
|
|
517
|
+
baseline,
|
|
518
|
+
profile
|
|
519
|
+
}) => {
|
|
520
|
+
let removeChangeListeners = [];
|
|
521
|
+
const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
|
|
522
|
+
const prepareMiddleware = () => {
|
|
523
|
+
if (profile === null) {
|
|
524
|
+
return createPassThroughMiddleware();
|
|
525
|
+
}
|
|
526
|
+
const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
|
|
527
|
+
const middlewareFunctions = pluginsWithMiddleware.map(plugin => plugin.getExperienceSelectionMiddleware({
|
|
528
|
+
experiences,
|
|
529
|
+
baseline
|
|
530
|
+
}));
|
|
531
|
+
return experience_jsShared.pipe(...middlewareFunctions);
|
|
532
|
+
};
|
|
533
|
+
const middleware = prepareMiddleware();
|
|
534
|
+
const addListeners = () => {
|
|
535
|
+
removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
|
|
536
|
+
const listener = () => {
|
|
537
|
+
onChange(middleware);
|
|
538
|
+
};
|
|
539
|
+
return plugin.onChangeEmitter.addListener(listener);
|
|
540
|
+
});
|
|
541
|
+
};
|
|
542
|
+
const removeListeners = () => {
|
|
543
|
+
removeChangeListeners.forEach(listener => listener());
|
|
544
|
+
};
|
|
545
|
+
return {
|
|
546
|
+
addListeners,
|
|
547
|
+
removeListeners,
|
|
548
|
+
middleware
|
|
549
|
+
};
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
class EventBuilder {
|
|
553
|
+
constructor(buildRequestContext) {
|
|
554
|
+
this.buildRequestContext = buildRequestContext || buildClientNinetailedRequestContext;
|
|
555
|
+
}
|
|
556
|
+
page(properties, data) {
|
|
557
|
+
return experience_jsShared.buildPageEvent(Object.assign(Object.assign({
|
|
558
|
+
messageId: (data === null || data === void 0 ? void 0 : data.messageId) || uuid.v4()
|
|
559
|
+
}, data), {
|
|
560
|
+
timestamp: Date.now(),
|
|
561
|
+
properties: properties || {},
|
|
562
|
+
ctx: this.buildRequestContext()
|
|
563
|
+
}));
|
|
564
|
+
}
|
|
565
|
+
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), {
|
|
570
|
+
event,
|
|
571
|
+
properties: properties || {},
|
|
572
|
+
ctx: this.buildRequestContext()
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
575
|
+
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), {
|
|
580
|
+
traits: traits || {},
|
|
581
|
+
userId: userId || '',
|
|
582
|
+
ctx: this.buildRequestContext()
|
|
583
|
+
}));
|
|
584
|
+
}
|
|
585
|
+
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), {
|
|
590
|
+
componentId,
|
|
591
|
+
experienceId: experienceId || '',
|
|
592
|
+
variantIndex: variantIndex || 0,
|
|
593
|
+
ctx: this.buildRequestContext()
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const hasComponentViewTrackingThreshold = arg => {
|
|
599
|
+
return typeof arg === 'object' && arg !== null && 'getComponentViewTrackingThreshold' in arg && typeof arg['getComponentViewTrackingThreshold'] === 'function';
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
const buildOverrideMiddleware = experienceSelectionMiddleware => _a => {
|
|
603
|
+
var {
|
|
604
|
+
experience: originalExperience,
|
|
605
|
+
variant: originalVariant,
|
|
606
|
+
variantIndex: originalVariantIndex
|
|
607
|
+
} = _a,
|
|
608
|
+
other = __rest(_a, ["experience", "variant", "variantIndex"]);
|
|
609
|
+
const {
|
|
610
|
+
experience,
|
|
611
|
+
variant,
|
|
612
|
+
variantIndex
|
|
613
|
+
} = experienceSelectionMiddleware({
|
|
614
|
+
experience: originalExperience,
|
|
615
|
+
variant: originalVariant,
|
|
616
|
+
variantIndex: originalVariantIndex
|
|
617
|
+
});
|
|
618
|
+
return Object.assign(Object.assign({}, other), {
|
|
619
|
+
audience: (experience === null || experience === void 0 ? void 0 : experience.audience) ? experience.audience : null,
|
|
620
|
+
experience,
|
|
621
|
+
variant,
|
|
622
|
+
variantIndex
|
|
623
|
+
});
|
|
624
|
+
};
|
|
371
625
|
class Ninetailed {
|
|
372
626
|
constructor(ninetailedApiClientInstanceOrOptions, {
|
|
373
627
|
plugins,
|
|
@@ -379,7 +633,8 @@ class Ninetailed {
|
|
|
379
633
|
buildClientContext,
|
|
380
634
|
onInitProfileId,
|
|
381
635
|
componentViewTrackingThreshold = 2000,
|
|
382
|
-
storageImpl
|
|
636
|
+
storageImpl,
|
|
637
|
+
useClientSideEvaluation = false
|
|
383
638
|
} = {}) {
|
|
384
639
|
this.isInitialized = false;
|
|
385
640
|
this.page = (data, options) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -416,18 +671,90 @@ class Ninetailed {
|
|
|
416
671
|
throw error;
|
|
417
672
|
}
|
|
418
673
|
});
|
|
674
|
+
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
675
|
+
try {
|
|
676
|
+
const result = experience_jsShared.Traits.default({}).safeParse(traits);
|
|
677
|
+
if (!result.success) {
|
|
678
|
+
throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
|
|
679
|
+
}
|
|
680
|
+
yield this.waitUntilInitialized();
|
|
681
|
+
yield this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, this.buildOptions(options));
|
|
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] "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;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
this.batch = events => __awaiter(this, void 0, void 0, function* () {
|
|
692
|
+
try {
|
|
693
|
+
yield this.waitUntilInitialized();
|
|
694
|
+
const promises = events.map(event => {
|
|
695
|
+
if (experience_jsShared.isPageViewEvent(event)) {
|
|
696
|
+
return this.instance.page(event.properties);
|
|
697
|
+
}
|
|
698
|
+
if (experience_jsShared.isTrackEvent(event)) {
|
|
699
|
+
return this.instance.track(event.event, event.properties);
|
|
700
|
+
}
|
|
701
|
+
if (experience_jsShared.isIdentifyEvent(event)) {
|
|
702
|
+
return this.instance.identify(event.userId || EMPTY_MERGE_ID, event.traits);
|
|
703
|
+
}
|
|
704
|
+
if (experience_jsShared.isComponentViewEvent(event)) {
|
|
705
|
+
return this.instance.dispatch({
|
|
706
|
+
experienceId: event.experienceId,
|
|
707
|
+
componentId: event.componentId,
|
|
708
|
+
variantIndex: event.variantIndex,
|
|
709
|
+
type: HAS_SEEN_STICKY_COMPONENT
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
return Promise.resolve();
|
|
713
|
+
});
|
|
714
|
+
yield Promise.all(promises);
|
|
715
|
+
return this.ninetailedCorePlugin.flush();
|
|
716
|
+
} catch (error) {
|
|
717
|
+
experience_jsShared.logger.error(error);
|
|
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;
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
this.trackStickyComponentView = ({
|
|
725
|
+
experienceId,
|
|
726
|
+
componentId,
|
|
727
|
+
variantIndex
|
|
728
|
+
}) => __awaiter(this, void 0, void 0, function* () {
|
|
729
|
+
try {
|
|
730
|
+
yield this.waitUntilInitialized();
|
|
731
|
+
yield this.instance.dispatch({
|
|
732
|
+
experienceId,
|
|
733
|
+
componentId,
|
|
734
|
+
variantIndex,
|
|
735
|
+
type: HAS_SEEN_STICKY_COMPONENT
|
|
736
|
+
});
|
|
737
|
+
return this.ninetailedCorePlugin.flush();
|
|
738
|
+
} catch (error) {
|
|
739
|
+
experience_jsShared.logger.error(error);
|
|
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;
|
|
744
|
+
}
|
|
745
|
+
});
|
|
419
746
|
/**
|
|
420
747
|
* @deprecated The legacy datamodel is not recommended anymore
|
|
421
748
|
* Will be removed in the next version of the SDK
|
|
422
749
|
*/
|
|
423
750
|
this.trackHasSeenComponent = properties => __awaiter(this, void 0, void 0, function* () {
|
|
424
751
|
return this.instance.dispatch(Object.assign(Object.assign({}, properties), {
|
|
425
|
-
type: HAS_SEEN_COMPONENT
|
|
752
|
+
type: experience_jsPluginAnalytics.HAS_SEEN_COMPONENT
|
|
426
753
|
}));
|
|
427
754
|
});
|
|
428
755
|
this.trackComponentView = properties => {
|
|
429
756
|
return this.instance.dispatch(Object.assign(Object.assign({}, properties), {
|
|
430
|
-
type: HAS_SEEN_ELEMENT
|
|
757
|
+
type: experience_jsPluginAnalytics.HAS_SEEN_ELEMENT
|
|
431
758
|
}));
|
|
432
759
|
};
|
|
433
760
|
this.observeElement = (payload, options) => {
|
|
@@ -441,54 +768,38 @@ class Ninetailed {
|
|
|
441
768
|
const isConstructorNameNotObject = constructorName && constructorName !== 'Object';
|
|
442
769
|
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.`);
|
|
443
770
|
} else {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
771
|
+
this.observedElements.set(element, remaingPayload);
|
|
772
|
+
const delays = this.pluginsWithCustomComponentViewThreshold.map(plugin => plugin.getComponentViewTrackingThreshold());
|
|
773
|
+
const uniqueDelays = Array.from(new Set([...delays, (options === null || options === void 0 ? void 0 : options.delay) || this.componentViewTrackingThreshold]));
|
|
774
|
+
uniqueDelays.forEach(delay => {
|
|
775
|
+
this.elementSeenObserver.observe(element, {
|
|
776
|
+
delay
|
|
450
777
|
});
|
|
451
|
-
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
this.observedElements.set(element, [...existingPayloads, remaingPayload]);
|
|
455
|
-
}
|
|
456
|
-
this.elementSeenObserver.observe(element, Object.assign({
|
|
457
|
-
delay: this.componentViewTrackingThreshold
|
|
458
|
-
}, options));
|
|
778
|
+
});
|
|
459
779
|
}
|
|
460
780
|
};
|
|
461
781
|
this.unobserveElement = element => {
|
|
462
782
|
this.observedElements.delete(element);
|
|
463
783
|
this.elementSeenObserver.unobserve(element);
|
|
464
784
|
};
|
|
465
|
-
this.onElementSeen = element => {
|
|
466
|
-
const
|
|
467
|
-
if (typeof
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}, payload));
|
|
785
|
+
this.onElementSeen = (element, delay) => {
|
|
786
|
+
const payload = this.observedElements.get(element);
|
|
787
|
+
if (typeof payload !== 'undefined') {
|
|
788
|
+
const pluginNamesInterestedInSeenElementMessage = [...this.pluginsWithCustomComponentViewThreshold.filter(plugin => plugin.getComponentViewTrackingThreshold() === delay), ...this.plugins.filter(plugin => !hasComponentViewTrackingThreshold(plugin))].map(plugin => plugin.name);
|
|
789
|
+
if (pluginNamesInterestedInSeenElementMessage.length === 0) {
|
|
790
|
+
return;
|
|
472
791
|
}
|
|
792
|
+
this.instance.dispatch(Object.assign(Object.assign({}, payload), {
|
|
793
|
+
element,
|
|
794
|
+
type: experience_jsPluginAnalytics.HAS_SEEN_ELEMENT,
|
|
795
|
+
plugins: Object.assign({
|
|
796
|
+
all: false
|
|
797
|
+
}, pluginNamesInterestedInSeenElementMessage.reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
798
|
+
[curr]: true
|
|
799
|
+
}), {}))
|
|
800
|
+
}));
|
|
473
801
|
}
|
|
474
802
|
};
|
|
475
|
-
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
476
|
-
try {
|
|
477
|
-
const result = experience_jsShared.Traits.default({}).safeParse(traits);
|
|
478
|
-
if (!result.success) {
|
|
479
|
-
throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
|
|
480
|
-
}
|
|
481
|
-
yield this.waitUntilInitialized();
|
|
482
|
-
yield this.instance.identify(uid && uid.toString() !== '' ? uid.toString() : EMPTY_MERGE_ID, result.data, this.buildOptions(options));
|
|
483
|
-
return this.ninetailedCorePlugin.flush();
|
|
484
|
-
} catch (error) {
|
|
485
|
-
experience_jsShared.logger.error(error);
|
|
486
|
-
if (error instanceof RangeError) {
|
|
487
|
-
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.`);
|
|
488
|
-
}
|
|
489
|
-
throw error;
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
803
|
this.reset = () => __awaiter(this, void 0, void 0, function* () {
|
|
493
804
|
yield this.waitUntilInitialized();
|
|
494
805
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -523,6 +834,165 @@ class Ninetailed {
|
|
|
523
834
|
}
|
|
524
835
|
});
|
|
525
836
|
};
|
|
837
|
+
this.onSelectVariant = ({
|
|
838
|
+
baseline,
|
|
839
|
+
experiences
|
|
840
|
+
}, cb) => {
|
|
841
|
+
let middlewareChangeListeners = [];
|
|
842
|
+
let state = null;
|
|
843
|
+
const removeMiddlewareChangeListeners = () => {
|
|
844
|
+
middlewareChangeListeners.forEach(removeListener => removeListener());
|
|
845
|
+
middlewareChangeListeners = [];
|
|
846
|
+
};
|
|
847
|
+
const setSelectedVariant = newState => {
|
|
848
|
+
state = newState;
|
|
849
|
+
cb(state);
|
|
850
|
+
};
|
|
851
|
+
const removeProfileChangeListener = this.onProfileChange(profileState => {
|
|
852
|
+
const {
|
|
853
|
+
addListeners,
|
|
854
|
+
removeListeners,
|
|
855
|
+
middleware: experienceSelectionMiddleware
|
|
856
|
+
} = makeExperienceSelectMiddleware({
|
|
857
|
+
plugins: this.plugins,
|
|
858
|
+
experiences,
|
|
859
|
+
baseline,
|
|
860
|
+
profile: profileState.profile,
|
|
861
|
+
onChange: middleware => {
|
|
862
|
+
const overrideResult = buildOverrideMiddleware(middleware);
|
|
863
|
+
if (state !== null) {
|
|
864
|
+
setSelectedVariant(overrideResult(state));
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
addListeners();
|
|
869
|
+
middlewareChangeListeners.push(removeListeners);
|
|
870
|
+
const overrideResult = buildOverrideMiddleware(experienceSelectionMiddleware);
|
|
871
|
+
const hasVariants = experiences.map(experience => experience_jsShared.selectHasVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
|
|
872
|
+
const baseReturn = Object.assign(Object.assign({}, profileState), {
|
|
873
|
+
hasVariants,
|
|
874
|
+
baseline
|
|
875
|
+
});
|
|
876
|
+
const emptyReturn = Object.assign(Object.assign({}, baseReturn), {
|
|
877
|
+
experience: null,
|
|
878
|
+
variant: baseline,
|
|
879
|
+
variantIndex: 0,
|
|
880
|
+
audience: null,
|
|
881
|
+
isPersonalized: false,
|
|
882
|
+
profile: null,
|
|
883
|
+
error: null
|
|
884
|
+
});
|
|
885
|
+
if (profileState.status === 'loading') {
|
|
886
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
887
|
+
loading: true,
|
|
888
|
+
status: 'loading'
|
|
889
|
+
})));
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (profileState.status === 'error') {
|
|
893
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
894
|
+
loading: false,
|
|
895
|
+
status: 'error',
|
|
896
|
+
error: profileState.error
|
|
897
|
+
})));
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const {
|
|
901
|
+
profile,
|
|
902
|
+
experiences: selectedExperiences
|
|
903
|
+
} = profileState;
|
|
904
|
+
if (!profile || !selectedExperiences) {
|
|
905
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
906
|
+
loading: false,
|
|
907
|
+
status: 'error',
|
|
908
|
+
error: new Error('No Profile or Selected Experiences were returned by the API')
|
|
909
|
+
})));
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
if (this.useClientSideEvaluation) {
|
|
913
|
+
const _experience = experience_jsShared.selectExperience({
|
|
914
|
+
experiences,
|
|
915
|
+
profile
|
|
916
|
+
});
|
|
917
|
+
if (!_experience) {
|
|
918
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
919
|
+
loading: false,
|
|
920
|
+
status: 'success',
|
|
921
|
+
profile
|
|
922
|
+
})));
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
const {
|
|
926
|
+
variant: _variant,
|
|
927
|
+
index
|
|
928
|
+
} = experience_jsShared.selectVariant({
|
|
929
|
+
baseline,
|
|
930
|
+
experience: _experience,
|
|
931
|
+
profile
|
|
932
|
+
});
|
|
933
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, baseReturn), {
|
|
934
|
+
status: 'success',
|
|
935
|
+
loading: false,
|
|
936
|
+
error: null,
|
|
937
|
+
experience: _experience,
|
|
938
|
+
variant: _variant,
|
|
939
|
+
variantIndex: index,
|
|
940
|
+
audience: _experience.audience ? _experience.audience : null,
|
|
941
|
+
profile,
|
|
942
|
+
isPersonalized: true
|
|
943
|
+
})));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const experience = experiences.find(experience => selectedExperiences.some(selectedExperience => selectedExperience.experienceId === experience.id));
|
|
947
|
+
const selectedExperience = selectedExperiences.find(({
|
|
948
|
+
experienceId
|
|
949
|
+
}) => experienceId === (experience === null || experience === void 0 ? void 0 : experience.id));
|
|
950
|
+
if (!experience || !selectedExperience) {
|
|
951
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
952
|
+
loading: false,
|
|
953
|
+
status: 'success',
|
|
954
|
+
profile
|
|
955
|
+
})));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const baselineVariants = experience_jsShared.selectBaselineWithVariants(experience, baseline);
|
|
959
|
+
if (!baselineVariants) {
|
|
960
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
961
|
+
loading: false,
|
|
962
|
+
status: 'success',
|
|
963
|
+
profile
|
|
964
|
+
})));
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
const {
|
|
968
|
+
variants
|
|
969
|
+
} = baselineVariants;
|
|
970
|
+
const variant = variants[selectedExperience.variantIndex - 1];
|
|
971
|
+
if (!variant) {
|
|
972
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, emptyReturn), {
|
|
973
|
+
loading: false,
|
|
974
|
+
status: 'success',
|
|
975
|
+
profile
|
|
976
|
+
})));
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
setSelectedVariant(overrideResult(Object.assign(Object.assign({}, baseReturn), {
|
|
980
|
+
status: 'success',
|
|
981
|
+
loading: false,
|
|
982
|
+
error: null,
|
|
983
|
+
experience,
|
|
984
|
+
variant,
|
|
985
|
+
variantIndex: selectedExperience.variantIndex,
|
|
986
|
+
audience: experience.audience ? experience.audience : null,
|
|
987
|
+
profile,
|
|
988
|
+
isPersonalized: true
|
|
989
|
+
})));
|
|
990
|
+
});
|
|
991
|
+
return () => {
|
|
992
|
+
removeProfileChangeListener();
|
|
993
|
+
removeMiddlewareChangeListeners();
|
|
994
|
+
};
|
|
995
|
+
};
|
|
526
996
|
this.onIsInitialized = onIsInitialized => {
|
|
527
997
|
if (typeof onIsInitialized === 'function') {
|
|
528
998
|
if (this.isInitialized) {
|
|
@@ -553,6 +1023,7 @@ class Ninetailed {
|
|
|
553
1023
|
}
|
|
554
1024
|
});
|
|
555
1025
|
};
|
|
1026
|
+
this.useClientSideEvaluation = useClientSideEvaluation;
|
|
556
1027
|
if (ninetailedApiClientInstanceOrOptions instanceof experience_jsShared.NinetailedApiClient) {
|
|
557
1028
|
this.apiClient = ninetailedApiClientInstanceOrOptions;
|
|
558
1029
|
} else {
|
|
@@ -592,8 +1063,9 @@ class Ninetailed {
|
|
|
592
1063
|
if (typeof onError === 'function') {
|
|
593
1064
|
experience_jsShared.logger.addSink(new experience_jsShared.OnErrorLogSink(onError));
|
|
594
1065
|
}
|
|
1066
|
+
this.eventBuilder = new EventBuilder(buildClientContext);
|
|
595
1067
|
this.logger = experience_jsShared.logger;
|
|
596
|
-
this.ninetailedCorePlugin =
|
|
1068
|
+
this.ninetailedCorePlugin = new NinetailedCorePlugin({
|
|
597
1069
|
apiClient: this.apiClient,
|
|
598
1070
|
locale,
|
|
599
1071
|
requestTimeout,
|
|
@@ -633,6 +1105,9 @@ class Ninetailed {
|
|
|
633
1105
|
}
|
|
634
1106
|
this.registerWindowHandlers();
|
|
635
1107
|
}
|
|
1108
|
+
get pluginsWithCustomComponentViewThreshold() {
|
|
1109
|
+
return [this.ninetailedCorePlugin, ...this.plugins].filter(plugin => hasComponentViewTrackingThreshold(plugin));
|
|
1110
|
+
}
|
|
636
1111
|
get profileState() {
|
|
637
1112
|
return this._profileState;
|
|
638
1113
|
}
|
|
@@ -741,155 +1216,6 @@ const selectVariant = (baseline, variants, {
|
|
|
741
1216
|
};
|
|
742
1217
|
};
|
|
743
1218
|
|
|
744
|
-
const TrackComponentProperties = zod.z.object({
|
|
745
|
-
variant: zod.z.object({
|
|
746
|
-
id: zod.z.string()
|
|
747
|
-
}),
|
|
748
|
-
audience: zod.z.object({
|
|
749
|
-
id: zod.z.string()
|
|
750
|
-
}),
|
|
751
|
-
isPersonalized: zod.z.boolean()
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
class NinetailedPlugin {}
|
|
755
|
-
|
|
756
|
-
const ElementSeenPayloadSchema = zod.z.object({
|
|
757
|
-
element: zod.z.any(),
|
|
758
|
-
experience: zod.z.object({
|
|
759
|
-
id: zod.z.string(),
|
|
760
|
-
type: zod.z.union([zod.z.literal('nt_experiment'), zod.z.literal('nt_personalization')]),
|
|
761
|
-
name: zod.z.string().optional(),
|
|
762
|
-
description: zod.z.string().optional()
|
|
763
|
-
}).optional().nullable(),
|
|
764
|
-
audience: zod.z.object({
|
|
765
|
-
id: zod.z.string(),
|
|
766
|
-
name: zod.z.string().optional(),
|
|
767
|
-
description: zod.z.string().optional()
|
|
768
|
-
}).optional().nullable().default({
|
|
769
|
-
id: 'ALL_VISITORS',
|
|
770
|
-
name: 'All Visitors',
|
|
771
|
-
description: 'This is the default all visitors audience as no audience was set.'
|
|
772
|
-
}),
|
|
773
|
-
variant: zod.z.object({
|
|
774
|
-
id: zod.z.string()
|
|
775
|
-
}).catchall(zod.z.unknown()),
|
|
776
|
-
variantIndex: zod.z.number()
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
780
|
-
return encodedExperienceVariantsMap.split(',').map(experienceIdWithVariant => {
|
|
781
|
-
const [experienceId, _variantIndex] = experienceIdWithVariant.split('=');
|
|
782
|
-
const variantIndex = parseInt(_variantIndex);
|
|
783
|
-
if (!experienceId || !variantIndex) {
|
|
784
|
-
return null;
|
|
785
|
-
}
|
|
786
|
-
return {
|
|
787
|
-
experienceId,
|
|
788
|
-
variantIndex
|
|
789
|
-
};
|
|
790
|
-
}).filter(x => !!x).reduce((acc, curr) => Object.assign(Object.assign({}, acc), {
|
|
791
|
-
[curr.experienceId]: curr.variantIndex
|
|
792
|
-
}), {});
|
|
793
|
-
};
|
|
794
|
-
|
|
795
|
-
class OnChangeEmitter {
|
|
796
|
-
constructor() {
|
|
797
|
-
this.onChangeListeners = [];
|
|
798
|
-
}
|
|
799
|
-
addListener(listener) {
|
|
800
|
-
this.onChangeListeners.push(listener);
|
|
801
|
-
return () => {
|
|
802
|
-
this.removeOnChangeListener(listener);
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
invokeListeners() {
|
|
806
|
-
this.onChangeListeners.forEach(listener => listener());
|
|
807
|
-
}
|
|
808
|
-
removeOnChangeListener(listener) {
|
|
809
|
-
this.onChangeListeners = this.onChangeListeners.filter(l => l !== listener);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const hasOnChangeEmitter = arg => {
|
|
814
|
-
return typeof arg === 'object' && arg !== null && 'onChangeEmitter' in arg && typeof arg.onChangeEmitter === 'object' && arg.onChangeEmitter !== null && arg.onChangeEmitter.constructor === OnChangeEmitter;
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
const selectPluginsHavingOnChangeEmitter = plugins => {
|
|
818
|
-
const filteredPlugins = [];
|
|
819
|
-
for (const plugin of plugins) {
|
|
820
|
-
if (hasOnChangeEmitter(plugin)) {
|
|
821
|
-
filteredPlugins.push(plugin);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
return filteredPlugins;
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
const hasExperienceSelectionMiddleware = arg => {
|
|
828
|
-
return typeof arg === 'object' && arg !== null && 'getExperienceSelectionMiddleware' in arg && typeof arg.getExperienceSelectionMiddleware === 'function';
|
|
829
|
-
};
|
|
830
|
-
|
|
831
|
-
const selectPluginsHavingExperienceSelectionMiddleware = plugins => {
|
|
832
|
-
const filteredPlugins = [];
|
|
833
|
-
for (const plugin of plugins) {
|
|
834
|
-
if (hasExperienceSelectionMiddleware(plugin)) {
|
|
835
|
-
filteredPlugins.push(plugin);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
return filteredPlugins;
|
|
839
|
-
};
|
|
840
|
-
|
|
841
|
-
const createPassThroughMiddleware = () => {
|
|
842
|
-
return ({
|
|
843
|
-
experience,
|
|
844
|
-
variant,
|
|
845
|
-
variantIndex
|
|
846
|
-
}) => {
|
|
847
|
-
return {
|
|
848
|
-
experience,
|
|
849
|
-
variant,
|
|
850
|
-
variantIndex
|
|
851
|
-
};
|
|
852
|
-
};
|
|
853
|
-
};
|
|
854
|
-
const makeExperienceSelectMiddleware = ({
|
|
855
|
-
plugins,
|
|
856
|
-
onChange,
|
|
857
|
-
experiences,
|
|
858
|
-
baseline,
|
|
859
|
-
profile
|
|
860
|
-
}) => {
|
|
861
|
-
let removeChangeListeners = [];
|
|
862
|
-
const pluginsHavingChangeEmitters = selectPluginsHavingOnChangeEmitter(plugins);
|
|
863
|
-
const prepareMiddleware = () => {
|
|
864
|
-
if (profile === null) {
|
|
865
|
-
return createPassThroughMiddleware();
|
|
866
|
-
}
|
|
867
|
-
const pluginsWithMiddleware = selectPluginsHavingExperienceSelectionMiddleware(plugins);
|
|
868
|
-
const middlewareFunctions = pluginsWithMiddleware.map(plugin => plugin.getExperienceSelectionMiddleware({
|
|
869
|
-
experiences,
|
|
870
|
-
baseline
|
|
871
|
-
}));
|
|
872
|
-
return experience_jsShared.pipe(...middlewareFunctions);
|
|
873
|
-
};
|
|
874
|
-
const addListeners = () => {
|
|
875
|
-
removeChangeListeners = pluginsHavingChangeEmitters.map(plugin => {
|
|
876
|
-
const listener = () => {
|
|
877
|
-
onChange();
|
|
878
|
-
};
|
|
879
|
-
return plugin.onChangeEmitter.addListener(listener);
|
|
880
|
-
});
|
|
881
|
-
};
|
|
882
|
-
const removeListeners = () => {
|
|
883
|
-
removeChangeListeners.forEach(listener => listener());
|
|
884
|
-
};
|
|
885
|
-
const middleware = prepareMiddleware();
|
|
886
|
-
return {
|
|
887
|
-
addListeners,
|
|
888
|
-
removeListeners,
|
|
889
|
-
middleware
|
|
890
|
-
};
|
|
891
|
-
};
|
|
892
|
-
|
|
893
1219
|
Object.defineProperty(exports, 'EXPERIENCE_TRAIT_PREFIX', {
|
|
894
1220
|
enumerable: true,
|
|
895
1221
|
get: function () { return experience_jsShared.EXPERIENCE_TRAIT_PREFIX; }
|
|
@@ -933,12 +1259,10 @@ exports.CONSENT = CONSENT;
|
|
|
933
1259
|
exports.DEBUG_FLAG = DEBUG_FLAG;
|
|
934
1260
|
exports.EMPTY_MERGE_ID = EMPTY_MERGE_ID;
|
|
935
1261
|
exports.EXPERIENCES_FALLBACK_CACHE = EXPERIENCES_FALLBACK_CACHE;
|
|
936
|
-
exports.
|
|
937
|
-
exports.HAS_SEEN_COMPONENT = HAS_SEEN_COMPONENT;
|
|
938
|
-
exports.HAS_SEEN_ELEMENT = HAS_SEEN_ELEMENT;
|
|
1262
|
+
exports.HAS_SEEN_STICKY_COMPONENT = HAS_SEEN_STICKY_COMPONENT;
|
|
939
1263
|
exports.LEGACY_ANONYMOUS_ID = LEGACY_ANONYMOUS_ID;
|
|
940
1264
|
exports.Ninetailed = Ninetailed;
|
|
941
|
-
exports.
|
|
1265
|
+
exports.NinetailedCorePlugin = NinetailedCorePlugin;
|
|
942
1266
|
exports.OnChangeEmitter = OnChangeEmitter;
|
|
943
1267
|
exports.PAGE_HIDDEN = PAGE_HIDDEN;
|
|
944
1268
|
exports.PLUGIN_NAME = PLUGIN_NAME;
|
|
@@ -946,11 +1270,9 @@ exports.PROFILE_CHANGE = PROFILE_CHANGE;
|
|
|
946
1270
|
exports.PROFILE_FALLBACK_CACHE = PROFILE_FALLBACK_CACHE;
|
|
947
1271
|
exports.PROFILE_RESET = PROFILE_RESET;
|
|
948
1272
|
exports.SET_ENABLED_FEATURES = SET_ENABLED_FEATURES;
|
|
949
|
-
exports.TrackComponentProperties = TrackComponentProperties;
|
|
950
1273
|
exports.buildClientNinetailedRequestContext = buildClientNinetailedRequestContext;
|
|
951
1274
|
exports.decodeExperienceVariantsMap = decodeExperienceVariantsMap;
|
|
952
1275
|
exports.makeExperienceSelectMiddleware = makeExperienceSelectMiddleware;
|
|
953
|
-
exports.ninetailedCorePlugin = ninetailedCorePlugin;
|
|
954
1276
|
exports.selectPluginsHavingExperienceSelectionMiddleware = selectPluginsHavingExperienceSelectionMiddleware;
|
|
955
1277
|
exports.selectPluginsHavingOnChangeEmitter = selectPluginsHavingOnChangeEmitter;
|
|
956
1278
|
exports.selectVariant = selectVariant;
|