@openreplay/tracker 16.2.0 → 16.2.1
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/dist/cjs/entry.js +211 -67
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +211 -67
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/index.d.ts +2 -9
- package/dist/cjs/main/app/observer/observer.d.ts +1 -1
- package/dist/cjs/main/app/observer/top_observer.d.ts +9 -5
- package/dist/lib/entry.js +211 -67
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +211 -67
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/index.d.ts +2 -9
- package/dist/lib/main/app/observer/observer.d.ts +1 -1
- package/dist/lib/main/app/observer/top_observer.d.ts +9 -5
- package/dist/types/main/app/index.d.ts +2 -9
- package/dist/types/main/app/observer/observer.d.ts +1 -1
- package/dist/types/main/app/observer/top_observer.d.ts +9 -5
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ import type { Options as SessOptions } from './session.js';
|
|
|
13
13
|
import Session from './session.js';
|
|
14
14
|
import Ticker from './ticker.js';
|
|
15
15
|
import { MaintainerOptions } from './nodes/maintainer.js';
|
|
16
|
+
export { InlineCssMode } from './observer/top_observer.js';
|
|
16
17
|
export interface StartOptions {
|
|
17
18
|
userID?: string;
|
|
18
19
|
metadata?: Record<string, string>;
|
|
@@ -63,18 +64,12 @@ type AppOptions = {
|
|
|
63
64
|
__is_snippet: boolean;
|
|
64
65
|
__debug_report_edp: string | null;
|
|
65
66
|
__debug__?: ILogLevel;
|
|
66
|
-
/** @deprecated see canvas prop */
|
|
67
|
-
__save_canvas_locally?: boolean;
|
|
68
|
-
/** @deprecated see canvas prop */
|
|
69
|
-
fixedCanvasScaling?: boolean;
|
|
70
67
|
localStorage: Storage | null;
|
|
71
68
|
sessionStorage: Storage | null;
|
|
72
69
|
forceSingleTab?: boolean;
|
|
73
70
|
/** Sometimes helps to prevent session breaking due to dict reset */
|
|
74
71
|
disableStringDict?: boolean;
|
|
75
72
|
assistSocketHost?: string;
|
|
76
|
-
/** @deprecated see canvas prop */
|
|
77
|
-
disableCanvas?: boolean;
|
|
78
73
|
canvas: {
|
|
79
74
|
disableCanvas?: boolean;
|
|
80
75
|
/**
|
|
@@ -150,7 +145,7 @@ export default class App {
|
|
|
150
145
|
private readonly startCallbacks;
|
|
151
146
|
private readonly stopCallbacks;
|
|
152
147
|
private readonly commitCallbacks;
|
|
153
|
-
readonly options:
|
|
148
|
+
readonly options: Options;
|
|
154
149
|
readonly networkOptions?: NetworkOptions;
|
|
155
150
|
private readonly revID;
|
|
156
151
|
private activityState;
|
|
@@ -172,7 +167,6 @@ export default class App {
|
|
|
172
167
|
private frameOderNumber;
|
|
173
168
|
private features;
|
|
174
169
|
private emptyBatchCounter;
|
|
175
|
-
private inlineCss;
|
|
176
170
|
constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>, signalError: (error: string, apis: string[]) => void, insideIframe: boolean);
|
|
177
171
|
/** used by child iframes for crossdomain only */
|
|
178
172
|
parentActive: boolean;
|
|
@@ -321,4 +315,3 @@ export default class App {
|
|
|
321
315
|
trackWs(channelName: string): (msgType: string, data: string, dir: 'up' | 'down') => void;
|
|
322
316
|
stop(stopWorker?: boolean): void;
|
|
323
317
|
}
|
|
324
|
-
export {};
|
|
@@ -2,10 +2,14 @@ import Observer from './observer.js';
|
|
|
2
2
|
import { Offset } from './iframe_offsets.js';
|
|
3
3
|
import App from '../index.js';
|
|
4
4
|
export declare enum InlineCssMode {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/** default behavior -- will parse and cache the css file on backend */
|
|
6
|
+
Disabled = 0,
|
|
7
|
+
/** will attempt to record the linked css file as AdoptedStyleSheet object */
|
|
8
|
+
Inline = 1,
|
|
9
|
+
/** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
|
|
10
|
+
InlineFetched = 2,
|
|
11
|
+
/** will fetch the file, then save it as plain css inside <style> node */
|
|
12
|
+
PlainFetched = 3
|
|
9
13
|
}
|
|
10
14
|
export interface Options {
|
|
11
15
|
captureIFrames: boolean;
|
|
@@ -15,7 +19,7 @@ export interface Options {
|
|
|
15
19
|
* we will try to parse the css text instead and send it as css rules set
|
|
16
20
|
* can (and probably will) affect performance to certain degree,
|
|
17
21
|
* especially if the css itself is crossdomain
|
|
18
|
-
* @default
|
|
22
|
+
* @default InlineCssMode.None = 0
|
|
19
23
|
* */
|
|
20
24
|
inlineCss: InlineCssMode;
|
|
21
25
|
}
|
package/dist/lib/entry.js
CHANGED
|
@@ -4693,11 +4693,52 @@ class IFrameOffsets {
|
|
|
4693
4693
|
|
|
4694
4694
|
var InlineCssMode;
|
|
4695
4695
|
(function (InlineCssMode) {
|
|
4696
|
-
|
|
4697
|
-
InlineCssMode[InlineCssMode["
|
|
4698
|
-
|
|
4699
|
-
InlineCssMode[InlineCssMode["
|
|
4696
|
+
/** default behavior -- will parse and cache the css file on backend */
|
|
4697
|
+
InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
|
|
4698
|
+
/** will attempt to record the linked css file as AdoptedStyleSheet object */
|
|
4699
|
+
InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
|
|
4700
|
+
/** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
|
|
4701
|
+
InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
|
|
4702
|
+
/** will fetch the file, then save it as plain css inside <style> node */
|
|
4703
|
+
InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
|
|
4700
4704
|
})(InlineCssMode || (InlineCssMode = {}));
|
|
4705
|
+
function getInlineOptions(mode) {
|
|
4706
|
+
switch (mode) {
|
|
4707
|
+
case InlineCssMode.Inline:
|
|
4708
|
+
return {
|
|
4709
|
+
inlineRemoteCss: true,
|
|
4710
|
+
inlinerOptions: {
|
|
4711
|
+
forceFetch: false,
|
|
4712
|
+
forcePlain: false,
|
|
4713
|
+
},
|
|
4714
|
+
};
|
|
4715
|
+
case InlineCssMode.InlineFetched:
|
|
4716
|
+
return {
|
|
4717
|
+
inlineRemoteCss: true,
|
|
4718
|
+
inlinerOptions: {
|
|
4719
|
+
forceFetch: true,
|
|
4720
|
+
forcePlain: false,
|
|
4721
|
+
},
|
|
4722
|
+
};
|
|
4723
|
+
case InlineCssMode.PlainFetched:
|
|
4724
|
+
return {
|
|
4725
|
+
inlineRemoteCss: true,
|
|
4726
|
+
inlinerOptions: {
|
|
4727
|
+
forceFetch: true,
|
|
4728
|
+
forcePlain: true,
|
|
4729
|
+
},
|
|
4730
|
+
};
|
|
4731
|
+
case InlineCssMode.Disabled:
|
|
4732
|
+
default:
|
|
4733
|
+
return {
|
|
4734
|
+
inlineRemoteCss: false,
|
|
4735
|
+
inlinerOptions: {
|
|
4736
|
+
forceFetch: false,
|
|
4737
|
+
forcePlain: false,
|
|
4738
|
+
},
|
|
4739
|
+
};
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4701
4742
|
const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
|
|
4702
4743
|
class TopObserver extends Observer {
|
|
4703
4744
|
constructor(params) {
|
|
@@ -4706,7 +4747,11 @@ class TopObserver extends Observer {
|
|
|
4706
4747
|
disableSprites: false,
|
|
4707
4748
|
inlineCss: 0,
|
|
4708
4749
|
}, params.options);
|
|
4709
|
-
|
|
4750
|
+
const observerOptions = {
|
|
4751
|
+
disableSprites: opts.disableSprites,
|
|
4752
|
+
...getInlineOptions(opts.inlineCss)
|
|
4753
|
+
};
|
|
4754
|
+
super(params.app, true, observerOptions);
|
|
4710
4755
|
this.iframeOffsets = new IFrameOffsets();
|
|
4711
4756
|
this.contextCallbacks = [];
|
|
4712
4757
|
// Attached once per Tracker instance
|
|
@@ -5126,43 +5171,6 @@ function getTimezone() {
|
|
|
5126
5171
|
return `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
5127
5172
|
}
|
|
5128
5173
|
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
5129
|
-
function getInlineOptions(mode) {
|
|
5130
|
-
switch (mode) {
|
|
5131
|
-
case InlineCssMode.RemoteOnly:
|
|
5132
|
-
return {
|
|
5133
|
-
inlineRemoteCss: true,
|
|
5134
|
-
inlinerOptions: {
|
|
5135
|
-
forceFetch: false,
|
|
5136
|
-
forcePlain: false,
|
|
5137
|
-
},
|
|
5138
|
-
};
|
|
5139
|
-
case InlineCssMode.RemoteWithForceFetch:
|
|
5140
|
-
return {
|
|
5141
|
-
inlineRemoteCss: true,
|
|
5142
|
-
inlinerOptions: {
|
|
5143
|
-
forceFetch: true,
|
|
5144
|
-
forcePlain: false,
|
|
5145
|
-
},
|
|
5146
|
-
};
|
|
5147
|
-
case InlineCssMode.All:
|
|
5148
|
-
return {
|
|
5149
|
-
inlineRemoteCss: true,
|
|
5150
|
-
inlinerOptions: {
|
|
5151
|
-
forceFetch: true,
|
|
5152
|
-
forcePlain: true,
|
|
5153
|
-
},
|
|
5154
|
-
};
|
|
5155
|
-
case InlineCssMode.None:
|
|
5156
|
-
default:
|
|
5157
|
-
return {
|
|
5158
|
-
inlineRemoteCss: false,
|
|
5159
|
-
inlinerOptions: {
|
|
5160
|
-
forceFetch: false,
|
|
5161
|
-
forcePlain: false,
|
|
5162
|
-
},
|
|
5163
|
-
};
|
|
5164
|
-
}
|
|
5165
|
-
}
|
|
5166
5174
|
const proto = {
|
|
5167
5175
|
// ask if there are any tabs alive
|
|
5168
5176
|
ask: 'never-gonna-give-you-up',
|
|
@@ -5194,7 +5202,7 @@ class App {
|
|
|
5194
5202
|
this.stopCallbacks = [];
|
|
5195
5203
|
this.commitCallbacks = [];
|
|
5196
5204
|
this.activityState = ActivityState.NotActive;
|
|
5197
|
-
this.version = '16.2.
|
|
5205
|
+
this.version = '16.2.1'; // TODO: version compatability check inside each plugin.
|
|
5198
5206
|
this.socketMode = false;
|
|
5199
5207
|
this.compressionThreshold = 24 * 1000;
|
|
5200
5208
|
this.bc = null;
|
|
@@ -5517,19 +5525,6 @@ class App {
|
|
|
5517
5525
|
this.onUxtCb = [];
|
|
5518
5526
|
this.contextId = Math.random().toString(36).slice(2);
|
|
5519
5527
|
this.projectKey = projectKey;
|
|
5520
|
-
this.inlineCss = getInlineOptions(options.inlineCss ?? 0);
|
|
5521
|
-
if (Object.keys(options).findIndex((k) => ['fixedCanvasScaling', 'disableCanvas'].includes(k)) !==
|
|
5522
|
-
-1) {
|
|
5523
|
-
console.warn('Openreplay: canvas options are moving to separate key "canvas" in next update. Please update your configuration.');
|
|
5524
|
-
options = {
|
|
5525
|
-
...options,
|
|
5526
|
-
canvas: {
|
|
5527
|
-
__save_canvas_locally: options.__save_canvas_locally,
|
|
5528
|
-
fixedCanvasScaling: options.fixedCanvasScaling,
|
|
5529
|
-
disableCanvas: options.disableCanvas,
|
|
5530
|
-
},
|
|
5531
|
-
};
|
|
5532
|
-
}
|
|
5533
5528
|
this.networkOptions = options.network;
|
|
5534
5529
|
const defaultOptions = {
|
|
5535
5530
|
revID: '',
|
|
@@ -5544,13 +5539,10 @@ class App {
|
|
|
5544
5539
|
__is_snippet: false,
|
|
5545
5540
|
__debug_report_edp: null,
|
|
5546
5541
|
__debug__: LogLevel.Silent,
|
|
5547
|
-
__save_canvas_locally: false,
|
|
5548
5542
|
localStorage: null,
|
|
5549
5543
|
sessionStorage: null,
|
|
5550
5544
|
forceSingleTab: false,
|
|
5551
5545
|
assistSocketHost: '',
|
|
5552
|
-
fixedCanvasScaling: false,
|
|
5553
|
-
disableCanvas: false,
|
|
5554
5546
|
captureIFrames: true,
|
|
5555
5547
|
obscureTextEmails: false,
|
|
5556
5548
|
obscureTextNumbers: false,
|
|
@@ -5588,7 +5580,7 @@ class App {
|
|
|
5588
5580
|
forceNgOff: Boolean(options.forceNgOff),
|
|
5589
5581
|
maintainer: this.options.nodes?.maintainer,
|
|
5590
5582
|
});
|
|
5591
|
-
this.observer = new TopObserver({ app: this, options
|
|
5583
|
+
this.observer = new TopObserver({ app: this, options });
|
|
5592
5584
|
this.ticker = new Ticker(this);
|
|
5593
5585
|
this.ticker.attach(() => this.commit());
|
|
5594
5586
|
this.debug = new Logger(this.options.__debug__);
|
|
@@ -8351,6 +8343,152 @@ function isObject(thing) {
|
|
|
8351
8343
|
return thing !== null && typeof thing === 'object';
|
|
8352
8344
|
}
|
|
8353
8345
|
|
|
8346
|
+
const sensitiveParams = new Set([
|
|
8347
|
+
"password",
|
|
8348
|
+
"pass",
|
|
8349
|
+
"pwd",
|
|
8350
|
+
"mdp",
|
|
8351
|
+
"token",
|
|
8352
|
+
"bearer",
|
|
8353
|
+
"jwt",
|
|
8354
|
+
"api_key",
|
|
8355
|
+
"api-key",
|
|
8356
|
+
"apiKey",
|
|
8357
|
+
"secret",
|
|
8358
|
+
"ssn",
|
|
8359
|
+
"zip",
|
|
8360
|
+
"zipcode",
|
|
8361
|
+
"x-api-key",
|
|
8362
|
+
"www-authenticate",
|
|
8363
|
+
"x-csrf-token",
|
|
8364
|
+
"x-requested-with",
|
|
8365
|
+
"x-forwarded-for",
|
|
8366
|
+
"x-real-ip",
|
|
8367
|
+
"cookie",
|
|
8368
|
+
"authorization",
|
|
8369
|
+
"auth",
|
|
8370
|
+
"proxy-authorization",
|
|
8371
|
+
"set-cookie",
|
|
8372
|
+
"account_key",
|
|
8373
|
+
]);
|
|
8374
|
+
function numDigits(x) {
|
|
8375
|
+
return (Math.log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
|
|
8376
|
+
}
|
|
8377
|
+
function obscure(value) {
|
|
8378
|
+
if (typeof value === "number") {
|
|
8379
|
+
const digits = numDigits(value);
|
|
8380
|
+
return "9".repeat(digits);
|
|
8381
|
+
}
|
|
8382
|
+
if (typeof value === "string") {
|
|
8383
|
+
return value.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
8384
|
+
}
|
|
8385
|
+
return value;
|
|
8386
|
+
}
|
|
8387
|
+
function filterHeaders(headers) {
|
|
8388
|
+
const filteredHeaders = {};
|
|
8389
|
+
if (Array.isArray(headers)) {
|
|
8390
|
+
headers.forEach(({ name, value }) => {
|
|
8391
|
+
if (sensitiveParams.has(name.toLowerCase())) {
|
|
8392
|
+
filteredHeaders[name] = obscure(value);
|
|
8393
|
+
}
|
|
8394
|
+
else {
|
|
8395
|
+
filteredHeaders[name] = value;
|
|
8396
|
+
}
|
|
8397
|
+
});
|
|
8398
|
+
}
|
|
8399
|
+
else {
|
|
8400
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
8401
|
+
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8402
|
+
filteredHeaders[key] = obscure(value);
|
|
8403
|
+
}
|
|
8404
|
+
else {
|
|
8405
|
+
filteredHeaders[key] = value;
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
return filteredHeaders;
|
|
8410
|
+
}
|
|
8411
|
+
function filterBody(body) {
|
|
8412
|
+
if (!body) {
|
|
8413
|
+
return body;
|
|
8414
|
+
}
|
|
8415
|
+
let parsedBody;
|
|
8416
|
+
let isJSON = false;
|
|
8417
|
+
try {
|
|
8418
|
+
parsedBody = JSON.parse(body);
|
|
8419
|
+
isJSON = true;
|
|
8420
|
+
}
|
|
8421
|
+
catch (e) {
|
|
8422
|
+
// not json
|
|
8423
|
+
}
|
|
8424
|
+
if (isJSON) {
|
|
8425
|
+
obscureSensitiveData(parsedBody);
|
|
8426
|
+
return JSON.stringify(parsedBody);
|
|
8427
|
+
}
|
|
8428
|
+
else {
|
|
8429
|
+
const isUrlSearch = typeof body === "string" && body.includes("?") && body.includes("=");
|
|
8430
|
+
if (isUrlSearch) {
|
|
8431
|
+
try {
|
|
8432
|
+
const params = new URLSearchParams(body);
|
|
8433
|
+
for (const key of params.keys()) {
|
|
8434
|
+
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8435
|
+
const value = obscure(params.get(key));
|
|
8436
|
+
params.set(key, value);
|
|
8437
|
+
}
|
|
8438
|
+
}
|
|
8439
|
+
return params.toString();
|
|
8440
|
+
}
|
|
8441
|
+
catch (e) {
|
|
8442
|
+
// not url query ?
|
|
8443
|
+
return body;
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
8446
|
+
else {
|
|
8447
|
+
// not json or url query
|
|
8448
|
+
return body;
|
|
8449
|
+
}
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
function sanitizeObject(obj) {
|
|
8453
|
+
obscureSensitiveData(obj);
|
|
8454
|
+
return obj;
|
|
8455
|
+
}
|
|
8456
|
+
function obscureSensitiveData(obj) {
|
|
8457
|
+
if (Array.isArray(obj)) {
|
|
8458
|
+
obj.forEach(obscureSensitiveData);
|
|
8459
|
+
}
|
|
8460
|
+
else if (obj && typeof obj === "object") {
|
|
8461
|
+
for (const key in obj) {
|
|
8462
|
+
if (Object.hasOwn(obj, key)) {
|
|
8463
|
+
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8464
|
+
obj[key] = obscure(obj[key]);
|
|
8465
|
+
}
|
|
8466
|
+
else if (obj[key] !== null && typeof obj[key] === "object") {
|
|
8467
|
+
obscureSensitiveData(obj[key]);
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8470
|
+
}
|
|
8471
|
+
}
|
|
8472
|
+
}
|
|
8473
|
+
function tryFilterUrl(url) {
|
|
8474
|
+
if (!url)
|
|
8475
|
+
return "";
|
|
8476
|
+
try {
|
|
8477
|
+
const urlObj = new URL(url);
|
|
8478
|
+
if (urlObj.searchParams) {
|
|
8479
|
+
for (const key of urlObj.searchParams.keys()) {
|
|
8480
|
+
if (sensitiveParams.has(key.toLowerCase())) {
|
|
8481
|
+
urlObj.searchParams.set(key, "******");
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
return urlObj.toString();
|
|
8486
|
+
}
|
|
8487
|
+
catch (e) {
|
|
8488
|
+
return url;
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
|
|
8354
8492
|
/**
|
|
8355
8493
|
* I know we're not using most of the information from this class
|
|
8356
8494
|
* but it can be useful in the future if we will decide to display more stuff in our ui
|
|
@@ -8382,13 +8520,18 @@ class NetworkMessage {
|
|
|
8382
8520
|
}
|
|
8383
8521
|
getMessage() {
|
|
8384
8522
|
const { reqHs, resHs } = this.writeHeaders();
|
|
8523
|
+
const reqBody = this.method === 'GET'
|
|
8524
|
+
? JSON.stringify(sanitizeObject(this.getData)) : filterBody(this.requestData);
|
|
8385
8525
|
const request = {
|
|
8386
|
-
headers: reqHs,
|
|
8387
|
-
body:
|
|
8526
|
+
headers: filterHeaders(reqHs),
|
|
8527
|
+
body: reqBody,
|
|
8528
|
+
};
|
|
8529
|
+
const response = {
|
|
8530
|
+
headers: filterHeaders(resHs),
|
|
8531
|
+
body: filterBody(this.response)
|
|
8388
8532
|
};
|
|
8389
|
-
const response = { headers: resHs, body: this.response };
|
|
8390
8533
|
const messageInfo = this.sanitize({
|
|
8391
|
-
url: this.url,
|
|
8534
|
+
url: tryFilterUrl(this.url),
|
|
8392
8535
|
method: this.method,
|
|
8393
8536
|
status: this.status,
|
|
8394
8537
|
request,
|
|
@@ -8704,9 +8847,10 @@ class ResponseProxyHandler {
|
|
|
8704
8847
|
if (typeof this.resp.body.getReader !== 'function') {
|
|
8705
8848
|
return;
|
|
8706
8849
|
}
|
|
8707
|
-
const
|
|
8850
|
+
const clonedResp = this.resp.clone();
|
|
8851
|
+
const _getReader = clonedResp.body.getReader;
|
|
8708
8852
|
// @ts-ignore
|
|
8709
|
-
|
|
8853
|
+
clonedResp.body.getReader = () => {
|
|
8710
8854
|
const reader = _getReader.apply(this.resp.body);
|
|
8711
8855
|
// when readyState is already 4,
|
|
8712
8856
|
// it's not a chunked stream, or it had already been read.
|
|
@@ -9548,7 +9692,7 @@ class API {
|
|
|
9548
9692
|
this.signalStartIssue = (reason, missingApi) => {
|
|
9549
9693
|
const doNotTrack = this.checkDoNotTrack();
|
|
9550
9694
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
9551
|
-
trackerVersion: '16.2.
|
|
9695
|
+
trackerVersion: '16.2.1',
|
|
9552
9696
|
projectKey: this.options.projectKey,
|
|
9553
9697
|
doNotTrack,
|
|
9554
9698
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|