@ninetailed/experience.js 2.0.1 → 2.1.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 +423 -286
- package/index.umd.js +346 -56
- package/lib/Ninetailed.d.ts +8 -3
- 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,19 +2761,409 @@ 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
|
-
|
|
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
|
+
}
|
|
2875
|
+
|
|
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
|
+
},
|
|
3058
|
+
page: ({
|
|
3059
|
+
payload,
|
|
3060
|
+
instance
|
|
3061
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3062
|
+
logger.info('Sending Page event.');
|
|
3063
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3064
|
+
localOnly = payload.localOnly;
|
|
3065
|
+
yield createEvent(buildPageEvent({
|
|
3066
|
+
// doing this here as the anonymous id is set to late from init
|
|
3067
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3068
|
+
messageId: payload.meta.rid,
|
|
3069
|
+
timestamp: payload.meta.ts,
|
|
3070
|
+
properties: payload.properties,
|
|
3071
|
+
ctx
|
|
3072
|
+
}), instance);
|
|
3073
|
+
}),
|
|
3074
|
+
track: ({
|
|
3075
|
+
payload,
|
|
3076
|
+
instance
|
|
3077
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3078
|
+
logger.info('Sending Track event.');
|
|
3079
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3080
|
+
localOnly = payload.localOnly;
|
|
3081
|
+
yield createEvent(buildTrackEvent({
|
|
3082
|
+
// doing this here as the anonymous id is set to late from init
|
|
3083
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3084
|
+
messageId: payload.meta.rid,
|
|
3085
|
+
timestamp: payload.meta.ts,
|
|
3086
|
+
event: payload.event,
|
|
3087
|
+
properties: payload.properties,
|
|
3088
|
+
ctx
|
|
3089
|
+
}), instance);
|
|
3090
|
+
}),
|
|
3091
|
+
identify: ({
|
|
3092
|
+
payload,
|
|
3093
|
+
instance
|
|
3094
|
+
}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3095
|
+
logger.info('Sending Identify event.');
|
|
3096
|
+
localOnly = payload.localOnly;
|
|
3097
|
+
const ctx = buildClientNinetailedRequestContext();
|
|
3098
|
+
yield createEvent(buildIdentifyEvent({
|
|
3099
|
+
// doing this here as the anonymous id is set to late from init
|
|
3100
|
+
anonymousId: (profile === null || profile === void 0 ? void 0 : profile.id) || payload.anonymousId,
|
|
3101
|
+
messageId: payload.meta.rid,
|
|
3102
|
+
timestamp: payload.meta.ts,
|
|
3103
|
+
traits: payload.traits,
|
|
3104
|
+
userId: payload.userId,
|
|
3105
|
+
ctx
|
|
3106
|
+
}), instance);
|
|
3107
|
+
}),
|
|
3108
|
+
loaded: () => {
|
|
3109
|
+
return isInitialized;
|
|
3110
|
+
},
|
|
3111
|
+
methods: {
|
|
3112
|
+
reset: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3113
|
+
const instance = args[args.length - 1];
|
|
3114
|
+
const cache = reset({
|
|
3115
|
+
instance
|
|
3116
|
+
});
|
|
3117
|
+
yield instance.storage.setItem('__anon_id', cache.id);
|
|
3118
|
+
instance.dispatch({
|
|
3119
|
+
type: NINETAILED_TRACKER_EVENTS.profile,
|
|
3120
|
+
profile: {
|
|
3121
|
+
id: cache.id,
|
|
3122
|
+
random: 0,
|
|
3123
|
+
audiences: [],
|
|
3124
|
+
traits: {},
|
|
3125
|
+
location: {}
|
|
3126
|
+
},
|
|
3127
|
+
cache: cache
|
|
3128
|
+
});
|
|
3129
|
+
yield delay(10);
|
|
3130
|
+
}),
|
|
3131
|
+
debug: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3132
|
+
const enabled = args[0];
|
|
3133
|
+
const instance = args[args.length - 1];
|
|
3134
|
+
const consoleLogSink = new ConsoleLogSink();
|
|
3135
|
+
|
|
3136
|
+
if (enabled) {
|
|
3137
|
+
yield instance.storage.setItem(DEBUG_FLAG_KEY, true); // cleanup other console log sinks if there are already some.
|
|
3138
|
+
|
|
3139
|
+
logger.removeSink(consoleLogSink.name);
|
|
3140
|
+
logger.addSink(consoleLogSink);
|
|
3141
|
+
logger.info('Debug mode enabled.');
|
|
3142
|
+
} else {
|
|
3143
|
+
yield instance.storage.removeItem(DEBUG_FLAG_KEY);
|
|
3144
|
+
logger.info('Debug mode disabled.');
|
|
3145
|
+
logger.removeSink(consoleLogSink.name);
|
|
3146
|
+
}
|
|
3147
|
+
})
|
|
3148
|
+
}
|
|
3149
|
+
};
|
|
3150
|
+
};
|
|
3151
|
+
|
|
3152
|
+
class Ninetailed {
|
|
3153
|
+
constructor({
|
|
3154
|
+
clientId,
|
|
3155
|
+
environment,
|
|
3156
|
+
preview
|
|
3157
|
+
}, {
|
|
3158
|
+
plugins,
|
|
3159
|
+
url,
|
|
3160
|
+
profile,
|
|
3161
|
+
locale,
|
|
3162
|
+
requestTimeout,
|
|
3163
|
+
onLog,
|
|
3164
|
+
onError
|
|
3165
|
+
} = {}) {
|
|
3166
|
+
this.page = (data, options) => {
|
|
3039
3167
|
return this.instance.page(data, this.buildOptions(options));
|
|
3040
3168
|
};
|
|
3041
3169
|
|
|
@@ -3104,6 +3232,15 @@ class Ninetailed {
|
|
|
3104
3232
|
};
|
|
3105
3233
|
}
|
|
3106
3234
|
|
|
3235
|
+
if (typeof onLog === 'function') {
|
|
3236
|
+
logger.addSink(new OnLogLogSink(onLog));
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
if (typeof onError === 'function') {
|
|
3240
|
+
logger.addSink(new OnErrorLogSink(onError));
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
this.logger = logger;
|
|
3107
3244
|
this.instance = Analytics({
|
|
3108
3245
|
app: 'ninetailed',
|
|
3109
3246
|
plugins: [...this.plugins, ninetailedPlugin({
|
|
@@ -3909,13 +4046,13 @@ const isExperienceMatch = ({
|
|
|
3909
4046
|
profile
|
|
3910
4047
|
}) => {
|
|
3911
4048
|
const trafficRandom = getTrafficRandom(profile, experience);
|
|
3912
|
-
|
|
4049
|
+
logger.info(`The traffic random factor for experience ${experience.id} is ${trafficRandom}. It's traffic allocation is set to ${experience.trafficAllocation}.`);
|
|
3913
4050
|
const isInTrafficRange = experience.trafficAllocation > trafficRandom;
|
|
3914
4051
|
const matchesAudience = !experience.audience || includes(profile.audiences, experience.audience.id);
|
|
3915
4052
|
const hasActiveExperiment = !!find(activeExperiments, {
|
|
3916
4053
|
id: experience.id
|
|
3917
4054
|
});
|
|
3918
|
-
|
|
4055
|
+
logger.info(`Is the profile in traffic allocation range? ${isInTrafficRange ? 'yes' : 'no'}.\n
|
|
3919
4056
|
Does the profile match the audience of the experience? ${matchesAudience ? 'yes' : 'no'}.\n
|
|
3920
4057
|
Is there an active experiment for this profile? ${hasActiveExperiment ? 'yes' : 'no'}.`);
|
|
3921
4058
|
return isInTrafficRange && (matchesAudience || // if the expriment is active already then it's selectible without further contraints to be fullfilled
|
|
@@ -3944,7 +4081,7 @@ const selectDistribution = ({
|
|
|
3944
4081
|
profile
|
|
3945
4082
|
}) => {
|
|
3946
4083
|
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)}.`);
|
|
4084
|
+
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
4085
|
const distribution = find(experience.distribution, ({
|
|
3949
4086
|
start,
|
|
3950
4087
|
end
|
|
@@ -4340,4 +4477,4 @@ const decodeExperienceVariantsMap = encodedExperienceVariantsMap => {
|
|
|
4340
4477
|
}), {});
|
|
4341
4478
|
};
|
|
4342
4479
|
|
|
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 };
|
|
4480
|
+
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 };
|