@openreplay/tracker 13.0.1 → 14.0.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 (50) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bun.lockb +0 -0
  3. package/cjs/app/canvas.d.ts +2 -0
  4. package/cjs/app/canvas.js +6 -5
  5. package/cjs/app/index.d.ts +55 -16
  6. package/cjs/app/index.js +417 -236
  7. package/cjs/app/messages.gen.d.ts +6 -3
  8. package/cjs/app/messages.gen.js +44 -10
  9. package/cjs/app/nodes.d.ts +2 -0
  10. package/cjs/app/nodes.js +15 -1
  11. package/cjs/app/observer/iframe_observer.d.ts +1 -0
  12. package/cjs/app/observer/iframe_observer.js +9 -0
  13. package/cjs/app/observer/iframe_offsets.js +0 -1
  14. package/cjs/app/observer/top_observer.d.ts +1 -0
  15. package/cjs/app/observer/top_observer.js +14 -0
  16. package/cjs/common/messages.gen.d.ts +38 -10
  17. package/cjs/index.d.ts +1 -0
  18. package/cjs/index.js +17 -8
  19. package/cjs/modules/conditionsManager.js +2 -2
  20. package/cjs/modules/mouse.js +14 -1
  21. package/cjs/modules/scroll.d.ts +1 -1
  22. package/cjs/modules/scroll.js +9 -4
  23. package/cjs/modules/viewport.js +2 -2
  24. package/cjs/utils.d.ts +2 -1
  25. package/cjs/utils.js +33 -6
  26. package/lib/app/canvas.d.ts +2 -0
  27. package/lib/app/canvas.js +6 -5
  28. package/lib/app/index.d.ts +55 -16
  29. package/lib/app/index.js +399 -218
  30. package/lib/app/messages.gen.d.ts +6 -3
  31. package/lib/app/messages.gen.js +37 -6
  32. package/lib/app/nodes.d.ts +2 -0
  33. package/lib/app/nodes.js +15 -1
  34. package/lib/app/observer/iframe_observer.d.ts +1 -0
  35. package/lib/app/observer/iframe_observer.js +9 -0
  36. package/lib/app/observer/iframe_offsets.js +0 -1
  37. package/lib/app/observer/top_observer.d.ts +1 -0
  38. package/lib/app/observer/top_observer.js +14 -0
  39. package/lib/common/messages.gen.d.ts +38 -10
  40. package/lib/common/tsconfig.tsbuildinfo +1 -1
  41. package/lib/index.d.ts +1 -0
  42. package/lib/index.js +18 -9
  43. package/lib/modules/conditionsManager.js +2 -2
  44. package/lib/modules/mouse.js +14 -1
  45. package/lib/modules/scroll.d.ts +1 -1
  46. package/lib/modules/scroll.js +9 -4
  47. package/lib/modules/viewport.js +2 -2
  48. package/lib/utils.d.ts +2 -1
  49. package/lib/utils.js +31 -5
  50. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -21,7 +21,7 @@ import Network from './modules/network.js';
21
21
  import ConstructedStyleSheets from './modules/constructedStyleSheets.js';
22
22
  import Selection from './modules/selection.js';
23
23
  import Tabs from './modules/tabs.js';
24
- import { IN_BROWSER, deprecationWarn, DOCS_HOST } from './utils.js';
24
+ import { IN_BROWSER, deprecationWarn, DOCS_HOST, inIframe } from './utils.js';
25
25
  const DOCS_SETUP = '/installation/javascript-sdk';
26
26
  function processOptions(obj) {
27
27
  if (obj == null) {
@@ -54,6 +54,7 @@ export default class API {
54
54
  constructor(options) {
55
55
  this.options = options;
56
56
  this.app = null;
57
+ this.crossdomainMode = false;
57
58
  this.checkDoNotTrack = () => {
58
59
  return (this.options.respectDoNotTrack &&
59
60
  (navigator.doNotTrack == '1' ||
@@ -66,7 +67,7 @@ export default class API {
66
67
  const orig = this.options.ingestPoint || DEFAULT_INGEST_POINT;
67
68
  req.open('POST', orig + '/v1/web/not-started');
68
69
  req.send(JSON.stringify({
69
- trackerVersion: '13.0.1',
70
+ trackerVersion: '14.0.0',
70
71
  projectKey: this.options.projectKey,
71
72
  doNotTrack,
72
73
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -94,6 +95,7 @@ export default class API {
94
95
  }
95
96
  }
96
97
  };
98
+ this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
97
99
  if (!IN_BROWSER || !processOptions(options)) {
98
100
  return;
99
101
  }
@@ -148,25 +150,32 @@ export default class API {
148
150
  this.signalStartIssue('missing_api', failReason);
149
151
  return;
150
152
  }
151
- const app = new App(options.projectKey, options.sessionToken, options, this.signalStartIssue);
153
+ const app = new App(options.projectKey, options.sessionToken, options, this.signalStartIssue, this.crossdomainMode);
152
154
  this.app = app;
153
- Viewport(app);
155
+ if (!this.crossdomainMode) {
156
+ // no need to send iframe viewport data since its a node for us
157
+ Viewport(app);
158
+ // calculated in main window
159
+ Connection(app);
160
+ // while we can calculate it here, trying to compute it for all parts is hard
161
+ Performance(app, options);
162
+ // no tabs in iframes yet
163
+ Tabs(app);
164
+ }
165
+ Mouse(app, options.mouse);
166
+ // inside iframe, we ignore viewport scroll
167
+ Scroll(app, this.crossdomainMode);
154
168
  CSSRules(app);
155
169
  ConstructedStyleSheets(app);
156
- Connection(app);
157
170
  Console(app, options);
158
171
  Exception(app, options);
159
172
  Img(app);
160
173
  Input(app, options);
161
- Mouse(app, options.mouse);
162
174
  Timing(app, options);
163
- Performance(app, options);
164
- Scroll(app);
165
175
  Focus(app);
166
176
  Fonts(app);
167
177
  Network(app, options.network);
168
178
  Selection(app);
169
- Tabs(app);
170
179
  window.__OPENREPLAY__ = this;
171
180
  if (options.flags && options.flags.onFlagsLoad) {
172
181
  this.onFlagsLoad(options.flags.onFlagsLoad);
@@ -84,10 +84,10 @@ export default class ConditionsManager {
84
84
  case 27 /* Type.CustomEvent */:
85
85
  this.customEvent(message);
86
86
  break;
87
- case 69 /* Type.MouseClick */:
87
+ case 68 /* Type.MouseClick */:
88
88
  this.clickEvent(message);
89
89
  break;
90
- case 4 /* Type.SetPageLocation */:
90
+ case 122 /* Type.SetPageLocation */:
91
91
  this.pageLocationEvent(message);
92
92
  break;
93
93
  case 83 /* Type.NetworkRequest */:
@@ -163,8 +163,14 @@ export default function (app, options) {
163
163
  }
164
164
  const id = app.nodes.getID(target);
165
165
  if (id !== undefined) {
166
+ const clickX = e.pageX;
167
+ const clickY = e.pageY;
168
+ const contentWidth = document.documentElement.scrollWidth;
169
+ const contentHeight = document.documentElement.scrollHeight;
170
+ const normalizedX = roundNumber(clickX / contentWidth);
171
+ const normalizedY = roundNumber(clickY / contentHeight);
166
172
  sendMouseMove();
167
- app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), isClickable(target) && !disableClickmaps ? getSelector(id, target, options) : ''), true);
173
+ app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), isClickable(target) && !disableClickmaps ? getSelector(id, target, options) : '', normalizedX, normalizedY), true);
168
174
  }
169
175
  mouseTarget = null;
170
176
  });
@@ -177,3 +183,10 @@ export default function (app, options) {
177
183
  patchDocument(document, true);
178
184
  app.ticker.attach(sendMouseMove, options?.trackingOffset || 7);
179
185
  }
186
+ /**
187
+ * we get 0 to 1 decimal number, convert and round it, then turn to %
188
+ * 0.39643 => 396.43 => 396 => 39.6%
189
+ * */
190
+ function roundNumber(num) {
191
+ return Math.round(num * 1e3) / 1e1;
192
+ }
@@ -1,2 +1,2 @@
1
1
  import type App from '../app/index.js';
2
- export default function (app: App): void;
2
+ export default function (app: App, insideIframe: boolean | null): void;
@@ -3,17 +3,17 @@ import { isNode, isElementNode, isRootNode, isDocument } from '../app/guards.js'
3
3
  function getDocumentScroll(doc) {
4
4
  const win = doc.defaultView;
5
5
  return [
6
- (win && win.pageXOffset) ||
6
+ (win && win.scrollX) ||
7
7
  (doc.documentElement && doc.documentElement.scrollLeft) ||
8
8
  (doc.body && doc.body.scrollLeft) ||
9
9
  0,
10
- (win && win.pageYOffset) ||
10
+ (win && win.scrollY) ||
11
11
  (doc.documentElement && doc.documentElement.scrollTop) ||
12
12
  (doc.body && doc.body.scrollTop) ||
13
13
  0,
14
14
  ];
15
15
  }
16
- export default function (app) {
16
+ export default function (app, insideIframe) {
17
17
  let documentScroll = false;
18
18
  const nodeScroll = new Map();
19
19
  function setNodeScroll(target) {
@@ -27,7 +27,12 @@ export default function (app) {
27
27
  nodeScroll.set(target, getDocumentScroll(target));
28
28
  }
29
29
  }
30
- const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(...getDocumentScroll(document))));
30
+ const sendSetViewportScroll = app.safe(() => {
31
+ if (insideIframe) {
32
+ return;
33
+ }
34
+ app.send(SetViewportScroll(...getDocumentScroll(document)));
35
+ });
31
36
  const sendSetNodeScroll = app.safe((s, node) => {
32
37
  const id = app.nodes.getID(node);
33
38
  if (id !== undefined) {
@@ -8,7 +8,7 @@ export default function (app) {
8
8
  const { URL } = document;
9
9
  if (URL !== url) {
10
10
  url = URL;
11
- app.send(SetPageLocation(url, referrer, navigationStart));
11
+ app.send(SetPageLocation(url, referrer, navigationStart, document.title));
12
12
  navigationStart = 0;
13
13
  referrer = url;
14
14
  }
@@ -25,7 +25,7 @@ export default function (app) {
25
25
  ? Function.prototype
26
26
  : app.safe(() => app.send(SetPageVisibility(document.hidden)));
27
27
  app.attachStartCallback(() => {
28
- url = '';
28
+ url = null;
29
29
  navigationStart = getTimeOrigin();
30
30
  width = height = -1;
31
31
  sendSetPageLocation();
package/lib/utils.d.ts CHANGED
@@ -16,7 +16,7 @@ export declare function hasOpenreplayAttribute(e: Element, attr: string): boolea
16
16
  **/
17
17
  export declare function canAccessIframe(iframe: HTMLIFrameElement): boolean;
18
18
  export declare function generateRandomId(len?: number): string;
19
- export declare function inIframe(): boolean;
19
+ export declare function inIframe(): boolean | null;
20
20
  /**
21
21
  * Because angular devs decided that its a good idea to override a browser apis
22
22
  * we need to use this to achieve safe behavior
@@ -26,3 +26,4 @@ export declare function createMutationObserver(cb: MutationCallback): MutationOb
26
26
  export declare function createEventListener(target: EventTarget, event: string, cb: EventListenerOrEventListenerObject, capture?: boolean): void;
27
27
  export declare function deleteEventListener(target: EventTarget, event: string, cb: EventListenerOrEventListenerObject, capture?: boolean): void;
28
28
  export declare function requestIdleCb(callback: () => void): void;
29
+ export declare function simpleMerge<T>(defaultObj: T, givenObj: Partial<T>): T;
package/lib/utils.js CHANGED
@@ -78,12 +78,17 @@ export function generateRandomId(len) {
78
78
  // msCrypto = IE11
79
79
  // @ts-ignore
80
80
  const safeCrypto = window.crypto || window.msCrypto;
81
- safeCrypto.getRandomValues(arr);
82
- return Array.from(arr, dec2hex).join('');
81
+ if (safeCrypto) {
82
+ safeCrypto.getRandomValues(arr);
83
+ return Array.from(arr, dec2hex).join('');
84
+ }
85
+ else {
86
+ return Array.from({ length: len || 40 }, () => dec2hex(Math.floor(Math.random() * 16))).join('');
87
+ }
83
88
  }
84
89
  export function inIframe() {
85
90
  try {
86
- return window.self !== window.top;
91
+ return window.self && window.top && window.self !== window.top;
87
92
  }
88
93
  catch (e) {
89
94
  return true;
@@ -110,9 +115,10 @@ export function createEventListener(target, event, cb, capture) {
110
115
  target[safeAddEventListener](event, cb, capture);
111
116
  }
112
117
  catch (e) {
118
+ const msg = e.message;
113
119
  console.debug(
114
120
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
115
- `Openreplay: ${e.messages}; if this error is caused by an IframeObserver, ignore it`);
121
+ `Openreplay: ${msg}; if this error is caused by an IframeObserver, ignore it`);
116
122
  }
117
123
  }
118
124
  export function deleteEventListener(target, event, cb, capture) {
@@ -121,9 +127,10 @@ export function deleteEventListener(target, event, cb, capture) {
121
127
  target[safeRemoveEventListener](event, cb, capture);
122
128
  }
123
129
  catch (e) {
130
+ const msg = e.message;
124
131
  console.debug(
125
132
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
126
- `Openreplay: ${e.messages}; if this error is caused by an IframeObserver, ignore it`);
133
+ `Openreplay: ${msg}; if this error is caused by an IframeObserver, ignore it`);
127
134
  }
128
135
  }
129
136
  class FIFOTaskScheduler {
@@ -183,3 +190,22 @@ export function requestIdleCb(callback) {
183
190
  // })
184
191
  // }
185
192
  }
193
+ export function simpleMerge(defaultObj, givenObj) {
194
+ const result = { ...defaultObj };
195
+ for (const key in givenObj) {
196
+ // eslint-disable-next-line no-prototype-builtins
197
+ if (givenObj.hasOwnProperty(key)) {
198
+ const userOptionValue = givenObj[key];
199
+ const defaultOptionValue = defaultObj[key];
200
+ if (typeof userOptionValue === 'object' &&
201
+ !Array.isArray(userOptionValue) &&
202
+ userOptionValue !== null) {
203
+ result[key] = simpleMerge(defaultOptionValue || {}, userOptionValue);
204
+ }
205
+ else {
206
+ result[key] = userOptionValue;
207
+ }
208
+ }
209
+ }
210
+ return result;
211
+ }
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": "13.0.1",
4
+ "version": "14.0.0",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"