diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 4765ac2c5..3692baa66 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.0.5", + "version": "3.1.0", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index 44efd9f84..6e862e0bb 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -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) => 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 = []; private readonly observer: Observer; - private readonly startCallbacks: Array; - private readonly stopCallbacks: Array; + private readonly startCallbacks: Array = []; + private readonly stopCallbacks: Array = []; + private readonly commitCallbacks: Array = []; 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 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 => { diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index d6c8481df..ff90b51e5 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -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(fn: (app: App | null) => T): T { - return fn(this.app); + use(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 { diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 50c6fde39..366a7d3fe 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -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) { diff --git a/tracker/tracker/src/webworker/transformer.js.temp b/tracker/tracker/src/webworker/transformer.js.temp new file mode 100644 index 000000000..cf80d681b --- /dev/null +++ b/tracker/tracker/src/webworker/transformer.js.temp @@ -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 + } + } + } + +} \ No newline at end of file