diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 1ea5ac2bb..00bf42046 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.2", + "version": "3.0.3", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index 91dc20322..3fe006ce8 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -174,7 +174,24 @@ export default class App { _start(reset: boolean): void { // TODO: return a promise instead of onStart handling if (!this.isActive) { this.isActive = true; + if (!this.worker) { + throw new Error("Stranger things: no worker found"); + } + + let pageNo: number = 0; + const pageNoStr = sessionStorage.getItem(this.options.session_pageno_key); + if (pageNoStr != null) { + pageNo = parseInt(pageNoStr); + pageNo++; + } + sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString()); const startTimestamp = timestamp(); + + this.worker.postMessage({ ingestPoint: this.options.ingestPoint, pageNo, startTimestamp }); // 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: { @@ -196,7 +213,7 @@ export default class App { .then(r => { if (r.status === 200) { return r.json() - } else { // TODO: handle canceling + } else { // TODO: handle canceling && 403 throw new Error("Server error"); } }) @@ -206,26 +223,14 @@ export default class App { typeof userUUID !== 'string') { throw new Error("Incorrect server responce"); } - if (!this.worker) { - throw new Error("Stranger things: worker is not started"); - } sessionStorage.setItem(this.options.session_token_key, token); localStorage.setItem(this.options.local_uuid_key, userUUID); - - let pageNo: number = 0; - const pageNoStr = sessionStorage.getItem(this.options.session_pageno_key); - if (pageNoStr != null) { - pageNo = parseInt(pageNoStr); - pageNo++; + if (!this.worker) { + throw new Error("Stranger things: no worker found after start request"); } - sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString()); - - this.worker.postMessage({ ingestPoint: this.options.ingestPoint, token, pageNo, startTimestamp }); - this.observer.observe(); - this.startCallbacks.forEach((cb) => cb()); - this.ticker.start(); - log("OpenReplay tracking started."); + this.worker.postMessage({ token }); + log("OpenReplay tracking started."); if (typeof this.options.onStart === 'function') { this.options.onStart({ sessionToken: token, userUUID, sessionID: token /* back compat (depricated) */ }); } @@ -254,7 +259,7 @@ export default class App { if (this.isActive) { try { if (this.worker) { - this.worker.postMessage(null); + this.worker.postMessage("stop"); } this.observer.disconnect(); this.nodes.clear(); diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index def27c55a..d6c8481df 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -85,6 +85,8 @@ export default class API { ? null : new App(options.projectKey, options.sessionToken, options); if (this.app !== null) { + Viewport(this.app); + CSSRules(this.app); Connection(this.app); Console(this.app, options); Exception(this.app, options); @@ -94,9 +96,7 @@ export default class API { Timing(this.app, options); Performance(this.app); Scroll(this.app); - Viewport(this.app); Longtasks(this.app); - CSSRules(this.app); (window as any).__OPENREPLAY__ = (window as any).__OPENREPLAY__ || this; } else { console.log("OpenReplay: broeser doesn't support API required for tracking.") diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index 2e9d3b0e0..f61b7bca5 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -1,4 +1,4 @@ -import { classes, BatchMeta, Timestamp, SetPageVisibility } from '../messages'; +import { classes, BatchMeta, Timestamp, SetPageVisibility, CreateDocument } from '../messages'; import Message from '../messages/message'; import Writer from '../messages/writer'; @@ -12,8 +12,11 @@ let ingestPoint: string = ""; let token: string = ""; let pageNo: number = 0; let timestamp: number = 0; +let timeAdjustment: number = 0; let nextIndex: number = 0; -let isEmpty: boolean = true; +// TODO: clear logic: isEmpty here means presense of BatchMeta but absence of other messages +// BatchWriter should be abstracted +let isEmpty: boolean = true; function writeBatchMeta(): boolean { // TODO: move to encoder return new BatchMeta(pageNo, nextIndex, timestamp).encode(writer) @@ -67,7 +70,7 @@ function sendBatch(batch: Uint8Array):void { } function send(): void { - if (isEmpty || ingestPoint === "") { + if (isEmpty || token === "" || ingestPoint === "") { return; } const batch = writer.flush(); @@ -82,29 +85,45 @@ function send(): void { } function reset() { + ingestPoint = "" + token = "" clearInterval(sendIntervalID); writer.reset(); } let restartTimeoutID: ReturnType; +function hasTimestamp(msg: any): msg is { timestamp: number } { + return typeof msg === 'object' && typeof msg.timestamp === 'number'; +} + self.onmessage = ({ data }: MessageEvent) => { if (data === null) { send(); return; } - if (!Array.isArray(data)) { + if (data === "stop") { + send(); reset(); - ingestPoint = data.ingestPoint; - token = data.token; - pageNo = data.pageNo; - timestamp = data.startTimestamp; - writeBatchMeta(); - sendIntervalID = setInterval(send, SEND_INTERVAL); + return; + } + if (!Array.isArray(data)) { + ingestPoint = data.ingestPoint || ingestPoint; + token = data.token || token; + pageNo = data.pageNo || pageNo; + timestamp = data.startTimestamp || timestamp; + timeAdjustment = data.timeAdjustment || timeAdjustment; + if (writer.isEmpty()) { + writeBatchMeta(); + } + if (sendIntervalID == null) { + sendIntervalID = setInterval(send, SEND_INTERVAL); + } return; } data.forEach((data: any) => { const message: Message = new (classes.get(data._id))(); + Object.assign(message, data); if (message instanceof Timestamp) { timestamp = (message).timestamp; @@ -116,7 +135,6 @@ self.onmessage = ({ data }: MessageEvent) => { } } - Object.assign(message, data); writer.checkpoint(); nextIndex++; if (message.encode(writer)) {