@statsig/web-analytics 3.21.0 → 3.22.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/web-analytics",
3
- "version": "3.21.0",
3
+ "version": "3.22.0",
4
4
  "license": "ISC",
5
5
  "homepage": "https://github.com/statsig-io/js-client-monorepo",
6
6
  "repository": {
@@ -9,8 +9,8 @@
9
9
  "directory": "packages/web-analytics"
10
10
  },
11
11
  "dependencies": {
12
- "@statsig/client-core": "3.21.0",
13
- "@statsig/js-client": "3.21.0",
12
+ "@statsig/client-core": "3.22.0",
13
+ "@statsig/js-client": "3.22.0",
14
14
  "web-vitals": "5.0.3"
15
15
  },
16
16
  "jsdelivr": "./build/statsig-web-analytics.min.js",
@@ -36,4 +36,5 @@ export declare class AutoCapture {
36
36
  private _flushImmediately;
37
37
  private _isNewSession;
38
38
  private _getSessionFromClient;
39
+ static getAllMetadata(): Record<string, string | number>;
39
40
  }
@@ -118,6 +118,12 @@ class AutoCapture {
118
118
  this._tryLogPageView();
119
119
  },
120
120
  });
121
+ window.history.replaceState = new Proxy(window.history.replaceState, {
122
+ apply: (target, thisArg, [state, unused, url]) => {
123
+ target.apply(thisArg, [state, unused, url]);
124
+ this._tryLogPageView();
125
+ },
126
+ });
121
127
  this._tryLogPageView();
122
128
  }
123
129
  _autoLogEvent(event) {
@@ -192,7 +198,7 @@ class AutoCapture {
192
198
  if (!this._isNewSession(session)) {
193
199
  return;
194
200
  }
195
- this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.SESSION_START, (0, commonUtils_1._getSanitizedPageUrl)(), { sessionID: session.data.sessionID }, { flushImmediately: true });
201
+ this._enqueueAutoCapture(AutoCaptureEvent_1.AutoCaptureEventName.SESSION_START, (0, commonUtils_1._getSanitizedPageUrl)(), Object.assign({ sessionID: session.data.sessionID }, (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)())), { flushImmediately: true });
196
202
  }
197
203
  catch (err) {
198
204
  this._errorBoundary.logError('AC::logSession', err);
@@ -271,6 +277,9 @@ class AutoCapture {
271
277
  }
272
278
  _enqueueAutoCapture(eventName, value, metadata, options) {
273
279
  var _a, _b, _c;
280
+ if (!value) {
281
+ return;
282
+ }
274
283
  const subname = eventName.slice('auto_capture::'.length);
275
284
  if (this._disabledEvents[eventName] || this._disabledEvents[subname]) {
276
285
  return;
@@ -316,5 +325,8 @@ class AutoCapture {
316
325
  _getSessionFromClient() {
317
326
  return this._client.getContext().session;
318
327
  }
328
+ static getAllMetadata() {
329
+ return (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)());
330
+ }
319
331
  }
320
332
  exports.AutoCapture = AutoCapture;
@@ -1,14 +1,11 @@
1
- import { LogLevel } from '@statsig/client-core';
2
1
  import { AutoCaptureEvent } from './AutoCaptureEvent';
2
+ import { ConsoleLogLevel } from './ConsoleLogManager';
3
3
  export type AutoCaptureOptions = {
4
4
  eventFilterFunc?: (event: AutoCaptureEvent) => boolean;
5
5
  consoleLogAutoCaptureSettings?: ConsoleLogAutoCaptureSettings;
6
6
  };
7
7
  export type ConsoleLogAutoCaptureSettings = {
8
8
  enabled: boolean;
9
- logLevel?: LogLevel;
10
- serviceName?: string;
11
- serviceVersion?: string;
9
+ logLevel?: ConsoleLogLevel;
12
10
  sampleRate?: number;
13
- resourceMetadata?: Record<string, string | number | boolean>;
14
11
  };
@@ -1,21 +1,13 @@
1
1
  import { AutoCaptureEventName } from './AutoCaptureEvent';
2
2
  import { ConsoleLogAutoCaptureSettings } from './AutoCaptureOptions';
3
- export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
4
- export type ConsoleLogEvent = {
5
- eventName: 'statsig::console_log';
6
- value: string;
7
- metadata: {
8
- timestamp: string;
9
- log_level: LogLevel;
10
- payload: string[];
11
- trace: string[];
12
- };
13
- };
3
+ export type ConsoleLogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug' | 'trace';
14
4
  export declare class ConsoleLogManager {
15
5
  private _enqueueFn;
16
6
  private _options;
17
7
  private _restoreFns;
18
8
  private _isTracking;
9
+ private _logLevel;
10
+ private readonly __source;
19
11
  constructor(_enqueueFn: (eventName: AutoCaptureEventName, value: string, metadata: Record<string, unknown>) => void, _options: ConsoleLogAutoCaptureSettings);
20
12
  startTracking(): void;
21
13
  stopTracking(): void;
@@ -6,12 +6,24 @@ const client_core_1 = require("@statsig/client-core");
6
6
  const AutoCaptureEvent_1 = require("./AutoCaptureEvent");
7
7
  const commonUtils_1 = require("./utils/commonUtils");
8
8
  const metadataUtils_1 = require("./utils/metadataUtils");
9
+ const ConsoleLogPriority = {
10
+ trace: 10,
11
+ debug: 20,
12
+ log: 30, // log & info are the same priority
13
+ info: 30,
14
+ warn: 40,
15
+ error: 50,
16
+ };
9
17
  class ConsoleLogManager {
10
18
  constructor(_enqueueFn, _options) {
19
+ var _a;
11
20
  this._enqueueFn = _enqueueFn;
12
21
  this._options = _options;
13
22
  this._restoreFns = [];
14
23
  this._isTracking = false;
24
+ this._logLevel = 'info';
25
+ this.__source = 'js-auto-capture';
26
+ this._logLevel = (_a = this._options.logLevel) !== null && _a !== void 0 ? _a : 'info';
15
27
  }
16
28
  startTracking() {
17
29
  if (this._isTracking || !this._options.enabled)
@@ -30,7 +42,9 @@ class ConsoleLogManager {
30
42
  this._isTracking = false;
31
43
  }
32
44
  _patchConsole() {
33
- ['log', 'info', 'warn', 'error', 'debug'].forEach((level) => {
45
+ Object.entries(ConsoleLogPriority).forEach(([level, priority]) => {
46
+ if (priority < ConsoleLogPriority[this._logLevel])
47
+ return;
34
48
  if (!console[level])
35
49
  return;
36
50
  const original = console[level].bind(console);
@@ -58,12 +72,11 @@ class ConsoleLogManager {
58
72
  });
59
73
  }
60
74
  _enqueueConsoleLog(level, payload, trace) {
61
- var _a, _b, _c;
62
75
  if (!this._shouldLog())
63
76
  return;
64
- const metadata = Object.assign(Object.assign({ log_level: level, payload,
65
- trace, timestamp: Date.now(), serviceName: (_a = this._options.serviceName) !== null && _a !== void 0 ? _a : '', serviceVersion: (_b = this._options.serviceVersion) !== null && _b !== void 0 ? _b : '' }, ((_c = this._options.resourceMetadata) !== null && _c !== void 0 ? _c : {})), (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)()));
66
- this._enqueueFn(AutoCaptureEvent_1.AutoCaptureEventName.CONSOLE_LOG, (0, commonUtils_1._getSafeUrlString)(), metadata);
77
+ const metadata = Object.assign({ status: level === 'log' ? 'info' : level, log_level: level, payload,
78
+ trace, timestamp: Date.now(), source: this.__source }, (0, metadataUtils_1._gatherAllMetadata)((0, commonUtils_1._getSafeUrl)()));
79
+ this._enqueueFn(AutoCaptureEvent_1.AutoCaptureEventName.CONSOLE_LOG, payload.join(' '), metadata);
67
80
  }
68
81
  _shouldLog() {
69
82
  if (!this._options.sampleRate ||
@@ -5,6 +5,7 @@ const client_core_1 = require("@statsig/client-core");
5
5
  const AutoCaptureEvent_1 = require("./AutoCaptureEvent");
6
6
  const commonUtils_1 = require("./utils/commonUtils");
7
7
  const eventUtils_1 = require("./utils/eventUtils");
8
+ const metadataUtils_1 = require("./utils/metadataUtils");
8
9
  exports.DeadClickConfig = {
9
10
  CLICK_CHECK_TIMEOUT: 1000,
10
11
  SCROLL_DELAY_MS: 100,
@@ -143,7 +144,10 @@ class DeadClickManager {
143
144
  }
144
145
  _logDeadClick(click, extraMetadata) {
145
146
  const { value, metadata } = (0, eventUtils_1._gatherEventData)(click.eventTarget);
146
- this._enqueueFn(AutoCaptureEvent_1.AutoCaptureEventName.DEAD_CLICK, value, Object.assign(Object.assign(Object.assign({}, metadata), extraMetadata), { scrollDelayMs: click.scrollDelayMs, selectionChangeDelayMs: click.selectionChangeDelayMs, mutationDelayMs: click.mutationDelayMs, absoluteDelayMs: click.absoluteDelayMs }));
147
+ if (!value) {
148
+ return;
149
+ }
150
+ this._enqueueFn(AutoCaptureEvent_1.AutoCaptureEventName.DEAD_CLICK, value, Object.assign(Object.assign(Object.assign(Object.assign({}, metadata), extraMetadata), (0, metadataUtils_1._gatherCommonMetadata)((0, commonUtils_1._getSafeUrl)())), { scrollDelayMs: click.scrollDelayMs, selectionChangeDelayMs: click.selectionChangeDelayMs, mutationDelayMs: click.mutationDelayMs, absoluteDelayMs: click.absoluteDelayMs }));
147
151
  }
148
152
  _updateClickDelayMs(click) {
149
153
  if (!click.mutationDelayMs &&
@@ -0,0 +1,5 @@
1
+ export declare function getLastEvent(requests: Record<string, any>[], eventName: string): Record<string, any>;
2
+ export declare function getLastPageViewEvent(requests: Record<string, any>[]): Record<string, any>;
3
+ export declare function getLastPageViewEndEvent(requests: Record<string, any>[]): Record<string, any>;
4
+ export declare function getLastSessionStartEvent(requests: Record<string, any>[]): Record<string, any>;
5
+ export declare function getLastPerformanceEvent(requests: Record<string, any>[]): Record<string, any>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getLastPerformanceEvent = exports.getLastSessionStartEvent = exports.getLastPageViewEndEvent = exports.getLastPageViewEvent = exports.getLastEvent = void 0;
4
+ function getLastEvent(requests, eventName) {
5
+ for (let ii = requests.length - 1; ii >= 0; ii--) {
6
+ const req = requests[ii];
7
+ if (req['events']) {
8
+ for (let jj = req['events'].length - 1; jj >= 0; jj--) {
9
+ const evt = req['events'][jj];
10
+ if (evt.eventName === eventName) {
11
+ return evt;
12
+ }
13
+ }
14
+ }
15
+ }
16
+ return {};
17
+ }
18
+ exports.getLastEvent = getLastEvent;
19
+ function getLastPageViewEvent(requests) {
20
+ return getLastEvent(requests, 'auto_capture::page_view');
21
+ }
22
+ exports.getLastPageViewEvent = getLastPageViewEvent;
23
+ function getLastPageViewEndEvent(requests) {
24
+ return getLastEvent(requests, 'auto_capture::page_view_end');
25
+ }
26
+ exports.getLastPageViewEndEvent = getLastPageViewEndEvent;
27
+ function getLastSessionStartEvent(requests) {
28
+ return getLastEvent(requests, 'auto_capture::session_start');
29
+ }
30
+ exports.getLastSessionStartEvent = getLastSessionStartEvent;
31
+ function getLastPerformanceEvent(requests) {
32
+ return getLastEvent(requests, 'auto_capture::performance');
33
+ }
34
+ exports.getLastPerformanceEvent = getLastPerformanceEvent;
@@ -1,5 +1,5 @@
1
1
  export declare function _gatherEventData(target: Element): {
2
- value: string;
2
+ value: string | null;
3
3
  metadata: Record<string, unknown>;
4
4
  };
5
5
  export declare function _gatherCopyEventData(e: Event): Record<string, unknown>;
@@ -7,17 +7,19 @@ const MAX_ATTRIBUTE_LENGTH = 1000;
7
7
  const MAX_CLASS_LIST_LENGTH = 100;
8
8
  const MAX_SELECTOR_DEPTH = 50;
9
9
  function _gatherEventData(target) {
10
+ if (_isSensitiveElement(target)) {
11
+ return { value: null, metadata: {} };
12
+ }
10
13
  const tagName = target.tagName.toLowerCase();
11
14
  const metadata = {};
12
- const value = (0, client_core_1._getCurrentPageUrlSafe)() || '';
15
+ const value = (0, commonUtils_1._getSanitizedPageUrl)() || '';
13
16
  metadata['tagName'] = tagName;
14
17
  const elementMetadata = _getMetadataFromElement(target);
15
18
  Object.assign(metadata, elementMetadata);
16
19
  if (tagName === 'form') {
17
20
  Object.assign(metadata, _getFormMetadata(target));
18
21
  }
19
- if (['input', 'select', 'textarea'].includes(tagName) &&
20
- target.getAttribute('type') !== 'password') {
22
+ if (tagName === 'input') {
21
23
  Object.assign(metadata, _getInputMetadata(target));
22
24
  }
23
25
  const anchor = (0, commonUtils_1._getAnchorNodeInHierarchy)(target);
@@ -40,6 +42,13 @@ function _gatherCopyEventData(e) {
40
42
  return metadata;
41
43
  }
42
44
  exports._gatherCopyEventData = _gatherCopyEventData;
45
+ function _isSensitiveElement(target) {
46
+ return ((target.tagName.toLowerCase() === 'input' &&
47
+ ['button', 'checkbox', 'submit', 'reset'].includes(target.getAttribute('type') || '')) ||
48
+ target.tagName.toLowerCase() === 'textarea' ||
49
+ target.tagName.toLowerCase() === 'select' ||
50
+ target.getAttribute('contenteditable') === 'true');
51
+ }
43
52
  function _getFormMetadata(target) {
44
53
  var _a;
45
54
  const metadata = {};
@@ -10,7 +10,7 @@ function _gatherCommonMetadata(url) {
10
10
  return (0, commonUtils_1._stripEmptyValues)(Object.assign({ title: safeDoc === null || safeDoc === void 0 ? void 0 : safeDoc.title, current_url: (_a = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.location) === null || _a === void 0 ? void 0 : _a.href, user_agent: ((_b = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _b === void 0 ? void 0 : _b.userAgent) &&
11
11
  ((_d = (_c = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _c === void 0 ? void 0 : _c.userAgent) === null || _d === void 0 ? void 0 : _d.length) > 200
12
12
  ? safeWnd.navigator.userAgent.substring(0, 200)
13
- : (_e = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _e === void 0 ? void 0 : _e.userAgent, locale: (_f = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _f === void 0 ? void 0 : _f.language, hostname: url.hostname, pathname: url.pathname, screen_width: (_g = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _g === void 0 ? void 0 : _g.width, screen_height: (_h = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _h === void 0 ? void 0 : _h.height, viewport_width: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerWidth, viewport_height: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerHeight, timestamp: Date.now(), timezone: (0, commonUtils_1._getSafeTimezone)(), timezone_offset: (0, commonUtils_1._getSafeTimezoneOffset)() }, _getNetworkInfo()));
13
+ : (_e = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _e === void 0 ? void 0 : _e.userAgent, locale: (_f = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.navigator) === null || _f === void 0 ? void 0 : _f.language, hostname: url.hostname, pathname: url.pathname, screen_width: (_g = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _g === void 0 ? void 0 : _g.width, screen_height: (_h = safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.screen) === null || _h === void 0 ? void 0 : _h.height, viewport_width: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerWidth, viewport_height: safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerHeight, viewport: `${safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerWidth}x${safeWnd === null || safeWnd === void 0 ? void 0 : safeWnd.innerHeight}`, timestamp: Date.now(), timezone: (0, commonUtils_1._getSafeTimezone)(), timezone_offset: (0, commonUtils_1._getSafeTimezoneOffset)() }, _getNetworkInfo()));
14
14
  }
15
15
  exports._gatherCommonMetadata = _gatherCommonMetadata;
16
16
  function _gatherAllMetadata(url) {