@openreplay/tracker 3.5.16-beta.1 → 3.5.16-beta.2

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 from "./nodes.js";
2
+ import Nodes, { CheckOptions } from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
@@ -49,7 +49,7 @@ declare type AppOptions = {
49
49
  maxMemorySize: number;
50
50
  memoryCheckInterval: number;
51
51
  onStart?: StartCallback;
52
- } & WebworkerOptions;
52
+ } & WebworkerOptions & CheckOptions;
53
53
  export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
54
54
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
55
55
  export default class App {
@@ -62,7 +62,6 @@ export default class App {
62
62
  readonly session: Session;
63
63
  readonly localStorage: Storage;
64
64
  readonly sessionStorage: Storage;
65
- private gc;
66
65
  private readonly messages;
67
66
  private readonly observer;
68
67
  private readonly startCallbacks;
package/cjs/app/index.js CHANGED
@@ -27,12 +27,13 @@ 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;
30
31
  this.messages = [];
31
32
  this.startCallbacks = [];
32
33
  this.stopCallbacks = [];
33
34
  this.commitCallbacks = [];
34
35
  this.activityState = ActivityState.NotActive;
35
- this.version = '3.5.16-beta.1'; // TODO: version compatability check inside each plugin.
36
+ this.version = '3.5.16-beta.2'; // TODO: version compatability check inside each plugin.
36
37
  this.projectKey = projectKey;
37
38
  this.options = Object.assign({
38
39
  revID: '',
@@ -48,12 +49,12 @@ class App {
48
49
  __debug_report_edp: null,
49
50
  localStorage: window.localStorage,
50
51
  sessionStorage: window.sessionStorage,
51
- maxMemorySize: 650 * 1e6,
52
+ maxMemorySize: 550 * 1e6,
52
53
  memoryCheckInterval: 2 * 60 * 1000,
53
54
  }, options);
54
55
  this.revID = this.options.revID;
55
56
  this.sanitizer = new sanitizer_js_1.default(this, options);
56
- this.nodes = new nodes_js_1.default(this.options.node_id);
57
+ this.nodes = new nodes_js_1.default(this.options.node_id, this.options.maxMemorySize, this.options.memoryCheckInterval);
57
58
  this.observer = new top_observer_js_1.default(this, options);
58
59
  this.ticker = new ticker_js_1.default(this);
59
60
  this.ticker.attach(() => this.commit());
@@ -145,6 +146,7 @@ class App {
145
146
  fn.apply(this, args);
146
147
  }
147
148
  catch (e) {
149
+ console.error(e);
148
150
  app._debug("safe_fn_call", e);
149
151
  // time: timestamp(),
150
152
  // name: e.name,
@@ -318,16 +320,16 @@ class App {
318
320
  this.observer.observe();
319
321
  this.ticker.start();
320
322
  this.notify.log("OpenReplay tracking started.");
321
- // GC
322
- if (this.gc) {
323
- clearInterval(this.gc);
324
- }
325
- this.gc = setInterval(() => {
326
- // @ts-ignore
327
- if (window.performance.memory.usedJSHeapSize > this.options.maxMemorySize) {
328
- this.restart();
329
- }
330
- }, this.options.memoryCheckInterval);
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
+ // }
331
333
  // get rid of onStart ?
332
334
  if (typeof this.options.onStart === 'function') {
333
335
  this.options.onStart(onStartInfo);
@@ -364,6 +366,7 @@ class App {
364
366
  stop(calledFromAPI = false, restarting = false) {
365
367
  if (this.activityState !== ActivityState.NotActive) {
366
368
  try {
369
+ // this.gc && clearInterval(this.gc)
367
370
  this.sanitizer.clear();
368
371
  this.observer.disconnect();
369
372
  this.nodes.clear();
@@ -1,14 +1,21 @@
1
+ /// <reference types="node" resolution-mode="require"/>
1
2
  declare type NodeCallback = (node: Node, isStart: boolean) => void;
3
+ export interface CheckOptions {
4
+ maxMemorySize: number;
5
+ memoryCheckInterval: number;
6
+ }
2
7
  export default class Nodes {
3
8
  private readonly node_id;
4
9
  private nodes;
5
10
  private readonly nodeCallbacks;
6
11
  private readonly elementListeners;
7
- constructor(node_id: string);
12
+ gc: NodeJS.Timer | undefined;
13
+ constructor(node_id: string, maxMemorySize: CheckOptions["maxMemorySize"], memoryCheckInterval: CheckOptions["memoryCheckInterval"]);
8
14
  attachNodeCallback(nodeCallback: NodeCallback): void;
9
15
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
10
16
  registerNode(node: Node): [id: number, isNew: boolean];
11
17
  unregisterNode(node: Node): number | undefined;
18
+ cleanTree(): void;
12
19
  callNodeCallbacks(node: Node, isStart: boolean): void;
13
20
  getID(node: Node): number | undefined;
14
21
  getNode(id: number): Node | undefined;
package/cjs/app/nodes.js CHANGED
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class Nodes {
4
- constructor(node_id) {
4
+ constructor(node_id, maxMemorySize, memoryCheckInterval) {
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);
9
15
  }
10
16
  attachNodeCallback(nodeCallback) {
11
17
  this.nodeCallbacks.push(nodeCallback);
@@ -47,12 +53,23 @@ class Nodes {
47
53
  }
48
54
  return id;
49
55
  }
50
- // cleanTree() {
51
- // if (this.nodes.length > 10000) {
52
- // this.nodes = this.nodes.filter((n) => n !== undefined && document.contains(n))
53
- // }
54
- // return;
55
- // }
56
+ cleanTree() {
57
+ // sadly we keep empty items in array here resulting in some memory still being used
58
+ // but its still better than keeping dead nodes or undef elements
59
+ // plus we keep our index positions for new/alive nodes
60
+ // performance test: 3ms for 30k nodes with 17k dead ones
61
+ for (let i = 0; i < this.nodes.length; i++) {
62
+ const node = this.nodes[i];
63
+ if (node === undefined) {
64
+ delete this.nodes[i];
65
+ continue;
66
+ }
67
+ if (!document.contains(node)) {
68
+ this.unregisterNode(node);
69
+ delete this.nodes[i];
70
+ }
71
+ }
72
+ }
56
73
  callNodeCallbacks(node, isStart) {
57
74
  this.nodeCallbacks.forEach((cb) => cb(node, isStart));
58
75
  }
@@ -71,6 +88,10 @@ class Nodes {
71
88
  this.unregisterNode(node);
72
89
  }
73
90
  this.nodes.length = 0;
91
+ if (this.gc) {
92
+ clearInterval(this.gc);
93
+ this.gc = undefined;
94
+ }
74
95
  }
75
96
  }
76
97
  exports.default = Nodes;
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.1',
130
+ trackerVersion: '3.5.16-beta.2',
131
131
  projectKey: options.projectKey,
132
132
  doNotTrack,
133
133
  // TODO: add precise reason (an exact API missing)
@@ -1,5 +1,5 @@
1
1
  import type Message from "../common/messages.js";
2
- import Nodes from "./nodes.js";
2
+ import Nodes, { CheckOptions } from "./nodes.js";
3
3
  import Sanitizer from "./sanitizer.js";
4
4
  import Ticker from "./ticker.js";
5
5
  import Logger from "./logger.js";
@@ -49,7 +49,7 @@ declare type AppOptions = {
49
49
  maxMemorySize: number;
50
50
  memoryCheckInterval: number;
51
51
  onStart?: StartCallback;
52
- } & WebworkerOptions;
52
+ } & WebworkerOptions & CheckOptions;
53
53
  export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
54
54
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
55
55
  export default class App {
@@ -62,7 +62,6 @@ export default class App {
62
62
  readonly session: Session;
63
63
  readonly localStorage: Storage;
64
64
  readonly sessionStorage: Storage;
65
- private gc;
66
65
  private readonly messages;
67
66
  private readonly observer;
68
67
  private readonly startCallbacks;
package/lib/app/index.js CHANGED
@@ -24,12 +24,13 @@ 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;
27
28
  this.messages = [];
28
29
  this.startCallbacks = [];
29
30
  this.stopCallbacks = [];
30
31
  this.commitCallbacks = [];
31
32
  this.activityState = ActivityState.NotActive;
32
- this.version = '3.5.16-beta.1'; // TODO: version compatability check inside each plugin.
33
+ this.version = '3.5.16-beta.2'; // TODO: version compatability check inside each plugin.
33
34
  this.projectKey = projectKey;
34
35
  this.options = Object.assign({
35
36
  revID: '',
@@ -45,12 +46,12 @@ export default class App {
45
46
  __debug_report_edp: null,
46
47
  localStorage: window.localStorage,
47
48
  sessionStorage: window.sessionStorage,
48
- maxMemorySize: 650 * 1e6,
49
+ maxMemorySize: 550 * 1e6,
49
50
  memoryCheckInterval: 2 * 60 * 1000,
50
51
  }, options);
51
52
  this.revID = this.options.revID;
52
53
  this.sanitizer = new Sanitizer(this, options);
53
- this.nodes = new Nodes(this.options.node_id);
54
+ this.nodes = new Nodes(this.options.node_id, this.options.maxMemorySize, this.options.memoryCheckInterval);
54
55
  this.observer = new Observer(this, options);
55
56
  this.ticker = new Ticker(this);
56
57
  this.ticker.attach(() => this.commit());
@@ -142,6 +143,7 @@ export default class App {
142
143
  fn.apply(this, args);
143
144
  }
144
145
  catch (e) {
146
+ console.error(e);
145
147
  app._debug("safe_fn_call", e);
146
148
  // time: timestamp(),
147
149
  // name: e.name,
@@ -315,16 +317,16 @@ export default class App {
315
317
  this.observer.observe();
316
318
  this.ticker.start();
317
319
  this.notify.log("OpenReplay tracking started.");
318
- // GC
319
- if (this.gc) {
320
- clearInterval(this.gc);
321
- }
322
- this.gc = setInterval(() => {
323
- // @ts-ignore
324
- if (window.performance.memory.usedJSHeapSize > this.options.maxMemorySize) {
325
- this.restart();
326
- }
327
- }, this.options.memoryCheckInterval);
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
+ // }
328
330
  // get rid of onStart ?
329
331
  if (typeof this.options.onStart === 'function') {
330
332
  this.options.onStart(onStartInfo);
@@ -361,6 +363,7 @@ export default class App {
361
363
  stop(calledFromAPI = false, restarting = false) {
362
364
  if (this.activityState !== ActivityState.NotActive) {
363
365
  try {
366
+ // this.gc && clearInterval(this.gc)
364
367
  this.sanitizer.clear();
365
368
  this.observer.disconnect();
366
369
  this.nodes.clear();
@@ -1,14 +1,21 @@
1
+ /// <reference types="node" resolution-mode="require"/>
1
2
  declare type NodeCallback = (node: Node, isStart: boolean) => void;
3
+ export interface CheckOptions {
4
+ maxMemorySize: number;
5
+ memoryCheckInterval: number;
6
+ }
2
7
  export default class Nodes {
3
8
  private readonly node_id;
4
9
  private nodes;
5
10
  private readonly nodeCallbacks;
6
11
  private readonly elementListeners;
7
- constructor(node_id: string);
12
+ gc: NodeJS.Timer | undefined;
13
+ constructor(node_id: string, maxMemorySize: CheckOptions["maxMemorySize"], memoryCheckInterval: CheckOptions["memoryCheckInterval"]);
8
14
  attachNodeCallback(nodeCallback: NodeCallback): void;
9
15
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
10
16
  registerNode(node: Node): [id: number, isNew: boolean];
11
17
  unregisterNode(node: Node): number | undefined;
18
+ cleanTree(): void;
12
19
  callNodeCallbacks(node: Node, isStart: boolean): void;
13
20
  getID(node: Node): number | undefined;
14
21
  getNode(id: number): Node | undefined;
package/lib/app/nodes.js CHANGED
@@ -1,9 +1,15 @@
1
1
  export default class Nodes {
2
- constructor(node_id) {
2
+ constructor(node_id, maxMemorySize, memoryCheckInterval) {
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);
7
13
  }
8
14
  attachNodeCallback(nodeCallback) {
9
15
  this.nodeCallbacks.push(nodeCallback);
@@ -45,12 +51,23 @@ export default class Nodes {
45
51
  }
46
52
  return id;
47
53
  }
48
- // cleanTree() {
49
- // if (this.nodes.length > 10000) {
50
- // this.nodes = this.nodes.filter((n) => n !== undefined && document.contains(n))
51
- // }
52
- // return;
53
- // }
54
+ cleanTree() {
55
+ // sadly we keep empty items in array here resulting in some memory still being used
56
+ // but its still better than keeping dead nodes or undef elements
57
+ // plus we keep our index positions for new/alive nodes
58
+ // performance test: 3ms for 30k nodes with 17k dead ones
59
+ for (let i = 0; i < this.nodes.length; i++) {
60
+ const node = this.nodes[i];
61
+ if (node === undefined) {
62
+ delete this.nodes[i];
63
+ continue;
64
+ }
65
+ if (!document.contains(node)) {
66
+ this.unregisterNode(node);
67
+ delete this.nodes[i];
68
+ }
69
+ }
70
+ }
54
71
  callNodeCallbacks(node, isStart) {
55
72
  this.nodeCallbacks.forEach((cb) => cb(node, isStart));
56
73
  }
@@ -69,5 +86,9 @@ export default class Nodes {
69
86
  this.unregisterNode(node);
70
87
  }
71
88
  this.nodes.length = 0;
89
+ if (this.gc) {
90
+ clearInterval(this.gc);
91
+ this.gc = undefined;
92
+ }
72
93
  }
73
94
  }
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.1',
126
+ trackerVersion: '3.5.16-beta.2',
127
127
  projectKey: options.projectKey,
128
128
  doNotTrack,
129
129
  // TODO: add precise reason (an exact API missing)
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.1",
4
+ "version": "3.5.16-beta.2",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"