@openreplay/tracker 5.0.0 → 5.0.1-beta.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 5.0.1
2
+
3
+ - Default text input mode is now Obscured
4
+ - Use `@medv/finder` instead of our own implementation of `getSelector` for better clickmaps experience
5
+
1
6
  ## 5.0.0
2
7
 
3
8
  - Added "tel" to supported input types
@@ -9,6 +9,7 @@ import type { Options as ObserverOptions } from './observer/top_observer.js';
9
9
  import type { Options as SanitizerOptions } from './sanitizer.js';
10
10
  import type { Options as LoggerOptions } from './logger.js';
11
11
  import type { Options as SessOptions } from './session.js';
12
+ import type { Options as NetworkOptions } from '../modules/network.js';
12
13
  import type { Options as WebworkerOptions } from '../common/interaction.js';
13
14
  export interface StartOptions {
14
15
  userID?: string;
@@ -50,6 +51,7 @@ type AppOptions = {
50
51
  localStorage: Storage | null;
51
52
  sessionStorage: Storage | null;
52
53
  onStart?: StartCallback;
54
+ network?: NetworkOptions;
53
55
  } & WebworkerOptions & SessOptions;
54
56
  export type Options = AppOptions & ObserverOptions & SanitizerOptions;
55
57
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
@@ -69,6 +71,7 @@ export default class App {
69
71
  private readonly stopCallbacks;
70
72
  private readonly commitCallbacks;
71
73
  private readonly options;
74
+ readonly networkOptions?: NetworkOptions;
72
75
  private readonly revID;
73
76
  private activityState;
74
77
  private readonly version;
package/cjs/app/index.js CHANGED
@@ -33,10 +33,11 @@ class App {
33
33
  this.stopCallbacks = [];
34
34
  this.commitCallbacks = [];
35
35
  this.activityState = ActivityState.NotActive;
36
- this.version = '5.0.0'; // TODO: version compatability check inside each plugin.
36
+ this.version = '5.0.1-beta.3'; // TODO: version compatability check inside each plugin.
37
37
  this._usingOldFetchPlugin = false;
38
38
  this.delay = 0;
39
39
  this.projectKey = projectKey;
40
+ this.networkOptions = options.network;
40
41
  this.options = Object.assign({
41
42
  revID: '',
42
43
  node_id: '__openreplay_id',
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const observer_js_1 = require("./observer.js");
4
4
  const guards_js_1 = require("../guards.js");
5
+ const network_js_1 = require("../../modules/network.js");
5
6
  const iframe_observer_js_1 = require("./iframe_observer.js");
6
7
  const shadow_root_observer_js_1 = require("./shadow_root_observer.js");
7
8
  const iframe_offsets_js_1 = require("./iframe_offsets.js");
@@ -67,6 +68,7 @@ class TopObserver extends observer_js_1.default {
67
68
  //TODO: more explicit logic
68
69
  ) {
69
70
  this.contextsSet.add(currentWin);
71
+ (0, network_js_1.default)(this.app, this.app.networkOptions, currentWin);
70
72
  //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
71
73
  this.contextCallbacks.forEach((cb) => cb(currentWin));
72
74
  }
package/cjs/index.js CHANGED
@@ -140,7 +140,7 @@ class API {
140
140
  // no-cors issue only with text/plain or not-set Content-Type
141
141
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
142
142
  req.send(JSON.stringify({
143
- trackerVersion: '5.0.0',
143
+ trackerVersion: '5.0.1-beta.3',
144
144
  projectKey: options.projectKey,
145
145
  doNotTrack,
146
146
  // TODO: add precise reason (an exact API missing)
@@ -68,7 +68,7 @@ function default_1(app, opts) {
68
68
  const options = Object.assign({
69
69
  obscureInputNumbers: true,
70
70
  obscureInputEmails: true,
71
- defaultInputMode: 0 /* InputMode.Plain */,
71
+ defaultInputMode: 1 /* InputMode.Obscured */,
72
72
  obscureInputDates: false,
73
73
  }, opts);
74
74
  function sendInputTarget(id, node) {
@@ -4,25 +4,15 @@ const guards_js_1 = require("../app/guards.js");
4
4
  const utils_js_1 = require("../utils.js");
5
5
  const messages_gen_js_1 = require("../app/messages.gen.js");
6
6
  const input_js_1 = require("./input.js");
7
+ const finder_1 = require("@medv/finder");
7
8
  function _getSelector(target, document) {
8
- let el = target;
9
- let selector = null;
10
- do {
11
- if (el.id) {
12
- return `#${el.id}` + (selector ? ` > ${selector}` : '');
13
- }
14
- selector =
15
- el.className
16
- .split(' ')
17
- .map((cn) => cn.trim())
18
- .filter((cn) => cn !== '')
19
- .reduce((sel, cn) => `${sel}.${cn}`, el.tagName.toLowerCase()) +
20
- (selector ? ` > ${selector}` : '');
21
- if (el === document.body) {
22
- return selector;
23
- }
24
- el = el.parentElement;
25
- } while (el !== document.body && el !== null);
9
+ const selector = (0, finder_1.finder)(target, {
10
+ root: document.body,
11
+ seedMinLength: 3,
12
+ optimizedMinLength: 2,
13
+ threshold: 1000,
14
+ maxNumberOfTries: 10000,
15
+ });
26
16
  return selector;
27
17
  }
28
18
  function isClickable(element) {
@@ -31,6 +21,8 @@ function isClickable(element) {
31
21
  tag === 'A' ||
32
22
  tag === 'LI' ||
33
23
  tag === 'SELECT' ||
24
+ tag === 'TR' ||
25
+ tag === 'TH' ||
34
26
  element.onclick != null ||
35
27
  element.getAttribute('role') === 'button');
36
28
  //|| element.className.includes("btn")
@@ -24,5 +24,5 @@ export interface Options {
24
24
  capturePayload: boolean;
25
25
  sanitizer?: Sanitizer;
26
26
  }
27
- export default function (app: App, opts?: Partial<Options>): void;
27
+ export default function (app: App, opts?: Partial<Options>, customEnv?: Record<string, any>): void;
28
28
  export {};
@@ -14,7 +14,7 @@ function getXHRRequestDataObject(xhr) {
14
14
  function strMethod(method) {
15
15
  return typeof method === 'string' ? method.toUpperCase() : 'GET';
16
16
  }
17
- function default_1(app, opts = {}) {
17
+ function default_1(app, opts = {}, customEnv) {
18
18
  const options = Object.assign({
19
19
  failuresOnly: false,
20
20
  ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
@@ -65,8 +65,10 @@ function default_1(app, opts = {}) {
65
65
  return JSON.stringify(r);
66
66
  }
67
67
  /* ====== Fetch ====== */
68
- const origFetch = window.fetch.bind(window);
69
- window.fetch = (input, init = {}) => {
68
+ const origFetch = customEnv
69
+ ? customEnv.fetch.bind(customEnv)
70
+ : window.fetch.bind(window);
71
+ const trackFetch = (input, init = {}) => {
70
72
  if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
71
73
  return origFetch(input, init);
72
74
  }
@@ -140,10 +142,19 @@ function default_1(app, opts = {}) {
140
142
  return response;
141
143
  });
142
144
  };
145
+ if (customEnv) {
146
+ customEnv.fetch = trackFetch;
147
+ }
148
+ else {
149
+ window.fetch = trackFetch;
150
+ }
143
151
  /* ====== <> ====== */
144
152
  /* ====== XHR ====== */
145
- const nativeOpen = XMLHttpRequest.prototype.open;
146
- XMLHttpRequest.prototype.open = function (initMethod, url) {
153
+ const nativeOpen = customEnv
154
+ ? customEnv.XMLHttpRequest.prototype.open
155
+ : XMLHttpRequest.prototype.open;
156
+ function trackXMLHttpReqOpen(initMethod, url) {
157
+ // @ts-ignore ??? this -> XMLHttpRequest
147
158
  const xhr = this;
148
159
  setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
149
160
  let startTime = 0;
@@ -182,22 +193,45 @@ function default_1(app, opts = {}) {
182
193
  }));
183
194
  //TODO: handle error (though it has no Error API nor any useful information)
184
195
  //xhr.addEventListener('error', (e) => {})
196
+ // @ts-ignore ??? this -> XMLHttpRequest
185
197
  return nativeOpen.apply(this, arguments);
186
- };
198
+ }
199
+ if (customEnv) {
200
+ customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv);
201
+ }
202
+ else {
203
+ XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
204
+ }
187
205
  const nativeSend = XMLHttpRequest.prototype.send;
188
- XMLHttpRequest.prototype.send = function (body) {
206
+ function trackXHRSend(body) {
207
+ // @ts-ignore ??? this -> XMLHttpRequest
189
208
  const rdo = getXHRRequestDataObject(this);
190
209
  rdo.body = body;
210
+ // @ts-ignore ??? this -> XMLHttpRequest
191
211
  return nativeSend.apply(this, arguments);
192
- };
212
+ }
213
+ if (customEnv) {
214
+ customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv);
215
+ }
216
+ else {
217
+ XMLHttpRequest.prototype.send = trackXHRSend;
218
+ }
193
219
  const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
194
- XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
220
+ function trackSetReqHeader(name, value) {
195
221
  if (!isHIgnored(name)) {
222
+ // @ts-ignore ??? this -> XMLHttpRequest
196
223
  const rdo = getXHRRequestDataObject(this);
197
224
  rdo.headers[name] = value;
198
225
  }
226
+ // @ts-ignore ??? this -> XMLHttpRequest
199
227
  return nativeSetRequestHeader.apply(this, arguments);
200
- };
228
+ }
229
+ if (customEnv) {
230
+ customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv);
231
+ }
232
+ else {
233
+ XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
234
+ }
201
235
  /* ====== <> ====== */
202
236
  }
203
237
  exports.default = default_1;
@@ -9,6 +9,7 @@ import type { Options as ObserverOptions } from './observer/top_observer.js';
9
9
  import type { Options as SanitizerOptions } from './sanitizer.js';
10
10
  import type { Options as LoggerOptions } from './logger.js';
11
11
  import type { Options as SessOptions } from './session.js';
12
+ import type { Options as NetworkOptions } from '../modules/network.js';
12
13
  import type { Options as WebworkerOptions } from '../common/interaction.js';
13
14
  export interface StartOptions {
14
15
  userID?: string;
@@ -50,6 +51,7 @@ type AppOptions = {
50
51
  localStorage: Storage | null;
51
52
  sessionStorage: Storage | null;
52
53
  onStart?: StartCallback;
54
+ network?: NetworkOptions;
53
55
  } & WebworkerOptions & SessOptions;
54
56
  export type Options = AppOptions & ObserverOptions & SanitizerOptions;
55
57
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
@@ -69,6 +71,7 @@ export default class App {
69
71
  private readonly stopCallbacks;
70
72
  private readonly commitCallbacks;
71
73
  private readonly options;
74
+ readonly networkOptions?: NetworkOptions;
72
75
  private readonly revID;
73
76
  private activityState;
74
77
  private readonly version;
package/lib/app/index.js CHANGED
@@ -30,10 +30,11 @@ export default class App {
30
30
  this.stopCallbacks = [];
31
31
  this.commitCallbacks = [];
32
32
  this.activityState = ActivityState.NotActive;
33
- this.version = '5.0.0'; // TODO: version compatability check inside each plugin.
33
+ this.version = '5.0.1-beta.3'; // TODO: version compatability check inside each plugin.
34
34
  this._usingOldFetchPlugin = false;
35
35
  this.delay = 0;
36
36
  this.projectKey = projectKey;
37
+ this.networkOptions = options.network;
37
38
  this.options = Object.assign({
38
39
  revID: '',
39
40
  node_id: '__openreplay_id',
@@ -1,5 +1,6 @@
1
1
  import Observer from './observer.js';
2
2
  import { isElementNode, hasTag } from '../guards.js';
3
+ import Network from '../../modules/network.js';
3
4
  import IFrameObserver from './iframe_observer.js';
4
5
  import ShadowRootObserver from './shadow_root_observer.js';
5
6
  import IFrameOffsets from './iframe_offsets.js';
@@ -65,6 +66,7 @@ export default class TopObserver extends Observer {
65
66
  //TODO: more explicit logic
66
67
  ) {
67
68
  this.contextsSet.add(currentWin);
69
+ Network(this.app, this.app.networkOptions, currentWin);
68
70
  //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
69
71
  this.contextCallbacks.forEach((cb) => cb(currentWin));
70
72
  }
package/lib/index.js CHANGED
@@ -135,7 +135,7 @@ export default class API {
135
135
  // no-cors issue only with text/plain or not-set Content-Type
136
136
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
137
137
  req.send(JSON.stringify({
138
- trackerVersion: '5.0.0',
138
+ trackerVersion: '5.0.1-beta.3',
139
139
  projectKey: options.projectKey,
140
140
  doNotTrack,
141
141
  // TODO: add precise reason (an exact API missing)
@@ -64,7 +64,7 @@ export default function (app, opts) {
64
64
  const options = Object.assign({
65
65
  obscureInputNumbers: true,
66
66
  obscureInputEmails: true,
67
- defaultInputMode: 0 /* InputMode.Plain */,
67
+ defaultInputMode: 1 /* InputMode.Obscured */,
68
68
  obscureInputDates: false,
69
69
  }, opts);
70
70
  function sendInputTarget(id, node) {
@@ -2,25 +2,15 @@ import { hasTag, isSVGElement, isDocument } from '../app/guards.js';
2
2
  import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js';
3
3
  import { MouseMove, MouseClick } from '../app/messages.gen.js';
4
4
  import { getInputLabel } from './input.js';
5
+ import { finder } from '@medv/finder';
5
6
  function _getSelector(target, document) {
6
- let el = target;
7
- let selector = null;
8
- do {
9
- if (el.id) {
10
- return `#${el.id}` + (selector ? ` > ${selector}` : '');
11
- }
12
- selector =
13
- el.className
14
- .split(' ')
15
- .map((cn) => cn.trim())
16
- .filter((cn) => cn !== '')
17
- .reduce((sel, cn) => `${sel}.${cn}`, el.tagName.toLowerCase()) +
18
- (selector ? ` > ${selector}` : '');
19
- if (el === document.body) {
20
- return selector;
21
- }
22
- el = el.parentElement;
23
- } while (el !== document.body && el !== null);
7
+ const selector = finder(target, {
8
+ root: document.body,
9
+ seedMinLength: 3,
10
+ optimizedMinLength: 2,
11
+ threshold: 1000,
12
+ maxNumberOfTries: 10000,
13
+ });
24
14
  return selector;
25
15
  }
26
16
  function isClickable(element) {
@@ -29,6 +19,8 @@ function isClickable(element) {
29
19
  tag === 'A' ||
30
20
  tag === 'LI' ||
31
21
  tag === 'SELECT' ||
22
+ tag === 'TR' ||
23
+ tag === 'TH' ||
32
24
  element.onclick != null ||
33
25
  element.getAttribute('role') === 'button');
34
26
  //|| element.className.includes("btn")
@@ -24,5 +24,5 @@ export interface Options {
24
24
  capturePayload: boolean;
25
25
  sanitizer?: Sanitizer;
26
26
  }
27
- export default function (app: App, opts?: Partial<Options>): void;
27
+ export default function (app: App, opts?: Partial<Options>, customEnv?: Record<string, any>): void;
28
28
  export {};
@@ -12,7 +12,7 @@ function getXHRRequestDataObject(xhr) {
12
12
  function strMethod(method) {
13
13
  return typeof method === 'string' ? method.toUpperCase() : 'GET';
14
14
  }
15
- export default function (app, opts = {}) {
15
+ export default function (app, opts = {}, customEnv) {
16
16
  const options = Object.assign({
17
17
  failuresOnly: false,
18
18
  ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
@@ -63,8 +63,10 @@ export default function (app, opts = {}) {
63
63
  return JSON.stringify(r);
64
64
  }
65
65
  /* ====== Fetch ====== */
66
- const origFetch = window.fetch.bind(window);
67
- window.fetch = (input, init = {}) => {
66
+ const origFetch = customEnv
67
+ ? customEnv.fetch.bind(customEnv)
68
+ : window.fetch.bind(window);
69
+ const trackFetch = (input, init = {}) => {
68
70
  if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
69
71
  return origFetch(input, init);
70
72
  }
@@ -138,10 +140,19 @@ export default function (app, opts = {}) {
138
140
  return response;
139
141
  });
140
142
  };
143
+ if (customEnv) {
144
+ customEnv.fetch = trackFetch;
145
+ }
146
+ else {
147
+ window.fetch = trackFetch;
148
+ }
141
149
  /* ====== <> ====== */
142
150
  /* ====== XHR ====== */
143
- const nativeOpen = XMLHttpRequest.prototype.open;
144
- XMLHttpRequest.prototype.open = function (initMethod, url) {
151
+ const nativeOpen = customEnv
152
+ ? customEnv.XMLHttpRequest.prototype.open
153
+ : XMLHttpRequest.prototype.open;
154
+ function trackXMLHttpReqOpen(initMethod, url) {
155
+ // @ts-ignore ??? this -> XMLHttpRequest
145
156
  const xhr = this;
146
157
  setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
147
158
  let startTime = 0;
@@ -180,21 +191,44 @@ export default function (app, opts = {}) {
180
191
  }));
181
192
  //TODO: handle error (though it has no Error API nor any useful information)
182
193
  //xhr.addEventListener('error', (e) => {})
194
+ // @ts-ignore ??? this -> XMLHttpRequest
183
195
  return nativeOpen.apply(this, arguments);
184
- };
196
+ }
197
+ if (customEnv) {
198
+ customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv);
199
+ }
200
+ else {
201
+ XMLHttpRequest.prototype.open = trackXMLHttpReqOpen;
202
+ }
185
203
  const nativeSend = XMLHttpRequest.prototype.send;
186
- XMLHttpRequest.prototype.send = function (body) {
204
+ function trackXHRSend(body) {
205
+ // @ts-ignore ??? this -> XMLHttpRequest
187
206
  const rdo = getXHRRequestDataObject(this);
188
207
  rdo.body = body;
208
+ // @ts-ignore ??? this -> XMLHttpRequest
189
209
  return nativeSend.apply(this, arguments);
190
- };
210
+ }
211
+ if (customEnv) {
212
+ customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv);
213
+ }
214
+ else {
215
+ XMLHttpRequest.prototype.send = trackXHRSend;
216
+ }
191
217
  const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
192
- XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
218
+ function trackSetReqHeader(name, value) {
193
219
  if (!isHIgnored(name)) {
220
+ // @ts-ignore ??? this -> XMLHttpRequest
194
221
  const rdo = getXHRRequestDataObject(this);
195
222
  rdo.headers[name] = value;
196
223
  }
224
+ // @ts-ignore ??? this -> XMLHttpRequest
197
225
  return nativeSetRequestHeader.apply(this, arguments);
198
- };
226
+ }
227
+ if (customEnv) {
228
+ customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv);
229
+ }
230
+ else {
231
+ XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader;
232
+ }
199
233
  /* ====== <> ====== */
200
234
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openreplay/tracker",
3
3
  "description": "The OpenReplay tracker main package",
4
- "version": "5.0.0",
4
+ "version": "5.0.1-beta.3",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"
@@ -47,6 +47,7 @@
47
47
  "typescript": "^4.9.4"
48
48
  },
49
49
  "dependencies": {
50
+ "@medv/finder": "^3.0.0",
50
51
  "error-stack-parser": "^2.0.6"
51
52
  },
52
53
  "engines": {