feat (tracker): 3.1.0 - updated app interface for the assist plugin & enable url-based cssRule
This commit is contained in:
parent
9d16b1feae
commit
6d8d46fcd5
5 changed files with 66 additions and 25 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@openreplay/tracker",
|
||||
"description": "The OpenReplay tracker main package",
|
||||
"version": "3.0.5",
|
||||
"version": "3.1.0",
|
||||
"keywords": [
|
||||
"logging",
|
||||
"replay"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { deviceMemory, jsHeapSizeLimit } from '../modules/performance';
|
|||
|
||||
import type { Options as ObserverOptions } from './observer';
|
||||
|
||||
import type { Options as WebworkerOptions, MessageData } from '../../webworker/types';
|
||||
import type { Options as WebworkerOptions, WorkerMessageData } from '../../messages/webworker';
|
||||
|
||||
export type Options = {
|
||||
revID: string;
|
||||
|
|
@ -23,19 +23,22 @@ export type Options = {
|
|||
} & ObserverOptions & WebworkerOptions;
|
||||
|
||||
type Callback = () => void;
|
||||
type CommitCallback = (messages: Array<Message>) => void;
|
||||
|
||||
export const DEFAULT_INGEST_POINT = 'https://ingest.openreplay.com';
|
||||
|
||||
export default class App {
|
||||
readonly nodes: Nodes;
|
||||
readonly ticker: Ticker;
|
||||
readonly projectKey: string;
|
||||
private readonly messages: Array<Message> = [];
|
||||
private readonly observer: Observer;
|
||||
private readonly startCallbacks: Array<Callback>;
|
||||
private readonly stopCallbacks: Array<Callback>;
|
||||
private readonly startCallbacks: Array<Callback> = [];
|
||||
private readonly stopCallbacks: Array<Callback> = [];
|
||||
private readonly commitCallbacks: Array<CommitCallback> = [];
|
||||
private readonly options: Options;
|
||||
private readonly projectKey: string;
|
||||
private readonly revID: string;
|
||||
private _sessionID: string | null = null;
|
||||
private isActive = false;
|
||||
private version = 'TRACKER_VERSION';
|
||||
private readonly worker?: Worker;
|
||||
|
|
@ -67,8 +70,6 @@ export default class App {
|
|||
this.observer = new Observer(this, this.options);
|
||||
this.ticker = new Ticker(this);
|
||||
this.ticker.attach(() => this.commit());
|
||||
this.startCallbacks = [];
|
||||
this.stopCallbacks = [];
|
||||
try {
|
||||
this.worker = new Worker(
|
||||
URL.createObjectURL(
|
||||
|
|
@ -94,8 +95,10 @@ export default class App {
|
|||
this.worker.postMessage(null);
|
||||
}
|
||||
}
|
||||
// TODO: keep better tactics, discard others (look https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon)
|
||||
this.attachEventListener(window, 'beforeunload', alertWorker, false);
|
||||
this.attachEventListener(document, 'mouseleave', alertWorker, false, false);
|
||||
this.attachEventListener(document, 'visibilitychange', alertWorker, false);
|
||||
} catch (e) { /* TODO: send report */}
|
||||
}
|
||||
send(message: Message, urgent = false): void {
|
||||
|
|
@ -111,10 +114,16 @@ export default class App {
|
|||
if (this.worker && this.messages.length) {
|
||||
this.messages.unshift(new Timestamp(timestamp()));
|
||||
this.worker.postMessage(this.messages);
|
||||
this.commitCallbacks.forEach(cb => cb(this.messages));
|
||||
this.messages.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
addCommitCallback(cb: CommitCallback): void {
|
||||
this.commitCallbacks.push(cb)
|
||||
}
|
||||
|
||||
|
||||
safe<T extends (...args: any[]) => void>(fn: T): T {
|
||||
const app = this;
|
||||
return function (this: any, ...args: any) {
|
||||
|
|
@ -161,9 +170,11 @@ export default class App {
|
|||
return token;
|
||||
}
|
||||
}
|
||||
// @Depricated; for the old fetch-plugin versions
|
||||
sessionID(): string | undefined {
|
||||
return this.getSessionToken();
|
||||
getSessionID(): string | undefined {
|
||||
return this._sessionID || undefined;
|
||||
}
|
||||
getHost(): string {
|
||||
return new URL(this.options.ingestPoint).host;
|
||||
}
|
||||
|
||||
isServiceURL(url: string): boolean {
|
||||
|
|
@ -189,7 +200,7 @@ export default class App {
|
|||
sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
|
||||
const startTimestamp = timestamp();
|
||||
|
||||
const messageData: MessageData = {
|
||||
const messageData: WorkerMessageData = {
|
||||
ingestPoint: this.options.ingestPoint,
|
||||
pageNo,
|
||||
startTimestamp,
|
||||
|
|
@ -197,10 +208,6 @@ export default class App {
|
|||
connAttemptGap: this.options.connAttemptGap,
|
||||
}
|
||||
this.worker.postMessage(messageData); // brings delay of 10th ms?
|
||||
this.observer.observe();
|
||||
this.startCallbacks.forEach((cb) => cb());
|
||||
this.ticker.start();
|
||||
|
||||
window.fetch(this.options.ingestPoint + '/v1/web/start', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -227,21 +234,27 @@ export default class App {
|
|||
}
|
||||
})
|
||||
.then(r => {
|
||||
const { token, userUUID } = r;
|
||||
const { token, userUUID, sessionID } = r;
|
||||
if (typeof token !== 'string' ||
|
||||
typeof userUUID !== 'string') {
|
||||
throw new Error("Incorrect server responce");
|
||||
}
|
||||
sessionStorage.setItem(this.options.session_token_key, token);
|
||||
localStorage.setItem(this.options.local_uuid_key, userUUID);
|
||||
if (typeof sessionID === 'string') {
|
||||
this._sessionID = sessionID;
|
||||
}
|
||||
if (!this.worker) {
|
||||
throw new Error("Stranger things: no worker found after start request");
|
||||
}
|
||||
this.worker.postMessage({ token });
|
||||
this.observer.observe();
|
||||
this.startCallbacks.forEach((cb) => cb());
|
||||
this.ticker.start();
|
||||
|
||||
log("OpenReplay tracking started.");
|
||||
if (typeof this.options.onStart === 'function') {
|
||||
this.options.onStart({ sessionToken: token, userUUID, sessionID: token /* back compat (depricated) */ });
|
||||
this.options.onStart({ sessionToken: token, userUUID, sessionID });
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ function processOptions(obj: any): obj is Options {
|
|||
|
||||
export default class API {
|
||||
private readonly app: App | null = null;
|
||||
constructor(options: Options) {
|
||||
constructor(private readonly options: Options) {
|
||||
if (!IN_BROWSER || !processOptions(options)) {
|
||||
return;
|
||||
}
|
||||
if (!options.__DISABLE_SECURE_MODE && location.protocol !== 'https:') {
|
||||
console.error("OpenReplay: Your website must be publicly accessible and running on SSL in order for OpenReplay to properly capture and replay the user session.")
|
||||
console.error("OpenReplay: Your website must be publicly accessible and running on SSL in order for OpenReplay to properly capture and replay the user session. You can disable this check by setting `__DISABLE_SECURE_MODE` option to `true` if you are testing in localhost. Keep in mind, that asset files on a local machine are not available to the outside world. This might affect tracking if you use css files.")
|
||||
return;
|
||||
}
|
||||
const doNotTrack = options.respectDoNotTrack && (navigator.doNotTrack == '1' || window.doNotTrack == '1');
|
||||
|
|
@ -114,8 +114,8 @@ export default class API {
|
|||
}
|
||||
}
|
||||
|
||||
use<T>(fn: (app: App | null) => T): T {
|
||||
return fn(this.app);
|
||||
use<T>(fn: (app: App | null, options?: Options) => T): T {
|
||||
return fn(this.app, this.options);
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
|
|
@ -152,9 +152,15 @@ export default class API {
|
|||
}
|
||||
return this.app.getSessionToken();
|
||||
}
|
||||
getSessionID(): string | null | undefined {
|
||||
if (this.app === null) {
|
||||
return null;
|
||||
}
|
||||
return this.app.getSessionID();
|
||||
}
|
||||
sessionID(): string | null | undefined {
|
||||
depricationWarn("'sessionID' method", "'getSessionToken' method", "/")
|
||||
return this.getSessionToken();
|
||||
depricationWarn("'sessionID' method", "'getSessionID' method", "/");
|
||||
return this.getSessionID();
|
||||
}
|
||||
|
||||
setUserID(id: string): void {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import App from '../app';
|
||||
import { CSSInsertRule, CSSDeleteRule, TechnicalInfo } from '../../messages';
|
||||
import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../../messages';
|
||||
import { getBaseURI } from '../utils';
|
||||
|
||||
export default function(app: App | null) {
|
||||
if (app === null) {
|
||||
|
|
@ -13,7 +14,7 @@ export default function(app: App | null) {
|
|||
const processOperation = app.safe(
|
||||
(stylesheet: CSSStyleSheet, index: number, rule?: string) => {
|
||||
const sendMessage = typeof rule === 'string'
|
||||
? (nodeID: number) => app.send(new CSSInsertRule(nodeID, rule, index))
|
||||
? (nodeID: number) => app.send(new CSSInsertRuleURLBased(nodeID, rule, index, getBaseURI()))
|
||||
: (nodeID: number) => app.send(new CSSDeleteRule(nodeID, index));
|
||||
// TODO: Extend messages to maintain nested rules (CSSGroupingRule prototype, as well as CSSKeyframesRule)
|
||||
if (stylesheet.ownerNode == null) {
|
||||
|
|
|
|||
21
tracker/tracker/src/webworker/transformer.js.temp
Normal file
21
tracker/tracker/src/webworker/transformer.js.temp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import Message from '../messages/message';
|
||||
|
||||
|
||||
|
||||
|
||||
class MessageTransformer {
|
||||
private urlRewriter?: URLRewriter
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
transform(m: Message): Message {
|
||||
if (m instanceof SetNodeAttribute) {
|
||||
if (m.name == "src" || m.name == "href") {
|
||||
sendAssetForCache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue