@openreplay/tracker 3.4.16 → 3.5.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.
Files changed (48) hide show
  1. package/README.md +8 -1
  2. package/cjs/app/context.d.ts +18 -0
  3. package/cjs/app/context.js +48 -0
  4. package/cjs/app/index.d.ts +39 -9
  5. package/cjs/app/index.js +155 -121
  6. package/cjs/app/logger.d.ts +27 -0
  7. package/cjs/app/logger.js +42 -0
  8. package/cjs/app/observer/observer.d.ts +2 -27
  9. package/cjs/app/observer/observer.js +18 -92
  10. package/cjs/app/observer/top_observer.d.ts +3 -3
  11. package/cjs/app/observer/top_observer.js +11 -8
  12. package/cjs/app/sanitizer.d.ts +16 -0
  13. package/cjs/app/sanitizer.js +46 -0
  14. package/cjs/index.d.ts +10 -9
  15. package/cjs/index.js +32 -21
  16. package/cjs/modules/console.js +1 -1
  17. package/cjs/modules/img.js +15 -1
  18. package/cjs/modules/input.d.ts +3 -1
  19. package/cjs/modules/input.js +6 -3
  20. package/cjs/modules/mouse.js +3 -1
  21. package/cjs/utils.d.ts +0 -8
  22. package/cjs/utils.js +3 -4
  23. package/lib/app/context.d.ts +18 -0
  24. package/lib/app/context.js +43 -0
  25. package/lib/app/index.d.ts +39 -9
  26. package/lib/app/index.js +156 -122
  27. package/lib/app/logger.d.ts +27 -0
  28. package/lib/app/logger.js +39 -1
  29. package/lib/app/observer/observer.d.ts +2 -27
  30. package/lib/app/observer/observer.js +7 -79
  31. package/lib/app/observer/top_observer.d.ts +3 -3
  32. package/lib/app/observer/top_observer.js +10 -7
  33. package/lib/app/sanitizer.d.ts +16 -0
  34. package/lib/app/sanitizer.js +44 -1
  35. package/lib/index.d.ts +10 -9
  36. package/lib/index.js +32 -21
  37. package/lib/modules/console.js +1 -1
  38. package/lib/modules/img.js +16 -2
  39. package/lib/modules/input.d.ts +3 -1
  40. package/lib/modules/input.js +6 -3
  41. package/lib/modules/mouse.js +3 -1
  42. package/lib/utils.d.ts +0 -8
  43. package/lib/utils.js +2 -3
  44. package/package.json +1 -1
  45. package/cjs/app/observer.d.ts +0 -47
  46. package/cjs/app/observer.js +0 -395
  47. package/lib/app/observer.d.ts +0 -47
  48. package/lib/app/observer.js +0 -392
@@ -1,30 +1,8 @@
1
- import { stars, hasOpenreplayAttribute } from "../../utils.js";
2
1
  import { RemoveNodeAttribute, SetNodeAttribute, SetNodeAttributeURLBased, SetCSSDataURLBased, SetNodeData, CreateTextNode, CreateElementNode, MoveNode, RemoveNode, } from "../../messages/index.js";
2
+ import { isInstance, inDocument } from "../context.js";
3
3
  function isSVGElement(node) {
4
4
  return node.namespaceURI === 'http://www.w3.org/2000/svg';
5
5
  }
6
- // TODO: we need a type expert here so we won't have to ignore the lines
7
- // TODO: use it everywhere (static function; export from which file? <-- global Window typing required)
8
- export function isInstance(node, constr) {
9
- const doc = node.ownerDocument;
10
- if (!doc) { // null if Document
11
- return constr.name === 'Document';
12
- }
13
- let context =
14
- // @ts-ignore (for EI, Safary)
15
- doc.parentWindow ||
16
- doc.defaultView; // TODO: smart global typing for Window object
17
- while (context.parent && context.parent !== context) {
18
- // @ts-ignore
19
- if (node instanceof context[constr.name]) {
20
- return true;
21
- }
22
- // @ts-ignore
23
- context = context.parent;
24
- }
25
- // @ts-ignore
26
- return node instanceof context[constr.name];
27
- }
28
6
  function isIgnored(node) {
29
7
  if (isInstance(node, Text)) {
30
8
  return false;
@@ -54,7 +32,7 @@ function isObservable(node) {
54
32
  return !isIgnored(node);
55
33
  }
56
34
  export default class Observer {
57
- constructor(app, options, context = window) {
35
+ constructor(app, context = window) {
58
36
  this.app = app;
59
37
  this.context = context;
60
38
  this.commited = [];
@@ -63,34 +41,12 @@ export default class Observer {
63
41
  this.indexes = [];
64
42
  this.attributesList = [];
65
43
  this.textSet = new Set();
66
- this.textMasked = new Set();
67
- this.options = Object.assign({
68
- obscureTextEmails: true,
69
- obscureTextNumbers: false,
70
- }, options);
71
- this.inUpperContext = context.parent === context;
44
+ this.inUpperContext = context.parent === context; //TODO: get rid of context here
72
45
  this.observer = new MutationObserver(this.app.safe((mutations) => {
73
46
  for (const mutation of mutations) {
74
47
  const target = mutation.target;
75
48
  const type = mutation.type;
76
- // TODO TODO TODO: move to iframe_observer/remove??? (check if )
77
- // Special case
78
- // 'childList' on Document might happen in case of iframe.
79
- // TODO: generalize as much as possible
80
- // if (isInstance(target, Document) // Also ShadowRoot can be here
81
- // && type === 'childList'
82
- // //&& new Array(mutation.addedNodes).some(node => isInstance(node, HTMLHtmlElement))
83
- // ) {
84
- // const parentFrame = target.defaultView?.frameElement
85
- // if (!parentFrame) { continue }
86
- // this.bindTree(target.documentElement)
87
- // const frameID = this.app.nodes.getID(parentFrame)
88
- // const docID = this.app.nodes.getID(target.documentElement)
89
- // if (frameID === undefined || docID === undefined) { continue }
90
- // this.app.send(CreateIFrameDocument(frameID, docID));
91
- // continue;
92
- // }
93
- if (!isObservable(target) || !context.document.contains(target)) {
49
+ if (!isObservable(target) || !inDocument(target)) {
94
50
  continue;
95
51
  }
96
52
  if (type === 'childList') {
@@ -135,7 +91,6 @@ export default class Observer {
135
91
  this.indexes.length = 1;
136
92
  this.attributesList.length = 0;
137
93
  this.textSet.clear();
138
- //this.textMasked.clear();
139
94
  }
140
95
  sendNodeAttribute(id, node, name, value) {
141
96
  if (isSVGElement(node)) {
@@ -184,35 +139,14 @@ export default class Observer {
184
139
  }
185
140
  this.app.send(new SetNodeAttribute(id, name, value));
186
141
  }
187
- /* TODO: abstract sanitation */
188
- getInnerTextSecure(el) {
189
- const id = this.app.nodes.getID(el);
190
- if (!id) {
191
- return '';
192
- }
193
- return this.checkObscure(id, el.innerText);
194
- }
195
- checkObscure(id, data) {
196
- if (this.textMasked.has(id)) {
197
- return data.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '█');
198
- }
199
- if (this.options.obscureTextNumbers) {
200
- data = data.replace(/\d/g, '0');
201
- }
202
- if (this.options.obscureTextEmails) {
203
- data = data.replace(/([^\s]+)@([^\s]+)\.([^\s]+)/g, (...f) => stars(f[1]) + '@' + stars(f[2]) + '.' + stars(f[3]));
204
- }
205
- return data;
206
- }
207
142
  sendNodeData(id, parentElement, data) {
208
143
  if (isInstance(parentElement, HTMLStyleElement) || isInstance(parentElement, SVGStyleElement)) {
209
144
  this.app.send(new SetCSSDataURLBased(id, data, this.app.getBaseHref()));
210
145
  return;
211
146
  }
212
- data = this.checkObscure(id, data);
147
+ data = this.app.sanitizer.sanitize(id, data);
213
148
  this.app.send(new SetNodeData(id, data));
214
149
  }
215
- /* end TODO: abstract sanitation */
216
150
  bindNode(node) {
217
151
  const r = this.app.nodes.registerNode(node);
218
152
  const id = r[0];
@@ -264,10 +198,7 @@ export default class Observer {
264
198
  this.unbindNode(node);
265
199
  return false;
266
200
  }
267
- if (this.textMasked.has(parentID) ||
268
- (isInstance(node, Element) && hasOpenreplayAttribute(node, 'masked'))) {
269
- this.textMasked.add(id);
270
- }
201
+ this.app.sanitizer.handleNode(id, parentID, node);
271
202
  }
272
203
  let sibling = node.previousSibling;
273
204
  while (sibling !== null) {
@@ -340,8 +271,7 @@ export default class Observer {
340
271
  let node;
341
272
  for (let id = 0; id < this.recents.length; id++) {
342
273
  // TODO: make things/logic nice here.
343
- // commit required in any case if recents[id] true or false (in case of unbinding).
344
- // ???!?!?R@TW:$HKJ$WLKn
274
+ // commit required in any case if recents[id] true or false (in case of unbinding) or undefined (in case of attr change).
345
275
  if (!this.myNodes[id]) {
346
276
  continue;
347
277
  }
@@ -369,8 +299,6 @@ export default class Observer {
369
299
  disconnect() {
370
300
  this.observer.disconnect();
371
301
  this.clear();
372
- // to sanitizer
373
- this.textMasked.clear();
374
302
  this.myNodes.length = 0;
375
303
  }
376
304
  }
@@ -1,10 +1,10 @@
1
1
  import Observer from "./observer.js";
2
- import type { Options as BaseOptions } from "./observer.js";
3
2
  import App from "../index.js";
4
- export interface Options extends Partial<BaseOptions> {
3
+ export interface Options {
5
4
  captureIFrames: boolean;
6
5
  }
7
- export default class TopObserver extends Observer<Options> {
6
+ export default class TopObserver extends Observer {
7
+ private readonly options;
8
8
  constructor(app: App, options: Partial<Options>);
9
9
  private iframeObservers;
10
10
  private handleIframe;
@@ -1,15 +1,18 @@
1
- import Observer, { isInstance } from "./observer.js";
1
+ import Observer from "./observer.js";
2
+ import { isInstance } from "../context.js";
2
3
  import IFrameObserver from "./iframe_observer.js";
3
4
  import ShadowRootObserver from "./shadow_root_observer.js";
4
5
  import { CreateDocument } from "../../messages/index.js";
5
- const attachShadowNativeFn = Element.prototype.attachShadow;
6
+ import { IN_BROWSER } from '../../utils.js';
7
+ const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
6
8
  export default class TopObserver extends Observer {
7
9
  constructor(app, options) {
8
- super(app, Object.assign({
9
- captureIFrames: false
10
- }, options));
10
+ super(app);
11
11
  this.iframeObservers = [];
12
12
  this.shadowRootObservers = [];
13
+ this.options = Object.assign({
14
+ captureIFrames: false
15
+ }, options);
13
16
  // IFrames
14
17
  this.app.nodes.attachNodeCallback(node => {
15
18
  if (isInstance(node, HTMLIFrameElement) &&
@@ -38,7 +41,7 @@ export default class TopObserver extends Observer {
38
41
  if (!context) {
39
42
  return;
40
43
  }
41
- const observer = new IFrameObserver(this.app, this.options, context);
44
+ const observer = new IFrameObserver(this.app, context);
42
45
  this.iframeObservers.push(observer);
43
46
  observer.observe(iframe);
44
47
  });
@@ -46,7 +49,7 @@ export default class TopObserver extends Observer {
46
49
  handle();
47
50
  }
48
51
  handleShadowRoot(shRoot) {
49
- const observer = new ShadowRootObserver(this.app, this.options, this.context);
52
+ const observer = new ShadowRootObserver(this.app, this.context);
50
53
  this.shadowRootObservers.push(observer);
51
54
  observer.observe(shRoot.host);
52
55
  }
@@ -0,0 +1,16 @@
1
+ import App from "./index.js";
2
+ export interface Options {
3
+ obscureTextEmails: boolean;
4
+ obscureTextNumbers: boolean;
5
+ }
6
+ export default class Sanitizer {
7
+ private readonly app;
8
+ private readonly masked;
9
+ private readonly options;
10
+ constructor(app: App, options: Partial<Options>);
11
+ handleNode(id: number, parentID: number, node: Node): void;
12
+ sanitize(id: number, data: string): string;
13
+ isMasked(id: number): boolean;
14
+ getInnerTextSecure(el: HTMLElement): string;
15
+ clear(): void;
16
+ }
@@ -1 +1,44 @@
1
- "use strict";
1
+ import { stars, hasOpenreplayAttribute } from "../utils.js";
2
+ import { isInstance } from "./context.js";
3
+ export default class Sanitizer {
4
+ constructor(app, options) {
5
+ this.app = app;
6
+ this.masked = new Set();
7
+ this.options = Object.assign({
8
+ obscureTextEmails: true,
9
+ obscureTextNumbers: false,
10
+ }, options);
11
+ }
12
+ handleNode(id, parentID, node) {
13
+ if (this.masked.has(parentID) ||
14
+ (isInstance(node, Element) && hasOpenreplayAttribute(node, 'masked'))) {
15
+ this.masked.add(id);
16
+ }
17
+ }
18
+ sanitize(id, data) {
19
+ if (this.masked.has(id)) {
20
+ // TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
21
+ return data.trim().replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '█');
22
+ }
23
+ if (this.options.obscureTextNumbers) {
24
+ data = data.replace(/\d/g, '0');
25
+ }
26
+ if (this.options.obscureTextEmails) {
27
+ data = data.replace(/([^\s]+)@([^\s]+)\.([^\s]+)/g, (...f) => stars(f[1]) + '@' + stars(f[2]) + '.' + stars(f[3]));
28
+ }
29
+ return data;
30
+ }
31
+ isMasked(id) {
32
+ return this.masked.has(id);
33
+ }
34
+ getInnerTextSecure(el) {
35
+ const id = this.app.nodes.getID(el);
36
+ if (!id) {
37
+ return '';
38
+ }
39
+ return this.sanitize(id, el.innerText);
40
+ }
41
+ clear() {
42
+ this.masked.clear();
43
+ }
44
+ }
package/lib/index.d.ts CHANGED
@@ -2,18 +2,20 @@ import App from "./app/index.js";
2
2
  export { default as App } from './app/index.js';
3
3
  import * as _Messages from "./messages/index.js";
4
4
  export declare const Messages: typeof _Messages;
5
- import { Options as AppOptions } from "./app/index.js";
6
- import { Options as ConsoleOptions } from "./modules/console.js";
7
- import { Options as ExceptionOptions } from "./modules/exception.js";
8
- import { Options as InputOptions } from "./modules/input.js";
9
- import { Options as PerformanceOptions } from "./modules/performance.js";
10
- import { Options as TimingOptions } from "./modules/timing.js";
11
- export type { OnStartInfo } from './app/index.js';
5
+ import type { Options as AppOptions } from "./app/index.js";
6
+ import type { Options as ConsoleOptions } from "./modules/console.js";
7
+ import type { Options as ExceptionOptions } from "./modules/exception.js";
8
+ import type { Options as InputOptions } from "./modules/input.js";
9
+ import type { Options as PerformanceOptions } from "./modules/performance.js";
10
+ import type { Options as TimingOptions } from "./modules/timing.js";
11
+ import type { StartOptions } from './app/index.js';
12
+ import type { OnStartInfo } from './app/index.js';
12
13
  export declare type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions> & {
13
14
  projectID?: number;
14
15
  projectKey: string;
15
16
  sessionToken?: string;
16
17
  respectDoNotTrack?: boolean;
18
+ autoResetOnWindowOpen?: boolean;
17
19
  __DISABLE_SECURE_MODE?: boolean;
18
20
  };
19
21
  export default class API {
@@ -22,8 +24,7 @@ export default class API {
22
24
  constructor(options: Options);
23
25
  use<T>(fn: (app: App | null, options?: Options) => T): T;
24
26
  isActive(): boolean;
25
- active(): boolean;
26
- start(): Promise<import("./app/index.js").OnStartInfo>;
27
+ start(startOpts?: StartOptions): Promise<OnStartInfo>;
27
28
  stop(): void;
28
29
  getSessionToken(): string | null | undefined;
29
30
  getSessionID(): string | null | undefined;
package/lib/index.js CHANGED
@@ -77,7 +77,7 @@ export default class API {
77
77
  (navigator.doNotTrack == '1'
78
78
  // @ts-ignore
79
79
  || window.doNotTrack == '1');
80
- this.app = doNotTrack ||
80
+ const app = this.app = doNotTrack ||
81
81
  !('Map' in window) ||
82
82
  !('Set' in window) ||
83
83
  !('MutationObserver' in window) ||
@@ -88,20 +88,34 @@ export default class API {
88
88
  !('Worker' in window)
89
89
  ? null
90
90
  : new App(options.projectKey, options.sessionToken, options);
91
- if (this.app !== null) {
92
- Viewport(this.app);
93
- CSSRules(this.app);
94
- Connection(this.app);
95
- Console(this.app, options);
96
- Exception(this.app, options);
97
- Img(this.app);
98
- Input(this.app, options);
99
- Mouse(this.app);
100
- Timing(this.app, options);
101
- Performance(this.app, options);
102
- Scroll(this.app);
103
- Longtasks(this.app);
91
+ if (app !== null) {
92
+ Viewport(app);
93
+ CSSRules(app);
94
+ Connection(app);
95
+ Console(app, options);
96
+ Exception(app, options);
97
+ Img(app);
98
+ Input(app, options);
99
+ Mouse(app);
100
+ Timing(app, options);
101
+ Performance(app, options);
102
+ Scroll(app);
103
+ Longtasks(app);
104
104
  window.__OPENREPLAY__ = this;
105
+ if (options.autoResetOnWindowOpen) {
106
+ const wOpen = window.open;
107
+ app.attachStartCallback(() => {
108
+ // @ts-ignore ?
109
+ window.open = function (...args) {
110
+ app.resetNextPageSession(true);
111
+ wOpen.call(window, ...args);
112
+ app.resetNextPageSession(false);
113
+ };
114
+ });
115
+ app.attachStopCallback(() => {
116
+ window.open = wOpen;
117
+ });
118
+ }
105
119
  }
106
120
  else {
107
121
  console.log("OpenReplay: browser doesn't support API required for tracking or doNotTrack is set to 1.");
@@ -111,7 +125,7 @@ export default class API {
111
125
  // no-cors issue only with text/plain or not-set Content-Type
112
126
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
113
127
  req.send(JSON.stringify({
114
- trackerVersion: '3.4.16',
128
+ trackerVersion: '3.5.0',
115
129
  projectKey: options.projectKey,
116
130
  doNotTrack,
117
131
  // TODO: add precise reason (an exact API missing)
@@ -127,11 +141,7 @@ export default class API {
127
141
  }
128
142
  return this.app.active();
129
143
  }
130
- active() {
131
- deprecationWarn("'active' method", "'isActive' method", "/");
132
- return this.isActive();
133
- }
134
- start() {
144
+ start(startOpts) {
135
145
  if (!IN_BROWSER) {
136
146
  console.error(`OpenReplay: you are trying to start Tracker on a node.js environment. If you want to use OpenReplay with SSR, please, use componentDidMount or useEffect API for placing the \`tracker.start()\` line. Check documentation on ${DOCS_HOST}${DOCS_SETUP}`);
137
147
  return Promise.reject("Trying to start not in browser.");
@@ -139,7 +149,8 @@ export default class API {
139
149
  if (this.app === null) {
140
150
  return Promise.reject("Browser doesn't support required api, or doNotTrack is active.");
141
151
  }
142
- return this.app.start();
152
+ // TODO: check argument typing
153
+ return this.app.start(startOpts);
143
154
  }
144
155
  stop() {
145
156
  if (this.app === null) {
@@ -16,7 +16,7 @@ function printString(arg) {
16
16
  if (Array.isArray(arg)) {
17
17
  return `Array(${arg.length})`;
18
18
  }
19
- return arg.toString();
19
+ return String(arg);
20
20
  }
21
21
  function printFloat(arg) {
22
22
  if (typeof arg !== 'number')
@@ -1,6 +1,17 @@
1
1
  import { timestamp, isURL } from "../utils.js";
2
- import { ResourceTiming, SetNodeAttributeURLBased } from "../messages/index.js";
2
+ import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../messages/index.js";
3
+ const PLACEHOLDER_SRC = "https://static.openreplay.com/tracker/placeholder.jpeg";
3
4
  export default function (app) {
5
+ function sendPlaceholder(id, node) {
6
+ app.send(new SetNodeAttribute(id, "src", PLACEHOLDER_SRC));
7
+ const { width, height } = node.getBoundingClientRect();
8
+ if (!node.hasAttribute("width")) {
9
+ app.send(new SetNodeAttribute(id, "width", String(width)));
10
+ }
11
+ if (!node.hasAttribute("height")) {
12
+ app.send(new SetNodeAttribute(id, "height", String(height)));
13
+ }
14
+ }
4
15
  const sendImgSrc = app.safe(function () {
5
16
  const id = app.nodes.getID(this);
6
17
  if (id === undefined) {
@@ -15,7 +26,10 @@ export default function (app) {
15
26
  app.send(new ResourceTiming(timestamp(), 0, 0, 0, 0, 0, src, 'img'));
16
27
  }
17
28
  }
18
- else if (src.length < 1e5) {
29
+ else if (src.length >= 1e5 || app.sanitizer.isMasked(id)) {
30
+ sendPlaceholder(id, this);
31
+ }
32
+ else {
19
33
  app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
20
34
  }
21
35
  });
@@ -1,5 +1,6 @@
1
1
  import App from "../app/index.js";
2
- export declare function getInputLabel(node: HTMLInputElement): string;
2
+ declare type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
3
+ export declare function getInputLabel(node: TextEditableElement): string;
3
4
  export declare const enum InputMode {
4
5
  Plain = 0,
5
6
  Obscured = 1,
@@ -11,3 +12,4 @@ export interface Options {
11
12
  defaultInputMode: InputMode;
12
13
  }
13
14
  export default function (app: App, opts: Partial<Options>): void;
15
+ export {};
@@ -1,6 +1,9 @@
1
1
  import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from "../utils.js";
2
2
  import { SetInputTarget, SetInputValue, SetInputChecked } from "../messages/index.js";
3
- function isInput(node) {
3
+ function isTextEditable(node) {
4
+ if (node instanceof HTMLTextAreaElement) {
5
+ return true;
6
+ }
4
7
  if (!(node instanceof HTMLInputElement)) {
5
8
  return false;
6
9
  }
@@ -107,7 +110,7 @@ export default function (app, opts) {
107
110
  app.ticker.attach(() => {
108
111
  inputValues.forEach((value, id) => {
109
112
  const node = app.nodes.getNode(id);
110
- if (!isInput(node)) {
113
+ if (!isTextEditable(node)) {
111
114
  inputValues.delete(id);
112
115
  return;
113
116
  }
@@ -138,7 +141,7 @@ export default function (app, opts) {
138
141
  if (id === undefined) {
139
142
  return;
140
143
  }
141
- if (isInput(node)) {
144
+ if (isTextEditable(node)) {
142
145
  inputValues.set(id, node.value);
143
146
  sendInputValue(id, node);
144
147
  return;
@@ -55,6 +55,7 @@ function _getTarget(target) {
55
55
  if (tag === 'BUTTON' ||
56
56
  tag === 'A' ||
57
57
  tag === 'LI' ||
58
+ tag === 'SELECT' ||
58
59
  element.onclick != null ||
59
60
  element.getAttribute('role') === 'button' ||
60
61
  getLabelAttribute(element) !== null) {
@@ -81,9 +82,10 @@ export default function (app) {
81
82
  if (tag === 'BUTTON' ||
82
83
  tag === 'A' ||
83
84
  tag === 'LI' ||
85
+ tag === 'SELECT' ||
84
86
  target.onclick != null ||
85
87
  target.getAttribute('role') === 'button') {
86
- const label = app.observer.getInnerTextSecure(target);
88
+ const label = app.sanitizer.getInnerTextSecure(target);
87
89
  return normSpaces(label).slice(0, 100);
88
90
  }
89
91
  return '';
package/lib/utils.d.ts CHANGED
@@ -3,14 +3,6 @@ export declare const stars: (str: string) => string;
3
3
  export declare function normSpaces(str: string): string;
4
4
  export declare function isURL(s: string): boolean;
5
5
  export declare const IN_BROWSER: boolean;
6
- export declare const log: {
7
- (...data: any[]): void;
8
- (message?: any, ...optionalParams: any[]): void;
9
- };
10
- export declare const warn: {
11
- (...data: any[]): void;
12
- (message?: any, ...optionalParams: any[]): void;
13
- };
14
6
  export declare const DOCS_HOST = "https://docs.openreplay.com";
15
7
  export declare function deprecationWarn(nameOfFeature: string, useInstead: string, docsPath?: string): void;
16
8
  export declare function getLabelAttribute(e: Element): string | null;
package/lib/utils.js CHANGED
@@ -12,15 +12,14 @@ export function isURL(s) {
12
12
  return s.substr(0, 8) === 'https://' || s.substr(0, 7) === 'http://';
13
13
  }
14
14
  export const IN_BROWSER = !(typeof window === "undefined");
15
- export const log = console.log;
16
- export const warn = console.warn;
15
+ // TODO: JOIN IT WITH LOGGER somehow (use logging decorators?); Don't forget about index.js loggin when there is no logger instance.
17
16
  export const DOCS_HOST = 'https://docs.openreplay.com';
18
17
  const warnedFeatures = {};
19
18
  export function deprecationWarn(nameOfFeature, useInstead, docsPath = "/") {
20
19
  if (warnedFeatures[nameOfFeature]) {
21
20
  return;
22
21
  }
23
- warn(`OpenReplay: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ""} Visit ${DOCS_HOST}${docsPath} for more information.`);
22
+ console.warn(`OpenReplay: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ""} Visit ${DOCS_HOST}${docsPath} for more information.`);
24
23
  warnedFeatures[nameOfFeature] = true;
25
24
  }
26
25
  export function getLabelAttribute(e) {
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": "3.4.16",
4
+ "version": "3.5.0",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"
@@ -1,47 +0,0 @@
1
- import App from "./index.js";
2
- interface Window extends WindowProxy {
3
- HTMLInputElement: typeof HTMLInputElement;
4
- HTMLLinkElement: typeof HTMLLinkElement;
5
- HTMLStyleElement: typeof HTMLStyleElement;
6
- SVGStyleElement: typeof SVGStyleElement;
7
- HTMLIFrameElement: typeof HTMLIFrameElement;
8
- Text: typeof Text;
9
- Element: typeof Element;
10
- }
11
- export interface Options {
12
- obscureTextEmails: boolean;
13
- obscureTextNumbers: boolean;
14
- captureIFrames: boolean;
15
- }
16
- export default class Observer {
17
- private readonly app;
18
- private readonly options;
19
- private readonly context;
20
- private readonly observer;
21
- private readonly commited;
22
- private readonly recents;
23
- private readonly indexes;
24
- private readonly attributesList;
25
- private readonly textSet;
26
- private readonly textMasked;
27
- constructor(app: App, options: Options, context?: Window);
28
- private clear;
29
- private isInstance;
30
- private isIgnored;
31
- private sendNodeAttribute;
32
- getInnerTextSecure(el: HTMLElement): string;
33
- private checkObscure;
34
- private sendNodeData;
35
- private bindNode;
36
- private bindTree;
37
- private unbindNode;
38
- private _commitNode;
39
- private commitNode;
40
- private commitNodes;
41
- private iframeObservers;
42
- private handleIframe;
43
- private observeIframe;
44
- observe(): void;
45
- disconnect(): void;
46
- }
47
- export {};