@ninetailed/experience.js 2.0.2 → 2.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +2 -0
- package/index.esm.js +467 -296
- package/index.umd.js +441 -66
- package/lib/Ninetailed.d.ts +16 -5
- package/lib/analytics/utility/index.d.ts +0 -1
- package/lib/logger/ConsoleLogSink.d.ts +10 -0
- package/lib/logger/LogSink.d.ts +10 -0
- package/lib/logger/Logger.d.ts +17 -0
- package/lib/logger/OnErrorLogSink.d.ts +10 -0
- package/lib/logger/OnLogLogSink.d.ts +12 -0
- package/lib/logger/index.d.ts +7 -0
- package/package.json +4 -4
- package/lib/analytics/utility/debug.d.ts +0 -3
package/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import get$2 from 'lodash/get';
|
|
2
2
|
import unionBy from 'lodash/unionBy';
|
|
3
3
|
import { buildEmptyCache, buildTrackEvent, buildIdentifyEvent, buildPageEvent } from '@ninetailed/experience.js-shared';
|
|
4
|
-
import
|
|
4
|
+
import { diary, enable } from 'diary';
|
|
5
5
|
import Analytics from 'analytics';
|
|
6
6
|
import flatten from 'lodash/flatten';
|
|
7
7
|
import find from 'lodash/find';
|
|
@@ -2236,15 +2236,6 @@ const buildClientNinetailedRequestContext = () => ({
|
|
|
2236
2236
|
}
|
|
2237
2237
|
});
|
|
2238
2238
|
|
|
2239
|
-
const enable = () => {
|
|
2240
|
-
loglevel.enableAll();
|
|
2241
|
-
};
|
|
2242
|
-
const disable = () => {
|
|
2243
|
-
loglevel.disableAll();
|
|
2244
|
-
};
|
|
2245
|
-
const Logger = loglevel.getLogger('ninetailed:experience.js');
|
|
2246
|
-
const log = Logger.log;
|
|
2247
|
-
|
|
2248
2239
|
const BASE_URL = 'https://api.ninetailed.co';
|
|
2249
2240
|
|
|
2250
2241
|
const fetchWithTimeout = (input, init) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -2253,7 +2244,7 @@ const fetchWithTimeout = (input, init) => __awaiter(void 0, void 0, void 0, func
|
|
|
2253
2244
|
} = init;
|
|
2254
2245
|
const controller = new AbortController();
|
|
2255
2246
|
const id = setTimeout(() => {
|
|
2256
|
-
|
|
2247
|
+
logger.error(new Error('Ninetailed Request timed out.'));
|
|
2257
2248
|
controller.abort();
|
|
2258
2249
|
}, timeout);
|
|
2259
2250
|
const response = yield fetch(input, Object.assign(Object.assign({}, init), {
|
|
@@ -2276,14 +2267,14 @@ class NinetailedApiClient {
|
|
|
2276
2267
|
|
|
2277
2268
|
profile(options) {
|
|
2278
2269
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2279
|
-
|
|
2270
|
+
logger.info('Sending Profile Request.');
|
|
2280
2271
|
const {
|
|
2281
2272
|
events
|
|
2282
2273
|
} = options; // TODO throw error if not all events have same anon id.
|
|
2283
2274
|
|
|
2284
2275
|
const anonymousId = events[0].anonymousId;
|
|
2285
2276
|
const body = options;
|
|
2286
|
-
|
|
2277
|
+
logger.debug('Profile Request Body: ', body);
|
|
2287
2278
|
|
|
2288
2279
|
try {
|
|
2289
2280
|
const request = yield fetchWithTimeout(`${this.url}/v1/organizations/${this.clientId}/environments/${this.environment}/profiles/${anonymousId}/events`, {
|
|
@@ -2294,15 +2285,15 @@ class NinetailedApiClient {
|
|
|
2294
2285
|
body: JSON.stringify(body),
|
|
2295
2286
|
timeout: options.timeout || 3000
|
|
2296
2287
|
});
|
|
2297
|
-
|
|
2288
|
+
logger.debug('Profile Request: ', request);
|
|
2298
2289
|
const {
|
|
2299
2290
|
data
|
|
2300
2291
|
} = yield request.json();
|
|
2301
|
-
|
|
2292
|
+
logger.debug('Profile Request completed.');
|
|
2302
2293
|
return data;
|
|
2303
2294
|
} catch (error) {
|
|
2304
2295
|
// TODO only during debug.
|
|
2305
|
-
|
|
2296
|
+
logger.error(error);
|
|
2306
2297
|
throw error;
|
|
2307
2298
|
}
|
|
2308
2299
|
});
|
|
@@ -2310,259 +2301,6 @@ class NinetailedApiClient {
|
|
|
2310
2301
|
|
|
2311
2302
|
}
|
|
2312
2303
|
|
|
2313
|
-
const NINETAILED_TRACKER_EVENTS = {
|
|
2314
|
-
/**
|
|
2315
|
-
* `profile` - Fires when the profile is returned by the cdp API.
|
|
2316
|
-
*/
|
|
2317
|
-
profile: 'profile'
|
|
2318
|
-
};
|
|
2319
|
-
const PLUGIN_NAME = 'ninetailed';
|
|
2320
|
-
|
|
2321
|
-
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
2322
|
-
/**
|
|
2323
|
-
* Similar to _.throttle but waits for the promise to resolve. There is no
|
|
2324
|
-
* wait time because you can simply await `Promise.timeout` inside `fn`
|
|
2325
|
-
* to wait some time before the next call.
|
|
2326
|
-
*/
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
function asyncThrottle(fn) {
|
|
2330
|
-
let runningPromise;
|
|
2331
|
-
let queuedPromise;
|
|
2332
|
-
let nextArgs;
|
|
2333
|
-
return args => __awaiter(this, void 0, void 0, function* () {
|
|
2334
|
-
if (runningPromise) {
|
|
2335
|
-
nextArgs = args;
|
|
2336
|
-
|
|
2337
|
-
if (queuedPromise) {
|
|
2338
|
-
return queuedPromise;
|
|
2339
|
-
} else {
|
|
2340
|
-
queuedPromise = runningPromise.then(() => {
|
|
2341
|
-
queuedPromise = undefined;
|
|
2342
|
-
runningPromise = fn(nextArgs);
|
|
2343
|
-
return runningPromise;
|
|
2344
|
-
});
|
|
2345
|
-
return queuedPromise;
|
|
2346
|
-
}
|
|
2347
|
-
} else {
|
|
2348
|
-
runningPromise = fn(args);
|
|
2349
|
-
return runningPromise;
|
|
2350
|
-
}
|
|
2351
|
-
});
|
|
2352
|
-
}
|
|
2353
|
-
const ninetailedPlugin = ({
|
|
2354
|
-
clientId,
|
|
2355
|
-
environment,
|
|
2356
|
-
preview: _preview = false,
|
|
2357
|
-
url,
|
|
2358
|
-
profile,
|
|
2359
|
-
locale
|
|
2360
|
-
}) => {
|
|
2361
|
-
let isInitialized = false;
|
|
2362
|
-
const apiClient = new NinetailedApiClient({
|
|
2363
|
-
clientId,
|
|
2364
|
-
environment,
|
|
2365
|
-
url
|
|
2366
|
-
});
|
|
2367
|
-
let queue = [];
|
|
2368
|
-
let localOnly = false;
|
|
2369
|
-
|
|
2370
|
-
const flush = instance => __awaiter(void 0, void 0, void 0, function* () {
|
|
2371
|
-
const events = Object.assign([], queue);
|
|
2372
|
-
log('Start flushing events.');
|
|
2373
|
-
queue = [];
|
|
2374
|
-
|
|
2375
|
-
if (!events.length) {
|
|
2376
|
-
return;
|
|
2377
|
-
}
|
|
2378
|
-
|
|
2379
|
-
const cache = get({
|
|
2380
|
-
instance
|
|
2381
|
-
});
|
|
2382
|
-
|
|
2383
|
-
try {
|
|
2384
|
-
log('Cache: ', cache);
|
|
2385
|
-
const {
|
|
2386
|
-
profile,
|
|
2387
|
-
signals,
|
|
2388
|
-
traitsUpdatedAt,
|
|
2389
|
-
sessions
|
|
2390
|
-
} = yield apiClient.profile({
|
|
2391
|
-
events,
|
|
2392
|
-
signals: get$2(cache, 'signals', {}),
|
|
2393
|
-
traits: get$2(cache, 'traits', {}),
|
|
2394
|
-
traitsUpdatedAt: get$2(cache, 'traitsUpdatedAt', new Date().toISOString()),
|
|
2395
|
-
localOnly,
|
|
2396
|
-
preview: _preview,
|
|
2397
|
-
options: {
|
|
2398
|
-
locale: locale || 'en'
|
|
2399
|
-
},
|
|
2400
|
-
sessions: get$2(cache, 'sessions', [])
|
|
2401
|
-
});
|
|
2402
|
-
log('Profile from api: ', profile);
|
|
2403
|
-
const updatedCache = set({
|
|
2404
|
-
id: profile.id,
|
|
2405
|
-
random: profile.random,
|
|
2406
|
-
audiences: profile.audiences,
|
|
2407
|
-
traits: profile.traits,
|
|
2408
|
-
location: profile.location,
|
|
2409
|
-
signals,
|
|
2410
|
-
traitsUpdatedAt,
|
|
2411
|
-
sessions,
|
|
2412
|
-
session: profile.session
|
|
2413
|
-
}, {
|
|
2414
|
-
instance
|
|
2415
|
-
});
|
|
2416
|
-
instance.dispatch({
|
|
2417
|
-
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
2418
|
-
profile,
|
|
2419
|
-
cache: updatedCache
|
|
2420
|
-
});
|
|
2421
|
-
} catch (error) {
|
|
2422
|
-
log('An error occurred during flushing the events: ', error);
|
|
2423
|
-
instance.dispatch({
|
|
2424
|
-
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
2425
|
-
profile: {
|
|
2426
|
-
id: cache.id,
|
|
2427
|
-
random: cache.random,
|
|
2428
|
-
audiences: cache.audiences,
|
|
2429
|
-
traits: cache.traits,
|
|
2430
|
-
location: cache.location,
|
|
2431
|
-
session: cache.session
|
|
2432
|
-
},
|
|
2433
|
-
cache
|
|
2434
|
-
});
|
|
2435
|
-
} // This is necessary to make sure that the cache is updated before the next flush is performed
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
yield delay(20);
|
|
2439
|
-
}); // const throttledFlush = throttle(flush, 200, {
|
|
2440
|
-
// leading: false,
|
|
2441
|
-
// trailing: true,
|
|
2442
|
-
// });
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
const throttledFlush = asyncThrottle(flush);
|
|
2446
|
-
|
|
2447
|
-
const createEvent = (event, instance) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2448
|
-
queue = unionBy([event], queue, 'messageId');
|
|
2449
|
-
throttledFlush(instance);
|
|
2450
|
-
});
|
|
2451
|
-
|
|
2452
|
-
return {
|
|
2453
|
-
name: 'ninetailed',
|
|
2454
|
-
config: {},
|
|
2455
|
-
initialize: ({
|
|
2456
|
-
instance
|
|
2457
|
-
}) => {
|
|
2458
|
-
const cache = get({
|
|
2459
|
-
instance
|
|
2460
|
-
});
|
|
2461
|
-
|
|
2462
|
-
if (profile) {
|
|
2463
|
-
instance.storage.setItem('__anon_id', profile.id);
|
|
2464
|
-
initialize({
|
|
2465
|
-
instance
|
|
2466
|
-
}, Object.assign(Object.assign({}, cache), {
|
|
2467
|
-
id: profile.id
|
|
2468
|
-
}));
|
|
2469
|
-
} else {
|
|
2470
|
-
const anonymousId = instance.storage.getItem('__anon_id');
|
|
2471
|
-
initialize({
|
|
2472
|
-
instance
|
|
2473
|
-
}, Object.assign(Object.assign({}, cache), {
|
|
2474
|
-
id: anonymousId
|
|
2475
|
-
}));
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
isInitialized = true;
|
|
2479
|
-
},
|
|
2480
|
-
page: ({
|
|
2481
|
-
payload,
|
|
2482
|
-
instance
|
|
2483
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2484
|
-
log('Sending Page event.');
|
|
2485
|
-
const ctx = buildClientNinetailedRequestContext();
|
|
2486
|
-
localOnly = payload.localOnly;
|
|
2487
|
-
yield createEvent(buildPageEvent({
|
|
2488
|
-
// doing this here as the anonymous id is set to late from init
|
|
2489
|
-
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
2490
|
-
messageId: payload.meta.rid,
|
|
2491
|
-
timestamp: payload.meta.ts,
|
|
2492
|
-
properties: payload.properties,
|
|
2493
|
-
ctx
|
|
2494
|
-
}), instance);
|
|
2495
|
-
}),
|
|
2496
|
-
track: ({
|
|
2497
|
-
payload,
|
|
2498
|
-
instance
|
|
2499
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2500
|
-
log('Sending Track event.');
|
|
2501
|
-
const ctx = buildClientNinetailedRequestContext();
|
|
2502
|
-
localOnly = payload.localOnly;
|
|
2503
|
-
yield createEvent(buildTrackEvent({
|
|
2504
|
-
// doing this here as the anonymous id is set to late from init
|
|
2505
|
-
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
2506
|
-
messageId: payload.meta.rid,
|
|
2507
|
-
timestamp: payload.meta.ts,
|
|
2508
|
-
event: payload.event,
|
|
2509
|
-
properties: payload.properties,
|
|
2510
|
-
ctx
|
|
2511
|
-
}), instance);
|
|
2512
|
-
}),
|
|
2513
|
-
identify: ({
|
|
2514
|
-
payload,
|
|
2515
|
-
instance
|
|
2516
|
-
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2517
|
-
log('Sending Identify event.');
|
|
2518
|
-
localOnly = payload.localOnly;
|
|
2519
|
-
const ctx = buildClientNinetailedRequestContext();
|
|
2520
|
-
yield createEvent(buildIdentifyEvent({
|
|
2521
|
-
// doing this here as the anonymous id is set to late from init
|
|
2522
|
-
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
2523
|
-
messageId: payload.meta.rid,
|
|
2524
|
-
timestamp: payload.meta.ts,
|
|
2525
|
-
traits: payload.traits,
|
|
2526
|
-
userId: payload.userId,
|
|
2527
|
-
ctx
|
|
2528
|
-
}), instance);
|
|
2529
|
-
}),
|
|
2530
|
-
loaded: () => {
|
|
2531
|
-
return isInitialized;
|
|
2532
|
-
},
|
|
2533
|
-
methods: {
|
|
2534
|
-
reset: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2535
|
-
const instance = args[args.length - 1];
|
|
2536
|
-
const cache = reset({
|
|
2537
|
-
instance
|
|
2538
|
-
});
|
|
2539
|
-
yield instance.storage.setItem('__anon_id', cache.id);
|
|
2540
|
-
instance.dispatch({
|
|
2541
|
-
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
2542
|
-
profile: {
|
|
2543
|
-
id: cache.id,
|
|
2544
|
-
random: 0,
|
|
2545
|
-
audiences: [],
|
|
2546
|
-
traits: {},
|
|
2547
|
-
location: {}
|
|
2548
|
-
},
|
|
2549
|
-
cache: cache
|
|
2550
|
-
});
|
|
2551
|
-
yield delay(10);
|
|
2552
|
-
}),
|
|
2553
|
-
debug: enabled => {
|
|
2554
|
-
if (enabled) {
|
|
2555
|
-
enable();
|
|
2556
|
-
log('Debug mode is now enabled.');
|
|
2557
|
-
} else {
|
|
2558
|
-
disable();
|
|
2559
|
-
log('Debug mode is now disabled.');
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
};
|
|
2564
|
-
};
|
|
2565
|
-
|
|
2566
2304
|
var objectDefineProperties = {};
|
|
2567
2305
|
|
|
2568
2306
|
var DESCRIPTORS$1 = descriptors;
|
|
@@ -3023,34 +2761,429 @@ for (var COLLECTION_NAME in DOMIterables) {
|
|
|
3023
2761
|
|
|
3024
2762
|
handlePrototype(DOMTokenListPrototype, 'DOMTokenList');
|
|
3025
2763
|
|
|
3026
|
-
class
|
|
3027
|
-
constructor({
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
2764
|
+
class Logger {
|
|
2765
|
+
constructor() {
|
|
2766
|
+
this.name = '@ninetailed/experience.js';
|
|
2767
|
+
this.sinks = [];
|
|
2768
|
+
this.diary = diary(this.name, this.onLogEvent.bind(this));
|
|
2769
|
+
enable(this.name);
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
addSink(sink) {
|
|
2773
|
+
this.sinks = [...this.sinks, sink];
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
removeSink(name) {
|
|
2777
|
+
this.sinks = this.sinks.filter(sink => sink.name !== name);
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
debug(message, ...args) {
|
|
2781
|
+
this.diary.debug(message, ...args);
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
info(message, ...args) {
|
|
2785
|
+
this.diary.info(message, ...args);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
log(message, ...args) {
|
|
2789
|
+
this.diary.log(message, ...args);
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
warn(message, ...args) {
|
|
2793
|
+
this.diary.warn(message, ...args);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
error(message, ...args) {
|
|
2797
|
+
this.diary.error(message, ...args);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
fatal(message, ...args) {
|
|
2801
|
+
this.diary.fatal(message, ...args);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
onLogEvent(event) {
|
|
2805
|
+
this.sinks.forEach(sink => {
|
|
2806
|
+
if (sink[event.level]) {
|
|
2807
|
+
sink[event.level](event.message, ...event.extra);
|
|
2808
|
+
}
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
}
|
|
2813
|
+
const logger = new Logger();
|
|
2814
|
+
|
|
2815
|
+
class ConsoleLogSink {
|
|
2816
|
+
constructor() {
|
|
2817
|
+
this.name = "ConsoleLogSink";
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
debug(message, ...args) {
|
|
2821
|
+
console.debug(message, ...args);
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
info(message, ...args) {
|
|
2825
|
+
console.info(message, ...args);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
log(message, ...args) {
|
|
2829
|
+
console.log(message, ...args);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
warn(message, ...args) {
|
|
2833
|
+
console.warn(message, ...args);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
error(message, ...args) {
|
|
2837
|
+
console.error(message, ...args);
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
fatal(message, ...args) {
|
|
2841
|
+
console.error(message, ...args);
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
class OnLogLogSink {
|
|
2847
|
+
constructor(onLog) {
|
|
2848
|
+
this.onLog = onLog;
|
|
2849
|
+
this.name = 'OnLogLogSink';
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
debug(message, ...args) {
|
|
2853
|
+
this.onLog(message, ...args);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
info(message, ...args) {
|
|
2857
|
+
this.onLog(message, ...args);
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
log(message, ...args) {
|
|
2861
|
+
this.onLog(message, ...args);
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
warn(message, ...args) {
|
|
2865
|
+
this.onLog(message, ...args);
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
class OnErrorLogSink {
|
|
2871
|
+
constructor(onError) {
|
|
2872
|
+
this.onError = onError;
|
|
2873
|
+
this.name = 'OnErrorLogSink';
|
|
2874
|
+
}
|
|
3041
2875
|
|
|
3042
|
-
|
|
2876
|
+
error(message, ...args) {
|
|
2877
|
+
this.onError(message, ...args);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
fatal(message, ...args) {
|
|
2881
|
+
this.onError(message, ...args);
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
const NINETAILED_TRACKER_EVENTS = {
|
|
2887
|
+
/**
|
|
2888
|
+
* `profile` - Fires when the profile is returned by the cdp API.
|
|
2889
|
+
*/
|
|
2890
|
+
profile: 'profile'
|
|
2891
|
+
};
|
|
2892
|
+
const PLUGIN_NAME = 'ninetailed';
|
|
2893
|
+
const DEBUG_FLAG_KEY = 'nt-debug';
|
|
2894
|
+
|
|
2895
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
2896
|
+
/**
|
|
2897
|
+
* Similar to _.throttle but waits for the promise to resolve. There is no
|
|
2898
|
+
* wait time because you can simply await `Promise.timeout` inside `fn`
|
|
2899
|
+
* to wait some time before the next call.
|
|
2900
|
+
*/
|
|
2901
|
+
|
|
2902
|
+
|
|
2903
|
+
function asyncThrottle(fn) {
|
|
2904
|
+
let runningPromise;
|
|
2905
|
+
let queuedPromise;
|
|
2906
|
+
let nextArgs;
|
|
2907
|
+
return args => __awaiter(this, void 0, void 0, function* () {
|
|
2908
|
+
if (runningPromise) {
|
|
2909
|
+
nextArgs = args;
|
|
2910
|
+
|
|
2911
|
+
if (queuedPromise) {
|
|
2912
|
+
return queuedPromise;
|
|
2913
|
+
} else {
|
|
2914
|
+
queuedPromise = runningPromise.then(() => {
|
|
2915
|
+
queuedPromise = undefined;
|
|
2916
|
+
runningPromise = fn(nextArgs);
|
|
2917
|
+
return runningPromise;
|
|
2918
|
+
});
|
|
2919
|
+
return queuedPromise;
|
|
2920
|
+
}
|
|
2921
|
+
} else {
|
|
2922
|
+
runningPromise = fn(args);
|
|
2923
|
+
return runningPromise;
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
const ninetailedPlugin = ({
|
|
2928
|
+
clientId,
|
|
2929
|
+
environment,
|
|
2930
|
+
preview: _preview = false,
|
|
2931
|
+
url,
|
|
2932
|
+
profile,
|
|
2933
|
+
locale
|
|
2934
|
+
}) => {
|
|
2935
|
+
let isInitialized = false;
|
|
2936
|
+
const apiClient = new NinetailedApiClient({
|
|
2937
|
+
clientId,
|
|
2938
|
+
environment,
|
|
2939
|
+
url
|
|
2940
|
+
});
|
|
2941
|
+
let queue = [];
|
|
2942
|
+
let localOnly = false;
|
|
2943
|
+
|
|
2944
|
+
const flush = instance => __awaiter(void 0, void 0, void 0, function* () {
|
|
2945
|
+
const events = Object.assign([], queue);
|
|
2946
|
+
logger.info('Start flushing events.');
|
|
2947
|
+
queue = [];
|
|
2948
|
+
|
|
2949
|
+
if (!events.length) {
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
const cache = get({
|
|
2954
|
+
instance
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
try {
|
|
2958
|
+
logger.debug('Cache: ', cache);
|
|
2959
|
+
const {
|
|
2960
|
+
profile,
|
|
2961
|
+
signals,
|
|
2962
|
+
traitsUpdatedAt,
|
|
2963
|
+
sessions
|
|
2964
|
+
} = yield apiClient.profile({
|
|
2965
|
+
events,
|
|
2966
|
+
signals: get$2(cache, 'signals', {}),
|
|
2967
|
+
traits: get$2(cache, 'traits', {}),
|
|
2968
|
+
traitsUpdatedAt: get$2(cache, 'traitsUpdatedAt', new Date().toISOString()),
|
|
2969
|
+
localOnly,
|
|
2970
|
+
preview: _preview,
|
|
2971
|
+
options: {
|
|
2972
|
+
locale: locale || 'en'
|
|
2973
|
+
},
|
|
2974
|
+
sessions: get$2(cache, 'sessions', [])
|
|
2975
|
+
});
|
|
2976
|
+
logger.debug('Profile from api: ', profile);
|
|
2977
|
+
const updatedCache = set({
|
|
2978
|
+
id: profile.id,
|
|
2979
|
+
random: profile.random,
|
|
2980
|
+
audiences: profile.audiences,
|
|
2981
|
+
traits: profile.traits,
|
|
2982
|
+
location: profile.location,
|
|
2983
|
+
signals,
|
|
2984
|
+
traitsUpdatedAt,
|
|
2985
|
+
sessions,
|
|
2986
|
+
session: profile.session
|
|
2987
|
+
}, {
|
|
2988
|
+
instance
|
|
2989
|
+
});
|
|
2990
|
+
instance.dispatch({
|
|
2991
|
+
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
2992
|
+
profile,
|
|
2993
|
+
cache: updatedCache
|
|
2994
|
+
});
|
|
2995
|
+
} catch (error) {
|
|
2996
|
+
logger.debug('An error occurred during flushing the events: ', error);
|
|
2997
|
+
instance.dispatch({
|
|
2998
|
+
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
2999
|
+
profile: {
|
|
3000
|
+
id: cache.id,
|
|
3001
|
+
random: cache.random,
|
|
3002
|
+
audiences: cache.audiences,
|
|
3003
|
+
traits: cache.traits,
|
|
3004
|
+
location: cache.location,
|
|
3005
|
+
session: cache.session
|
|
3006
|
+
},
|
|
3007
|
+
cache
|
|
3008
|
+
});
|
|
3009
|
+
} // This is necessary to make sure that the cache is updated before the next flush is performed
|
|
3010
|
+
|
|
3011
|
+
|
|
3012
|
+
yield delay(20);
|
|
3013
|
+
}); // const throttledFlush = throttle(flush, 200, {
|
|
3014
|
+
// leading: false,
|
|
3015
|
+
// trailing: true,
|
|
3016
|
+
// });
|
|
3017
|
+
|
|
3018
|
+
|
|
3019
|
+
const throttledFlush = asyncThrottle(flush);
|
|
3020
|
+
|
|
3021
|
+
const createEvent = (event, instance) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3022
|
+
queue = unionBy([event], queue, 'messageId');
|
|
3023
|
+
throttledFlush(instance);
|
|
3024
|
+
});
|
|
3025
|
+
|
|
3026
|
+
return {
|
|
3027
|
+
name: 'ninetailed',
|
|
3028
|
+
config: {},
|
|
3029
|
+
initialize: ({
|
|
3030
|
+
instance
|
|
3031
|
+
}) => {
|
|
3032
|
+
if (instance.storage.getItem(DEBUG_FLAG_KEY)) {
|
|
3033
|
+
logger.addSink(new ConsoleLogSink());
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
const cache = get({
|
|
3037
|
+
instance
|
|
3038
|
+
});
|
|
3039
|
+
|
|
3040
|
+
if (profile) {
|
|
3041
|
+
instance.storage.setItem('__anon_id', profile.id);
|
|
3042
|
+
initialize({
|
|
3043
|
+
instance
|
|
3044
|
+
}, Object.assign(Object.assign({}, cache), {
|
|
3045
|
+
id: profile.id
|
|
3046
|
+
}));
|
|
3047
|
+
} else {
|
|
3048
|
+
const anonymousId = instance.storage.getItem('__anon_id');
|
|
3049
|
+
initialize({
|
|
3050
|
+
instance
|
|
3051
|
+
}, Object.assign(Object.assign({}, cache), {
|
|
3052
|
+
id: anonymousId
|
|
3053
|
+
}));
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
isInitialized = true;
|
|
3057
|
+
logger.debug('Ninetailed Core plugin initialized.');
|
|
3058
|
+
},
|
|
3059
|
+
page: ({
|
|
3060
|
+
payload,
|
|
3061
|
+
instance
|
|
3062
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3063
|
+
logger.info('Sending Page event.');
|
|
3064
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3065
|
+
localOnly = payload.localOnly;
|
|
3066
|
+
yield createEvent(buildPageEvent({
|
|
3067
|
+
// doing this here as the anonymous id is set to late from init
|
|
3068
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3069
|
+
messageId: payload.meta.rid,
|
|
3070
|
+
timestamp: payload.meta.ts,
|
|
3071
|
+
properties: payload.properties,
|
|
3072
|
+
ctx
|
|
3073
|
+
}), instance);
|
|
3074
|
+
}),
|
|
3075
|
+
track: ({
|
|
3076
|
+
payload,
|
|
3077
|
+
instance
|
|
3078
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3079
|
+
logger.info('Sending Track event.');
|
|
3080
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3081
|
+
localOnly = payload.localOnly;
|
|
3082
|
+
yield createEvent(buildTrackEvent({
|
|
3083
|
+
// doing this here as the anonymous id is set to late from init
|
|
3084
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3085
|
+
messageId: payload.meta.rid,
|
|
3086
|
+
timestamp: payload.meta.ts,
|
|
3087
|
+
event: payload.event,
|
|
3088
|
+
properties: payload.properties,
|
|
3089
|
+
ctx
|
|
3090
|
+
}), instance);
|
|
3091
|
+
}),
|
|
3092
|
+
identify: ({
|
|
3093
|
+
payload,
|
|
3094
|
+
instance
|
|
3095
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3096
|
+
logger.info('Sending Identify event.');
|
|
3097
|
+
localOnly = payload.localOnly;
|
|
3098
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3099
|
+
yield createEvent(buildIdentifyEvent({
|
|
3100
|
+
// doing this here as the anonymous id is set to late from init
|
|
3101
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3102
|
+
messageId: payload.meta.rid,
|
|
3103
|
+
timestamp: payload.meta.ts,
|
|
3104
|
+
traits: payload.traits,
|
|
3105
|
+
userId: payload.userId,
|
|
3106
|
+
ctx
|
|
3107
|
+
}), instance);
|
|
3108
|
+
}),
|
|
3109
|
+
loaded: () => {
|
|
3110
|
+
return isInitialized;
|
|
3111
|
+
},
|
|
3112
|
+
methods: {
|
|
3113
|
+
reset: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3114
|
+
const instance = args[args.length - 1];
|
|
3115
|
+
const cache = reset({
|
|
3116
|
+
instance
|
|
3117
|
+
});
|
|
3118
|
+
yield instance.storage.setItem('__anon_id', cache.id);
|
|
3119
|
+
instance.dispatch({
|
|
3120
|
+
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
3121
|
+
profile: {
|
|
3122
|
+
id: cache.id,
|
|
3123
|
+
random: 0,
|
|
3124
|
+
audiences: [],
|
|
3125
|
+
traits: {},
|
|
3126
|
+
location: {}
|
|
3127
|
+
},
|
|
3128
|
+
cache: cache
|
|
3129
|
+
});
|
|
3130
|
+
yield delay(10);
|
|
3131
|
+
}),
|
|
3132
|
+
debug: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3133
|
+
const enabled = args[0];
|
|
3134
|
+
const instance = args[args.length - 1];
|
|
3135
|
+
const consoleLogSink = new ConsoleLogSink();
|
|
3136
|
+
|
|
3137
|
+
if (enabled) {
|
|
3138
|
+
yield instance.storage.setItem(DEBUG_FLAG_KEY, true); // cleanup other console log sinks if there are already some.
|
|
3139
|
+
|
|
3140
|
+
logger.removeSink(consoleLogSink.name);
|
|
3141
|
+
logger.addSink(consoleLogSink);
|
|
3142
|
+
logger.info('Debug mode enabled.');
|
|
3143
|
+
} else {
|
|
3144
|
+
yield instance.storage.removeItem(DEBUG_FLAG_KEY);
|
|
3145
|
+
logger.info('Debug mode disabled.');
|
|
3146
|
+
logger.removeSink(consoleLogSink.name);
|
|
3147
|
+
}
|
|
3148
|
+
})
|
|
3149
|
+
}
|
|
3150
|
+
};
|
|
3151
|
+
};
|
|
3152
|
+
|
|
3153
|
+
class Ninetailed {
|
|
3154
|
+
constructor({
|
|
3155
|
+
clientId,
|
|
3156
|
+
environment,
|
|
3157
|
+
preview
|
|
3158
|
+
}, {
|
|
3159
|
+
plugins,
|
|
3160
|
+
url,
|
|
3161
|
+
profile,
|
|
3162
|
+
locale,
|
|
3163
|
+
requestTimeout,
|
|
3164
|
+
onLog,
|
|
3165
|
+
onError
|
|
3166
|
+
} = {}) {
|
|
3167
|
+
this.isInitialized = false;
|
|
3168
|
+
|
|
3169
|
+
this.page = (data, options) => __awaiter(this, void 0, void 0, function* () {
|
|
3170
|
+
yield this.waitUntilInitialized();
|
|
3171
|
+
return this.instance.page(data, this.buildOptions(options));
|
|
3172
|
+
});
|
|
3173
|
+
|
|
3174
|
+
this.track = (event, payload, options) => __awaiter(this, void 0, void 0, function* () {
|
|
3175
|
+
yield this.waitUntilInitialized();
|
|
3043
3176
|
return this.instance.track(event, payload, this.buildOptions(options));
|
|
3044
|
-
};
|
|
3177
|
+
});
|
|
3045
3178
|
|
|
3046
|
-
this.trackHasSeenComponent = payload => {
|
|
3179
|
+
this.trackHasSeenComponent = payload => __awaiter(this, void 0, void 0, function* () {
|
|
3047
3180
|
return this.track('hasSeenComponent', payload, {
|
|
3048
3181
|
plugins: {
|
|
3049
3182
|
all: true,
|
|
3050
3183
|
ninetailed: false
|
|
3051
3184
|
}
|
|
3052
3185
|
});
|
|
3053
|
-
};
|
|
3186
|
+
});
|
|
3054
3187
|
|
|
3055
3188
|
this.trackExperience = payload => {
|
|
3056
3189
|
return this.track('nt_experience', payload, {
|
|
@@ -3061,16 +3194,21 @@ class Ninetailed {
|
|
|
3061
3194
|
});
|
|
3062
3195
|
};
|
|
3063
3196
|
|
|
3064
|
-
this.identify = (uid, traits, options) => {
|
|
3197
|
+
this.identify = (uid, traits, options) => __awaiter(this, void 0, void 0, function* () {
|
|
3198
|
+
yield this.waitUntilInitialized();
|
|
3065
3199
|
return this.instance.identify(uid, traits, this.buildOptions(options));
|
|
3066
|
-
};
|
|
3200
|
+
});
|
|
3067
3201
|
|
|
3068
3202
|
this.reset = () => {
|
|
3069
|
-
|
|
3203
|
+
this.onIsInitialized(() => {
|
|
3204
|
+
this.instance.plugins[PLUGIN_NAME].reset();
|
|
3205
|
+
});
|
|
3070
3206
|
};
|
|
3071
3207
|
|
|
3072
3208
|
this.debug = enabled => {
|
|
3073
|
-
|
|
3209
|
+
this.onIsInitialized(() => {
|
|
3210
|
+
this.instance.plugins[PLUGIN_NAME].debug(enabled);
|
|
3211
|
+
});
|
|
3074
3212
|
};
|
|
3075
3213
|
|
|
3076
3214
|
this.onProfileChange = cb => {
|
|
@@ -3086,6 +3224,25 @@ class Ninetailed {
|
|
|
3086
3224
|
});
|
|
3087
3225
|
};
|
|
3088
3226
|
|
|
3227
|
+
this.onIsInitialized = onIsInitialized => {
|
|
3228
|
+
if (typeof onIsInitialized === 'function') {
|
|
3229
|
+
if (this.isInitialized) {
|
|
3230
|
+
onIsInitialized();
|
|
3231
|
+
} else {
|
|
3232
|
+
const detachOnReadyListener = this.instance.on('ready', () => {
|
|
3233
|
+
onIsInitialized();
|
|
3234
|
+
detachOnReadyListener();
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
|
|
3240
|
+
this.waitUntilInitialized = () => {
|
|
3241
|
+
return new Promise(resolve => {
|
|
3242
|
+
this.onIsInitialized(resolve);
|
|
3243
|
+
});
|
|
3244
|
+
};
|
|
3245
|
+
|
|
3089
3246
|
this.plugins = flatten(plugins || []);
|
|
3090
3247
|
|
|
3091
3248
|
if (profile) {
|
|
@@ -3104,6 +3261,15 @@ class Ninetailed {
|
|
|
3104
3261
|
};
|
|
3105
3262
|
}
|
|
3106
3263
|
|
|
3264
|
+
if (typeof onLog === 'function') {
|
|
3265
|
+
logger.addSink(new OnLogLogSink(onLog));
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
if (typeof onError === 'function') {
|
|
3269
|
+
logger.addSink(new OnErrorLogSink(onError));
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
this.logger = logger;
|
|
3107
3273
|
this.instance = Analytics({
|
|
3108
3274
|
app: 'ninetailed',
|
|
3109
3275
|
plugins: [...this.plugins, ninetailedPlugin({
|
|
@@ -3115,6 +3281,11 @@ class Ninetailed {
|
|
|
3115
3281
|
requestTimeout,
|
|
3116
3282
|
preview
|
|
3117
3283
|
})]
|
|
3284
|
+
});
|
|
3285
|
+
const detachOnReadyListener = this.instance.on('ready', () => {
|
|
3286
|
+
this.isInitialized = true;
|
|
3287
|
+
logger.info('Ninetailed Experience.js SDK is completely initialized.');
|
|
3288
|
+
detachOnReadyListener();
|
|
3118
3289
|
}); // put in private method
|
|
3119
3290
|
|
|
3120
3291
|
this.onProfileChange(profileState => {
|
|
@@ -3909,13 +4080,13 @@ const isExperienceMatch = ({
|
|
|
3909
4080
|
profile
|
|
3910
4081
|
}) => {
|
|
3911
4082
|
const trafficRandom = getTrafficRandom(profile, experience);
|
|
3912
|
-
|
|
4083
|
+
logger.info(`The traffic random factor for experience ${experience.id} is ${trafficRandom}. It's traffic allocation is set to ${experience.trafficAllocation}.`);
|
|
3913
4084
|
const isInTrafficRange = experience.trafficAllocation > trafficRandom;
|
|
3914
4085
|
const matchesAudience = !experience.audience || includes(profile.audiences, experience.audience.id);
|
|
3915
4086
|
const hasActiveExperiment = !!find(activeExperiments, {
|
|
3916
4087
|
id: experience.id
|
|
3917
4088
|
});
|
|
3918
|
-
|
|
4089
|
+
logger.info(`Is the profile in traffic allocation range? ${isInTrafficRange ? 'yes' : 'no'}.\n
|
|
3919
4090
|
Does the profile match the audience of the experience? ${matchesAudience ? 'yes' : 'no'}.\n
|
|
3920
4091
|
Is there an active experiment for this profile? ${hasActiveExperiment ? 'yes' : 'no'}.`);
|
|
3921
4092
|
return isInTrafficRange && (matchesAudience || // if the expriment is active already then it's selectible without further contraints to be fullfilled
|
|
@@ -3944,7 +4115,7 @@ const selectDistribution = ({
|
|
|
3944
4115
|
profile
|
|
3945
4116
|
}) => {
|
|
3946
4117
|
const distributionRandom = getDistributionRandom(profile, experience);
|
|
3947
|
-
log(`The distribution random factor for experience ${experience.id} is ${distributionRandom}. It's distribution is set to ${JSON.stringify(experience.distribution, null, 2)}.`);
|
|
4118
|
+
logger.log(`The distribution random factor for experience ${experience.id} is ${distributionRandom}. It's distribution is set to ${JSON.stringify(experience.distribution, null, 2)}.`);
|
|
3948
4119
|
const distribution = find(experience.distribution, ({
|
|
3949
4120
|
start,
|
|
3950
4121
|
end
|
|
@@ -4340,4 +4511,4 @@ const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
|
4340
4511
|
}), {});
|
|
4341
4512
|
};
|
|
4342
4513
|
|
|
4343
|
-
export { index as Cache, EXPERIENCE_TRAIT_PREFIX, NINETAILED_TRACKER_EVENTS, Ninetailed, PLUGIN_NAME, decodeExperienceVariantsMap, isExperienceMatch, ninetailedPlugin, selectActiveExperiments, selectDistribution, selectEligibleExperiences, selectExperience, selectBaselineWithVariants as selectExperienceBaselineWithVariants, selectVariant as selectExperienceVariant, selectVariants as selectExperienceVariants, selectHasVariants as selectHasExperienceVariants, selectVariant$1 as selectVariant };
|
|
4514
|
+
export { index as Cache, EXPERIENCE_TRAIT_PREFIX, NINETAILED_TRACKER_EVENTS, Ninetailed, PLUGIN_NAME, decodeExperienceVariantsMap, isExperienceMatch, logger, ninetailedPlugin, selectActiveExperiments, selectDistribution, selectEligibleExperiences, selectExperience, selectBaselineWithVariants as selectExperienceBaselineWithVariants, selectVariant as selectExperienceVariant, selectVariants as selectExperienceVariants, selectHasVariants as selectHasExperienceVariants, selectVariant$1 as selectVariant };
|