@openreplay/tracker 3.5.16-beta.2 → 3.5.16-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.
@@ -1,5 +1,5 @@
1
1
  import type Message from "../common/messages.js";
2
- import Nodes, { CheckOptions } from "./nodes.js";
2
+ import Nodes from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
@@ -46,10 +46,8 @@ declare type AppOptions = {
46
46
  __debug__?: LoggerOptions;
47
47
  localStorage: Storage;
48
48
  sessionStorage: Storage;
49
- maxMemorySize: number;
50
- memoryCheckInterval: number;
51
49
  onStart?: StartCallback;
52
- } & WebworkerOptions & CheckOptions;
50
+ } & WebworkerOptions;
53
51
  export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
54
52
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
55
53
  export default class App {
package/cjs/app/index.js CHANGED
@@ -27,13 +27,12 @@ class App {
27
27
  // if (options.onStart !== undefined) {
28
28
  // deprecationWarn("'onStart' option", "tracker.start().then(/* handle session info */)")
29
29
  // } ?? maybe onStart is good
30
- // private gc?: NodeJS.Timer = undefined;
31
30
  this.messages = [];
32
31
  this.startCallbacks = [];
33
32
  this.stopCallbacks = [];
34
33
  this.commitCallbacks = [];
35
34
  this.activityState = ActivityState.NotActive;
36
- this.version = '3.5.16-beta.2'; // TODO: version compatability check inside each plugin.
35
+ this.version = '3.5.16-beta.3'; // TODO: version compatability check inside each plugin.
37
36
  this.projectKey = projectKey;
38
37
  this.options = Object.assign({
39
38
  revID: '',
@@ -49,12 +48,10 @@ class App {
49
48
  __debug_report_edp: null,
50
49
  localStorage: window.localStorage,
51
50
  sessionStorage: window.sessionStorage,
52
- maxMemorySize: 550 * 1e6,
53
- memoryCheckInterval: 2 * 60 * 1000,
54
51
  }, options);
55
52
  this.revID = this.options.revID;
56
53
  this.sanitizer = new sanitizer_js_1.default(this, options);
57
- this.nodes = new nodes_js_1.default(this.options.node_id, this.options.maxMemorySize, this.options.memoryCheckInterval);
54
+ this.nodes = new nodes_js_1.default(this.options.node_id);
58
55
  this.observer = new top_observer_js_1.default(this, options);
59
56
  this.ticker = new ticker_js_1.default(this);
60
57
  this.ticker.attach(() => this.commit());
@@ -320,16 +317,6 @@ class App {
320
317
  this.observer.observe();
321
318
  this.ticker.start();
322
319
  this.notify.log("OpenReplay tracking started.");
323
- // // GC
324
- // if (!this.gc) {
325
- // this.gc = setInterval(() => {
326
- // console.log('checking')
327
- // // @ts-ignore
328
- // if (window.performance.memory.usedJSHeapSize > this.options.maxMemorySize) {
329
- // this.restart();
330
- // }
331
- // }, this.options.memoryCheckInterval)
332
- // }
333
320
  // get rid of onStart ?
334
321
  if (typeof this.options.onStart === 'function') {
335
322
  this.options.onStart(onStartInfo);
@@ -366,7 +353,6 @@ class App {
366
353
  stop(calledFromAPI = false, restarting = false) {
367
354
  if (this.activityState !== ActivityState.NotActive) {
368
355
  try {
369
- // this.gc && clearInterval(this.gc)
370
356
  this.sanitizer.clear();
371
357
  this.observer.disconnect();
372
358
  this.nodes.clear();
@@ -1,16 +1,10 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  declare type NodeCallback = (node: Node, isStart: boolean) => void;
3
- export interface CheckOptions {
4
- maxMemorySize: number;
5
- memoryCheckInterval: number;
6
- }
7
2
  export default class Nodes {
8
3
  private readonly node_id;
9
4
  private nodes;
10
5
  private readonly nodeCallbacks;
11
6
  private readonly elementListeners;
12
- gc: NodeJS.Timer | undefined;
13
- constructor(node_id: string, maxMemorySize: CheckOptions["maxMemorySize"], memoryCheckInterval: CheckOptions["memoryCheckInterval"]);
7
+ constructor(node_id: string);
14
8
  attachNodeCallback(nodeCallback: NodeCallback): void;
15
9
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
16
10
  registerNode(node: Node): [id: number, isNew: boolean];
package/cjs/app/nodes.js CHANGED
@@ -1,17 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class Nodes {
4
- constructor(node_id, maxMemorySize, memoryCheckInterval) {
4
+ constructor(node_id) {
5
5
  this.node_id = node_id;
6
6
  this.nodes = [];
7
7
  this.nodeCallbacks = [];
8
8
  this.elementListeners = new Map();
9
- this.gc = setInterval(() => {
10
- // @ts-ignore should use settings object here
11
- if (window.performance.memory.usedJSHeapSize > maxMemorySize) {
12
- this.cleanTree();
13
- }
14
- }, memoryCheckInterval);
15
9
  }
16
10
  attachNodeCallback(nodeCallback) {
17
11
  this.nodeCallbacks.push(nodeCallback);
@@ -88,10 +82,6 @@ class Nodes {
88
82
  this.unregisterNode(node);
89
83
  }
90
84
  this.nodes.length = 0;
91
- if (this.gc) {
92
- clearInterval(this.gc);
93
- this.gc = undefined;
94
- }
95
85
  }
96
86
  }
97
87
  exports.default = Nodes;
@@ -32,11 +32,17 @@ function isObservable(node) {
32
32
  - fix unbinding logic + send all removals first (ensure sequence is correct)
33
33
  - use document as a 0-node in the upper context (should be updated in player at first)
34
34
  */
35
+ /*
36
+ Nikita:
37
+ - rn we only send unbind event for parent (all child nodes will be cut in the live replay anyways)
38
+ to prevent sending 1k+ unbinds for child nodes and making replay file bigger than it should be
39
+ */
35
40
  var RecentsType;
36
41
  (function (RecentsType) {
37
42
  RecentsType[RecentsType["New"] = 0] = "New";
38
43
  RecentsType[RecentsType["Removed"] = 1] = "Removed";
39
44
  RecentsType[RecentsType["Changed"] = 2] = "Changed";
45
+ RecentsType[RecentsType["RemovedChild"] = 3] = "RemovedChild";
40
46
  })(RecentsType || (RecentsType = {}));
41
47
  class Observer {
42
48
  constructor(app, isTopContext = false) {
@@ -56,7 +62,7 @@ class Observer {
56
62
  }
57
63
  if (type === 'childList') {
58
64
  for (let i = 0; i < mutation.removedNodes.length; i++) {
59
- this.bindTree(mutation.removedNodes[i]);
65
+ this.bindTree(mutation.removedNodes[i], true);
60
66
  }
61
67
  for (let i = 0; i < mutation.addedNodes.length; i++) {
62
68
  this.bindTree(mutation.addedNodes[i]);
@@ -152,29 +158,30 @@ class Observer {
152
158
  data = this.app.sanitizer.sanitize(id, data);
153
159
  this.app.send(new messages_js_1.SetNodeData(id, data));
154
160
  }
155
- bindNode(node) {
161
+ bindNode(node, isRemove = false) {
156
162
  const [id, isNew] = this.app.nodes.registerNode(node);
157
163
  if (isNew) {
158
164
  this.recents.set(id, RecentsType.New);
159
165
  }
160
166
  else if (this.recents.get(id) !== RecentsType.New) { // can we do just `else` here?
161
- this.recents.set(id, RecentsType.Removed);
167
+ this.recents.set(id, isRemove ? RecentsType.RemovedChild : RecentsType.Removed);
162
168
  }
163
169
  }
164
- bindTree(node) {
170
+ bindTree(node, isRemove = false) {
165
171
  if (!isObservable(node)) {
166
172
  return;
167
173
  }
168
174
  this.bindNode(node);
169
175
  const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
170
- acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) !== undefined
176
+ acceptNode: (node) => isIgnored(node)
177
+ || (this.app.nodes.getID(node) !== undefined && !isRemove)
171
178
  ? NodeFilter.FILTER_REJECT
172
179
  : NodeFilter.FILTER_ACCEPT,
173
180
  },
174
181
  // @ts-ignore
175
182
  false);
176
183
  while (walker.nextNode()) {
177
- this.bindNode(walker.currentNode);
184
+ this.bindNode(walker.currentNode, isRemove);
178
185
  }
179
186
  }
180
187
  unbindNode(node) {
package/cjs/index.js CHANGED
@@ -127,7 +127,7 @@ class API {
127
127
  // no-cors issue only with text/plain or not-set Content-Type
128
128
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
129
129
  req.send(JSON.stringify({
130
- trackerVersion: '3.5.16-beta.2',
130
+ trackerVersion: '3.5.16-beta.3',
131
131
  projectKey: options.projectKey,
132
132
  doNotTrack,
133
133
  // TODO: add precise reason (an exact API missing)
@@ -114,11 +114,11 @@ function default_1(app, opts) {
114
114
  app.ticker.attach(() => {
115
115
  inputValues.forEach((value, id) => {
116
116
  const node = app.nodes.getNode(id);
117
- if (!isTextEditable(node)) {
117
+ if (node && !isTextEditable(node)) {
118
118
  inputValues.delete(id);
119
119
  return;
120
120
  }
121
- if (value !== node.value) {
121
+ if (node && value !== node.value) {
122
122
  inputValues.set(id, node.value);
123
123
  if (!registeredTargets.has(id)) {
124
124
  registeredTargets.add(id);
@@ -129,11 +129,11 @@ function default_1(app, opts) {
129
129
  });
130
130
  checkableValues.forEach((checked, id) => {
131
131
  const node = app.nodes.getNode(id);
132
- if (!isCheckable(node)) {
132
+ if (node && !isCheckable(node)) {
133
133
  checkableValues.delete(id);
134
134
  return;
135
135
  }
136
- if (checked !== node.checked) {
136
+ if (node && checked !== node.checked) {
137
137
  checkableValues.set(id, node.checked);
138
138
  app.send(new messages_js_1.SetInputChecked(id, node.checked));
139
139
  }
@@ -1,5 +1,5 @@
1
1
  import type Message from "../common/messages.js";
2
- import Nodes, { CheckOptions } from "./nodes.js";
2
+ import Nodes from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
@@ -46,10 +46,8 @@ declare type AppOptions = {
46
46
  __debug__?: LoggerOptions;
47
47
  localStorage: Storage;
48
48
  sessionStorage: Storage;
49
- maxMemorySize: number;
50
- memoryCheckInterval: number;
51
49
  onStart?: StartCallback;
52
- } & WebworkerOptions & CheckOptions;
50
+ } & WebworkerOptions;
53
51
  export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
54
52
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
55
53
  export default class App {
package/lib/app/index.js CHANGED
@@ -24,13 +24,12 @@ export default class App {
24
24
  // if (options.onStart !== undefined) {
25
25
  // deprecationWarn("'onStart' option", "tracker.start().then(/* handle session info */)")
26
26
  // } ?? maybe onStart is good
27
- // private gc?: NodeJS.Timer = undefined;
28
27
  this.messages = [];
29
28
  this.startCallbacks = [];
30
29
  this.stopCallbacks = [];
31
30
  this.commitCallbacks = [];
32
31
  this.activityState = ActivityState.NotActive;
33
- this.version = '3.5.16-beta.2'; // TODO: version compatability check inside each plugin.
32
+ this.version = '3.5.16-beta.3'; // TODO: version compatability check inside each plugin.
34
33
  this.projectKey = projectKey;
35
34
  this.options = Object.assign({
36
35
  revID: '',
@@ -46,12 +45,10 @@ export default class App {
46
45
  __debug_report_edp: null,
47
46
  localStorage: window.localStorage,
48
47
  sessionStorage: window.sessionStorage,
49
- maxMemorySize: 550 * 1e6,
50
- memoryCheckInterval: 2 * 60 * 1000,
51
48
  }, options);
52
49
  this.revID = this.options.revID;
53
50
  this.sanitizer = new Sanitizer(this, options);
54
- this.nodes = new Nodes(this.options.node_id, this.options.maxMemorySize, this.options.memoryCheckInterval);
51
+ this.nodes = new Nodes(this.options.node_id);
55
52
  this.observer = new Observer(this, options);
56
53
  this.ticker = new Ticker(this);
57
54
  this.ticker.attach(() => this.commit());
@@ -317,16 +314,6 @@ export default class App {
317
314
  this.observer.observe();
318
315
  this.ticker.start();
319
316
  this.notify.log("OpenReplay tracking started.");
320
- // // GC
321
- // if (!this.gc) {
322
- // this.gc = setInterval(() => {
323
- // console.log('checking')
324
- // // @ts-ignore
325
- // if (window.performance.memory.usedJSHeapSize > this.options.maxMemorySize) {
326
- // this.restart();
327
- // }
328
- // }, this.options.memoryCheckInterval)
329
- // }
330
317
  // get rid of onStart ?
331
318
  if (typeof this.options.onStart === 'function') {
332
319
  this.options.onStart(onStartInfo);
@@ -363,7 +350,6 @@ export default class App {
363
350
  stop(calledFromAPI = false, restarting = false) {
364
351
  if (this.activityState !== ActivityState.NotActive) {
365
352
  try {
366
- // this.gc && clearInterval(this.gc)
367
353
  this.sanitizer.clear();
368
354
  this.observer.disconnect();
369
355
  this.nodes.clear();
@@ -1,16 +1,10 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  declare type NodeCallback = (node: Node, isStart: boolean) => void;
3
- export interface CheckOptions {
4
- maxMemorySize: number;
5
- memoryCheckInterval: number;
6
- }
7
2
  export default class Nodes {
8
3
  private readonly node_id;
9
4
  private nodes;
10
5
  private readonly nodeCallbacks;
11
6
  private readonly elementListeners;
12
- gc: NodeJS.Timer | undefined;
13
- constructor(node_id: string, maxMemorySize: CheckOptions["maxMemorySize"], memoryCheckInterval: CheckOptions["memoryCheckInterval"]);
7
+ constructor(node_id: string);
14
8
  attachNodeCallback(nodeCallback: NodeCallback): void;
15
9
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
16
10
  registerNode(node: Node): [id: number, isNew: boolean];
package/lib/app/nodes.js CHANGED
@@ -1,15 +1,9 @@
1
1
  export default class Nodes {
2
- constructor(node_id, maxMemorySize, memoryCheckInterval) {
2
+ constructor(node_id) {
3
3
  this.node_id = node_id;
4
4
  this.nodes = [];
5
5
  this.nodeCallbacks = [];
6
6
  this.elementListeners = new Map();
7
- this.gc = setInterval(() => {
8
- // @ts-ignore should use settings object here
9
- if (window.performance.memory.usedJSHeapSize > maxMemorySize) {
10
- this.cleanTree();
11
- }
12
- }, memoryCheckInterval);
13
7
  }
14
8
  attachNodeCallback(nodeCallback) {
15
9
  this.nodeCallbacks.push(nodeCallback);
@@ -86,9 +80,5 @@ export default class Nodes {
86
80
  this.unregisterNode(node);
87
81
  }
88
82
  this.nodes.length = 0;
89
- if (this.gc) {
90
- clearInterval(this.gc);
91
- this.gc = undefined;
92
- }
93
83
  }
94
84
  }
@@ -30,11 +30,17 @@ function isObservable(node) {
30
30
  - fix unbinding logic + send all removals first (ensure sequence is correct)
31
31
  - use document as a 0-node in the upper context (should be updated in player at first)
32
32
  */
33
+ /*
34
+ Nikita:
35
+ - rn we only send unbind event for parent (all child nodes will be cut in the live replay anyways)
36
+ to prevent sending 1k+ unbinds for child nodes and making replay file bigger than it should be
37
+ */
33
38
  var RecentsType;
34
39
  (function (RecentsType) {
35
40
  RecentsType[RecentsType["New"] = 0] = "New";
36
41
  RecentsType[RecentsType["Removed"] = 1] = "Removed";
37
42
  RecentsType[RecentsType["Changed"] = 2] = "Changed";
43
+ RecentsType[RecentsType["RemovedChild"] = 3] = "RemovedChild";
38
44
  })(RecentsType || (RecentsType = {}));
39
45
  export default class Observer {
40
46
  constructor(app, isTopContext = false) {
@@ -54,7 +60,7 @@ export default class Observer {
54
60
  }
55
61
  if (type === 'childList') {
56
62
  for (let i = 0; i < mutation.removedNodes.length; i++) {
57
- this.bindTree(mutation.removedNodes[i]);
63
+ this.bindTree(mutation.removedNodes[i], true);
58
64
  }
59
65
  for (let i = 0; i < mutation.addedNodes.length; i++) {
60
66
  this.bindTree(mutation.addedNodes[i]);
@@ -150,29 +156,30 @@ export default class Observer {
150
156
  data = this.app.sanitizer.sanitize(id, data);
151
157
  this.app.send(new SetNodeData(id, data));
152
158
  }
153
- bindNode(node) {
159
+ bindNode(node, isRemove = false) {
154
160
  const [id, isNew] = this.app.nodes.registerNode(node);
155
161
  if (isNew) {
156
162
  this.recents.set(id, RecentsType.New);
157
163
  }
158
164
  else if (this.recents.get(id) !== RecentsType.New) { // can we do just `else` here?
159
- this.recents.set(id, RecentsType.Removed);
165
+ this.recents.set(id, isRemove ? RecentsType.RemovedChild : RecentsType.Removed);
160
166
  }
161
167
  }
162
- bindTree(node) {
168
+ bindTree(node, isRemove = false) {
163
169
  if (!isObservable(node)) {
164
170
  return;
165
171
  }
166
172
  this.bindNode(node);
167
173
  const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
168
- acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) !== undefined
174
+ acceptNode: (node) => isIgnored(node)
175
+ || (this.app.nodes.getID(node) !== undefined && !isRemove)
169
176
  ? NodeFilter.FILTER_REJECT
170
177
  : NodeFilter.FILTER_ACCEPT,
171
178
  },
172
179
  // @ts-ignore
173
180
  false);
174
181
  while (walker.nextNode()) {
175
- this.bindNode(walker.currentNode);
182
+ this.bindNode(walker.currentNode, isRemove);
176
183
  }
177
184
  }
178
185
  unbindNode(node) {
package/lib/index.js CHANGED
@@ -123,7 +123,7 @@ export default class API {
123
123
  // no-cors issue only with text/plain or not-set Content-Type
124
124
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
125
125
  req.send(JSON.stringify({
126
- trackerVersion: '3.5.16-beta.2',
126
+ trackerVersion: '3.5.16-beta.3',
127
127
  projectKey: options.projectKey,
128
128
  doNotTrack,
129
129
  // TODO: add precise reason (an exact API missing)
@@ -110,11 +110,11 @@ export default function (app, opts) {
110
110
  app.ticker.attach(() => {
111
111
  inputValues.forEach((value, id) => {
112
112
  const node = app.nodes.getNode(id);
113
- if (!isTextEditable(node)) {
113
+ if (node && !isTextEditable(node)) {
114
114
  inputValues.delete(id);
115
115
  return;
116
116
  }
117
- if (value !== node.value) {
117
+ if (node && value !== node.value) {
118
118
  inputValues.set(id, node.value);
119
119
  if (!registeredTargets.has(id)) {
120
120
  registeredTargets.add(id);
@@ -125,11 +125,11 @@ export default function (app, opts) {
125
125
  });
126
126
  checkableValues.forEach((checked, id) => {
127
127
  const node = app.nodes.getNode(id);
128
- if (!isCheckable(node)) {
128
+ if (node && !isCheckable(node)) {
129
129
  checkableValues.delete(id);
130
130
  return;
131
131
  }
132
- if (checked !== node.checked) {
132
+ if (node && checked !== node.checked) {
133
133
  checkableValues.set(id, node.checked);
134
134
  app.send(new SetInputChecked(id, node.checked));
135
135
  }
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.5.16-beta.2",
4
+ "version": "3.5.16-beta.3",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"