@openreplay/tracker 3.4.13 → 3.4.17-beta.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.
- package/cjs/app/context.d.ts +18 -0
- package/cjs/app/context.js +48 -0
- package/cjs/app/index.d.ts +11 -6
- package/cjs/app/index.js +33 -22
- package/cjs/app/observer/iframe_observer.d.ts +4 -0
- package/cjs/app/observer/iframe_observer.js +22 -0
- package/cjs/app/observer/observer.d.ts +25 -0
- package/cjs/app/{observer.js → observer/observer.js} +82 -170
- package/cjs/app/observer/shadow_root_observer.d.ts +4 -0
- package/cjs/app/observer/shadow_root_observer.js +21 -0
- package/cjs/app/observer/top_observer.d.ts +15 -0
- package/cjs/app/observer/top_observer.js +84 -0
- package/cjs/app/sanitizer.d.ts +16 -0
- package/cjs/app/sanitizer.js +46 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +7 -1
- package/cjs/modules/exception.js +8 -1
- package/cjs/modules/img.js +15 -1
- package/cjs/modules/input.d.ts +3 -1
- package/cjs/modules/input.js +6 -3
- package/cjs/modules/mouse.js +1 -1
- package/lib/app/context.d.ts +18 -0
- package/lib/app/context.js +43 -0
- package/lib/app/index.d.ts +11 -6
- package/lib/app/index.js +33 -22
- package/lib/app/observer/iframe_observer.d.ts +4 -0
- package/lib/app/observer/iframe_observer.js +19 -0
- package/lib/app/observer/observer.d.ts +25 -0
- package/lib/app/{observer.js → observer/observer.js} +82 -170
- package/lib/app/observer/shadow_root_observer.d.ts +4 -0
- package/lib/app/observer/shadow_root_observer.js +18 -0
- package/lib/app/observer/top_observer.d.ts +15 -0
- package/lib/app/observer/top_observer.js +81 -0
- package/lib/app/sanitizer.d.ts +16 -0
- package/lib/app/sanitizer.js +44 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +7 -1
- package/lib/modules/exception.js +8 -1
- package/lib/modules/img.js +16 -2
- package/lib/modules/input.d.ts +3 -1
- package/lib/modules/input.js +6 -3
- package/lib/modules/mouse.js +1 -1
- package/package.json +1 -1
- package/cjs/app/observer.d.ts +0 -47
- package/lib/app/observer.d.ts +0 -47
package/cjs/modules/input.js
CHANGED
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getInputLabel = void 0;
|
|
4
4
|
const utils_js_1 = require("../utils.js");
|
|
5
5
|
const index_js_1 = require("../messages/index.js");
|
|
6
|
-
function
|
|
6
|
+
function isTextEditable(node) {
|
|
7
|
+
if (node instanceof HTMLTextAreaElement) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
7
10
|
if (!(node instanceof HTMLInputElement)) {
|
|
8
11
|
return false;
|
|
9
12
|
}
|
|
@@ -111,7 +114,7 @@ function default_1(app, opts) {
|
|
|
111
114
|
app.ticker.attach(() => {
|
|
112
115
|
inputValues.forEach((value, id) => {
|
|
113
116
|
const node = app.nodes.getNode(id);
|
|
114
|
-
if (!
|
|
117
|
+
if (!isTextEditable(node)) {
|
|
115
118
|
inputValues.delete(id);
|
|
116
119
|
return;
|
|
117
120
|
}
|
|
@@ -142,7 +145,7 @@ function default_1(app, opts) {
|
|
|
142
145
|
if (id === undefined) {
|
|
143
146
|
return;
|
|
144
147
|
}
|
|
145
|
-
if (
|
|
148
|
+
if (isTextEditable(node)) {
|
|
146
149
|
inputValues.set(id, node.value);
|
|
147
150
|
sendInputValue(id, node);
|
|
148
151
|
return;
|
package/cjs/modules/mouse.js
CHANGED
|
@@ -85,7 +85,7 @@ function default_1(app) {
|
|
|
85
85
|
tag === 'LI' ||
|
|
86
86
|
target.onclick != null ||
|
|
87
87
|
target.getAttribute('role') === 'button') {
|
|
88
|
-
const label = app.
|
|
88
|
+
const label = app.sanitizer.getInnerTextSecure(target);
|
|
89
89
|
return (0, utils_js_1.normSpaces)(label).slice(0, 100);
|
|
90
90
|
}
|
|
91
91
|
return '';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface Window extends globalThis.Window {
|
|
2
|
+
HTMLInputElement: typeof HTMLInputElement;
|
|
3
|
+
HTMLLinkElement: typeof HTMLLinkElement;
|
|
4
|
+
HTMLStyleElement: typeof HTMLStyleElement;
|
|
5
|
+
SVGStyleElement: typeof SVGStyleElement;
|
|
6
|
+
HTMLIFrameElement: typeof HTMLIFrameElement;
|
|
7
|
+
Text: typeof Text;
|
|
8
|
+
Element: typeof Element;
|
|
9
|
+
ShadowRoot: typeof ShadowRoot;
|
|
10
|
+
}
|
|
11
|
+
declare type WindowConstructor = Document | Element | Text | ShadowRoot | HTMLInputElement | HTMLLinkElement | HTMLStyleElement | HTMLIFrameElement;
|
|
12
|
+
declare type Constructor<T> = {
|
|
13
|
+
new (...args: any[]): T;
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function isInstance<T extends WindowConstructor>(node: Node, constr: Constructor<T>): node is T;
|
|
17
|
+
export declare function inDocument(node: Node): boolean;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// TODO: we need a type expert here so we won't have to ignore the lines
|
|
2
|
+
// TODO: use it everywhere (static function; export from which file? <-- global Window typing required)
|
|
3
|
+
export function isInstance(node, constr) {
|
|
4
|
+
const doc = node.ownerDocument;
|
|
5
|
+
if (!doc) { // null if Document
|
|
6
|
+
return constr.name === 'Document';
|
|
7
|
+
}
|
|
8
|
+
let context =
|
|
9
|
+
// @ts-ignore (for EI, Safary)
|
|
10
|
+
doc.parentWindow ||
|
|
11
|
+
doc.defaultView; // TODO: smart global typing for Window object
|
|
12
|
+
while (context.parent && context.parent !== context) {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
if (node instanceof context[constr.name]) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
context = context.parent;
|
|
19
|
+
}
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
return node instanceof context[constr.name];
|
|
22
|
+
}
|
|
23
|
+
export function inDocument(node) {
|
|
24
|
+
const doc = node.ownerDocument;
|
|
25
|
+
if (!doc) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (doc.contains(node)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
let context =
|
|
32
|
+
// @ts-ignore (for EI, Safary)
|
|
33
|
+
doc.parentWindow ||
|
|
34
|
+
doc.defaultView;
|
|
35
|
+
while (context.parent && context.parent !== context) {
|
|
36
|
+
if (context.document.contains(node)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
context = context.parent;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
package/lib/app/index.d.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import Message from "../messages/message.js";
|
|
2
2
|
import Nodes from "./nodes.js";
|
|
3
|
-
import
|
|
3
|
+
import Sanitizer from "./sanitizer.js";
|
|
4
4
|
import Ticker from "./ticker.js";
|
|
5
|
-
import type { Options as ObserverOptions } from "./observer.js";
|
|
5
|
+
import type { Options as ObserverOptions } from "./observer/top_observer.js";
|
|
6
|
+
import type { Options as SanitizerOptions } from "./sanitizer.js";
|
|
6
7
|
import type { Options as WebworkerOptions } from "../messages/webworker.js";
|
|
7
8
|
export interface OnStartInfo {
|
|
8
9
|
sessionID: string;
|
|
9
10
|
sessionToken: string;
|
|
10
11
|
userUUID: string;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
declare type AppOptions = {
|
|
13
14
|
revID: string;
|
|
14
15
|
node_id: string;
|
|
15
16
|
session_token_key: string;
|
|
16
17
|
session_pageno_key: string;
|
|
18
|
+
session_reset_key: string;
|
|
17
19
|
local_uuid_key: string;
|
|
18
20
|
ingestPoint: string;
|
|
19
21
|
resourceBaseHref: string | null;
|
|
@@ -21,7 +23,8 @@ export declare type Options = {
|
|
|
21
23
|
__debug_report_edp: string | null;
|
|
22
24
|
__debug_log: boolean;
|
|
23
25
|
onStart?: (info: OnStartInfo) => void;
|
|
24
|
-
} &
|
|
26
|
+
} & WebworkerOptions;
|
|
27
|
+
export declare type Options = AppOptions & ObserverOptions & SanitizerOptions;
|
|
25
28
|
declare type Callback = () => void;
|
|
26
29
|
declare type CommitCallback = (messages: Array<Message>) => void;
|
|
27
30
|
export declare const DEFAULT_INGEST_POINT = "https://api.openreplay.com/ingest";
|
|
@@ -29,8 +32,9 @@ export default class App {
|
|
|
29
32
|
readonly nodes: Nodes;
|
|
30
33
|
readonly ticker: Ticker;
|
|
31
34
|
readonly projectKey: string;
|
|
35
|
+
readonly sanitizer: Sanitizer;
|
|
32
36
|
private readonly messages;
|
|
33
|
-
readonly observer
|
|
37
|
+
private readonly observer;
|
|
34
38
|
private readonly startCallbacks;
|
|
35
39
|
private readonly stopCallbacks;
|
|
36
40
|
private readonly commitCallbacks;
|
|
@@ -40,7 +44,7 @@ export default class App {
|
|
|
40
44
|
private isActive;
|
|
41
45
|
private version;
|
|
42
46
|
private readonly worker?;
|
|
43
|
-
constructor(projectKey: string, sessionToken: string | null | undefined,
|
|
47
|
+
constructor(projectKey: string, sessionToken: string | null | undefined, options: Partial<Options>);
|
|
44
48
|
private _debug;
|
|
45
49
|
send(message: Message, urgent?: boolean): void;
|
|
46
50
|
private commit;
|
|
@@ -58,6 +62,7 @@ export default class App {
|
|
|
58
62
|
resolveResourceURL(resourceURL: string): string;
|
|
59
63
|
isServiceURL(url: string): boolean;
|
|
60
64
|
active(): boolean;
|
|
65
|
+
resetNextPageSession(flag: boolean): void;
|
|
61
66
|
private _start;
|
|
62
67
|
start(reset?: boolean): Promise<OnStartInfo>;
|
|
63
68
|
stop(): void;
|
package/lib/app/index.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { timestamp, log, warn } from "../utils.js";
|
|
2
2
|
import { Timestamp } from "../messages/index.js";
|
|
3
3
|
import Nodes from "./nodes.js";
|
|
4
|
-
import Observer from "./observer.js";
|
|
4
|
+
import Observer from "./observer/top_observer.js";
|
|
5
|
+
import Sanitizer from "./sanitizer.js";
|
|
5
6
|
import Ticker from "./ticker.js";
|
|
6
7
|
import { deviceMemory, jsHeapSizeLimit } from "../modules/performance.js";
|
|
7
8
|
// TODO: use backendHost only
|
|
8
9
|
export const DEFAULT_INGEST_POINT = 'https://api.openreplay.com/ingest';
|
|
9
10
|
export default class App {
|
|
10
|
-
constructor(projectKey, sessionToken,
|
|
11
|
+
constructor(projectKey, sessionToken, options) {
|
|
11
12
|
this.messages = [];
|
|
12
13
|
this.startCallbacks = [];
|
|
13
14
|
this.stopCallbacks = [];
|
|
14
15
|
this.commitCallbacks = [];
|
|
15
16
|
this._sessionID = null;
|
|
16
17
|
this.isActive = false;
|
|
17
|
-
this.version = '3.4.
|
|
18
|
+
this.version = '3.4.17-beta.0';
|
|
18
19
|
this.projectKey = projectKey;
|
|
19
20
|
this.options = Object.assign({
|
|
20
21
|
revID: '',
|
|
21
22
|
node_id: '__openreplay_id',
|
|
22
23
|
session_token_key: '__openreplay_token',
|
|
23
24
|
session_pageno_key: '__openreplay_pageno',
|
|
25
|
+
session_reset_key: '__openreplay_reset',
|
|
24
26
|
local_uuid_key: '__openreplay_uuid',
|
|
25
27
|
ingestPoint: DEFAULT_INGEST_POINT,
|
|
26
28
|
resourceBaseHref: null,
|
|
27
29
|
__is_snippet: false,
|
|
28
30
|
__debug_report_edp: null,
|
|
29
31
|
__debug_log: false,
|
|
30
|
-
|
|
31
|
-
obscureTextNumbers: false,
|
|
32
|
-
captureIFrames: false,
|
|
33
|
-
}, opts);
|
|
32
|
+
}, options);
|
|
34
33
|
if (sessionToken != null) {
|
|
35
34
|
sessionStorage.setItem(this.options.session_token_key, sessionToken);
|
|
36
35
|
}
|
|
37
36
|
this.revID = this.options.revID;
|
|
37
|
+
this.sanitizer = new Sanitizer(this, options);
|
|
38
38
|
this.nodes = new Nodes(this.options.node_id);
|
|
39
|
-
this.observer = new Observer(this,
|
|
39
|
+
this.observer = new Observer(this, options);
|
|
40
40
|
this.ticker = new Ticker(this);
|
|
41
41
|
this.ticker.attach(() => this.commit());
|
|
42
42
|
try {
|
|
@@ -178,6 +178,14 @@ export default class App {
|
|
|
178
178
|
active() {
|
|
179
179
|
return this.isActive;
|
|
180
180
|
}
|
|
181
|
+
resetNextPageSession(flag) {
|
|
182
|
+
if (flag) {
|
|
183
|
+
sessionStorage.setItem(this.options.session_reset_key, 't');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
sessionStorage.removeItem(this.options.session_reset_key);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
181
189
|
_start(reset) {
|
|
182
190
|
if (!this.isActive) {
|
|
183
191
|
if (!this.worker) {
|
|
@@ -200,18 +208,20 @@ export default class App {
|
|
|
200
208
|
connAttemptGap: this.options.connAttemptGap,
|
|
201
209
|
};
|
|
202
210
|
this.worker.postMessage(messageData); // brings delay of 10th ms?
|
|
203
|
-
let token = sessionStorage.getItem(this.options.session_token_key)
|
|
204
|
-
const tokenIsActive = localStorage.getItem("__or_at_" + token)
|
|
205
|
-
if (tokenIsActive) {
|
|
206
|
-
|
|
207
|
-
}
|
|
211
|
+
// let token = sessionStorage.getItem(this.options.session_token_key)
|
|
212
|
+
// const tokenIsActive = localStorage.getItem("__or_at_" + token)
|
|
213
|
+
// if (tokenIsActive) {
|
|
214
|
+
// token = null
|
|
215
|
+
// }
|
|
216
|
+
const sReset = sessionStorage.getItem(this.options.session_reset_key);
|
|
217
|
+
sessionStorage.removeItem(this.options.session_reset_key);
|
|
208
218
|
return window.fetch(this.options.ingestPoint + '/v1/web/start', {
|
|
209
219
|
method: 'POST',
|
|
210
220
|
headers: {
|
|
211
221
|
'Content-Type': 'application/json',
|
|
212
222
|
},
|
|
213
223
|
body: JSON.stringify({
|
|
214
|
-
token,
|
|
224
|
+
token: sessionStorage.getItem(this.options.session_token_key),
|
|
215
225
|
userUUID: localStorage.getItem(this.options.local_uuid_key),
|
|
216
226
|
projectKey: this.projectKey,
|
|
217
227
|
revID: this.revID,
|
|
@@ -220,7 +230,7 @@ export default class App {
|
|
|
220
230
|
isSnippet: this.options.__is_snippet,
|
|
221
231
|
deviceMemory,
|
|
222
232
|
jsHeapSizeLimit,
|
|
223
|
-
reset,
|
|
233
|
+
reset: reset || sReset !== null,
|
|
224
234
|
}),
|
|
225
235
|
})
|
|
226
236
|
.then(r => {
|
|
@@ -242,13 +252,13 @@ export default class App {
|
|
|
242
252
|
}
|
|
243
253
|
sessionStorage.setItem(this.options.session_token_key, token);
|
|
244
254
|
localStorage.setItem(this.options.local_uuid_key, userUUID);
|
|
245
|
-
localStorage.setItem("__or_at_" + token, "true")
|
|
246
|
-
this.attachEventListener(window, 'beforeunload', ()
|
|
247
|
-
|
|
248
|
-
}, false);
|
|
249
|
-
this.attachEventListener(window, 'pagehide', ()
|
|
250
|
-
|
|
251
|
-
}, false);
|
|
255
|
+
// localStorage.setItem("__or_at_" + token, "true")
|
|
256
|
+
// this.attachEventListener(window, 'beforeunload', ()=>{
|
|
257
|
+
// localStorage.removeItem("__or_at_" + token)
|
|
258
|
+
// }, false);
|
|
259
|
+
// this.attachEventListener(window, 'pagehide', ()=>{
|
|
260
|
+
// localStorage.removeItem("__or_at_" + token)
|
|
261
|
+
// }, false);
|
|
252
262
|
if (typeof sessionID === 'string') {
|
|
253
263
|
this._sessionID = sessionID;
|
|
254
264
|
}
|
|
@@ -298,6 +308,7 @@ export default class App {
|
|
|
298
308
|
if (this.worker) {
|
|
299
309
|
this.worker.postMessage("stop");
|
|
300
310
|
}
|
|
311
|
+
this.sanitizer.clear();
|
|
301
312
|
this.observer.disconnect();
|
|
302
313
|
this.nodes.clear();
|
|
303
314
|
this.ticker.stop();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Observer from "./observer.js";
|
|
2
|
+
import { CreateIFrameDocument } from "../../messages/index.js";
|
|
3
|
+
export default class IFrameObserver extends Observer {
|
|
4
|
+
observe(iframe) {
|
|
5
|
+
const doc = iframe.contentDocument;
|
|
6
|
+
const hostID = this.app.nodes.getID(iframe);
|
|
7
|
+
if (!doc || hostID === undefined) {
|
|
8
|
+
return;
|
|
9
|
+
} //log TODO common app.logger
|
|
10
|
+
// Have to observe document, because the inner <html> might be changed
|
|
11
|
+
this.observeRoot(doc, (docID) => {
|
|
12
|
+
if (docID === undefined) {
|
|
13
|
+
console.log("OpenReplay: Iframe document not bound");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.app.send(CreateIFrameDocument(hostID, docID));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import App from "../index.js";
|
|
2
|
+
export default abstract class Observer {
|
|
3
|
+
protected readonly app: App;
|
|
4
|
+
protected readonly context: Window;
|
|
5
|
+
private readonly observer;
|
|
6
|
+
private readonly commited;
|
|
7
|
+
private readonly recents;
|
|
8
|
+
private readonly myNodes;
|
|
9
|
+
private readonly indexes;
|
|
10
|
+
private readonly attributesList;
|
|
11
|
+
private readonly textSet;
|
|
12
|
+
private readonly inUpperContext;
|
|
13
|
+
constructor(app: App, context?: Window);
|
|
14
|
+
private clear;
|
|
15
|
+
private sendNodeAttribute;
|
|
16
|
+
private sendNodeData;
|
|
17
|
+
private bindNode;
|
|
18
|
+
private bindTree;
|
|
19
|
+
private unbindNode;
|
|
20
|
+
private _commitNode;
|
|
21
|
+
private commitNode;
|
|
22
|
+
private commitNodes;
|
|
23
|
+
protected observeRoot(node: Node, beforeCommit: (id?: number) => unknown, nodeToBind?: Node): void;
|
|
24
|
+
disconnect(): void;
|
|
25
|
+
}
|