@ninetailed/experience.js 2.0.2 → 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.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 * as loglevel from 'loglevel';
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
- log(`Profile Request timed out.`);
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
- log('Sending Profile Request.');
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
- log('Profile Request Body: ', body);
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
- log('Profile Request: ', request);
2288
+ logger.debug('Profile Request: ', request);
2298
2289
  const {
2299
2290
  data
2300
2291
  } = yield request.json();
2301
- log('Profile Request completed.');
2292
+ logger.debug('Profile Request completed.');
2302
2293
  return data;
2303
2294
  } catch (error) {
2304
2295
  // TODO only during debug.
2305
- log(error);
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 Ninetailed {
3027
- constructor({
3028
- clientId,
3029
- environment,
3030
- preview
3031
- }, {
3032
- plugins,
3033
- url,
3034
- profile,
3035
- locale,
3036
- requestTimeout
3037
- } = {}) {
3038
- this.page = (data, options) => {
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
- log(`The traffic random factor for experience ${experience.id} is ${trafficRandom}. It's traffic allocation is set to ${experience.trafficAllocation}.`);
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
- log(`Is the profile in traffic allocation range? ${isInTrafficRange ? 'yes' : 'no'}.\n
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 };