@openreplay/tracker 4.1.9 → 4.1.10

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 (71) hide show
  1. package/.eslintignore +3 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +22 -18
  4. package/cjs/app/guards.d.ts +11 -12
  5. package/cjs/app/guards.js +2 -1
  6. package/cjs/app/index.d.ts +11 -8
  7. package/cjs/app/index.js +27 -8
  8. package/cjs/app/logger.d.ts +3 -3
  9. package/cjs/app/messages.gen.d.ts +8 -6
  10. package/cjs/app/messages.gen.js +116 -94
  11. package/cjs/app/nodes.d.ts +1 -1
  12. package/cjs/app/observer/iframe_offsets.d.ts +1 -1
  13. package/cjs/app/observer/observer.js +5 -5
  14. package/cjs/app/observer/top_observer.d.ts +2 -2
  15. package/cjs/app/observer/top_observer.js +1 -1
  16. package/cjs/app/session.d.ts +2 -2
  17. package/cjs/app/ticker.d.ts +1 -1
  18. package/cjs/common/interaction.d.ts +5 -5
  19. package/cjs/common/messages.gen.d.ts +104 -86
  20. package/cjs/index.d.ts +6 -2
  21. package/cjs/index.js +7 -5
  22. package/cjs/modules/cssrules.js +1 -1
  23. package/cjs/modules/focus.js +2 -2
  24. package/cjs/modules/fonts.js +2 -2
  25. package/cjs/modules/img.js +6 -5
  26. package/cjs/modules/input.d.ts +1 -1
  27. package/cjs/modules/input.js +15 -23
  28. package/cjs/modules/mouse.js +1 -1
  29. package/cjs/modules/network.d.ts +28 -0
  30. package/cjs/modules/network.js +203 -0
  31. package/cjs/modules/timing.js +7 -3
  32. package/cjs/modules/viewport.js +3 -1
  33. package/cjs/vendors/finder/finder.d.ts +1 -1
  34. package/jest.config.js +11 -0
  35. package/lib/app/guards.d.ts +11 -12
  36. package/lib/app/guards.js +2 -1
  37. package/lib/app/index.d.ts +11 -8
  38. package/lib/app/index.js +28 -9
  39. package/lib/app/logger.d.ts +3 -3
  40. package/lib/app/messages.gen.d.ts +8 -6
  41. package/lib/app/messages.gen.js +107 -87
  42. package/lib/app/nodes.d.ts +1 -1
  43. package/lib/app/observer/iframe_offsets.d.ts +1 -1
  44. package/lib/app/observer/observer.js +5 -5
  45. package/lib/app/observer/top_observer.d.ts +2 -2
  46. package/lib/app/observer/top_observer.js +1 -1
  47. package/lib/app/session.d.ts +2 -2
  48. package/lib/app/ticker.d.ts +1 -1
  49. package/lib/common/interaction.d.ts +5 -5
  50. package/lib/common/messages.gen.d.ts +104 -86
  51. package/lib/common/tsconfig.tsbuildinfo +1 -1
  52. package/lib/index.d.ts +6 -2
  53. package/lib/index.js +8 -6
  54. package/lib/modules/cssrules.js +1 -1
  55. package/lib/modules/focus.js +2 -2
  56. package/lib/modules/fonts.js +2 -2
  57. package/lib/modules/img.js +6 -5
  58. package/lib/modules/input.d.ts +1 -1
  59. package/lib/modules/input.js +15 -23
  60. package/lib/modules/mouse.js +1 -1
  61. package/lib/modules/network.d.ts +28 -0
  62. package/lib/modules/network.js +200 -0
  63. package/lib/modules/timing.js +8 -4
  64. package/lib/modules/viewport.js +3 -1
  65. package/lib/vendors/finder/finder.d.ts +1 -1
  66. package/package.json +8 -3
  67. package/tsconfig-base.json +2 -1
  68. package/cjs/app/messages.d.ts +0 -52
  69. package/cjs/app/messages.js +0 -234
  70. package/lib/app/messages.d.ts +0 -52
  71. package/lib/app/messages.js +0 -181
@@ -4,18 +4,18 @@ exports.getInputLabel = void 0;
4
4
  const utils_js_1 = require("../utils.js");
5
5
  const guards_js_1 = require("../app/guards.js");
6
6
  const messages_gen_js_1 = require("../app/messages.gen.js");
7
- const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
7
+ const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date', 'tel'];
8
8
  function isTextEditable(node) {
9
- if ((0, guards_js_1.hasTag)(node, 'TEXTAREA')) {
9
+ if ((0, guards_js_1.hasTag)(node, 'textarea')) {
10
10
  return true;
11
11
  }
12
- if (!(0, guards_js_1.hasTag)(node, 'INPUT')) {
12
+ if (!(0, guards_js_1.hasTag)(node, 'input')) {
13
13
  return false;
14
14
  }
15
15
  return INPUT_TYPES.includes(node.type);
16
16
  }
17
17
  function isCheckable(node) {
18
- if (!(0, guards_js_1.hasTag)(node, 'INPUT')) {
18
+ if (!(0, guards_js_1.hasTag)(node, 'input')) {
19
19
  return false;
20
20
  }
21
21
  const type = node.type;
@@ -25,7 +25,7 @@ const labelElementFor = utils_js_1.IN_BROWSER && 'labels' in HTMLInputElement.pr
25
25
  ? (node) => {
26
26
  let p = node;
27
27
  while ((p = p.parentNode) !== null) {
28
- if ((0, guards_js_1.hasTag)(p, 'LABEL')) {
28
+ if ((0, guards_js_1.hasTag)(p, 'label')) {
29
29
  return p;
30
30
  }
31
31
  }
@@ -37,7 +37,7 @@ const labelElementFor = utils_js_1.IN_BROWSER && 'labels' in HTMLInputElement.pr
37
37
  : (node) => {
38
38
  let p = node;
39
39
  while ((p = p.parentNode) !== null) {
40
- if ((0, guards_js_1.hasTag)(p, 'LABEL')) {
40
+ if ((0, guards_js_1.hasTag)(p, 'label')) {
41
41
  return p;
42
42
  }
43
43
  }
@@ -68,7 +68,7 @@ function default_1(app, opts) {
68
68
  const options = Object.assign({
69
69
  obscureInputNumbers: true,
70
70
  obscureInputEmails: true,
71
- defaultInputMode: 0 /* Plain */,
71
+ defaultInputMode: 0 /* InputMode.Plain */,
72
72
  obscureInputDates: false,
73
73
  }, opts);
74
74
  function sendInputTarget(id, node) {
@@ -81,22 +81,22 @@ function default_1(app, opts) {
81
81
  let value = node.value;
82
82
  let inputMode = options.defaultInputMode;
83
83
  if (node.type === 'password' || app.sanitizer.isHidden(id)) {
84
- inputMode = 2 /* Hidden */;
84
+ inputMode = 2 /* InputMode.Hidden */;
85
85
  }
86
86
  else if (app.sanitizer.isObscured(id) ||
87
- (inputMode === 0 /* Plain */ &&
87
+ (inputMode === 0 /* InputMode.Plain */ &&
88
88
  ((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
89
89
  (options.obscureInputDates && node.type === 'date') ||
90
90
  (options.obscureInputEmails && (node.type === 'email' || !!~value.indexOf('@')))))) {
91
- inputMode = 1 /* Obscured */;
91
+ inputMode = 1 /* InputMode.Obscured */;
92
92
  }
93
93
  let mask = 0;
94
94
  switch (inputMode) {
95
- case 2 /* Hidden */:
95
+ case 2 /* InputMode.Hidden */:
96
96
  mask = -1;
97
97
  value = '';
98
98
  break;
99
- case 1 /* Obscured */:
99
+ case 1 /* InputMode.Obscured */:
100
100
  mask = value.length;
101
101
  value = '';
102
102
  break;
@@ -115,11 +115,7 @@ function default_1(app, opts) {
115
115
  inputValues.forEach((value, id) => {
116
116
  const node = app.nodes.getNode(id);
117
117
  if (!node)
118
- return;
119
- if (!isTextEditable(node)) {
120
- inputValues.delete(id);
121
- return;
122
- }
118
+ return inputValues.delete(id);
123
119
  if (value !== node.value) {
124
120
  inputValues.set(id, node.value);
125
121
  if (!registeredTargets.has(id)) {
@@ -132,11 +128,7 @@ function default_1(app, opts) {
132
128
  checkableValues.forEach((checked, id) => {
133
129
  const node = app.nodes.getNode(id);
134
130
  if (!node)
135
- return;
136
- if (!isCheckable(node)) {
137
- checkableValues.delete(id);
138
- return;
139
- }
131
+ return checkableValues.delete(id);
140
132
  if (checked !== node.checked) {
141
133
  checkableValues.set(id, node.checked);
142
134
  app.send((0, messages_gen_js_1.SetInputChecked)(id, node.checked));
@@ -150,7 +142,7 @@ function default_1(app, opts) {
150
142
  return;
151
143
  }
152
144
  // TODO: support multiple select (?): use selectedOptions; Need send target?
153
- if ((0, guards_js_1.hasTag)(node, 'SELECT')) {
145
+ if ((0, guards_js_1.hasTag)(node, 'select')) {
154
146
  sendInputValue(id, node);
155
147
  app.attachEventListener(node, 'change', () => {
156
148
  sendInputValue(id, node);
@@ -80,7 +80,7 @@ function default_1(app) {
80
80
  if (dl !== null) {
81
81
  return dl;
82
82
  }
83
- if ((0, guards_js_1.hasTag)(target, 'INPUT')) {
83
+ if ((0, guards_js_1.hasTag)(target, 'input')) {
84
84
  return (0, input_js_1.getInputLabel)(target);
85
85
  }
86
86
  if (isClickable(target)) {
@@ -0,0 +1,28 @@
1
+ import type App from '../app/index.js';
2
+ type XHRRequestBody = Parameters<XMLHttpRequest['send']>[0];
3
+ type FetchRequestBody = RequestInit['body'];
4
+ interface RequestData {
5
+ body: XHRRequestBody | FetchRequestBody;
6
+ headers: Record<string, string>;
7
+ }
8
+ interface ResponseData {
9
+ body: any;
10
+ headers: Record<string, string>;
11
+ }
12
+ interface RequestResponseData {
13
+ readonly status: number;
14
+ readonly method: string;
15
+ url: string;
16
+ request: RequestData;
17
+ response: ResponseData;
18
+ }
19
+ type Sanitizer = (data: RequestResponseData) => RequestResponseData | null;
20
+ export interface Options {
21
+ sessionTokenHeader: string | boolean;
22
+ failuresOnly: boolean;
23
+ ignoreHeaders: Array<string> | boolean;
24
+ capturePayload: boolean;
25
+ sanitizer?: Sanitizer;
26
+ }
27
+ export default function (app: App, opts?: Partial<Options>): void;
28
+ export {};
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const messages_gen_js_1 = require("../app/messages.gen.js");
4
+ const utils_js_1 = require("../utils.js");
5
+ function getXHRRequestDataObject(xhr) {
6
+ // @ts-ignore this is 3x faster than using Map<XHR, XHRRequestData>
7
+ if (!xhr.__or_req_data__) {
8
+ // @ts-ignore
9
+ xhr.__or_req_data__ = { body: undefined, headers: {} };
10
+ }
11
+ // @ts-ignore
12
+ return xhr.__or_req_data__;
13
+ }
14
+ function strMethod(method) {
15
+ return typeof method === 'string' ? method.toUpperCase() : 'GET';
16
+ }
17
+ function default_1(app, opts = {}) {
18
+ const options = Object.assign({
19
+ failuresOnly: false,
20
+ ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
21
+ capturePayload: false,
22
+ sessionTokenHeader: false,
23
+ }, opts);
24
+ const ignoreHeaders = options.ignoreHeaders;
25
+ const isHIgnored = Array.isArray(ignoreHeaders)
26
+ ? (name) => ignoreHeaders.includes(name)
27
+ : () => ignoreHeaders;
28
+ const stHeader = options.sessionTokenHeader === true ? 'X-OpenReplay-SessionToken' : options.sessionTokenHeader;
29
+ function setSessionTokenHeader(setRequestHeader) {
30
+ if (stHeader) {
31
+ const sessionToken = app.getSessionToken();
32
+ if (sessionToken) {
33
+ app.safe(setRequestHeader)(stHeader, sessionToken);
34
+ }
35
+ }
36
+ }
37
+ function sanitize(reqResInfo) {
38
+ if (!options.capturePayload) {
39
+ delete reqResInfo.request.body;
40
+ delete reqResInfo.response.body;
41
+ }
42
+ if (options.sanitizer) {
43
+ const resBody = reqResInfo.response.body;
44
+ if (typeof resBody === 'string') {
45
+ // Parse response in order to have handy view in sanitisation function
46
+ try {
47
+ reqResInfo.response.body = JSON.parse(resBody);
48
+ }
49
+ catch (_a) { }
50
+ }
51
+ return options.sanitizer(reqResInfo);
52
+ }
53
+ return reqResInfo;
54
+ }
55
+ function stringify(r) {
56
+ if (r && typeof r.body !== 'string') {
57
+ try {
58
+ r.body = JSON.stringify(r.body);
59
+ }
60
+ catch (_a) {
61
+ r.body = '<unable to stringify>';
62
+ app.notify.warn("Openreplay fetch couldn't stringify body:", r.body);
63
+ }
64
+ }
65
+ return JSON.stringify(r);
66
+ }
67
+ /* ====== Fetch ====== */
68
+ const origFetch = window.fetch.bind(window);
69
+ window.fetch = (input, init = {}) => {
70
+ if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
71
+ return origFetch(input, init);
72
+ }
73
+ setSessionTokenHeader(function (name, value) {
74
+ if (init.headers === undefined) {
75
+ init.headers = {};
76
+ }
77
+ if (init.headers instanceof Headers) {
78
+ init.headers.append(name, value);
79
+ }
80
+ else if (Array.isArray(init.headers)) {
81
+ init.headers.push([name, value]);
82
+ }
83
+ else {
84
+ init.headers[name] = value;
85
+ }
86
+ });
87
+ const startTime = performance.now();
88
+ return origFetch(input, init).then((response) => {
89
+ const duration = performance.now() - startTime;
90
+ if (options.failuresOnly && response.status < 400) {
91
+ return response;
92
+ }
93
+ const r = response.clone();
94
+ r.text()
95
+ .then((text) => {
96
+ const reqHs = {};
97
+ const resHs = {};
98
+ if (ignoreHeaders !== true) {
99
+ // request headers
100
+ const writeReqHeader = ([n, v]) => {
101
+ if (!isHIgnored(n)) {
102
+ reqHs[n] = v;
103
+ }
104
+ };
105
+ if (init.headers instanceof Headers) {
106
+ init.headers.forEach((v, n) => writeReqHeader([n, v]));
107
+ }
108
+ else if (Array.isArray(init.headers)) {
109
+ init.headers.forEach(writeReqHeader);
110
+ }
111
+ else if (typeof init.headers === 'object') {
112
+ Object.entries(init.headers).forEach(writeReqHeader);
113
+ }
114
+ // response headers
115
+ r.headers.forEach((v, n) => {
116
+ if (!isHIgnored(n))
117
+ resHs[n] = v;
118
+ });
119
+ }
120
+ const method = strMethod(init.method);
121
+ const reqResInfo = sanitize({
122
+ url: String(input),
123
+ method,
124
+ status: r.status,
125
+ request: {
126
+ headers: reqHs,
127
+ body: init.body,
128
+ },
129
+ response: {
130
+ headers: resHs,
131
+ body: text,
132
+ },
133
+ });
134
+ if (!reqResInfo) {
135
+ return;
136
+ }
137
+ app.send((0, messages_gen_js_1.NetworkRequest)('fetch', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), r.status, startTime + (0, utils_js_1.getTimeOrigin)(), duration));
138
+ })
139
+ .catch((e) => app.debug.error('Could not process Fetch response:', e));
140
+ return response;
141
+ });
142
+ };
143
+ /* ====== <> ====== */
144
+ /* ====== XHR ====== */
145
+ const nativeOpen = XMLHttpRequest.prototype.open;
146
+ XMLHttpRequest.prototype.open = function (initMethod, url) {
147
+ const xhr = this;
148
+ setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value));
149
+ let startTime = 0;
150
+ xhr.addEventListener('loadstart', (e) => {
151
+ startTime = e.timeStamp;
152
+ });
153
+ xhr.addEventListener('load', app.safe((e) => {
154
+ const { headers: reqHs, body: reqBody } = getXHRRequestDataObject(xhr);
155
+ const duration = startTime > 0 ? e.timeStamp - startTime : 0;
156
+ const hString = ignoreHeaders ? '' : xhr.getAllResponseHeaders(); // might be null (though only if no response received though)
157
+ const resHs = hString
158
+ ? hString
159
+ .split('\r\n')
160
+ .map((h) => h.split(':'))
161
+ .filter((entry) => !isHIgnored(entry[0]))
162
+ .reduce((hds, [name, value]) => (Object.assign(Object.assign({}, hds), { [name]: value })), {})
163
+ : {};
164
+ const method = strMethod(initMethod);
165
+ const reqResInfo = sanitize({
166
+ url: String(url),
167
+ method,
168
+ status: xhr.status,
169
+ request: {
170
+ headers: reqHs,
171
+ body: reqBody,
172
+ },
173
+ response: {
174
+ headers: resHs,
175
+ body: xhr.response,
176
+ },
177
+ });
178
+ if (!reqResInfo) {
179
+ return;
180
+ }
181
+ app.send((0, messages_gen_js_1.NetworkRequest)('xhr', method, String(reqResInfo.url), stringify(reqResInfo.request), stringify(reqResInfo.response), xhr.status, startTime + (0, utils_js_1.getTimeOrigin)(), duration));
182
+ }));
183
+ //TODO: handle error (though it has no Error API nor any useful information)
184
+ //xhr.addEventListener('error', (e) => {})
185
+ return nativeOpen.apply(this, arguments);
186
+ };
187
+ const nativeSend = XMLHttpRequest.prototype.send;
188
+ XMLHttpRequest.prototype.send = function (body) {
189
+ const rdo = getXHRRequestDataObject(this);
190
+ rdo.body = body;
191
+ return nativeSend.apply(this, arguments);
192
+ };
193
+ const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
194
+ XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
195
+ if (!isHIgnored(name)) {
196
+ const rdo = getXHRRequestDataObject(this);
197
+ rdo.headers[name] = value;
198
+ }
199
+ return nativeSetRequestHeader.apply(this, arguments);
200
+ };
201
+ /* ====== <> ====== */
202
+ }
203
+ exports.default = default_1;
@@ -10,7 +10,7 @@ function getPaintBlocks(resources) {
10
10
  for (let i = 0; i < elements.length; i++) {
11
11
  const element = elements[i];
12
12
  let src = '';
13
- if ((0, guards_js_1.hasTag)(element, 'IMG')) {
13
+ if ((0, guards_js_1.hasTag)(element, 'img')) {
14
14
  src = element.currentSrc || element.src;
15
15
  }
16
16
  if (!src) {
@@ -75,7 +75,7 @@ function default_1(app, opts) {
75
75
  if (resources !== null) {
76
76
  resources[entry.name] = entry.startTime + entry.duration;
77
77
  }
78
- app.send((0, messages_gen_js_1.ResourceTiming)(entry.startTime + performance.timing.navigationStart, entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
78
+ app.send((0, messages_gen_js_1.ResourceTiming)(entry.startTime + (0, utils_js_1.getTimeOrigin)(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
79
79
  }
80
80
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
81
81
  let prevSessionID;
@@ -112,7 +112,11 @@ function default_1(app, opts) {
112
112
  }
113
113
  if (performance.timing.loadEventEnd || performance.now() > 30000) {
114
114
  pageLoadTimingSent = true;
115
- const { navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
115
+ const {
116
+ // should be ok to use here, (https://github.com/mdn/content/issues/4713)
117
+ // since it is compared with the values obtained on the page load (before any possible sleep state)
118
+ // deprecated though
119
+ navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
116
120
  app.send((0, messages_gen_js_1.PageLoadTiming)(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
117
121
  }
118
122
  }, 30);
@@ -5,12 +5,14 @@ const messages_gen_js_1 = require("../app/messages.gen.js");
5
5
  function default_1(app) {
6
6
  let url, width, height;
7
7
  let navigationStart;
8
+ let referrer = document.referrer;
8
9
  const sendSetPageLocation = app.safe(() => {
9
10
  const { URL } = document;
10
11
  if (URL !== url) {
11
12
  url = URL;
12
- app.send((0, messages_gen_js_1.SetPageLocation)(url, document.referrer, navigationStart));
13
+ app.send((0, messages_gen_js_1.SetPageLocation)(url, referrer, navigationStart));
13
14
  navigationStart = 0;
15
+ referrer = url;
14
16
  }
15
17
  });
16
18
  const sendSetViewportSize = app.safe(() => {
@@ -1,4 +1,4 @@
1
- export declare type Options = {
1
+ export type Options = {
2
2
  root: Element;
3
3
  idName: (name: string) => boolean;
4
4
  className: (name: string) => boolean;
package/jest.config.js ADDED
@@ -0,0 +1,11 @@
1
+ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2
+ const config = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'jsdom',
5
+ // .js file extension fix
6
+ moduleNameMapper: {
7
+ '(.+)\\.js': '$1',
8
+ },
9
+ }
10
+
11
+ export default config
@@ -4,18 +4,17 @@ export declare function isElementNode(node: Node): node is Element;
4
4
  export declare function isTextNode(node: Node): node is Text;
5
5
  export declare function isDocument(node: Node): node is Document;
6
6
  export declare function isRootNode(node: Node): node is Document | DocumentFragment;
7
- declare type TagTypeMap = {
8
- HTML: HTMLHtmlElement;
9
- BODY: HTMLBodyElement;
10
- IMG: HTMLImageElement;
11
- INPUT: HTMLInputElement;
12
- TEXTAREA: HTMLTextAreaElement;
13
- SELECT: HTMLSelectElement;
14
- LABEL: HTMLLabelElement;
15
- IFRAME: HTMLIFrameElement;
16
- STYLE: HTMLStyleElement;
17
- style: SVGStyleElement;
18
- LINK: HTMLLinkElement;
7
+ type TagTypeMap = {
8
+ html: HTMLHtmlElement;
9
+ body: HTMLBodyElement;
10
+ img: HTMLImageElement;
11
+ input: HTMLInputElement;
12
+ textarea: HTMLTextAreaElement;
13
+ select: HTMLSelectElement;
14
+ label: HTMLLabelElement;
15
+ iframe: HTMLIFrameElement;
16
+ style: HTMLStyleElement | SVGStyleElement;
17
+ link: HTMLLinkElement;
19
18
  };
20
19
  export declare function hasTag<T extends keyof TagTypeMap>(el: Node, tagName: T): el is TagTypeMap[typeof tagName];
21
20
  export {};
package/lib/app/guards.js CHANGED
@@ -18,5 +18,6 @@ export function isRootNode(node) {
18
18
  return node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
19
19
  }
20
20
  export function hasTag(el, tagName) {
21
- return el.nodeName === tagName;
21
+ // @ts-ignore
22
+ return el.localName === tagName;
22
23
  }
@@ -22,19 +22,19 @@ interface OnStartInfo {
22
22
  userUUID: string;
23
23
  }
24
24
  declare const CANCELED: "canceled";
25
- declare type SuccessfulStart = OnStartInfo & {
25
+ type SuccessfulStart = OnStartInfo & {
26
26
  success: true;
27
27
  };
28
- declare type UnsuccessfulStart = {
28
+ type UnsuccessfulStart = {
29
29
  reason: typeof CANCELED | string;
30
30
  success: false;
31
31
  };
32
32
  declare const UnsuccessfulStart: (reason: string) => UnsuccessfulStart;
33
33
  declare const SuccessfulStart: (body: OnStartInfo) => SuccessfulStart;
34
- export declare type StartPromiseReturn = SuccessfulStart | UnsuccessfulStart;
35
- declare type StartCallback = (i: OnStartInfo) => void;
36
- declare type CommitCallback = (messages: Array<Message>) => void;
37
- declare type AppOptions = {
34
+ export type StartPromiseReturn = SuccessfulStart | UnsuccessfulStart;
35
+ type StartCallback = (i: OnStartInfo) => void;
36
+ type CommitCallback = (messages: Array<Message>) => void;
37
+ type AppOptions = {
38
38
  revID: string;
39
39
  node_id: string;
40
40
  session_reset_key: string;
@@ -51,7 +51,7 @@ declare type AppOptions = {
51
51
  sessionStorage: Storage | null;
52
52
  onStart?: StartCallback;
53
53
  } & WebworkerOptions & SessOptions;
54
- export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
54
+ export type Options = AppOptions & ObserverOptions & SanitizerOptions;
55
55
  export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
56
56
  export default class App {
57
57
  readonly nodes: Nodes;
@@ -75,6 +75,7 @@ export default class App {
75
75
  private readonly worker?;
76
76
  constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>);
77
77
  private _debug;
78
+ private _usingOldFetchPlugin;
78
79
  send(message: Message, urgent?: boolean): void;
79
80
  private commit;
80
81
  private delay;
@@ -100,7 +101,9 @@ export default class App {
100
101
  };
101
102
  getSessionToken(): string | undefined;
102
103
  getSessionID(): string | undefined;
103
- getSessionURL(): string | undefined;
104
+ getSessionURL(options?: {
105
+ withCurrentTime?: boolean;
106
+ }): string | undefined;
104
107
  getHost(): string;
105
108
  getProjectKey(): string;
106
109
  getBaseHref(): string;
package/lib/app/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Timestamp, Metadata, UserID } from './messages.gen.js';
2
- import { now, adjustTimeOrigin } from '../utils.js';
2
+ import { now, adjustTimeOrigin, deprecationWarn } from '../utils.js';
3
3
  import Nodes from './nodes.js';
4
4
  import Observer from './observer/top_observer.js';
5
5
  import Sanitizer from './sanitizer.js';
@@ -30,7 +30,8 @@ export default class App {
30
30
  this.stopCallbacks = [];
31
31
  this.commitCallbacks = [];
32
32
  this.activityState = ActivityState.NotActive;
33
- this.version = '4.1.8'; // TODO: version compatability check inside each plugin.
33
+ this.version = '4.1.10'; // TODO: version compatability check inside each plugin.
34
+ this._usingOldFetchPlugin = false;
34
35
  this.delay = 0;
35
36
  this.projectKey = projectKey;
36
37
  this.options = Object.assign({
@@ -73,7 +74,7 @@ export default class App {
73
74
  this.session.applySessionHash(sessionToken);
74
75
  }
75
76
  try {
76
- this.worker = new Worker(URL.createObjectURL(new Blob(['"use strict";class t{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();if(i.status>=400)return void this.retry(t);this.attemptsCount=0;const s=this.queue.shift();s?this.sendBatch(s):this.busy=!1}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0}}const i="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(r=t.charCodeAt(h),!(r>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+r-56320+65536,h+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class s extends class{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const s=i.encode(t),e=s.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(s,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}{encode(t){switch(t[0]){case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 82:return this.uint(t[1])&&this.uint(t[2]);case 0:return this.uint(t[1]);case 4:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 11:return this.uint(t[1]);case 12:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 17:return this.uint(t[1])&&this.string(t[2]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 20:return this.uint(t[1])&&this.uint(t[2]);case 22:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 24:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 25:return this.string(t[1])&&this.string(t[2])&&this.string(t[3]);case 27:return this.string(t[1])&&this.string(t[2]);case 28:case 29:return this.string(t[1]);case 30:return this.string(t[1])&&this.string(t[2]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 38:return this.uint(t[1])&&this.uint(t[2]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 41:return this.string(t[1])&&this.string(t[2]);case 42:return this.string(t[1]);case 44:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 45:case 46:return this.string(t[1])&&this.string(t[2]);case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 54:return this.uint(t[1])&&this.string(t[2]);case 55:return this.boolean(t[1]);case 57:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 61:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 63:case 64:return this.string(t[1])&&this.string(t[2]);case 67:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 70:return this.uint(t[1])&&this.uint(t[2]);case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 79:return this.string(t[1])&&this.string(t[2]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])}}}class e{constructor(t,i,e,n){this.pageNo=t,this.timestamp=i,this.url=e,this.onBatch=n,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new s(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}prepare(){if(!this.encoder.isEmpty())return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url];this.writeType(t),this.writeFields(t),this.isEmpty=!0}writeWithSize(t){const i=this.encoder;if(!this.writeType(t)||!i.skip(3))return!1;const s=i.getCurrentOffset(),e=this.writeFields(t);if(e){const e=i.getCurrentOffset()-s;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,s-3),i.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),!this.writeWithSize(t))for(this.finaliseBatch();!this.writeWithSize(t);){if(this.beaconSize===this.beaconSizeLimit)return console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder.reset(),void this.prepare();this.beaconSize=Math.min(2*this.beaconSize,this.beaconSizeLimit),this.encoder=new s(this.beaconSize),this.prepare()}}finaliseBatch(){this.isEmpty||(this.onBatch(this.encoder.flush()),this.prepare())}clean(){this.encoder.reset()}}var n;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active"}(n||(n={}));let r=null,h=null;function u(){h&&h.finaliseBatch()}function a(){n.Stopping,null!==g&&(clearInterval(g),g=null),h&&(h.clean(),h=null),r&&(r.clean(),r=null),n.NotActive}function o(){postMessage("restart"),a()}n.NotActive;let c,g=null;self.onmessage=({data:i})=>{if(null!=i){if("stop"===i)return u(),void a();if(Array.isArray(i)){if(!h)throw new Error("WebWorker: writer not initialised. Service Should be Started.");const t=h;i.forEach(i=>{55===i[0]&&(i[1]?c=setTimeout(()=>o(),18e5):clearTimeout(c)),t.writeMessage(i)})}else{if("start"===i.type)return n.Starting,r=new t(i.ingestPoint,()=>{o()},t=>{!function(t){postMessage({type:"failure",reason:t}),a()}(t)},i.connAttemptCount,i.connAttemptGap),h=new e(i.pageNo,i.timestamp,i.url,t=>r&&r.push(t)),null===g&&(g=setInterval(u,1e4)),n.Active;if("auth"===i.type){if(!r)throw new Error("WebWorker: sender not initialised. Received auth.");if(!h)throw new Error("WebWorker: writer not initialised. Received auth.");return r.authorise(i.token),void(i.beaconSizeLimit&&h.setBeaconSizeLimit(i.beaconSizeLimit))}}}else u()};'], { type: 'text/javascript' })));
77
+ this.worker = new Worker(URL.createObjectURL(new Blob(['"use strict";class t{constructor(t,i,s,e=10,n=1e3){this.onUnauthorised=i,this.onFailure=s,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.ingestURL=t+"/v1/web/i"}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){this.busy||!this.token?this.queue.push(t):this.sendBatch(t)}sendNext(){const t=this.queue.shift();t?this.sendBatch(t):this.busy=!1}retry(t){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout(()=>this.sendBatch(t),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t){this.busy=!0,fetch(this.ingestURL,{body:t,method:"POST",headers:{Authorization:"Bearer "+this.token},keepalive:t.length<65536}).then(i=>{if(401===i.status)return this.busy=!1,void this.onUnauthorised();i.status>=400?this.retry(t):(this.attemptsCount=0,this.sendNext())}).catch(i=>{console.warn("OpenReplay:",i),this.retry(t)})}clean(){this.queue.length=0,this.token=null}}const i="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(r=t.charCodeAt(h),!(r>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+r-56320+65536,h+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class s extends class{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const s=i.encode(t),e=s.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(s,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}{encode(t){switch(t[0]){case 0:return this.uint(t[1]);case 4:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 11:return this.uint(t[1]);case 12:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 17:return this.uint(t[1])&&this.string(t[2]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 20:return this.uint(t[1])&&this.uint(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 24:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 27:return this.string(t[1])&&this.string(t[2]);case 28:case 29:return this.string(t[1]);case 30:return this.string(t[1])&&this.string(t[2]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 38:return this.uint(t[1])&&this.uint(t[2]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 41:return this.string(t[1])&&this.string(t[2]);case 42:return this.string(t[1]);case 44:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 45:case 46:return this.string(t[1])&&this.string(t[2]);case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 50:return this.uint(t[1])&&this.string(t[2]);case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 54:return this.uint(t[1])&&this.string(t[2]);case 55:return this.boolean(t[1]);case 57:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 61:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 63:case 64:return this.string(t[1])&&this.string(t[2]);case 67:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 70:return this.uint(t[1])&&this.uint(t[2]);case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 79:return this.string(t[1])&&this.string(t[2]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 82:return this.uint(t[1])&&this.uint(t[2])}}}class e{constructor(){this.idx=1,this.backDict={}}getKey(t){let i=!1;return this.backDict[t]||(i=!0,this.backDict[t]=this.idx++),[this.backDict[t],i]}}class n{constructor(t,i,n,r){this.pageNo=t,this.timestamp=i,this.url=n,this.onBatch=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new s(this.beaconSize),this.strDict=new e,this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}prepare(){if(!this.encoder.isEmpty)return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url];this.writeType(t),this.writeFields(t),this.isEmpty=!0}writeWithSize(t){const i=this.encoder;if(!this.writeType(t)||!i.skip(3))return!1;const s=i.getCurrentOffset(),e=this.writeFields(t);if(e){const e=i.getCurrentOffset()-s;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,s-3),i.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}applyDict(t){const[i,s]=this.strDict.getKey(t);return s&&this.writeMessage([50,i,t]),i}writeMessage(t){0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),12===t[0]&&(t=[51,t[1],this.applyDict(t[2]),this.applyDict(t[3])]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new s(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)||console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder=new s(this.beaconSize),this.prepare()))}finaliseBatch(){this.isEmpty||(this.onBatch(this.encoder.flush()),this.prepare())}clean(){this.encoder.reset()}}var r;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active"}(r||(r={}));let h=null,u=null;r.NotActive;let a=0;function c(){u&&u.finaliseBatch()}function o(){r.Stopping,null!==l&&(clearInterval(l),l=null),u&&(u.clean(),u=null),h&&(h.clean(),h=null),r.NotActive}function g(){postMessage("restart"),o()}let f,l=null;self.onmessage=({data:i})=>{if(null!=i){if("stop"===i)return c(),void o();if(Array.isArray(i)){if(null!==u){const t=u;i.forEach(i=>{55===i[0]&&(i[1]?f=setTimeout(()=>g(),18e5):clearTimeout(f)),t.writeMessage(i)})}u||(postMessage("not_init"),0===a&&(a+=1,g()))}else{if("start"===i.type)return r.Starting,h=new t(i.ingestPoint,()=>{g()},t=>{!function(t){postMessage({type:"failure",reason:t}),o()}(t)},i.connAttemptCount,i.connAttemptGap),u=new n(i.pageNo,i.timestamp,i.url,t=>h&&h.push(t)),null===l&&(l=setInterval(c,1e4)),r.Active;if("auth"===i.type){if(!h)throw new Error("WebWorker: sender not initialised. Received auth.");if(!u)throw new Error("WebWorker: writer not initialised. Received auth.");return h.authorise(i.token),void(i.beaconSizeLimit&&u.setBeaconSizeLimit(i.beaconSizeLimit))}}}else c()};'], { type: 'text/javascript' })));
77
78
  this.worker.onerror = (e) => {
78
79
  this._debug('webworker_error', e);
79
80
  };
@@ -82,6 +83,9 @@ export default class App {
82
83
  this.stop(false);
83
84
  this.start({}, true);
84
85
  }
86
+ else if (data === 'not_init') {
87
+ console.warn('WebWorker: writer not initialised. Restarting tracker');
88
+ }
85
89
  else if (data.type === 'failure') {
86
90
  this.stop(false);
87
91
  this._debug('worker_failed', data.reason);
@@ -119,6 +123,16 @@ export default class App {
119
123
  if (this.activityState === ActivityState.NotActive) {
120
124
  return;
121
125
  }
126
+ // === Back compatibility with Fetch/Axios plugins ===
127
+ if (message[0] === 39 /* MType.Fetch */) {
128
+ this._usingOldFetchPlugin = true;
129
+ deprecationWarn('Fetch plugin', "'network' init option", '/installation/network-options');
130
+ deprecationWarn('Axios plugin', "'network' init option", '/installation/network-options');
131
+ }
132
+ if (this._usingOldFetchPlugin && message[0] === 21 /* MType.NetworkRequest */) {
133
+ return;
134
+ }
135
+ // ====================================================
122
136
  this.messages.push(message);
123
137
  // TODO: commit on start if there were `urgent` sends;
124
138
  // Clarify where urgent can be used for;
@@ -213,16 +227,21 @@ export default class App {
213
227
  getSessionID() {
214
228
  return this.session.getInfo().sessionID || undefined;
215
229
  }
216
- getSessionURL() {
217
- const { projectID, sessionID } = this.session.getInfo();
230
+ getSessionURL(options) {
231
+ const { projectID, sessionID, timestamp } = this.session.getInfo();
218
232
  if (!projectID || !sessionID) {
219
233
  this.debug.error('OpenReplay error: Unable to build session URL');
220
234
  return undefined;
221
235
  }
222
236
  const ingest = this.options.ingestPoint;
223
- const isSaas = ingest === DEFAULT_INGEST_POINT;
224
- const projectPath = isSaas ? ingest.replace('api', 'app') : ingest;
225
- return projectPath.replace(/ingest$/, `${projectID}/session/${sessionID}`);
237
+ const isSaas = /api\.openreplay\.com/.test(ingest);
238
+ const projectPath = isSaas ? 'https://openreplay.com/ingest' : ingest;
239
+ const url = projectPath.replace(/ingest$/, `${projectID}/session/${sessionID}`);
240
+ if (options === null || options === void 0 ? void 0 : options.withCurrentTime) {
241
+ const jumpTo = now() - timestamp;
242
+ return `${url}?jumpto=${jumpTo}`;
243
+ }
244
+ return url;
226
245
  }
227
246
  getHost() {
228
247
  return new URL(this.options.ingestPoint).host;
@@ -325,7 +344,7 @@ export default class App {
325
344
  return Promise.reject('no worker found after start request (this might not happen)');
326
345
  }
327
346
  if (this.activityState === ActivityState.NotActive) {
328
- return Promise.reject('Tracker stopped during authorisation');
347
+ return Promise.reject('Tracker stopped during authorization');
329
348
  }
330
349
  const { token, userUUID, projectID, beaconSizeLimit, delay, // derived from token
331
350
  sessionID, // derived from token