@openreplay/tracker 3.5.17-beta.0 → 3.6.1

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 (92) hide show
  1. package/.eslintignore +1 -1
  2. package/.prettierignore +1 -0
  3. package/LICENSE +1 -1
  4. package/cjs/app/guards.d.ts +2 -1
  5. package/cjs/app/guards.js +5 -1
  6. package/cjs/app/index.d.ts +17 -12
  7. package/cjs/app/index.js +63 -49
  8. package/cjs/app/messages.d.ts +52 -0
  9. package/cjs/app/messages.gen.d.ts +57 -0
  10. package/cjs/app/messages.gen.js +493 -0
  11. package/cjs/app/messages.js +234 -0
  12. package/cjs/app/nodes.d.ts +1 -1
  13. package/cjs/app/nodes.js +2 -0
  14. package/cjs/app/observer/iframe_observer.js +2 -2
  15. package/cjs/app/observer/observer.d.ts +1 -2
  16. package/cjs/app/observer/observer.js +40 -39
  17. package/cjs/app/observer/shadow_root_observer.js +2 -2
  18. package/cjs/app/observer/top_observer.d.ts +11 -0
  19. package/cjs/app/observer/top_observer.js +46 -12
  20. package/cjs/app/session.d.ts +19 -1
  21. package/cjs/app/session.js +61 -3
  22. package/cjs/common/{webworker.d.ts → interaction.d.ts} +3 -3
  23. package/cjs/common/{types.js → interaction.js} +0 -0
  24. package/cjs/common/messages.gen.d.ts +382 -0
  25. package/cjs/common/{webworker.js → messages.gen.js} +1 -0
  26. package/cjs/index.d.ts +3 -2
  27. package/cjs/index.js +20 -9
  28. package/cjs/modules/adoptedStyleSheets.d.ts +2 -0
  29. package/cjs/modules/adoptedStyleSheets.js +127 -0
  30. package/cjs/modules/connection.js +2 -2
  31. package/cjs/modules/console.js +6 -20
  32. package/cjs/modules/cssrules.js +16 -12
  33. package/cjs/modules/exception.d.ts +2 -2
  34. package/cjs/modules/exception.js +16 -12
  35. package/cjs/modules/img.js +31 -21
  36. package/cjs/modules/input.js +6 -6
  37. package/cjs/modules/mouse.js +42 -33
  38. package/cjs/modules/performance.js +2 -2
  39. package/cjs/modules/scroll.js +16 -7
  40. package/cjs/modules/timing.js +4 -4
  41. package/cjs/modules/viewport.js +4 -4
  42. package/lib/app/guards.d.ts +2 -1
  43. package/lib/app/guards.js +3 -0
  44. package/lib/app/index.d.ts +17 -12
  45. package/lib/app/index.js +64 -50
  46. package/lib/app/messages.d.ts +52 -0
  47. package/lib/app/messages.gen.d.ts +57 -0
  48. package/lib/app/messages.gen.js +434 -0
  49. package/lib/app/messages.js +181 -0
  50. package/lib/app/nodes.d.ts +1 -1
  51. package/lib/app/nodes.js +2 -0
  52. package/lib/app/observer/iframe_observer.js +1 -1
  53. package/lib/app/observer/observer.d.ts +1 -2
  54. package/lib/app/observer/observer.js +40 -39
  55. package/lib/app/observer/shadow_root_observer.js +1 -1
  56. package/lib/app/observer/top_observer.d.ts +11 -0
  57. package/lib/app/observer/top_observer.js +46 -12
  58. package/lib/app/session.d.ts +19 -1
  59. package/lib/app/session.js +61 -3
  60. package/lib/common/{webworker.d.ts → interaction.d.ts} +3 -3
  61. package/lib/common/{types.js → interaction.js} +0 -0
  62. package/lib/common/messages.gen.d.ts +382 -0
  63. package/lib/common/messages.gen.js +2 -0
  64. package/lib/common/tsconfig.tsbuildinfo +1 -1
  65. package/lib/index.d.ts +3 -2
  66. package/lib/index.js +20 -9
  67. package/lib/modules/adoptedStyleSheets.d.ts +2 -0
  68. package/lib/modules/adoptedStyleSheets.js +124 -0
  69. package/lib/modules/connection.js +2 -2
  70. package/lib/modules/console.js +6 -20
  71. package/lib/modules/cssrules.js +16 -12
  72. package/lib/modules/exception.d.ts +2 -2
  73. package/lib/modules/exception.js +16 -12
  74. package/lib/modules/img.js +31 -21
  75. package/lib/modules/input.js +6 -6
  76. package/lib/modules/mouse.js +43 -34
  77. package/lib/modules/performance.js +2 -2
  78. package/lib/modules/scroll.js +17 -8
  79. package/lib/modules/timing.js +4 -4
  80. package/lib/modules/viewport.js +4 -4
  81. package/package.json +1 -1
  82. package/cjs/common/messages.d.ts +0 -444
  83. package/cjs/common/messages.js +0 -794
  84. package/cjs/common/types.d.ts +0 -9
  85. package/cjs/modules/longtasks.d.ts +0 -2
  86. package/cjs/modules/longtasks.js +0 -34
  87. package/lib/common/messages.d.ts +0 -444
  88. package/lib/common/messages.js +0 -790
  89. package/lib/common/types.d.ts +0 -9
  90. package/lib/common/webworker.js +0 -1
  91. package/lib/modules/longtasks.d.ts +0 -2
  92. package/lib/modules/longtasks.js +0 -31
@@ -7,7 +7,7 @@ export default class Nodes {
7
7
  constructor(node_id: string);
8
8
  attachNodeCallback(nodeCallback: NodeCallback): void;
9
9
  attachElementListener(type: string, node: Element, elementListener: EventListener): void;
10
- registerNode(node: Node): [id: number, isNew: boolean];
10
+ registerNode(node: Node): [/*id:*/ number, /*isNew:*/ boolean];
11
11
  unregisterNode(node: Node): number | undefined;
12
12
  cleanTree(): void;
13
13
  callNodeCallbacks(node: Node, isStart: boolean): void;
package/cjs/app/nodes.js CHANGED
@@ -7,9 +7,11 @@ class Nodes {
7
7
  this.nodeCallbacks = [];
8
8
  this.elementListeners = new Map();
9
9
  }
10
+ // Attached once per Tracker instance
10
11
  attachNodeCallback(nodeCallback) {
11
12
  this.nodeCallbacks.push(nodeCallback);
12
13
  }
14
+ // TODO: what is the difference with app.attachEventListener. can we use only one of those?
13
15
  attachElementListener(type, node, elementListener) {
14
16
  const id = this.getID(node);
15
17
  if (id === undefined) {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const observer_js_1 = require("./observer.js");
4
- const messages_js_1 = require("../../common/messages.js");
4
+ const messages_gen_js_1 = require("../messages.gen.js");
5
5
  class IFrameObserver extends observer_js_1.default {
6
6
  observe(iframe) {
7
7
  const doc = iframe.contentDocument;
@@ -15,7 +15,7 @@ class IFrameObserver extends observer_js_1.default {
15
15
  console.log('OpenReplay: Iframe document not bound');
16
16
  return;
17
17
  }
18
- this.app.send((0, messages_js_1.CreateIFrameDocument)(hostID, docID));
18
+ this.app.send((0, messages_gen_js_1.CreateIFrameDocument)(hostID, docID));
19
19
  });
20
20
  }
21
21
  }
@@ -13,9 +13,8 @@ export default abstract class Observer {
13
13
  private sendNodeAttribute;
14
14
  private sendNodeData;
15
15
  private bindNode;
16
- private unbindChildNode;
17
16
  private bindTree;
18
- private unbindNode;
17
+ private unbindTree;
19
18
  private _commitNode;
20
19
  private commitNode;
21
20
  private commitNodes;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const messages_js_1 = require("../../common/messages.js");
3
+ const messages_gen_js_1 = require("../messages.gen.js");
4
4
  const guards_js_1 = require("../guards.js");
5
5
  function isIgnored(node) {
6
6
  if ((0, guards_js_1.isTextNode)(node)) {
@@ -28,17 +28,11 @@ function isObservable(node) {
28
28
  - fix unbinding logic + send all removals first (ensure sequence is correct)
29
29
  - use document as a 0-node in the upper context (should be updated in player at first)
30
30
  */
31
- /*
32
- Nikita:
33
- - rn we only send unbind event for parent (all child nodes will be cut in the live replay anyways)
34
- to prevent sending 1k+ unbinds for child nodes and making replay file bigger than it should be
35
- */
36
31
  var RecentsType;
37
32
  (function (RecentsType) {
38
33
  RecentsType[RecentsType["New"] = 0] = "New";
39
34
  RecentsType[RecentsType["Removed"] = 1] = "Removed";
40
35
  RecentsType[RecentsType["Changed"] = 2] = "Changed";
41
- RecentsType[RecentsType["RemovedChild"] = 3] = "RemovedChild";
42
36
  })(RecentsType || (RecentsType = {}));
43
37
  class Observer {
44
38
  constructor(app, isTopContext = false) {
@@ -59,7 +53,10 @@ class Observer {
59
53
  }
60
54
  if (type === 'childList') {
61
55
  for (let i = 0; i < mutation.removedNodes.length; i++) {
62
- this.bindTree(mutation.removedNodes[i], true);
56
+ // Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied
57
+ if (isObservable(mutation.removedNodes[i])) {
58
+ this.bindNode(mutation.removedNodes[i]);
59
+ }
63
60
  }
64
61
  for (let i = 0; i < mutation.addedNodes.length; i++) {
65
62
  this.bindTree(mutation.addedNodes[i]);
@@ -106,16 +103,16 @@ class Observer {
106
103
  name = name.substr(6);
107
104
  }
108
105
  if (value === null) {
109
- this.app.send(new messages_js_1.RemoveNodeAttribute(id, name));
106
+ this.app.send((0, messages_gen_js_1.RemoveNodeAttribute)(id, name));
110
107
  }
111
108
  else if (name === 'href') {
112
109
  if (value.length > 1e5) {
113
110
  value = '';
114
111
  }
115
- this.app.send(new messages_js_1.SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
112
+ this.app.send((0, messages_gen_js_1.SetNodeAttributeURLBased)(id, name, value, this.app.getBaseHref()));
116
113
  }
117
114
  else {
118
- this.app.send(new messages_js_1.SetNodeAttribute(id, name, value));
115
+ this.app.send((0, messages_gen_js_1.SetNodeAttribute)(id, name, value));
119
116
  }
120
117
  return;
121
118
  }
@@ -135,25 +132,25 @@ class Observer {
135
132
  return;
136
133
  }
137
134
  if (value === null) {
138
- this.app.send(new messages_js_1.RemoveNodeAttribute(id, name));
135
+ this.app.send((0, messages_gen_js_1.RemoveNodeAttribute)(id, name));
139
136
  return;
140
137
  }
141
138
  if (name === 'style' || (name === 'href' && (0, guards_js_1.hasTag)(node, 'LINK'))) {
142
- this.app.send(new messages_js_1.SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref()));
139
+ this.app.send((0, messages_gen_js_1.SetNodeAttributeURLBased)(id, name, value, this.app.getBaseHref()));
143
140
  return;
144
141
  }
145
142
  if (name === 'href' || value.length > 1e5) {
146
143
  value = '';
147
144
  }
148
- this.app.send(new messages_js_1.SetNodeAttribute(id, name, value));
145
+ this.app.send((0, messages_gen_js_1.SetNodeAttribute)(id, name, value));
149
146
  }
150
147
  sendNodeData(id, parentElement, data) {
151
148
  if ((0, guards_js_1.hasTag)(parentElement, 'STYLE') || (0, guards_js_1.hasTag)(parentElement, 'style')) {
152
- this.app.send(new messages_js_1.SetCSSDataURLBased(id, data, this.app.getBaseHref()));
149
+ this.app.send((0, messages_gen_js_1.SetCSSDataURLBased)(id, data, this.app.getBaseHref()));
153
150
  return;
154
151
  }
155
152
  data = this.app.sanitizer.sanitize(id, data);
156
- this.app.send(new messages_js_1.SetNodeData(id, data));
153
+ this.app.send((0, messages_gen_js_1.SetNodeData)(id, data));
157
154
  }
158
155
  bindNode(node) {
159
156
  const [id, isNew] = this.app.nodes.registerNode(node);
@@ -161,39 +158,42 @@ class Observer {
161
158
  this.recents.set(id, RecentsType.New);
162
159
  }
163
160
  else if (this.recents.get(id) !== RecentsType.New) {
164
- // can we do just `else` here?
165
161
  this.recents.set(id, RecentsType.Removed);
166
162
  }
167
163
  }
168
- unbindChildNode(node) {
169
- const [id] = this.app.nodes.registerNode(node);
170
- this.recents.set(id, RecentsType.RemovedChild);
171
- }
172
- bindTree(node, isChildUnbinding = false) {
164
+ bindTree(node) {
173
165
  if (!isObservable(node)) {
174
166
  return;
175
167
  }
176
168
  this.bindNode(node);
177
169
  const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
178
- acceptNode: (node) => isIgnored(node) || (this.app.nodes.getID(node) !== undefined && !isChildUnbinding)
170
+ acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) !== undefined
179
171
  ? NodeFilter.FILTER_REJECT
180
172
  : NodeFilter.FILTER_ACCEPT,
181
173
  },
182
174
  // @ts-ignore
183
175
  false);
184
176
  while (walker.nextNode()) {
185
- if (isChildUnbinding) {
186
- this.unbindChildNode(walker.currentNode);
187
- }
188
- else {
189
- this.bindNode(walker.currentNode);
190
- }
177
+ this.bindNode(walker.currentNode);
191
178
  }
192
179
  }
193
- unbindNode(node) {
180
+ unbindTree(node) {
194
181
  const id = this.app.nodes.unregisterNode(node);
195
182
  if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
196
- this.app.send(new messages_js_1.RemoveNode(id));
183
+ // Sending RemoveNode only for parent to maintain
184
+ this.app.send((0, messages_gen_js_1.RemoveNode)(id));
185
+ // Unregistering all the children in order to clear the memory
186
+ const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
187
+ acceptNode: (node) => isIgnored(node) || this.app.nodes.getID(node) === undefined
188
+ ? NodeFilter.FILTER_REJECT
189
+ : NodeFilter.FILTER_ACCEPT,
190
+ },
191
+ // @ts-ignore
192
+ false);
193
+ while (walker.nextNode()) {
194
+ this.app.nodes.unregisterNode(walker.currentNode);
195
+ }
196
+ // MBTODO: count and send RemovedNodesCount (for the page crash detection in heuristics)
197
197
  }
198
198
  }
199
199
  // A top-consumption function on the infinite lists test. (~1% of performance resources)
@@ -209,17 +209,17 @@ class Observer {
209
209
  if (!(0, guards_js_1.hasTag)(node, 'HTML') || !this.isTopContext) {
210
210
  if (parent === null) {
211
211
  // Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
212
- // That shouldn't affect the visual rendering ( should it? )
213
- this.unbindNode(node);
212
+ // That shouldn't affect the visual rendering ( should it? maybe when transition applied? )
213
+ this.unbindTree(node);
214
214
  return false;
215
215
  }
216
216
  parentID = this.app.nodes.getID(parent);
217
217
  if (parentID === undefined) {
218
- this.unbindNode(node);
218
+ this.unbindTree(node);
219
219
  return false;
220
220
  }
221
221
  if (!this.commitNode(parentID)) {
222
- this.unbindNode(node);
222
+ this.unbindTree(node);
223
223
  return false;
224
224
  }
225
225
  this.app.sanitizer.handleNode(id, parentID, node);
@@ -258,7 +258,7 @@ class Observer {
258
258
  el.style.width = width + 'px';
259
259
  el.style.height = height + 'px';
260
260
  }
261
- this.app.send(new messages_js_1.CreateElementNode(id, parentID, index, el.tagName, (0, guards_js_1.isSVGElement)(node)));
261
+ this.app.send((0, messages_gen_js_1.CreateElementNode)(id, parentID, index, el.tagName, (0, guards_js_1.isSVGElement)(node)));
262
262
  }
263
263
  for (let i = 0; i < el.attributes.length; i++) {
264
264
  const attr = el.attributes[i];
@@ -267,13 +267,13 @@ class Observer {
267
267
  }
268
268
  else if ((0, guards_js_1.isTextNode)(node)) {
269
269
  // for text node id != 0, hence parentID !== undefined and parent is Element
270
- this.app.send(new messages_js_1.CreateTextNode(id, parentID, index));
270
+ this.app.send((0, messages_gen_js_1.CreateTextNode)(id, parentID, index));
271
271
  this.sendNodeData(id, parent, node.data);
272
272
  }
273
273
  return true;
274
274
  }
275
275
  if (recentsType === RecentsType.Removed && parentID !== undefined) {
276
- this.app.send(new messages_js_1.MoveNode(id, parentID, index));
276
+ this.app.send((0, messages_gen_js_1.MoveNode)(id, parentID, index));
277
277
  }
278
278
  const attr = this.attributesMap.get(id);
279
279
  if (attr !== undefined) {
@@ -314,7 +314,8 @@ class Observer {
314
314
  });
315
315
  this.clear();
316
316
  }
317
- // ISSSUE
317
+ // ISSSUE (nodeToBinde should be the same as node. Look at the comment about 0-node at the beginning of the file.)
318
+ // TODO: use one observer instance for all iframes/shadowRoots (composition instiad of inheritance)
318
319
  observeRoot(node, beforeCommit, nodeToBind = node) {
319
320
  this.observer.observe(node, {
320
321
  childList: true,
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const observer_js_1 = require("./observer.js");
4
- const messages_js_1 = require("../../common/messages.js");
4
+ const messages_gen_js_1 = require("../messages.gen.js");
5
5
  class ShadowRootObserver extends observer_js_1.default {
6
6
  observe(el) {
7
7
  const shRoot = el.shadowRoot;
@@ -14,7 +14,7 @@ class ShadowRootObserver extends observer_js_1.default {
14
14
  console.log('OpenReplay: Shadow Root was not bound');
15
15
  return;
16
16
  }
17
- this.app.send((0, messages_js_1.CreateIFrameDocument)(hostID, rootID));
17
+ this.app.send((0, messages_gen_js_1.CreateIFrameDocument)(hostID, rootID));
18
18
  });
19
19
  }
20
20
  }
@@ -3,9 +3,19 @@ import App from '../index.js';
3
3
  export interface Options {
4
4
  captureIFrames: boolean;
5
5
  }
6
+ declare type Context = Window & typeof globalThis;
7
+ declare type ContextCallback = (context: Context) => void;
8
+ declare type Offset = {
9
+ top: number;
10
+ left: number;
11
+ };
6
12
  export default class TopObserver extends Observer {
7
13
  private readonly options;
8
14
  constructor(app: App, options: Partial<Options>);
15
+ private readonly contextCallbacks;
16
+ private readonly contextsSet;
17
+ attachContextCallback(cb: ContextCallback): void;
18
+ getDocumentOffset(doc: Document): Offset;
9
19
  private iframeObservers;
10
20
  private handleIframe;
11
21
  private shadowRootObservers;
@@ -13,3 +23,4 @@ export default class TopObserver extends Observer {
13
23
  observe(): void;
14
24
  disconnect(): void;
15
25
  }
26
+ export {};
@@ -4,12 +4,19 @@ const observer_js_1 = require("./observer.js");
4
4
  const guards_js_1 = require("../guards.js");
5
5
  const iframe_observer_js_1 = require("./iframe_observer.js");
6
6
  const shadow_root_observer_js_1 = require("./shadow_root_observer.js");
7
- const messages_js_1 = require("../../common/messages.js");
7
+ const messages_gen_js_1 = require("../messages.gen.js");
8
8
  const utils_js_1 = require("../../utils.js");
9
+ function isPatchedDocument(doc) {
10
+ // @ts-ignore
11
+ return typeof doc.__openreplay__getOffset === 'function';
12
+ }
9
13
  const attachShadowNativeFn = utils_js_1.IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot();
10
14
  class TopObserver extends observer_js_1.default {
11
15
  constructor(app, options) {
12
16
  super(app, true);
17
+ this.contextCallbacks = [];
18
+ // Attached once per Tracker instance
19
+ this.contextsSet = new Set();
13
20
  this.iframeObservers = [];
14
21
  this.shadowRootObservers = [];
15
22
  this.options = Object.assign({
@@ -30,23 +37,50 @@ class TopObserver extends observer_js_1.default {
30
37
  }
31
38
  });
32
39
  }
40
+ attachContextCallback(cb) {
41
+ this.contextCallbacks.push(cb);
42
+ }
43
+ // Le truc
44
+ getDocumentOffset(doc) {
45
+ if (isPatchedDocument(doc)) {
46
+ return doc.__openreplay__getOffset();
47
+ }
48
+ return { top: 0, left: 0 };
49
+ }
33
50
  handleIframe(iframe) {
34
51
  let doc = null;
52
+ let win = null;
35
53
  const handle = this.app.safe(() => {
36
54
  const id = this.app.nodes.getID(iframe);
37
55
  if (id === undefined) {
56
+ //log
38
57
  return;
39
- } //log
40
- if (iframe.contentDocument === doc) {
41
- return;
42
- } // How frequently can it happen?
43
- doc = iframe.contentDocument;
44
- if (!doc || !iframe.contentWindow) {
45
- return;
46
58
  }
47
- const observer = new iframe_observer_js_1.default(this.app);
48
- this.iframeObservers.push(observer);
49
- observer.observe(iframe);
59
+ const currentWin = iframe.contentWindow;
60
+ const currentDoc = iframe.contentDocument;
61
+ if (currentDoc && currentDoc !== doc) {
62
+ const observer = new iframe_observer_js_1.default(this.app);
63
+ this.iframeObservers.push(observer);
64
+ observer.observe(iframe);
65
+ doc = currentDoc;
66
+ doc.__openreplay__getOffset = () => {
67
+ const { top, left } = this.getDocumentOffset(iframe.ownerDocument);
68
+ return {
69
+ top: iframe.offsetTop + top,
70
+ left: iframe.offsetLeft + left,
71
+ };
72
+ };
73
+ }
74
+ if (currentWin &&
75
+ // Sometimes currentWin.window is null (not in specification). Such window object is not functional
76
+ currentWin === currentWin.window &&
77
+ !this.contextsSet.has(currentWin) // for each context callbacks called once per Tracker (TopObserver) instance
78
+ ) {
79
+ this.contextsSet.add(currentWin);
80
+ //@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
81
+ this.contextCallbacks.forEach((cb) => cb(currentWin));
82
+ win = currentWin;
83
+ }
50
84
  });
51
85
  iframe.addEventListener('load', handle); // why app.attachEventListener not working?
52
86
  handle();
@@ -72,7 +106,7 @@ class TopObserver extends observer_js_1.default {
72
106
  // the 0-node ("fRoot") will become #document rather than documentElement as it is now.
73
107
  // Alternatively - observe(#document) then bindNode(documentElement)
74
108
  this.observeRoot(window.document, () => {
75
- this.app.send(new messages_js_1.CreateDocument());
109
+ this.app.send((0, messages_gen_js_1.CreateDocument)());
76
110
  }, window.document.documentElement);
77
111
  }
78
112
  disconnect() {
@@ -1,19 +1,37 @@
1
+ import type App from './index.js';
1
2
  interface SessionInfo {
2
- sessionID: string | null;
3
+ sessionID: string | undefined;
3
4
  metadata: Record<string, string>;
4
5
  userID: string | null;
6
+ timestamp: number;
7
+ projectID?: string;
5
8
  }
6
9
  declare type OnUpdateCallback = (i: Partial<SessionInfo>) => void;
10
+ export declare type Options = {
11
+ session_token_key: string;
12
+ session_pageno_key: string;
13
+ };
7
14
  export default class Session {
15
+ private readonly app;
16
+ private readonly options;
8
17
  private metadata;
9
18
  private userID;
10
19
  private sessionID;
11
20
  private readonly callbacks;
21
+ private timestamp;
22
+ private projectID;
23
+ constructor(app: App, options: Options);
12
24
  attachUpdateCallback(cb: OnUpdateCallback): void;
13
25
  private handleUpdate;
14
26
  update(newInfo: Partial<SessionInfo>): void;
15
27
  setMetadata(key: string, value: string): void;
16
28
  setUserID(userID: string): void;
29
+ private getPageNumber;
30
+ incPageNo(): number;
31
+ getSessionToken(): string | undefined;
32
+ setSessionToken(token: string): void;
33
+ applySessionHash(hash: string): void;
34
+ getSessionHash(): string | undefined;
17
35
  getInfo(): SessionInfo;
18
36
  reset(): void;
19
37
  }
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class Session {
4
- constructor() {
4
+ constructor(app, options) {
5
+ this.app = app;
6
+ this.options = options;
5
7
  this.metadata = {};
6
8
  this.userID = null;
7
- this.sessionID = null;
8
9
  this.callbacks = [];
10
+ this.timestamp = 0;
9
11
  }
10
12
  attachUpdateCallback(cb) {
11
13
  this.callbacks.push(cb);
@@ -30,6 +32,12 @@ class Session {
30
32
  if (newInfo.sessionID !== undefined) {
31
33
  this.sessionID = newInfo.sessionID;
32
34
  }
35
+ if (newInfo.timestamp !== undefined) {
36
+ this.timestamp = newInfo.timestamp;
37
+ }
38
+ if (newInfo.projectID !== undefined) {
39
+ this.projectID = newInfo.projectID;
40
+ }
33
41
  this.handleUpdate(newInfo);
34
42
  }
35
43
  setMetadata(key, value) {
@@ -40,17 +48,67 @@ class Session {
40
48
  this.userID = userID;
41
49
  this.handleUpdate({ userID });
42
50
  }
51
+ getPageNumber() {
52
+ const pageNoStr = this.app.sessionStorage.getItem(this.options.session_pageno_key);
53
+ if (pageNoStr == null) {
54
+ return undefined;
55
+ }
56
+ return parseInt(pageNoStr);
57
+ }
58
+ incPageNo() {
59
+ let pageNo = this.getPageNumber();
60
+ if (pageNo === undefined) {
61
+ pageNo = 0;
62
+ }
63
+ else {
64
+ pageNo++;
65
+ }
66
+ this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
67
+ return pageNo;
68
+ }
69
+ getSessionToken() {
70
+ return this.app.sessionStorage.getItem(this.options.session_token_key) || undefined;
71
+ }
72
+ setSessionToken(token) {
73
+ this.app.sessionStorage.setItem(this.options.session_token_key, token);
74
+ }
75
+ applySessionHash(hash) {
76
+ const hashParts = decodeURI(hash).split('&');
77
+ let token = hash;
78
+ let pageNoStr = '100500'; // back-compat for sessionToken
79
+ if (hashParts.length == 2) {
80
+ ;
81
+ [token, pageNoStr] = hashParts;
82
+ }
83
+ if (!pageNoStr || !token) {
84
+ return;
85
+ }
86
+ this.app.sessionStorage.setItem(this.options.session_token_key, token);
87
+ this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNoStr);
88
+ }
89
+ getSessionHash() {
90
+ const pageNo = this.getPageNumber();
91
+ const token = this.getSessionToken();
92
+ if (pageNo === undefined || token === undefined) {
93
+ return;
94
+ }
95
+ return encodeURI(String(pageNo) + '&' + token);
96
+ }
43
97
  getInfo() {
44
98
  return {
45
99
  sessionID: this.sessionID,
46
100
  metadata: this.metadata,
47
101
  userID: this.userID,
102
+ timestamp: this.timestamp,
103
+ projectID: this.projectID,
48
104
  };
49
105
  }
50
106
  reset() {
107
+ this.app.sessionStorage.removeItem(this.options.session_token_key);
51
108
  this.metadata = {};
52
109
  this.userID = null;
53
- this.sessionID = null;
110
+ this.sessionID = undefined;
111
+ this.timestamp = 0;
54
112
  }
55
113
  }
56
114
  exports.default = Session;
@@ -1,3 +1,4 @@
1
+ import Message from './messages.gen.js';
1
2
  export interface Options {
2
3
  connAttemptCount?: number;
3
4
  connAttemptGap?: number;
@@ -7,13 +8,12 @@ declare type Start = {
7
8
  ingestPoint: string;
8
9
  pageNo: number;
9
10
  timestamp: number;
11
+ url: string;
10
12
  } & Options;
11
13
  declare type Auth = {
12
14
  type: 'auth';
13
15
  token: string;
14
16
  beaconSizeLimit?: number;
15
17
  };
16
- export declare type WorkerMessageData = null | 'stop' | Start | Auth | Array<{
17
- _id: number;
18
- }>;
18
+ export declare type WorkerMessageData = null | 'stop' | Start | Auth | Array<Message>;
19
19
  export {};
File without changes