@ninetailed/experience.js 7.6.0-beta.1 → 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} +275 -243
- package/{index.js → index.esm.js} +410 -382
- package/package.json +13 -10
- package/src/index.d.ts +1 -1
- package/src/lib/ElementSeenObserver.d.ts +1 -1
- package/src/lib/Ninetailed.d.ts +4 -2
- 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 +0 -2
- package/src/lib/experience/makeExperienceSelectMiddleware.d.ts +1 -1
- package/src/lib/guards/hasComponentViewTrackingThreshold.d.ts +3 -0
- package/src/lib/plugins/selectPluginsHavingExperienceSelectionMiddleware.d.ts +1 -1
- package/src/lib/plugins/selectPluginsHavingOnChangeEmitter.d.ts +1 -1
- package/src/lib/types/index.d.ts +2 -8
- package/src/lib/types/interfaces/HasComponentViewTrackingThreshold.d.ts +3 -0
- 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/ninetailedCorePlugin/ninetailedCorePlugin.d.ts +0 -17
- 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
|
@@ -1,53 +1,14 @@
|
|
|
1
|
-
import { FEATURES, logger, ConsoleLogSink, buildPageEvent, buildTrackEvent, buildIdentifyEvent,
|
|
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
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';
|
|
3
4
|
import Analytics from 'analytics';
|
|
4
5
|
import { v4 } from 'uuid';
|
|
5
|
-
import { z } from 'zod';
|
|
6
6
|
|
|
7
|
-
const HAS_SEEN_COMPONENT = 'has_seen_component';
|
|
8
|
-
const HAS_SEEN_ELEMENT = 'has_seen_element';
|
|
9
7
|
const COMPONENT = 'component';
|
|
10
8
|
const COMPONENT_START = 'componentStart';
|
|
11
9
|
const PAGE_HIDDEN = 'page_hidden';
|
|
12
10
|
const HAS_SEEN_STICKY_COMPONENT = 'sticky_component_view';
|
|
13
11
|
|
|
14
|
-
/******************************************************************************
|
|
15
|
-
Copyright (c) Microsoft Corporation.
|
|
16
|
-
|
|
17
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
18
|
-
purpose with or without fee is hereby granted.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
21
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
22
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
23
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
24
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
25
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
26
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
27
|
-
***************************************************************************** */
|
|
28
|
-
|
|
29
|
-
function __rest(s, e) {
|
|
30
|
-
var t = {};
|
|
31
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
32
|
-
t[p] = s[p];
|
|
33
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
34
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
35
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
36
|
-
t[p[i]] = s[p[i]];
|
|
37
|
-
}
|
|
38
|
-
return t;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
42
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
43
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
44
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
45
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
46
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
47
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
12
|
const buildClientLocale = () => navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language;
|
|
52
13
|
|
|
53
14
|
const buildClientNinetailedRequestContext = () => ({
|
|
@@ -60,15 +21,15 @@ const buildClientNinetailedRequestContext = () => ({
|
|
|
60
21
|
}
|
|
61
22
|
});
|
|
62
23
|
|
|
63
|
-
/**
|
|
64
|
-
* Similar to _.throttle but waits for the promise to resolve.
|
|
65
|
-
* There is no "wait time" because you can simply await `Promise.timeout` inside `fn` to wait some time before the next call.
|
|
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.
|
|
66
27
|
*/
|
|
67
28
|
function asyncThrottle(fn) {
|
|
68
29
|
let runningPromise;
|
|
69
30
|
let queuedPromise;
|
|
70
31
|
let nextArgs;
|
|
71
|
-
return args =>
|
|
32
|
+
return async args => {
|
|
72
33
|
if (runningPromise) {
|
|
73
34
|
nextArgs = args;
|
|
74
35
|
if (queuedPromise) {
|
|
@@ -85,7 +46,7 @@ function asyncThrottle(fn) {
|
|
|
85
46
|
runningPromise = fn(args);
|
|
86
47
|
return runningPromise;
|
|
87
48
|
}
|
|
88
|
-
}
|
|
49
|
+
};
|
|
89
50
|
}
|
|
90
51
|
|
|
91
52
|
const LEGACY_ANONYMOUS_ID = '__anon_id';
|
|
@@ -99,68 +60,262 @@ const CONSENT = '__nt-consent__';
|
|
|
99
60
|
const SET_ENABLED_FEATURES = 'set-enabled-features';
|
|
100
61
|
const EMPTY_MERGE_ID = 'nt:empty-merge-id';
|
|
101
62
|
|
|
102
|
-
const PLUGIN_NAME = 'ninetailed';
|
|
63
|
+
const PLUGIN_NAME = 'ninetailed:core';
|
|
103
64
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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);
|
|
117
272
|
logger.info('Start flushing events.');
|
|
118
|
-
queue = [];
|
|
273
|
+
this.queue = [];
|
|
119
274
|
if (!events.length) {
|
|
120
275
|
return {
|
|
121
276
|
success: true
|
|
122
277
|
};
|
|
123
278
|
}
|
|
124
279
|
try {
|
|
125
|
-
const anonymousId =
|
|
280
|
+
const anonymousId = this.instance.storage.getItem(ANONYMOUS_ID);
|
|
126
281
|
const {
|
|
127
282
|
profile,
|
|
128
283
|
experiences
|
|
129
|
-
} =
|
|
284
|
+
} = await this.apiClient.upsertProfile({
|
|
130
285
|
profileId: anonymousId,
|
|
131
286
|
events
|
|
132
287
|
}, {
|
|
133
|
-
locale,
|
|
134
|
-
enabledFeatures
|
|
288
|
+
locale: this.locale,
|
|
289
|
+
enabledFeatures: this.enabledFeatures
|
|
135
290
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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);
|
|
139
294
|
logger.debug('Profile from api: ', profile);
|
|
140
295
|
logger.debug('Experiences from api: ', experiences);
|
|
141
|
-
|
|
296
|
+
this.instance.dispatch({
|
|
142
297
|
type: PROFILE_CHANGE,
|
|
143
298
|
profile,
|
|
144
299
|
experiences
|
|
145
300
|
});
|
|
146
|
-
|
|
301
|
+
await delay(20);
|
|
147
302
|
return {
|
|
148
303
|
success: true
|
|
149
304
|
};
|
|
150
305
|
} catch (error) {
|
|
151
306
|
logger.debug('An error occurred during flushing the events: ', error);
|
|
152
|
-
const fallbackProfile =
|
|
153
|
-
const fallbackExperiences =
|
|
307
|
+
const fallbackProfile = this.instance.storage.getItem(PROFILE_FALLBACK_CACHE);
|
|
308
|
+
const fallbackExperiences = this.instance.storage.getItem(EXPERIENCES_FALLBACK_CACHE) || [];
|
|
154
309
|
if (fallbackProfile) {
|
|
155
310
|
logger.debug('Found a fallback profile - will use this.');
|
|
156
|
-
|
|
311
|
+
this.instance.dispatch({
|
|
157
312
|
type: PROFILE_CHANGE,
|
|
158
313
|
profile: fallbackProfile,
|
|
159
314
|
experiences: fallbackExperiences
|
|
160
315
|
});
|
|
161
316
|
} else {
|
|
162
317
|
logger.debug('No fallback profile found - setting profile to null.');
|
|
163
|
-
|
|
318
|
+
this.instance.dispatch({
|
|
164
319
|
type: PROFILE_CHANGE,
|
|
165
320
|
profile: null,
|
|
166
321
|
experiences: fallbackExperiences,
|
|
@@ -171,166 +326,27 @@ const ninetailedCorePlugin = ({
|
|
|
171
326
|
success: false
|
|
172
327
|
};
|
|
173
328
|
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
config: {},
|
|
190
|
-
initialize: ({
|
|
191
|
-
instance
|
|
192
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
193
|
-
_instance = instance;
|
|
194
|
-
if (instance.storage.getItem(DEBUG_FLAG)) {
|
|
195
|
-
logger.addSink(new ConsoleLogSink());
|
|
196
|
-
logger.info('Ninetailed Debug Mode is enabled.');
|
|
197
|
-
}
|
|
198
|
-
// legacy support for the old anonymousId
|
|
199
|
-
const legacyAnonymousId = instance.storage.getItem(LEGACY_ANONYMOUS_ID);
|
|
200
|
-
if (legacyAnonymousId) {
|
|
201
|
-
logger.debug('Found legacy anonymousId, migrating to new one.', legacyAnonymousId);
|
|
202
|
-
instance.storage.setItem(ANONYMOUS_ID, legacyAnonymousId);
|
|
203
|
-
instance.storage.removeItem(LEGACY_ANONYMOUS_ID);
|
|
204
|
-
}
|
|
205
|
-
if (typeof onInitProfileId === 'function') {
|
|
206
|
-
const profileId = yield onInitProfileId(instance.storage.getItem(ANONYMOUS_ID));
|
|
207
|
-
if (typeof profileId === 'string') {
|
|
208
|
-
instance.storage.setItem(ANONYMOUS_ID, profileId);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
instance.on(SET_ENABLED_FEATURES, ({
|
|
212
|
-
payload
|
|
213
|
-
}) => {
|
|
214
|
-
enabledFeatures = payload.features || [];
|
|
215
|
-
});
|
|
216
|
-
logger.debug('Ninetailed Core plugin initialized.');
|
|
217
|
-
}),
|
|
218
|
-
flush: asyncThrottle(flush),
|
|
219
|
-
pageStart: params => {
|
|
220
|
-
return abortNonClientEvents(params);
|
|
221
|
-
},
|
|
222
|
-
page: ({
|
|
223
|
-
payload
|
|
224
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
225
|
-
logger.info('Sending Page event.');
|
|
226
|
-
const ctx = buildContext();
|
|
227
|
-
return enqueueEvent(buildPageEvent({
|
|
228
|
-
messageId: payload.meta.rid,
|
|
229
|
-
timestamp: payload.meta.ts,
|
|
230
|
-
properties: payload.properties,
|
|
231
|
-
ctx
|
|
232
|
-
}));
|
|
233
|
-
}),
|
|
234
|
-
trackStart: params => {
|
|
235
|
-
return abortNonClientEvents(params);
|
|
236
|
-
},
|
|
237
|
-
track: ({
|
|
238
|
-
payload
|
|
239
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
240
|
-
logger.info('Sending Track event.');
|
|
241
|
-
const ctx = buildContext();
|
|
242
|
-
return enqueueEvent(buildTrackEvent({
|
|
243
|
-
messageId: payload.meta.rid,
|
|
244
|
-
timestamp: payload.meta.ts,
|
|
245
|
-
event: payload.event,
|
|
246
|
-
properties: payload.properties,
|
|
247
|
-
ctx
|
|
248
|
-
}));
|
|
249
|
-
}),
|
|
250
|
-
identifyStart: params => {
|
|
251
|
-
return abortNonClientEvents(params);
|
|
252
|
-
},
|
|
253
|
-
identify: ({
|
|
254
|
-
payload
|
|
255
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
256
|
-
logger.info('Sending Identify event.');
|
|
257
|
-
const ctx = buildContext();
|
|
258
|
-
if (payload.userId === EMPTY_MERGE_ID && (!payload.traits || typeof payload.traits === 'object' && Object.keys(payload.traits).length === 0)) {
|
|
259
|
-
logger.info('Skipping Identify event as no userId and no traits are set.');
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
return enqueueEvent(buildIdentifyEvent({
|
|
263
|
-
messageId: payload.meta.rid,
|
|
264
|
-
timestamp: payload.meta.ts,
|
|
265
|
-
traits: payload.traits,
|
|
266
|
-
userId: payload.userId === EMPTY_MERGE_ID ? '' : payload.userId,
|
|
267
|
-
ctx
|
|
268
|
-
}));
|
|
269
|
-
}),
|
|
270
|
-
[HAS_SEEN_STICKY_COMPONENT]: ({
|
|
271
|
-
payload
|
|
272
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
273
|
-
logger.info('Sending Sticky Components event.');
|
|
274
|
-
const ctx = buildContext();
|
|
275
|
-
return enqueueEvent(buildComponentViewEvent({
|
|
276
|
-
messageId: payload.meta.rid,
|
|
277
|
-
timestamp: payload.meta.ts,
|
|
278
|
-
componentId: payload.componentId,
|
|
279
|
-
experienceId: payload.experienceId,
|
|
280
|
-
variantIndex: payload.variantIndex,
|
|
281
|
-
ctx
|
|
282
|
-
}));
|
|
283
|
-
}),
|
|
284
|
-
setItemStart: ({
|
|
285
|
-
abort,
|
|
286
|
-
payload
|
|
287
|
-
}) => {
|
|
288
|
-
if (![ANONYMOUS_ID, DEBUG_FLAG, PROFILE_FALLBACK_CACHE, EXPERIENCES_FALLBACK_CACHE, CONSENT].includes(payload.key)) {
|
|
289
|
-
return abort();
|
|
290
|
-
}
|
|
291
|
-
return payload;
|
|
292
|
-
},
|
|
293
|
-
methods: {
|
|
294
|
-
reset: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
295
|
-
logger.debug('Resetting profile.');
|
|
296
|
-
const instance = args[args.length - 1];
|
|
297
|
-
instance.dispatch({
|
|
298
|
-
type: PROFILE_RESET
|
|
299
|
-
});
|
|
300
|
-
instance.storage.removeItem(ANONYMOUS_ID);
|
|
301
|
-
instance.storage.removeItem(PROFILE_FALLBACK_CACHE);
|
|
302
|
-
instance.storage.removeItem(EXPERIENCES_FALLBACK_CACHE);
|
|
303
|
-
logger.debug('Removed old profile data from localstorage.');
|
|
304
|
-
if (typeof onInitProfileId === 'function') {
|
|
305
|
-
const profileId = yield onInitProfileId(undefined);
|
|
306
|
-
if (typeof profileId === 'string') {
|
|
307
|
-
instance.storage.setItem(ANONYMOUS_ID, profileId);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
yield ninetailed.track('nt_reset');
|
|
311
|
-
logger.info('Profile reset successful.');
|
|
312
|
-
yield delay(10);
|
|
313
|
-
}),
|
|
314
|
-
debug: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
315
|
-
const enabled = args[0];
|
|
316
|
-
const instance = args[args.length - 1];
|
|
317
|
-
const consoleLogSink = new ConsoleLogSink();
|
|
318
|
-
if (enabled) {
|
|
319
|
-
instance.storage.setItem(DEBUG_FLAG, true);
|
|
320
|
-
logger.addSink(consoleLogSink);
|
|
321
|
-
logger.info('Debug mode enabled.');
|
|
322
|
-
} else {
|
|
323
|
-
instance.storage.removeItem(DEBUG_FLAG);
|
|
324
|
-
logger.info('Debug mode disabled.');
|
|
325
|
-
logger.removeSink(consoleLogSink.name);
|
|
326
|
-
}
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
};
|
|
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
|
+
}
|
|
331
344
|
|
|
332
345
|
class ElementSeenObserver {
|
|
333
346
|
constructor(_options) {
|
|
347
|
+
this._intersectionObserver = void 0;
|
|
348
|
+
this._elementDelays = void 0;
|
|
349
|
+
this._intersectionTimers = void 0;
|
|
334
350
|
this._options = _options;
|
|
335
351
|
this._elementDelays = new WeakMap();
|
|
336
352
|
this._intersectionTimers = new WeakMap();
|
|
@@ -345,27 +361,33 @@ class ElementSeenObserver {
|
|
|
345
361
|
target
|
|
346
362
|
} = entry;
|
|
347
363
|
if (isIntersecting) {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
});
|
|
353
372
|
} else {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
373
|
+
const timeOuts = this._intersectionTimers.get(target);
|
|
374
|
+
timeOuts == null || timeOuts.forEach(timeOut => {
|
|
375
|
+
if (typeof timeOut !== 'undefined') {
|
|
376
|
+
window.clearTimeout(timeOut);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
358
379
|
}
|
|
359
380
|
});
|
|
360
381
|
}
|
|
361
382
|
observe(element, options) {
|
|
362
|
-
var
|
|
363
|
-
this._elementDelays.
|
|
364
|
-
(
|
|
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);
|
|
365
387
|
}
|
|
366
388
|
unobserve(element) {
|
|
367
|
-
var
|
|
368
|
-
(
|
|
389
|
+
var _this$_intersectionOb2;
|
|
390
|
+
(_this$_intersectionOb2 = this._intersectionObserver) == null || _this$_intersectionOb2.unobserve(element);
|
|
369
391
|
}
|
|
370
392
|
}
|
|
371
393
|
|
|
@@ -388,7 +410,7 @@ const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
|
388
410
|
experienceId,
|
|
389
411
|
variantIndex
|
|
390
412
|
};
|
|
391
|
-
}).filter(x => !!x).reduce((acc, curr) => Object.assign(
|
|
413
|
+
}).filter(x => !!x).reduce((acc, curr) => Object.assign({}, acc, {
|
|
392
414
|
[curr.experienceId]: curr.variantIndex
|
|
393
415
|
}), {});
|
|
394
416
|
};
|
|
@@ -493,42 +515,43 @@ const makeExperienceSelectMiddleware = ({
|
|
|
493
515
|
|
|
494
516
|
class EventBuilder {
|
|
495
517
|
constructor(buildRequestContext) {
|
|
518
|
+
this.buildRequestContext = void 0;
|
|
496
519
|
this.buildRequestContext = buildRequestContext || buildClientNinetailedRequestContext;
|
|
497
520
|
}
|
|
498
521
|
page(properties, data) {
|
|
499
|
-
return buildPageEvent(Object.assign(
|
|
500
|
-
messageId: (data
|
|
501
|
-
}, data
|
|
522
|
+
return buildPageEvent(Object.assign({
|
|
523
|
+
messageId: (data == null ? void 0 : data.messageId) || v4()
|
|
524
|
+
}, data, {
|
|
502
525
|
timestamp: Date.now(),
|
|
503
526
|
properties: properties || {},
|
|
504
527
|
ctx: this.buildRequestContext()
|
|
505
528
|
}));
|
|
506
529
|
}
|
|
507
530
|
track(event, properties, data) {
|
|
508
|
-
return buildTrackEvent(Object.assign(
|
|
509
|
-
messageId: (data
|
|
531
|
+
return buildTrackEvent(Object.assign({
|
|
532
|
+
messageId: (data == null ? void 0 : data.messageId) || v4(),
|
|
510
533
|
timestamp: Date.now()
|
|
511
|
-
}, data
|
|
534
|
+
}, data, {
|
|
512
535
|
event,
|
|
513
536
|
properties: properties || {},
|
|
514
537
|
ctx: this.buildRequestContext()
|
|
515
538
|
}));
|
|
516
539
|
}
|
|
517
540
|
identify(userId, traits, data) {
|
|
518
|
-
return buildIdentifyEvent(Object.assign(
|
|
519
|
-
messageId: (data
|
|
541
|
+
return buildIdentifyEvent(Object.assign({
|
|
542
|
+
messageId: (data == null ? void 0 : data.messageId) || v4(),
|
|
520
543
|
timestamp: Date.now()
|
|
521
|
-
}, data
|
|
544
|
+
}, data, {
|
|
522
545
|
traits: traits || {},
|
|
523
546
|
userId: userId || '',
|
|
524
547
|
ctx: this.buildRequestContext()
|
|
525
548
|
}));
|
|
526
549
|
}
|
|
527
550
|
component(componentId, experienceId, variantIndex, data) {
|
|
528
|
-
return buildComponentViewEvent(Object.assign(
|
|
529
|
-
messageId: (data
|
|
551
|
+
return buildComponentViewEvent(Object.assign({
|
|
552
|
+
messageId: (data == null ? void 0 : data.messageId) || v4(),
|
|
530
553
|
timestamp: Date.now()
|
|
531
|
-
}, data
|
|
554
|
+
}, data, {
|
|
532
555
|
componentId,
|
|
533
556
|
experienceId: experienceId || '',
|
|
534
557
|
variantIndex: variantIndex || 0,
|
|
@@ -537,13 +560,19 @@ class EventBuilder {
|
|
|
537
560
|
}
|
|
538
561
|
}
|
|
539
562
|
|
|
540
|
-
const
|
|
541
|
-
|
|
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 {
|
|
542
571
|
experience: originalExperience,
|
|
543
572
|
variant: originalVariant,
|
|
544
573
|
variantIndex: originalVariantIndex
|
|
545
|
-
} =
|
|
546
|
-
other =
|
|
574
|
+
} = _ref,
|
|
575
|
+
other = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
547
576
|
const {
|
|
548
577
|
experience,
|
|
549
578
|
variant,
|
|
@@ -553,8 +582,8 @@ const buildOverrideMiddleware = experienceSelectionMiddleware => _a => {
|
|
|
553
582
|
variant: originalVariant,
|
|
554
583
|
variantIndex: originalVariantIndex
|
|
555
584
|
});
|
|
556
|
-
return Object.assign(
|
|
557
|
-
audience:
|
|
585
|
+
return Object.assign({}, other, {
|
|
586
|
+
audience: experience != null && experience.audience ? experience.audience : null,
|
|
558
587
|
experience,
|
|
559
588
|
variant,
|
|
560
589
|
variantIndex
|
|
@@ -574,16 +603,30 @@ class Ninetailed {
|
|
|
574
603
|
storageImpl,
|
|
575
604
|
useClientSideEvaluation = false
|
|
576
605
|
} = {}) {
|
|
606
|
+
var _this = this;
|
|
607
|
+
this.instance = void 0;
|
|
608
|
+
this._profileState = void 0;
|
|
577
609
|
this.isInitialized = false;
|
|
578
|
-
this.
|
|
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) {
|
|
579
622
|
try {
|
|
580
623
|
const result = PageviewProperties.partial().default({}).safeParse(data);
|
|
581
624
|
if (!result.success) {
|
|
582
625
|
throw new Error(`[Validation Error] "page" was called with invalid params. Page data is not valid: ${result.error.format()}`);
|
|
583
626
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
return
|
|
627
|
+
await _this.waitUntilInitialized();
|
|
628
|
+
await _this.instance.page(data, _this.buildOptions(options));
|
|
629
|
+
return _this.ninetailedCorePlugin.flush();
|
|
587
630
|
} catch (error) {
|
|
588
631
|
logger.error(error);
|
|
589
632
|
if (error instanceof RangeError) {
|
|
@@ -591,16 +634,16 @@ class Ninetailed {
|
|
|
591
634
|
}
|
|
592
635
|
throw error;
|
|
593
636
|
}
|
|
594
|
-
}
|
|
595
|
-
this.track = (event, properties, options)
|
|
637
|
+
};
|
|
638
|
+
this.track = async function (event, properties, options) {
|
|
596
639
|
try {
|
|
597
640
|
const result = Properties.default({}).safeParse(properties);
|
|
598
641
|
if (!result.success) {
|
|
599
642
|
throw new Error(`[Validation Error] "track" was called with invalid params. Properties are no valid json object: ${result.error.format()}`);
|
|
600
643
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
return
|
|
644
|
+
await _this.waitUntilInitialized();
|
|
645
|
+
await _this.instance.track(event.toString(), result.data, _this.buildOptions(options));
|
|
646
|
+
return _this.ninetailedCorePlugin.flush();
|
|
604
647
|
} catch (error) {
|
|
605
648
|
logger.error(error);
|
|
606
649
|
if (error instanceof RangeError) {
|
|
@@ -608,16 +651,16 @@ class Ninetailed {
|
|
|
608
651
|
}
|
|
609
652
|
throw error;
|
|
610
653
|
}
|
|
611
|
-
}
|
|
612
|
-
this.identify = (uid, traits, options)
|
|
654
|
+
};
|
|
655
|
+
this.identify = async function (uid, traits, options) {
|
|
613
656
|
try {
|
|
614
657
|
const result = Traits.default({}).safeParse(traits);
|
|
615
658
|
if (!result.success) {
|
|
616
659
|
throw new Error(`[Validation Error] "identify" was called with invalid params. Traits are no valid json: ${result.error.format()}`);
|
|
617
660
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
return
|
|
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();
|
|
621
664
|
} catch (error) {
|
|
622
665
|
logger.error(error);
|
|
623
666
|
if (error instanceof RangeError) {
|
|
@@ -625,22 +668,22 @@ class Ninetailed {
|
|
|
625
668
|
}
|
|
626
669
|
throw error;
|
|
627
670
|
}
|
|
628
|
-
}
|
|
629
|
-
this.batch =
|
|
671
|
+
};
|
|
672
|
+
this.batch = async function (events) {
|
|
630
673
|
try {
|
|
631
|
-
|
|
674
|
+
await _this.waitUntilInitialized();
|
|
632
675
|
const promises = events.map(event => {
|
|
633
676
|
if (isPageViewEvent(event)) {
|
|
634
|
-
return
|
|
677
|
+
return _this.instance.page(event.properties);
|
|
635
678
|
}
|
|
636
679
|
if (isTrackEvent(event)) {
|
|
637
|
-
return
|
|
680
|
+
return _this.instance.track(event.event, event.properties);
|
|
638
681
|
}
|
|
639
682
|
if (isIdentifyEvent(event)) {
|
|
640
|
-
return
|
|
683
|
+
return _this.instance.identify(event.userId || EMPTY_MERGE_ID, event.traits);
|
|
641
684
|
}
|
|
642
685
|
if (isComponentViewEvent(event)) {
|
|
643
|
-
return
|
|
686
|
+
return _this.instance.dispatch({
|
|
644
687
|
experienceId: event.experienceId,
|
|
645
688
|
componentId: event.componentId,
|
|
646
689
|
variantIndex: event.variantIndex,
|
|
@@ -649,8 +692,8 @@ class Ninetailed {
|
|
|
649
692
|
}
|
|
650
693
|
return Promise.resolve();
|
|
651
694
|
});
|
|
652
|
-
|
|
653
|
-
return
|
|
695
|
+
await Promise.all(promises);
|
|
696
|
+
return _this.ninetailedCorePlugin.flush();
|
|
654
697
|
} catch (error) {
|
|
655
698
|
logger.error(error);
|
|
656
699
|
if (error instanceof RangeError) {
|
|
@@ -658,21 +701,21 @@ class Ninetailed {
|
|
|
658
701
|
}
|
|
659
702
|
throw error;
|
|
660
703
|
}
|
|
661
|
-
}
|
|
662
|
-
this.trackStickyComponentView = ({
|
|
704
|
+
};
|
|
705
|
+
this.trackStickyComponentView = async function ({
|
|
663
706
|
experienceId,
|
|
664
707
|
componentId,
|
|
665
708
|
variantIndex
|
|
666
|
-
})
|
|
709
|
+
}) {
|
|
667
710
|
try {
|
|
668
|
-
|
|
669
|
-
|
|
711
|
+
await _this.waitUntilInitialized();
|
|
712
|
+
await _this.instance.dispatch({
|
|
670
713
|
experienceId,
|
|
671
714
|
componentId,
|
|
672
715
|
variantIndex,
|
|
673
716
|
type: HAS_SEEN_STICKY_COMPONENT
|
|
674
717
|
});
|
|
675
|
-
return
|
|
718
|
+
return _this.ninetailedCorePlugin.flush();
|
|
676
719
|
} catch (error) {
|
|
677
720
|
logger.error(error);
|
|
678
721
|
if (error instanceof RangeError) {
|
|
@@ -680,18 +723,18 @@ class Ninetailed {
|
|
|
680
723
|
}
|
|
681
724
|
throw error;
|
|
682
725
|
}
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* @deprecated The legacy datamodel is not recommended anymore
|
|
686
|
-
* Will be removed in the next version of the SDK
|
|
726
|
+
};
|
|
727
|
+
/**
|
|
728
|
+
* @deprecated The legacy datamodel is not recommended anymore
|
|
729
|
+
* Will be removed in the next version of the SDK
|
|
687
730
|
*/
|
|
688
|
-
this.trackHasSeenComponent =
|
|
689
|
-
return
|
|
731
|
+
this.trackHasSeenComponent = async function (properties) {
|
|
732
|
+
return _this.instance.dispatch(Object.assign({}, properties, {
|
|
690
733
|
type: HAS_SEEN_COMPONENT
|
|
691
734
|
}));
|
|
692
|
-
}
|
|
735
|
+
};
|
|
693
736
|
this.trackComponentView = properties => {
|
|
694
|
-
return this.instance.dispatch(Object.assign(
|
|
737
|
+
return this.instance.dispatch(Object.assign({}, properties, {
|
|
695
738
|
type: HAS_SEEN_ELEMENT
|
|
696
739
|
}));
|
|
697
740
|
};
|
|
@@ -699,7 +742,7 @@ class Ninetailed {
|
|
|
699
742
|
const {
|
|
700
743
|
element
|
|
701
744
|
} = payload,
|
|
702
|
-
remaingPayload =
|
|
745
|
+
remaingPayload = _objectWithoutPropertiesLoose(payload, _excluded2);
|
|
703
746
|
if (!(element instanceof Element)) {
|
|
704
747
|
const isObject = typeof element === 'object' && element !== null;
|
|
705
748
|
const constructorName = isObject ? element.constructor.name : '';
|
|
@@ -707,49 +750,64 @@ class Ninetailed {
|
|
|
707
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.`);
|
|
708
751
|
} else {
|
|
709
752
|
this.observedElements.set(element, remaingPayload);
|
|
710
|
-
this.
|
|
711
|
-
|
|
712
|
-
|
|
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
|
+
});
|
|
713
760
|
}
|
|
714
761
|
};
|
|
715
762
|
this.unobserveElement = element => {
|
|
716
763
|
this.observedElements.delete(element);
|
|
717
764
|
this.elementSeenObserver.unobserve(element);
|
|
718
765
|
};
|
|
719
|
-
this.onElementSeen = element => {
|
|
766
|
+
this.onElementSeen = (element, delay) => {
|
|
720
767
|
const payload = this.observedElements.get(element);
|
|
721
768
|
if (typeof payload !== 'undefined') {
|
|
722
|
-
this.
|
|
723
|
-
|
|
724
|
-
|
|
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
|
+
}));
|
|
725
782
|
}
|
|
726
783
|
};
|
|
727
|
-
this.reset =
|
|
728
|
-
|
|
784
|
+
this.reset = async function () {
|
|
785
|
+
await _this.waitUntilInitialized();
|
|
786
|
+
|
|
729
787
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
730
788
|
// @ts-ignore
|
|
731
|
-
|
|
732
|
-
}
|
|
733
|
-
this.debug =
|
|
734
|
-
|
|
789
|
+
_this.instance.plugins[PLUGIN_NAME].reset();
|
|
790
|
+
};
|
|
791
|
+
this.debug = async function (enabled) {
|
|
792
|
+
await _this.waitUntilInitialized();
|
|
735
793
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
736
794
|
// @ts-ignore
|
|
737
|
-
|
|
738
|
-
}
|
|
795
|
+
_this.instance.plugins[PLUGIN_NAME].debug(enabled);
|
|
796
|
+
};
|
|
739
797
|
this.onProfileChange = cb => {
|
|
740
798
|
cb(this.profileState);
|
|
741
799
|
return this.instance.on(PROFILE_CHANGE, ({
|
|
742
800
|
payload
|
|
743
801
|
}) => {
|
|
744
802
|
if (payload.error) {
|
|
745
|
-
cb(Object.assign(
|
|
803
|
+
cb(Object.assign({}, this._profileState, {
|
|
746
804
|
status: 'error',
|
|
747
805
|
profile: payload.profile,
|
|
748
806
|
experiences: payload.experiences,
|
|
749
807
|
error: payload.error
|
|
750
808
|
}));
|
|
751
809
|
} else {
|
|
752
|
-
cb(Object.assign(
|
|
810
|
+
cb(Object.assign({}, this._profileState, {
|
|
753
811
|
status: 'success',
|
|
754
812
|
profile: payload.profile,
|
|
755
813
|
experiences: payload.experiences,
|
|
@@ -793,11 +851,11 @@ class Ninetailed {
|
|
|
793
851
|
middlewareChangeListeners.push(removeListeners);
|
|
794
852
|
const overrideResult = buildOverrideMiddleware(experienceSelectionMiddleware);
|
|
795
853
|
const hasVariants = experiences.map(experience => selectHasVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
|
|
796
|
-
const baseReturn = Object.assign(
|
|
854
|
+
const baseReturn = Object.assign({}, profileState, {
|
|
797
855
|
hasVariants,
|
|
798
856
|
baseline
|
|
799
857
|
});
|
|
800
|
-
const emptyReturn = Object.assign(
|
|
858
|
+
const emptyReturn = Object.assign({}, baseReturn, {
|
|
801
859
|
experience: null,
|
|
802
860
|
variant: baseline,
|
|
803
861
|
variantIndex: 0,
|
|
@@ -807,14 +865,14 @@ class Ninetailed {
|
|
|
807
865
|
error: null
|
|
808
866
|
});
|
|
809
867
|
if (profileState.status === 'loading') {
|
|
810
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
868
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
811
869
|
loading: true,
|
|
812
870
|
status: 'loading'
|
|
813
871
|
})));
|
|
814
872
|
return;
|
|
815
873
|
}
|
|
816
874
|
if (profileState.status === 'error') {
|
|
817
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
875
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
818
876
|
loading: false,
|
|
819
877
|
status: 'error',
|
|
820
878
|
error: profileState.error
|
|
@@ -826,7 +884,7 @@ class Ninetailed {
|
|
|
826
884
|
experiences: selectedExperiences
|
|
827
885
|
} = profileState;
|
|
828
886
|
if (!profile || !selectedExperiences) {
|
|
829
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
887
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
830
888
|
loading: false,
|
|
831
889
|
status: 'error',
|
|
832
890
|
error: new Error('No Profile or Selected Experiences were returned by the API')
|
|
@@ -839,7 +897,7 @@ class Ninetailed {
|
|
|
839
897
|
profile
|
|
840
898
|
});
|
|
841
899
|
if (!_experience) {
|
|
842
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
900
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
843
901
|
loading: false,
|
|
844
902
|
status: 'success',
|
|
845
903
|
profile
|
|
@@ -854,7 +912,7 @@ class Ninetailed {
|
|
|
854
912
|
experience: _experience,
|
|
855
913
|
profile
|
|
856
914
|
});
|
|
857
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
915
|
+
setSelectedVariant(overrideResult(Object.assign({}, baseReturn, {
|
|
858
916
|
status: 'success',
|
|
859
917
|
loading: false,
|
|
860
918
|
error: null,
|
|
@@ -870,9 +928,9 @@ class Ninetailed {
|
|
|
870
928
|
const experience = experiences.find(experience => selectedExperiences.some(selectedExperience => selectedExperience.experienceId === experience.id));
|
|
871
929
|
const selectedExperience = selectedExperiences.find(({
|
|
872
930
|
experienceId
|
|
873
|
-
}) => experienceId === (experience
|
|
931
|
+
}) => experienceId === (experience == null ? void 0 : experience.id));
|
|
874
932
|
if (!experience || !selectedExperience) {
|
|
875
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
933
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
876
934
|
loading: false,
|
|
877
935
|
status: 'success',
|
|
878
936
|
profile
|
|
@@ -881,7 +939,7 @@ class Ninetailed {
|
|
|
881
939
|
}
|
|
882
940
|
const baselineVariants = selectBaselineWithVariants(experience, baseline);
|
|
883
941
|
if (!baselineVariants) {
|
|
884
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
942
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
885
943
|
loading: false,
|
|
886
944
|
status: 'success',
|
|
887
945
|
profile
|
|
@@ -893,14 +951,14 @@ class Ninetailed {
|
|
|
893
951
|
} = baselineVariants;
|
|
894
952
|
const variant = variants[selectedExperience.variantIndex - 1];
|
|
895
953
|
if (!variant) {
|
|
896
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
954
|
+
setSelectedVariant(overrideResult(Object.assign({}, emptyReturn, {
|
|
897
955
|
loading: false,
|
|
898
956
|
status: 'success',
|
|
899
957
|
profile
|
|
900
958
|
})));
|
|
901
959
|
return;
|
|
902
960
|
}
|
|
903
|
-
setSelectedVariant(overrideResult(Object.assign(
|
|
961
|
+
setSelectedVariant(overrideResult(Object.assign({}, baseReturn, {
|
|
904
962
|
status: 'success',
|
|
905
963
|
loading: false,
|
|
906
964
|
error: null,
|
|
@@ -965,7 +1023,7 @@ class Ninetailed {
|
|
|
965
1023
|
preview
|
|
966
1024
|
});
|
|
967
1025
|
}
|
|
968
|
-
this.plugins = (plugins
|
|
1026
|
+
this.plugins = (plugins != null ? plugins : []).flat();
|
|
969
1027
|
this.plugins.forEach(plugin => {
|
|
970
1028
|
if (acceptsCredentials(plugin) && this.clientId && this.environment) {
|
|
971
1029
|
plugin.setCredentials({
|
|
@@ -989,7 +1047,7 @@ class Ninetailed {
|
|
|
989
1047
|
}
|
|
990
1048
|
this.eventBuilder = new EventBuilder(buildClientContext);
|
|
991
1049
|
this.logger = logger;
|
|
992
|
-
this.ninetailedCorePlugin =
|
|
1050
|
+
this.ninetailedCorePlugin = new NinetailedCorePlugin({
|
|
993
1051
|
apiClient: this.apiClient,
|
|
994
1052
|
locale,
|
|
995
1053
|
requestTimeout,
|
|
@@ -1003,11 +1061,12 @@ class Ninetailed {
|
|
|
1003
1061
|
}, storageImpl ? {
|
|
1004
1062
|
storage: storageImpl
|
|
1005
1063
|
} : {}));
|
|
1006
|
-
const
|
|
1064
|
+
const _detachOnReadyListener = this.instance.on('ready', () => {
|
|
1007
1065
|
this.isInitialized = true;
|
|
1008
1066
|
logger.info('Ninetailed Experience.js SDK is completely initialized.');
|
|
1009
|
-
|
|
1067
|
+
_detachOnReadyListener();
|
|
1010
1068
|
});
|
|
1069
|
+
|
|
1011
1070
|
// put in private method
|
|
1012
1071
|
this.onProfileChange(profileState => {
|
|
1013
1072
|
this._profileState = profileState;
|
|
@@ -1029,6 +1088,9 @@ class Ninetailed {
|
|
|
1029
1088
|
}
|
|
1030
1089
|
this.registerWindowHandlers();
|
|
1031
1090
|
}
|
|
1091
|
+
get pluginsWithCustomComponentViewThreshold() {
|
|
1092
|
+
return [this.ninetailedCorePlugin, ...this.plugins].filter(plugin => hasComponentViewTrackingThreshold(plugin));
|
|
1093
|
+
}
|
|
1032
1094
|
get profileState() {
|
|
1033
1095
|
return this._profileState;
|
|
1034
1096
|
}
|
|
@@ -1059,7 +1121,7 @@ const selectVariant = (baseline, variants, {
|
|
|
1059
1121
|
if (status === 'loading') {
|
|
1060
1122
|
return {
|
|
1061
1123
|
loading: true,
|
|
1062
|
-
variant: Object.assign(
|
|
1124
|
+
variant: Object.assign({}, baseline, {
|
|
1063
1125
|
id: 'baseline',
|
|
1064
1126
|
audience: {
|
|
1065
1127
|
id: 'baseline'
|
|
@@ -1075,7 +1137,7 @@ const selectVariant = (baseline, variants, {
|
|
|
1075
1137
|
if (status === 'error') {
|
|
1076
1138
|
return {
|
|
1077
1139
|
loading: false,
|
|
1078
|
-
variant: Object.assign(
|
|
1140
|
+
variant: Object.assign({}, baseline, {
|
|
1079
1141
|
id: 'baseline',
|
|
1080
1142
|
audience: {
|
|
1081
1143
|
id: 'baseline'
|
|
@@ -1089,19 +1151,19 @@ const selectVariant = (baseline, variants, {
|
|
|
1089
1151
|
};
|
|
1090
1152
|
}
|
|
1091
1153
|
const variant = variants.find(variant => {
|
|
1092
|
-
var
|
|
1093
|
-
return
|
|
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);
|
|
1094
1156
|
});
|
|
1095
1157
|
if (variant) {
|
|
1096
|
-
if (
|
|
1158
|
+
if (options != null && options.holdout || -1 > ((profile == null ? void 0 : profile.random) || 0)) {
|
|
1097
1159
|
return {
|
|
1098
1160
|
loading: false,
|
|
1099
|
-
variant: Object.assign(
|
|
1161
|
+
variant: Object.assign({}, baseline, {
|
|
1100
1162
|
audience: {
|
|
1101
1163
|
id: 'baseline'
|
|
1102
1164
|
}
|
|
1103
1165
|
}),
|
|
1104
|
-
audience: Object.assign(
|
|
1166
|
+
audience: Object.assign({}, variant.audience, {
|
|
1105
1167
|
id: variant.audience.id
|
|
1106
1168
|
}),
|
|
1107
1169
|
isPersonalized: false,
|
|
@@ -1111,19 +1173,20 @@ const selectVariant = (baseline, variants, {
|
|
|
1111
1173
|
return {
|
|
1112
1174
|
loading: false,
|
|
1113
1175
|
variant,
|
|
1114
|
-
audience: Object.assign(
|
|
1176
|
+
audience: Object.assign({}, variant.audience, {
|
|
1115
1177
|
id: variant.audience.id
|
|
1116
1178
|
}),
|
|
1117
1179
|
isPersonalized: true,
|
|
1118
1180
|
error: null
|
|
1119
1181
|
};
|
|
1120
1182
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* There was no matching audience found.
|
|
1123
1186
|
*/
|
|
1124
1187
|
return {
|
|
1125
1188
|
loading: false,
|
|
1126
|
-
variant: Object.assign(
|
|
1189
|
+
variant: Object.assign({}, baseline, {
|
|
1127
1190
|
id: 'baseline',
|
|
1128
1191
|
audience: {
|
|
1129
1192
|
id: 'baseline'
|
|
@@ -1137,39 +1200,4 @@ const selectVariant = (baseline, variants, {
|
|
|
1137
1200
|
};
|
|
1138
1201
|
};
|
|
1139
1202
|
|
|
1140
|
-
|
|
1141
|
-
variant: z.object({
|
|
1142
|
-
id: z.string()
|
|
1143
|
-
}),
|
|
1144
|
-
audience: z.object({
|
|
1145
|
-
id: z.string()
|
|
1146
|
-
}),
|
|
1147
|
-
isPersonalized: z.boolean()
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
class NinetailedPlugin {}
|
|
1151
|
-
|
|
1152
|
-
const ElementSeenPayloadSchema = z.object({
|
|
1153
|
-
element: z.any(),
|
|
1154
|
-
experience: z.object({
|
|
1155
|
-
id: z.string(),
|
|
1156
|
-
type: z.union([z.literal('nt_experiment'), z.literal('nt_personalization')]),
|
|
1157
|
-
name: z.string().optional(),
|
|
1158
|
-
description: z.string().optional()
|
|
1159
|
-
}).optional().nullable(),
|
|
1160
|
-
audience: z.object({
|
|
1161
|
-
id: z.string(),
|
|
1162
|
-
name: z.string().optional(),
|
|
1163
|
-
description: z.string().optional()
|
|
1164
|
-
}).optional().nullable().default({
|
|
1165
|
-
id: 'ALL_VISITORS',
|
|
1166
|
-
name: 'All Visitors',
|
|
1167
|
-
description: 'This is the default all visitors audience as no audience was set.'
|
|
1168
|
-
}),
|
|
1169
|
-
variant: z.object({
|
|
1170
|
-
id: z.string()
|
|
1171
|
-
}).catchall(z.unknown()),
|
|
1172
|
-
variantIndex: z.number()
|
|
1173
|
-
});
|
|
1174
|
-
|
|
1175
|
-
export { ANONYMOUS_ID, COMPONENT, COMPONENT_START, CONSENT, DEBUG_FLAG, EMPTY_MERGE_ID, EXPERIENCES_FALLBACK_CACHE, ElementSeenPayloadSchema, HAS_SEEN_COMPONENT, HAS_SEEN_ELEMENT, HAS_SEEN_STICKY_COMPONENT, LEGACY_ANONYMOUS_ID, Ninetailed, NinetailedPlugin, OnChangeEmitter, PAGE_HIDDEN, PLUGIN_NAME, PROFILE_CHANGE, PROFILE_FALLBACK_CACHE, PROFILE_RESET, SET_ENABLED_FEATURES, TrackComponentProperties, buildClientNinetailedRequestContext, decodeExperienceVariantsMap, makeExperienceSelectMiddleware, ninetailedCorePlugin, selectPluginsHavingExperienceSelectionMiddleware, selectPluginsHavingOnChangeEmitter, selectVariant };
|
|
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 };
|