From 64d481aa4c980bd16845584d5422530a2ea3acd2 Mon Sep 17 00:00:00 2001 From: Alex Kaminskii Date: Tue, 2 Aug 2022 16:37:30 +0200 Subject: [PATCH] feat(tracker): update message schema with BatchMetadata; separate message-related responsibilities; add message size --- mobs/messages.rb | 9 +- ...tracker~tracker~src~common~messages.ts.erb | 34 +- ...acker~tracker~src~main~app~messages.ts.erb | 15 + ...racker~src~webworker~MessageEncoder.ts.erb | 20 + .../common/{webworker.ts => interaction.ts} | 5 +- tracker/tracker/src/common/messages.ts | 1216 +++++------------ tracker/tracker/src/common/types.ts | 10 - tracker/tracker/src/main/app/index.ts | 13 +- tracker/tracker/src/main/app/messages.ts | 334 +++++ .../src/main/app/observer/iframe_observer.ts | 2 +- .../tracker/src/main/app/observer/observer.ts | 26 +- .../main/app/observer/shadow_root_observer.ts | 2 +- .../src/main/app/observer/top_observer.ts | 4 +- tracker/tracker/src/main/app/session.ts | 2 - tracker/tracker/src/main/index.ts | 16 +- .../tracker/src/main/modules/connection.ts | 7 +- tracker/tracker/src/main/modules/console.ts | 4 +- tracker/tracker/src/main/modules/cssrules.ts | 8 +- tracker/tracker/src/main/modules/exception.ts | 10 +- tracker/tracker/src/main/modules/img.ts | 22 +- tracker/tracker/src/main/modules/input.ts | 10 +- tracker/tracker/src/main/modules/longtasks.ts | 4 +- tracker/tracker/src/main/modules/mouse.ts | 6 +- .../tracker/src/main/modules/performance.ts | 4 +- tracker/tracker/src/main/modules/scroll.ts | 6 +- tracker/tracker/src/main/modules/timing.ts | 8 +- tracker/tracker/src/main/modules/viewport.ts | 8 +- tracker/tracker/src/webworker/BatchWriter.ts | 58 +- .../tracker/src/webworker/MessageEncoder.ts | 264 ++++ ...PrimitiveWriter.ts => PrimitiveEncoder.ts} | 12 +- tracker/tracker/src/webworker/index.ts | 15 +- 31 files changed, 1104 insertions(+), 1050 deletions(-) create mode 100644 mobs/templates/tracker~tracker~src~main~app~messages.ts.erb create mode 100644 mobs/templates/tracker~tracker~src~webworker~MessageEncoder.ts.erb rename tracker/tracker/src/common/{webworker.ts => interaction.ts} (83%) delete mode 100644 tracker/tracker/src/common/types.ts create mode 100644 tracker/tracker/src/main/app/messages.ts create mode 100644 tracker/tracker/src/webworker/MessageEncoder.ts rename tracker/tracker/src/webworker/{PrimitiveWriter.ts => PrimitiveEncoder.ts} (93%) diff --git a/mobs/messages.rb b/mobs/messages.rb index e50d02be4..f1c5ee5cb 100644 --- a/mobs/messages.rb +++ b/mobs/messages.rb @@ -1,6 +1,6 @@ # Special one for Batch Meta. Message id could define the version -# Depricated since tracker 3.6.0 -message 80, 'BatchMeta', :replayer => false do +# Depricated since tracker 3.6.0 in favor of BatchMetadata +message 80, 'BatchMeta', :js => false, :replayer => false do uint 'PageNo' uint 'FirstIndex' int 'Timestamp' @@ -98,7 +98,6 @@ message 14, 'SetNodeData' do uint 'ID' string 'Data' end -# Depricated starting from 5.5.11 in favor of SetStyleData message 15, 'SetCSSData', :js => false do uint 'ID' string 'Data' @@ -400,10 +399,6 @@ message 64, 'CustomIssue', :replayer => false do string 'Name' string 'Payload' end -# Since 5.6.6; only for websocket (might be probably replaced with ws.close()) -# Depricated -message 65, 'PageClose', :replayer => false do -end message 66, 'AssetCache', :replayer => false, :js => false do string 'URL' end diff --git a/mobs/templates/tracker~tracker~src~common~messages.ts.erb b/mobs/templates/tracker~tracker~src~common~messages.ts.erb index 3b3849370..51eaeb042 100644 --- a/mobs/templates/tracker~tracker~src~common~messages.ts.erb +++ b/mobs/templates/tracker~tracker~src~common~messages.ts.erb @@ -1,31 +1,15 @@ // Auto-generated, do not edit -import type { Writer, Message }from "./types.js"; -export default Message -function bindNew( - Class: C & { new(...args: A): T } -): C & ((...args: A) => T) { - function _Class(...args: A) { - return new Class(...args); - } - _Class.prototype = Class.prototype; - return T)>_Class; +export enum Type { + <%= $messages.select { |msg| msg.js }.map { |msg| "#{ msg.name } = #{ msg.id }," }.join "\n " %> } -export const classes: Map = new Map(); - <% $messages.select { |msg| msg.js }.each do |msg| %> -class _<%= msg.name %> implements Message { - readonly _id: number = <%= msg.id %>; - constructor( - <%= msg.attributes.map { |attr| "public #{attr.name.first_lower}: #{attr.type_js}" }.join ",\n " %> - ) {} - encode(writer: Writer): boolean { - return writer.uint(<%= msg.id %>)<%= " &&" if msg.attributes.length() > 0 %> - <%= msg.attributes.map { |attr| "writer.#{attr.type}(this.#{attr.name.first_lower})" }.join " &&\n " %>; - } -} -export const <%= msg.name %> = bindNew(_<%= msg.name %>); -classes.set(<%= msg.id %>, <%= msg.name %>); - +export type <%= msg.name %> = [ + type: Type.<%= msg.name %>, + <%= msg.attributes.map { |attr| "#{attr.name.first_lower}: #{attr.type_js}," }.join "\n " %> +] <% end %> + +type Message = <%= $messages.select { |msg| msg.js }.map { |msg| "#{msg.name}" }.join " | " %> +export default Message diff --git a/mobs/templates/tracker~tracker~src~main~app~messages.ts.erb b/mobs/templates/tracker~tracker~src~main~app~messages.ts.erb new file mode 100644 index 000000000..3993362a3 --- /dev/null +++ b/mobs/templates/tracker~tracker~src~main~app~messages.ts.erb @@ -0,0 +1,15 @@ +// Auto-generated, do not edit + +import * as Messages from '../../common/messages.js' +export { default } from '../../common/messages.js' + +<% $messages.select { |msg| msg.js }.each do |msg| %> +export function <%= msg.name %>( + <%= msg.attributes.map { |attr| "#{attr.name.first_lower}: #{attr.type_js}," }.join "\n " %> +): Messages.<%= msg.name %> { + return [ + Messages.Type.<%= msg.name %>, + <%= msg.attributes.map { |attr| "#{attr.name.first_lower}," }.join "\n " %> + ] +} +<% end %> diff --git a/mobs/templates/tracker~tracker~src~webworker~MessageEncoder.ts.erb b/mobs/templates/tracker~tracker~src~webworker~MessageEncoder.ts.erb new file mode 100644 index 000000000..0d98e99ea --- /dev/null +++ b/mobs/templates/tracker~tracker~src~webworker~MessageEncoder.ts.erb @@ -0,0 +1,20 @@ +// Auto-generated, do not edit + +import * as Messages from '../common/messages.js' +import Message from '../common/messages.js' +import PrimitiveEncoder from './PrimitiveEncoder.js' + + +export default class MessageEncoder extends PrimitiveEncoder { + encode(msg: Message): boolean { + switch(msg[0]) { + <% $messages.select { |msg| msg.js }.each do |msg| %> + case Messages.Type.<%= msg.name %>: + return <% if msg.attributes.size == 0 %> true <% else %> <%= msg.attributes.map.with_index { |attr, index| "this.#{attr.type}(msg[#{index+1}])" }.join " && " %> <% end %> + break + <% end %> + } + } + +} + diff --git a/tracker/tracker/src/common/webworker.ts b/tracker/tracker/src/common/interaction.ts similarity index 83% rename from tracker/tracker/src/common/webworker.ts rename to tracker/tracker/src/common/interaction.ts index 5abf9b8fc..d75e837fc 100644 --- a/tracker/tracker/src/common/webworker.ts +++ b/tracker/tracker/src/common/interaction.ts @@ -1,3 +1,5 @@ +import Message from './messages.js'; + export interface Options { connAttemptCount?: number; connAttemptGap?: number; @@ -8,6 +10,7 @@ type Start = { ingestPoint: string; pageNo: number; timestamp: number; + url: string; } & Options; type Auth = { @@ -16,4 +19,4 @@ type Auth = { beaconSizeLimit?: number; }; -export type WorkerMessageData = null | 'stop' | Start | Auth | Array<{ _id: number }>; +export type WorkerMessageData = null | 'stop' | Start | Auth | Array; diff --git a/tracker/tracker/src/common/messages.ts b/tracker/tracker/src/common/messages.ts index 140bcaac6..14e868325 100644 --- a/tracker/tracker/src/common/messages.ts +++ b/tracker/tracker/src/common/messages.ts @@ -1,903 +1,325 @@ // Auto-generated, do not edit -import type { Writer, Message }from "./types.js"; -export default Message -function bindNew( - Class: C & { new(...args: A): T } -): C & ((...args: A) => T) { - function _Class(...args: A) { - return new Class(...args); - } - _Class.prototype = Class.prototype; - return T)>_Class; +export enum Type { + BatchMetadata = 81, + PartitionedMessage = 82, + Timestamp = 0, + SetPageLocation = 4, + SetViewportSize = 5, + SetViewportScroll = 6, + CreateDocument = 7, + CreateElementNode = 8, + CreateTextNode = 9, + MoveNode = 10, + RemoveNode = 11, + SetNodeAttribute = 12, + RemoveNodeAttribute = 13, + SetNodeData = 14, + SetNodeScroll = 16, + SetInputTarget = 17, + SetInputValue = 18, + SetInputChecked = 19, + MouseMove = 20, + ConsoleLog = 22, + PageLoadTiming = 23, + PageRenderTiming = 24, + JSException = 25, + RawCustomEvent = 27, + UserID = 28, + UserAnonymousID = 29, + Metadata = 30, + CSSInsertRule = 37, + CSSDeleteRule = 38, + Fetch = 39, + Profiler = 40, + OTable = 41, + StateAction = 42, + Redux = 44, + Vuex = 45, + MobX = 46, + NgRx = 47, + GraphQL = 48, + PerformanceTrack = 49, + ResourceTiming = 53, + ConnectionInformation = 54, + SetPageVisibility = 55, + LongTask = 59, + SetNodeAttributeURLBased = 60, + SetCSSDataURLBased = 61, + TechnicalInfo = 63, + CustomIssue = 64, + CSSInsertRuleURLBased = 67, + MouseClick = 69, + CreateIFrameDocument = 70, } -export const classes: Map = new Map(); - - -class _BatchMeta implements Message { - readonly _id: number = 80; - constructor( - public pageNo: number, - public firstIndex: number, - public timestamp: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(80) && - writer.uint(this.pageNo) && - writer.uint(this.firstIndex) && - writer.int(this.timestamp); - } -} -export const BatchMeta = bindNew(_BatchMeta); -classes.set(80, BatchMeta); - - -class _Timestamp implements Message { - readonly _id: number = 0; - constructor( - public timestamp: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(0) && - writer.uint(this.timestamp); - } -} -export const Timestamp = bindNew(_Timestamp); -classes.set(0, Timestamp); - - -class _SetPageLocation implements Message { - readonly _id: number = 4; - constructor( - public url: string, - public referrer: string, - public navigationStart: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(4) && - writer.string(this.url) && - writer.string(this.referrer) && - writer.uint(this.navigationStart); - } -} -export const SetPageLocation = bindNew(_SetPageLocation); -classes.set(4, SetPageLocation); - - -class _SetViewportSize implements Message { - readonly _id: number = 5; - constructor( - public width: number, - public height: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(5) && - writer.uint(this.width) && - writer.uint(this.height); - } -} -export const SetViewportSize = bindNew(_SetViewportSize); -classes.set(5, SetViewportSize); - - -class _SetViewportScroll implements Message { - readonly _id: number = 6; - constructor( - public x: number, - public y: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(6) && - writer.int(this.x) && - writer.int(this.y); - } -} -export const SetViewportScroll = bindNew(_SetViewportScroll); -classes.set(6, SetViewportScroll); - - -class _CreateDocument implements Message { - readonly _id: number = 7; - constructor( - - ) {} - encode(writer: Writer): boolean { - return writer.uint(7) - ; - } -} -export const CreateDocument = bindNew(_CreateDocument); -classes.set(7, CreateDocument); - - -class _CreateElementNode implements Message { - readonly _id: number = 8; - constructor( - public id: number, - public parentID: number, - public index: number, - public tag: string, - public svg: boolean - ) {} - encode(writer: Writer): boolean { - return writer.uint(8) && - writer.uint(this.id) && - writer.uint(this.parentID) && - writer.uint(this.index) && - writer.string(this.tag) && - writer.boolean(this.svg); - } -} -export const CreateElementNode = bindNew(_CreateElementNode); -classes.set(8, CreateElementNode); - - -class _CreateTextNode implements Message { - readonly _id: number = 9; - constructor( - public id: number, - public parentID: number, - public index: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(9) && - writer.uint(this.id) && - writer.uint(this.parentID) && - writer.uint(this.index); - } -} -export const CreateTextNode = bindNew(_CreateTextNode); -classes.set(9, CreateTextNode); - - -class _MoveNode implements Message { - readonly _id: number = 10; - constructor( - public id: number, - public parentID: number, - public index: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(10) && - writer.uint(this.id) && - writer.uint(this.parentID) && - writer.uint(this.index); - } -} -export const MoveNode = bindNew(_MoveNode); -classes.set(10, MoveNode); - - -class _RemoveNode implements Message { - readonly _id: number = 11; - constructor( - public id: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(11) && - writer.uint(this.id); - } -} -export const RemoveNode = bindNew(_RemoveNode); -classes.set(11, RemoveNode); - - -class _SetNodeAttribute implements Message { - readonly _id: number = 12; - constructor( - public id: number, - public name: string, - public value: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(12) && - writer.uint(this.id) && - writer.string(this.name) && - writer.string(this.value); - } -} -export const SetNodeAttribute = bindNew(_SetNodeAttribute); -classes.set(12, SetNodeAttribute); - - -class _RemoveNodeAttribute implements Message { - readonly _id: number = 13; - constructor( - public id: number, - public name: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(13) && - writer.uint(this.id) && - writer.string(this.name); - } -} -export const RemoveNodeAttribute = bindNew(_RemoveNodeAttribute); -classes.set(13, RemoveNodeAttribute); - - -class _SetNodeData implements Message { - readonly _id: number = 14; - constructor( - public id: number, - public data: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(14) && - writer.uint(this.id) && - writer.string(this.data); - } -} -export const SetNodeData = bindNew(_SetNodeData); -classes.set(14, SetNodeData); - - -class _SetNodeScroll implements Message { - readonly _id: number = 16; - constructor( - public id: number, - public x: number, - public y: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(16) && - writer.uint(this.id) && - writer.int(this.x) && - writer.int(this.y); - } -} -export const SetNodeScroll = bindNew(_SetNodeScroll); -classes.set(16, SetNodeScroll); - - -class _SetInputTarget implements Message { - readonly _id: number = 17; - constructor( - public id: number, - public label: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(17) && - writer.uint(this.id) && - writer.string(this.label); - } -} -export const SetInputTarget = bindNew(_SetInputTarget); -classes.set(17, SetInputTarget); - - -class _SetInputValue implements Message { - readonly _id: number = 18; - constructor( - public id: number, - public value: string, - public mask: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(18) && - writer.uint(this.id) && - writer.string(this.value) && - writer.int(this.mask); - } -} -export const SetInputValue = bindNew(_SetInputValue); -classes.set(18, SetInputValue); - - -class _SetInputChecked implements Message { - readonly _id: number = 19; - constructor( - public id: number, - public checked: boolean - ) {} - encode(writer: Writer): boolean { - return writer.uint(19) && - writer.uint(this.id) && - writer.boolean(this.checked); - } -} -export const SetInputChecked = bindNew(_SetInputChecked); -classes.set(19, SetInputChecked); - - -class _MouseMove implements Message { - readonly _id: number = 20; - constructor( - public x: number, - public y: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(20) && - writer.uint(this.x) && - writer.uint(this.y); - } -} -export const MouseMove = bindNew(_MouseMove); -classes.set(20, MouseMove); - - -class _ConsoleLog implements Message { - readonly _id: number = 22; - constructor( - public level: string, - public value: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(22) && - writer.string(this.level) && - writer.string(this.value); - } -} -export const ConsoleLog = bindNew(_ConsoleLog); -classes.set(22, ConsoleLog); - - -class _PageLoadTiming implements Message { - readonly _id: number = 23; - constructor( - public requestStart: number, - public responseStart: number, - public responseEnd: number, - public domContentLoadedEventStart: number, - public domContentLoadedEventEnd: number, - public loadEventStart: number, - public loadEventEnd: number, - public firstPaint: number, - public firstContentfulPaint: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(23) && - writer.uint(this.requestStart) && - writer.uint(this.responseStart) && - writer.uint(this.responseEnd) && - writer.uint(this.domContentLoadedEventStart) && - writer.uint(this.domContentLoadedEventEnd) && - writer.uint(this.loadEventStart) && - writer.uint(this.loadEventEnd) && - writer.uint(this.firstPaint) && - writer.uint(this.firstContentfulPaint); - } -} -export const PageLoadTiming = bindNew(_PageLoadTiming); -classes.set(23, PageLoadTiming); - - -class _PageRenderTiming implements Message { - readonly _id: number = 24; - constructor( - public speedIndex: number, - public visuallyComplete: number, - public timeToInteractive: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(24) && - writer.uint(this.speedIndex) && - writer.uint(this.visuallyComplete) && - writer.uint(this.timeToInteractive); - } -} -export const PageRenderTiming = bindNew(_PageRenderTiming); -classes.set(24, PageRenderTiming); - - -class _JSException implements Message { - readonly _id: number = 25; - constructor( - public name: string, - public message: string, - public payload: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(25) && - writer.string(this.name) && - writer.string(this.message) && - writer.string(this.payload); - } -} -export const JSException = bindNew(_JSException); -classes.set(25, JSException); - - -class _RawCustomEvent implements Message { - readonly _id: number = 27; - constructor( - public name: string, - public payload: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(27) && - writer.string(this.name) && - writer.string(this.payload); - } -} -export const RawCustomEvent = bindNew(_RawCustomEvent); -classes.set(27, RawCustomEvent); - - -class _UserID implements Message { - readonly _id: number = 28; - constructor( - public id: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(28) && - writer.string(this.id); - } -} -export const UserID = bindNew(_UserID); -classes.set(28, UserID); - - -class _UserAnonymousID implements Message { - readonly _id: number = 29; - constructor( - public id: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(29) && - writer.string(this.id); - } -} -export const UserAnonymousID = bindNew(_UserAnonymousID); -classes.set(29, UserAnonymousID); - - -class _Metadata implements Message { - readonly _id: number = 30; - constructor( - public key: string, - public value: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(30) && - writer.string(this.key) && - writer.string(this.value); - } -} -export const Metadata = bindNew(_Metadata); -classes.set(30, Metadata); - - -class _CSSInsertRule implements Message { - readonly _id: number = 37; - constructor( - public id: number, - public rule: string, - public index: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(37) && - writer.uint(this.id) && - writer.string(this.rule) && - writer.uint(this.index); - } -} -export const CSSInsertRule = bindNew(_CSSInsertRule); -classes.set(37, CSSInsertRule); - - -class _CSSDeleteRule implements Message { - readonly _id: number = 38; - constructor( - public id: number, - public index: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(38) && - writer.uint(this.id) && - writer.uint(this.index); - } -} -export const CSSDeleteRule = bindNew(_CSSDeleteRule); -classes.set(38, CSSDeleteRule); - - -class _Fetch implements Message { - readonly _id: number = 39; - constructor( - public method: string, - public url: string, - public request: string, - public response: string, - public status: number, - public timestamp: number, - public duration: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(39) && - writer.string(this.method) && - writer.string(this.url) && - writer.string(this.request) && - writer.string(this.response) && - writer.uint(this.status) && - writer.uint(this.timestamp) && - writer.uint(this.duration); - } -} -export const Fetch = bindNew(_Fetch); -classes.set(39, Fetch); - - -class _Profiler implements Message { - readonly _id: number = 40; - constructor( - public name: string, - public duration: number, - public args: string, - public result: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(40) && - writer.string(this.name) && - writer.uint(this.duration) && - writer.string(this.args) && - writer.string(this.result); - } -} -export const Profiler = bindNew(_Profiler); -classes.set(40, Profiler); - - -class _OTable implements Message { - readonly _id: number = 41; - constructor( - public key: string, - public value: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(41) && - writer.string(this.key) && - writer.string(this.value); - } -} -export const OTable = bindNew(_OTable); -classes.set(41, OTable); - - -class _StateAction implements Message { - readonly _id: number = 42; - constructor( - public type: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(42) && - writer.string(this.type); - } -} -export const StateAction = bindNew(_StateAction); -classes.set(42, StateAction); - - -class _Redux implements Message { - readonly _id: number = 44; - constructor( - public action: string, - public state: string, - public duration: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(44) && - writer.string(this.action) && - writer.string(this.state) && - writer.uint(this.duration); - } -} -export const Redux = bindNew(_Redux); -classes.set(44, Redux); - - -class _Vuex implements Message { - readonly _id: number = 45; - constructor( - public mutation: string, - public state: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(45) && - writer.string(this.mutation) && - writer.string(this.state); - } -} -export const Vuex = bindNew(_Vuex); -classes.set(45, Vuex); - - -class _MobX implements Message { - readonly _id: number = 46; - constructor( - public type: string, - public payload: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(46) && - writer.string(this.type) && - writer.string(this.payload); - } -} -export const MobX = bindNew(_MobX); -classes.set(46, MobX); - - -class _NgRx implements Message { - readonly _id: number = 47; - constructor( - public action: string, - public state: string, - public duration: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(47) && - writer.string(this.action) && - writer.string(this.state) && - writer.uint(this.duration); - } -} -export const NgRx = bindNew(_NgRx); -classes.set(47, NgRx); - - -class _GraphQL implements Message { - readonly _id: number = 48; - constructor( - public operationKind: string, - public operationName: string, - public variables: string, - public response: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(48) && - writer.string(this.operationKind) && - writer.string(this.operationName) && - writer.string(this.variables) && - writer.string(this.response); - } -} -export const GraphQL = bindNew(_GraphQL); -classes.set(48, GraphQL); - - -class _PerformanceTrack implements Message { - readonly _id: number = 49; - constructor( - public frames: number, - public ticks: number, - public totalJSHeapSize: number, - public usedJSHeapSize: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(49) && - writer.int(this.frames) && - writer.int(this.ticks) && - writer.uint(this.totalJSHeapSize) && - writer.uint(this.usedJSHeapSize); - } -} -export const PerformanceTrack = bindNew(_PerformanceTrack); -classes.set(49, PerformanceTrack); - - -class _ResourceTiming implements Message { - readonly _id: number = 53; - constructor( - public timestamp: number, - public duration: number, - public ttfb: number, - public headerSize: number, - public encodedBodySize: number, - public decodedBodySize: number, - public url: string, - public initiator: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(53) && - writer.uint(this.timestamp) && - writer.uint(this.duration) && - writer.uint(this.ttfb) && - writer.uint(this.headerSize) && - writer.uint(this.encodedBodySize) && - writer.uint(this.decodedBodySize) && - writer.string(this.url) && - writer.string(this.initiator); - } -} -export const ResourceTiming = bindNew(_ResourceTiming); -classes.set(53, ResourceTiming); - - -class _ConnectionInformation implements Message { - readonly _id: number = 54; - constructor( - public downlink: number, - public type: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(54) && - writer.uint(this.downlink) && - writer.string(this.type); - } -} -export const ConnectionInformation = bindNew(_ConnectionInformation); -classes.set(54, ConnectionInformation); - - -class _SetPageVisibility implements Message { - readonly _id: number = 55; - constructor( - public hidden: boolean - ) {} - encode(writer: Writer): boolean { - return writer.uint(55) && - writer.boolean(this.hidden); - } -} -export const SetPageVisibility = bindNew(_SetPageVisibility); -classes.set(55, SetPageVisibility); - - -class _LongTask implements Message { - readonly _id: number = 59; - constructor( - public timestamp: number, - public duration: number, - public context: number, - public containerType: number, - public containerSrc: string, - public containerId: string, - public containerName: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(59) && - writer.uint(this.timestamp) && - writer.uint(this.duration) && - writer.uint(this.context) && - writer.uint(this.containerType) && - writer.string(this.containerSrc) && - writer.string(this.containerId) && - writer.string(this.containerName); - } -} -export const LongTask = bindNew(_LongTask); -classes.set(59, LongTask); - - -class _SetNodeAttributeURLBased implements Message { - readonly _id: number = 60; - constructor( - public id: number, - public name: string, - public value: string, - public baseURL: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(60) && - writer.uint(this.id) && - writer.string(this.name) && - writer.string(this.value) && - writer.string(this.baseURL); - } -} -export const SetNodeAttributeURLBased = bindNew(_SetNodeAttributeURLBased); -classes.set(60, SetNodeAttributeURLBased); - - -class _SetCSSDataURLBased implements Message { - readonly _id: number = 61; - constructor( - public id: number, - public data: string, - public baseURL: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(61) && - writer.uint(this.id) && - writer.string(this.data) && - writer.string(this.baseURL); - } -} -export const SetCSSDataURLBased = bindNew(_SetCSSDataURLBased); -classes.set(61, SetCSSDataURLBased); - - -class _TechnicalInfo implements Message { - readonly _id: number = 63; - constructor( - public type: string, - public value: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(63) && - writer.string(this.type) && - writer.string(this.value); - } -} -export const TechnicalInfo = bindNew(_TechnicalInfo); -classes.set(63, TechnicalInfo); - - -class _CustomIssue implements Message { - readonly _id: number = 64; - constructor( - public name: string, - public payload: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(64) && - writer.string(this.name) && - writer.string(this.payload); - } -} -export const CustomIssue = bindNew(_CustomIssue); -classes.set(64, CustomIssue); - - -class _PageClose implements Message { - readonly _id: number = 65; - constructor( - - ) {} - encode(writer: Writer): boolean { - return writer.uint(65) - ; - } -} -export const PageClose = bindNew(_PageClose); -classes.set(65, PageClose); - - -class _CSSInsertRuleURLBased implements Message { - readonly _id: number = 67; - constructor( - public id: number, - public rule: string, - public index: number, - public baseURL: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(67) && - writer.uint(this.id) && - writer.string(this.rule) && - writer.uint(this.index) && - writer.string(this.baseURL); - } -} -export const CSSInsertRuleURLBased = bindNew(_CSSInsertRuleURLBased); -classes.set(67, CSSInsertRuleURLBased); - - -class _MouseClick implements Message { - readonly _id: number = 69; - constructor( - public id: number, - public hesitationTime: number, - public label: string, - public selector: string - ) {} - encode(writer: Writer): boolean { - return writer.uint(69) && - writer.uint(this.id) && - writer.uint(this.hesitationTime) && - writer.string(this.label) && - writer.string(this.selector); - } -} -export const MouseClick = bindNew(_MouseClick); -classes.set(69, MouseClick); - - -class _CreateIFrameDocument implements Message { - readonly _id: number = 70; - constructor( - public frameID: number, - public id: number - ) {} - encode(writer: Writer): boolean { - return writer.uint(70) && - writer.uint(this.frameID) && - writer.uint(this.id); - } -} -export const CreateIFrameDocument = bindNew(_CreateIFrameDocument); -classes.set(70, CreateIFrameDocument); +export type BatchMetadata = [ + type: Type.BatchMetadata, + version: number, + pageNo: number, + firstIndex: number, + timestamp: number, + location: string, +]; +export type PartitionedMessage = [type: Type.PartitionedMessage, partNo: number, partTotal: number]; +export type Timestamp = [type: Type.Timestamp, timestamp: number]; + +export type SetPageLocation = [ + type: Type.SetPageLocation, + url: string, + referrer: string, + navigationStart: number, +]; + +export type SetViewportSize = [type: Type.SetViewportSize, width: number, height: number]; + +export type SetViewportScroll = [type: Type.SetViewportScroll, x: number, y: number]; + +export type CreateDocument = [type: Type.CreateDocument]; + +export type CreateElementNode = [ + type: Type.CreateElementNode, + id: number, + parentID: number, + index: number, + tag: string, + svg: boolean, +]; + +export type CreateTextNode = [ + type: Type.CreateTextNode, + id: number, + parentID: number, + index: number, +]; + +export type MoveNode = [type: Type.MoveNode, id: number, parentID: number, index: number]; + +export type RemoveNode = [type: Type.RemoveNode, id: number]; + +export type SetNodeAttribute = [ + type: Type.SetNodeAttribute, + id: number, + name: string, + value: string, +]; + +export type RemoveNodeAttribute = [type: Type.RemoveNodeAttribute, id: number, name: string]; + +export type SetNodeData = [type: Type.SetNodeData, id: number, data: string]; + +export type SetNodeScroll = [type: Type.SetNodeScroll, id: number, x: number, y: number]; + +export type SetInputTarget = [type: Type.SetInputTarget, id: number, label: string]; + +export type SetInputValue = [type: Type.SetInputValue, id: number, value: string, mask: number]; + +export type SetInputChecked = [type: Type.SetInputChecked, id: number, checked: boolean]; + +export type MouseMove = [type: Type.MouseMove, x: number, y: number]; + +export type ConsoleLog = [type: Type.ConsoleLog, level: string, value: string]; + +export type PageLoadTiming = [ + type: Type.PageLoadTiming, + requestStart: number, + responseStart: number, + responseEnd: number, + domContentLoadedEventStart: number, + domContentLoadedEventEnd: number, + loadEventStart: number, + loadEventEnd: number, + firstPaint: number, + firstContentfulPaint: number, +]; + +export type PageRenderTiming = [ + type: Type.PageRenderTiming, + speedIndex: number, + visuallyComplete: number, + timeToInteractive: number, +]; + +export type JSException = [type: Type.JSException, name: string, message: string, payload: string]; + +export type RawCustomEvent = [type: Type.RawCustomEvent, name: string, payload: string]; + +export type UserID = [type: Type.UserID, id: string]; + +export type UserAnonymousID = [type: Type.UserAnonymousID, id: string]; + +export type Metadata = [type: Type.Metadata, key: string, value: string]; + +export type CSSInsertRule = [type: Type.CSSInsertRule, id: number, rule: string, index: number]; + +export type CSSDeleteRule = [type: Type.CSSDeleteRule, id: number, index: number]; + +export type Fetch = [ + type: Type.Fetch, + method: string, + url: string, + request: string, + response: string, + status: number, + timestamp: number, + duration: number, +]; + +export type Profiler = [ + type: Type.Profiler, + name: string, + duration: number, + args: string, + result: string, +]; + +export type OTable = [type: Type.OTable, key: string, value: string]; + +export type StateAction = [type: Type.StateAction, type: string]; + +export type Redux = [type: Type.Redux, action: string, state: string, duration: number]; + +export type Vuex = [type: Type.Vuex, mutation: string, state: string]; + +export type MobX = [type: Type.MobX, type: string, payload: string]; + +export type NgRx = [type: Type.NgRx, action: string, state: string, duration: number]; + +export type GraphQL = [ + type: Type.GraphQL, + operationKind: string, + operationName: string, + variables: string, + response: string, +]; + +export type PerformanceTrack = [ + type: Type.PerformanceTrack, + frames: number, + ticks: number, + totalJSHeapSize: number, + usedJSHeapSize: number, +]; + +export type ResourceTiming = [ + type: Type.ResourceTiming, + timestamp: number, + duration: number, + ttfb: number, + headerSize: number, + encodedBodySize: number, + decodedBodySize: number, + url: string, + initiator: string, +]; + +export type ConnectionInformation = [ + type: Type.ConnectionInformation, + downlink: number, + type: string, +]; + +export type SetPageVisibility = [type: Type.SetPageVisibility, hidden: boolean]; + +export type LongTask = [ + type: Type.LongTask, + timestamp: number, + duration: number, + context: number, + containerType: number, + containerSrc: string, + containerId: string, + containerName: string, +]; + +export type SetNodeAttributeURLBased = [ + type: Type.SetNodeAttributeURLBased, + id: number, + name: string, + value: string, + baseURL: string, +]; + +export type SetCSSDataURLBased = [ + type: Type.SetCSSDataURLBased, + id: number, + data: string, + baseURL: string, +]; + +export type TechnicalInfo = [type: Type.TechnicalInfo, type: string, value: string]; + +export type CustomIssue = [type: Type.CustomIssue, name: string, payload: string]; + +export type CSSInsertRuleURLBased = [ + type: Type.CSSInsertRuleURLBased, + id: number, + rule: string, + index: number, + baseURL: string, +]; + +export type MouseClick = [ + type: Type.MouseClick, + id: number, + hesitationTime: number, + label: string, + selector: string, +]; + +export type CreateIFrameDocument = [type: Type.CreateIFrameDocument, frameID: number, id: number]; + +type Message = + | BatchMetadata + | PartitionedMessage + | Timestamp + | SetPageLocation + | SetViewportSize + | SetViewportScroll + | CreateDocument + | CreateElementNode + | CreateTextNode + | MoveNode + | RemoveNode + | SetNodeAttribute + | RemoveNodeAttribute + | SetNodeData + | SetNodeScroll + | SetInputTarget + | SetInputValue + | SetInputChecked + | MouseMove + | ConsoleLog + | PageLoadTiming + | PageRenderTiming + | JSException + | RawCustomEvent + | UserID + | UserAnonymousID + | Metadata + | CSSInsertRule + | CSSDeleteRule + | Fetch + | Profiler + | OTable + | StateAction + | Redux + | Vuex + | MobX + | NgRx + | GraphQL + | PerformanceTrack + | ResourceTiming + | ConnectionInformation + | SetPageVisibility + | LongTask + | SetNodeAttributeURLBased + | SetCSSDataURLBased + | TechnicalInfo + | CustomIssue + | CSSInsertRuleURLBased + | MouseClick + | CreateIFrameDocument; +export default Message; diff --git a/tracker/tracker/src/common/types.ts b/tracker/tracker/src/common/types.ts deleted file mode 100644 index 6717eb8bc..000000000 --- a/tracker/tracker/src/common/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface Writer { - uint(n: number): boolean - int(n: number): boolean - string(s: string): boolean - boolean(b: boolean): boolean -} - -export interface Message { - encode(w: Writer): boolean; -} diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index 71e2d3e30..6807496c7 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -1,5 +1,5 @@ -import type Message from '../../common/messages.js'; -import { Timestamp, Metadata, UserID } from '../../common/messages.js'; +import type Message from './messages.js'; +import { Timestamp, Metadata, UserID } from './messages.js'; import { timestamp, deprecationWarn } from '../utils.js'; import Nodes from './nodes.js'; import Observer from './observer/top_observer.js'; @@ -13,7 +13,7 @@ import { deviceMemory, jsHeapSizeLimit } from '../modules/performance.js'; import type { Options as ObserverOptions } from './observer/top_observer.js'; import type { Options as SanitizerOptions } from './sanitizer.js'; import type { Options as LoggerOptions } from './logger.js'; -import type { Options as WebworkerOptions, WorkerMessageData } from '../../common/webworker.js'; +import type { Options as WebworkerOptions, WorkerMessageData } from '../../common/interaction.js'; // TODO: Unify and clearly describe options logic export interface StartOptions { @@ -133,10 +133,10 @@ export default class App { this.session.attachUpdateCallback(({ userID, metadata }) => { if (userID != null) { // TODO: nullable userID - this.send(new UserID(userID)); + this.send(UserID(userID)); } if (metadata != null) { - Object.entries(metadata).forEach(([key, value]) => this.send(new Metadata(key, value))); + Object.entries(metadata).forEach(([key, value]) => this.send(Metadata(key, value))); } }); this.localStorage = this.options.localStorage; @@ -206,7 +206,7 @@ export default class App { } private commit(): void { if (this.worker && this.messages.length) { - this.messages.unshift(new Timestamp(timestamp())); + this.messages.unshift(Timestamp(timestamp())); this.worker.postMessage(this.messages); this.commitCallbacks.forEach((cb) => cb(this.messages)); this.messages.length = 0; @@ -358,6 +358,7 @@ export default class App { pageNo, ingestPoint: this.options.ingestPoint, timestamp: startInfo.timestamp, + url: document.URL, connAttemptCount: this.options.connAttemptCount, connAttemptGap: this.options.connAttemptGap, }; diff --git a/tracker/tracker/src/main/app/messages.ts b/tracker/tracker/src/main/app/messages.ts new file mode 100644 index 000000000..eee1a638b --- /dev/null +++ b/tracker/tracker/src/main/app/messages.ts @@ -0,0 +1,334 @@ +// Auto-generated, do not edit + +import * as Messages from '../../common/messages.js'; +export { default } from '../../common/messages.js'; + +export function BatchMetadata( + version: number, + pageNo: number, + firstIndex: number, + timestamp: number, + location: string, +): Messages.BatchMetadata { + return [Messages.Type.BatchMetadata, version, pageNo, firstIndex, timestamp, location]; +} + +export function PartitionedMessage(partNo: number, partTotal: number): Messages.PartitionedMessage { + return [Messages.Type.PartitionedMessage, partNo, partTotal]; +} + +export function Timestamp(timestamp: number): Messages.Timestamp { + return [Messages.Type.Timestamp, timestamp]; +} + +export function SetPageLocation( + url: string, + referrer: string, + navigationStart: number, +): Messages.SetPageLocation { + return [Messages.Type.SetPageLocation, url, referrer, navigationStart]; +} + +export function SetViewportSize(width: number, height: number): Messages.SetViewportSize { + return [Messages.Type.SetViewportSize, width, height]; +} + +export function SetViewportScroll(x: number, y: number): Messages.SetViewportScroll { + return [Messages.Type.SetViewportScroll, x, y]; +} + +export function CreateDocument(): Messages.CreateDocument { + return [Messages.Type.CreateDocument]; +} + +export function CreateElementNode( + id: number, + parentID: number, + index: number, + tag: string, + svg: boolean, +): Messages.CreateElementNode { + return [Messages.Type.CreateElementNode, id, parentID, index, tag, svg]; +} + +export function CreateTextNode( + id: number, + parentID: number, + index: number, +): Messages.CreateTextNode { + return [Messages.Type.CreateTextNode, id, parentID, index]; +} + +export function MoveNode(id: number, parentID: number, index: number): Messages.MoveNode { + return [Messages.Type.MoveNode, id, parentID, index]; +} + +export function RemoveNode(id: number): Messages.RemoveNode { + return [Messages.Type.RemoveNode, id]; +} + +export function SetNodeAttribute( + id: number, + name: string, + value: string, +): Messages.SetNodeAttribute { + return [Messages.Type.SetNodeAttribute, id, name, value]; +} + +export function RemoveNodeAttribute(id: number, name: string): Messages.RemoveNodeAttribute { + return [Messages.Type.RemoveNodeAttribute, id, name]; +} + +export function SetNodeData(id: number, data: string): Messages.SetNodeData { + return [Messages.Type.SetNodeData, id, data]; +} + +export function SetNodeScroll(id: number, x: number, y: number): Messages.SetNodeScroll { + return [Messages.Type.SetNodeScroll, id, x, y]; +} + +export function SetInputTarget(id: number, label: string): Messages.SetInputTarget { + return [Messages.Type.SetInputTarget, id, label]; +} + +export function SetInputValue(id: number, value: string, mask: number): Messages.SetInputValue { + return [Messages.Type.SetInputValue, id, value, mask]; +} + +export function SetInputChecked(id: number, checked: boolean): Messages.SetInputChecked { + return [Messages.Type.SetInputChecked, id, checked]; +} + +export function MouseMove(x: number, y: number): Messages.MouseMove { + return [Messages.Type.MouseMove, x, y]; +} + +export function ConsoleLog(level: string, value: string): Messages.ConsoleLog { + return [Messages.Type.ConsoleLog, level, value]; +} + +export function PageLoadTiming( + requestStart: number, + responseStart: number, + responseEnd: number, + domContentLoadedEventStart: number, + domContentLoadedEventEnd: number, + loadEventStart: number, + loadEventEnd: number, + firstPaint: number, + firstContentfulPaint: number, +): Messages.PageLoadTiming { + return [ + Messages.Type.PageLoadTiming, + requestStart, + responseStart, + responseEnd, + domContentLoadedEventStart, + domContentLoadedEventEnd, + loadEventStart, + loadEventEnd, + firstPaint, + firstContentfulPaint, + ]; +} + +export function PageRenderTiming( + speedIndex: number, + visuallyComplete: number, + timeToInteractive: number, +): Messages.PageRenderTiming { + return [Messages.Type.PageRenderTiming, speedIndex, visuallyComplete, timeToInteractive]; +} + +export function JSException(name: string, message: string, payload: string): Messages.JSException { + return [Messages.Type.JSException, name, message, payload]; +} + +export function RawCustomEvent(name: string, payload: string): Messages.RawCustomEvent { + return [Messages.Type.RawCustomEvent, name, payload]; +} + +export function UserID(id: string): Messages.UserID { + return [Messages.Type.UserID, id]; +} + +export function UserAnonymousID(id: string): Messages.UserAnonymousID { + return [Messages.Type.UserAnonymousID, id]; +} + +export function Metadata(key: string, value: string): Messages.Metadata { + return [Messages.Type.Metadata, key, value]; +} + +export function CSSInsertRule(id: number, rule: string, index: number): Messages.CSSInsertRule { + return [Messages.Type.CSSInsertRule, id, rule, index]; +} + +export function CSSDeleteRule(id: number, index: number): Messages.CSSDeleteRule { + return [Messages.Type.CSSDeleteRule, id, index]; +} + +export function Fetch( + method: string, + url: string, + request: string, + response: string, + status: number, + timestamp: number, + duration: number, +): Messages.Fetch { + return [Messages.Type.Fetch, method, url, request, response, status, timestamp, duration]; +} + +export function Profiler( + name: string, + duration: number, + args: string, + result: string, +): Messages.Profiler { + return [Messages.Type.Profiler, name, duration, args, result]; +} + +export function OTable(key: string, value: string): Messages.OTable { + return [Messages.Type.OTable, key, value]; +} + +export function StateAction(type: string): Messages.StateAction { + return [Messages.Type.StateAction, type]; +} + +export function Redux(action: string, state: string, duration: number): Messages.Redux { + return [Messages.Type.Redux, action, state, duration]; +} + +export function Vuex(mutation: string, state: string): Messages.Vuex { + return [Messages.Type.Vuex, mutation, state]; +} + +export function MobX(type: string, payload: string): Messages.MobX { + return [Messages.Type.MobX, type, payload]; +} + +export function NgRx(action: string, state: string, duration: number): Messages.NgRx { + return [Messages.Type.NgRx, action, state, duration]; +} + +export function GraphQL( + operationKind: string, + operationName: string, + variables: string, + response: string, +): Messages.GraphQL { + return [Messages.Type.GraphQL, operationKind, operationName, variables, response]; +} + +export function PerformanceTrack( + frames: number, + ticks: number, + totalJSHeapSize: number, + usedJSHeapSize: number, +): Messages.PerformanceTrack { + return [Messages.Type.PerformanceTrack, frames, ticks, totalJSHeapSize, usedJSHeapSize]; +} + +export function ResourceTiming( + timestamp: number, + duration: number, + ttfb: number, + headerSize: number, + encodedBodySize: number, + decodedBodySize: number, + url: string, + initiator: string, +): Messages.ResourceTiming { + return [ + Messages.Type.ResourceTiming, + timestamp, + duration, + ttfb, + headerSize, + encodedBodySize, + decodedBodySize, + url, + initiator, + ]; +} + +export function ConnectionInformation( + downlink: number, + type: string, +): Messages.ConnectionInformation { + return [Messages.Type.ConnectionInformation, downlink, type]; +} + +export function SetPageVisibility(hidden: boolean): Messages.SetPageVisibility { + return [Messages.Type.SetPageVisibility, hidden]; +} + +export function LongTask( + timestamp: number, + duration: number, + context: number, + containerType: number, + containerSrc: string, + containerId: string, + containerName: string, +): Messages.LongTask { + return [ + Messages.Type.LongTask, + timestamp, + duration, + context, + containerType, + containerSrc, + containerId, + containerName, + ]; +} + +export function SetNodeAttributeURLBased( + id: number, + name: string, + value: string, + baseURL: string, +): Messages.SetNodeAttributeURLBased { + return [Messages.Type.SetNodeAttributeURLBased, id, name, value, baseURL]; +} + +export function SetCSSDataURLBased( + id: number, + data: string, + baseURL: string, +): Messages.SetCSSDataURLBased { + return [Messages.Type.SetCSSDataURLBased, id, data, baseURL]; +} + +export function TechnicalInfo(type: string, value: string): Messages.TechnicalInfo { + return [Messages.Type.TechnicalInfo, type, value]; +} + +export function CustomIssue(name: string, payload: string): Messages.CustomIssue { + return [Messages.Type.CustomIssue, name, payload]; +} + +export function CSSInsertRuleURLBased( + id: number, + rule: string, + index: number, + baseURL: string, +): Messages.CSSInsertRuleURLBased { + return [Messages.Type.CSSInsertRuleURLBased, id, rule, index, baseURL]; +} + +export function MouseClick( + id: number, + hesitationTime: number, + label: string, + selector: string, +): Messages.MouseClick { + return [Messages.Type.MouseClick, id, hesitationTime, label, selector]; +} + +export function CreateIFrameDocument(frameID: number, id: number): Messages.CreateIFrameDocument { + return [Messages.Type.CreateIFrameDocument, frameID, id]; +} diff --git a/tracker/tracker/src/main/app/observer/iframe_observer.ts b/tracker/tracker/src/main/app/observer/iframe_observer.ts index faea6a5e6..7c902788a 100644 --- a/tracker/tracker/src/main/app/observer/iframe_observer.ts +++ b/tracker/tracker/src/main/app/observer/iframe_observer.ts @@ -1,5 +1,5 @@ import Observer from './observer.js'; -import { CreateIFrameDocument } from '../../../common/messages.js'; +import { CreateIFrameDocument } from '../messages.js'; export default class IFrameObserver extends Observer { observe(iframe: HTMLIFrameElement) { diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index 6d11a704d..912c0ed41 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -8,7 +8,7 @@ import { CreateElementNode, MoveNode, RemoveNode, -} from '../../../common/messages.js'; +} from '../messages.js'; import App from '../index.js'; import { isRootNode, isTextNode, isElementNode, isSVGElement, hasTag } from '../guards.js'; @@ -125,14 +125,14 @@ export default abstract class Observer { name = name.substr(6); } if (value === null) { - this.app.send(new RemoveNodeAttribute(id, name)); + this.app.send(RemoveNodeAttribute(id, name)); } else if (name === 'href') { if (value.length > 1e5) { value = ''; } - this.app.send(new SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref())); + this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref())); } else { - this.app.send(new SetNodeAttribute(id, name, value)); + this.app.send(SetNodeAttribute(id, name, value)); } return; } @@ -156,26 +156,26 @@ export default abstract class Observer { return; } if (value === null) { - this.app.send(new RemoveNodeAttribute(id, name)); + this.app.send(RemoveNodeAttribute(id, name)); return; } if (name === 'style' || (name === 'href' && hasTag(node, 'LINK'))) { - this.app.send(new SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref())); + this.app.send(SetNodeAttributeURLBased(id, name, value, this.app.getBaseHref())); return; } if (name === 'href' || value.length > 1e5) { value = ''; } - this.app.send(new SetNodeAttribute(id, name, value)); + this.app.send(SetNodeAttribute(id, name, value)); } private sendNodeData(id: number, parentElement: Element, data: string): void { if (hasTag(parentElement, 'STYLE') || hasTag(parentElement, 'style')) { - this.app.send(new SetCSSDataURLBased(id, data, this.app.getBaseHref())); + this.app.send(SetCSSDataURLBased(id, data, this.app.getBaseHref())); return; } data = this.app.sanitizer.sanitize(id, data); - this.app.send(new SetNodeData(id, data)); + this.app.send(SetNodeData(id, data)); } private bindNode(node: Node): void { @@ -221,7 +221,7 @@ export default abstract class Observer { private unbindNode(node: Node) { const id = this.app.nodes.unregisterNode(node); if (id !== undefined && this.recents.get(id) === RecentsType.Removed) { - this.app.send(new RemoveNode(id)); + this.app.send(RemoveNode(id)); } } @@ -289,7 +289,7 @@ export default abstract class Observer { (el as HTMLElement | SVGElement).style.height = height + 'px'; } - this.app.send(new CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node))); + this.app.send(CreateElementNode(id, parentID, index, el.tagName, isSVGElement(node))); } for (let i = 0; i < el.attributes.length; i++) { const attr = el.attributes[i]; @@ -297,13 +297,13 @@ export default abstract class Observer { } } else if (isTextNode(node)) { // for text node id != 0, hence parentID !== undefined and parent is Element - this.app.send(new CreateTextNode(id, parentID as number, index)); + this.app.send(CreateTextNode(id, parentID as number, index)); this.sendNodeData(id, parent as Element, node.data); } return true; } if (recentsType === RecentsType.Removed && parentID !== undefined) { - this.app.send(new MoveNode(id, parentID, index)); + this.app.send(MoveNode(id, parentID, index)); } const attr = this.attributesMap.get(id); if (attr !== undefined) { diff --git a/tracker/tracker/src/main/app/observer/shadow_root_observer.ts b/tracker/tracker/src/main/app/observer/shadow_root_observer.ts index f6059e2f5..7b45e4bf5 100644 --- a/tracker/tracker/src/main/app/observer/shadow_root_observer.ts +++ b/tracker/tracker/src/main/app/observer/shadow_root_observer.ts @@ -1,5 +1,5 @@ import Observer from './observer.js'; -import { CreateIFrameDocument } from '../../../common/messages.js'; +import { CreateIFrameDocument } from '../messages.js'; export default class ShadowRootObserver extends Observer { observe(el: Element) { diff --git a/tracker/tracker/src/main/app/observer/top_observer.ts b/tracker/tracker/src/main/app/observer/top_observer.ts index cc921967f..cc1b85584 100644 --- a/tracker/tracker/src/main/app/observer/top_observer.ts +++ b/tracker/tracker/src/main/app/observer/top_observer.ts @@ -4,7 +4,7 @@ import { isElementNode, hasTag } from '../guards.js'; import IFrameObserver from './iframe_observer.js'; import ShadowRootObserver from './shadow_root_observer.js'; -import { CreateDocument } from '../../../common/messages.js'; +import { CreateDocument } from '../messages.js'; import App from '../index.js'; import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js'; @@ -95,7 +95,7 @@ export default class TopObserver extends Observer { this.observeRoot( window.document, () => { - this.app.send(new CreateDocument()); + this.app.send(CreateDocument()); }, window.document.documentElement, ); diff --git a/tracker/tracker/src/main/app/session.ts b/tracker/tracker/src/main/app/session.ts index 938883359..357a2aca7 100644 --- a/tracker/tracker/src/main/app/session.ts +++ b/tracker/tracker/src/main/app/session.ts @@ -1,5 +1,3 @@ -import { UserID, UserAnonymousID, Metadata } from '../../common/messages.js'; - interface SessionInfo { sessionID: string | null; metadata: Record; diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index 310545dd9..8daed5161 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -1,14 +1,8 @@ import App, { DEFAULT_INGEST_POINT } from './app/index.js'; export { default as App } from './app/index.js'; -import { - UserID, - UserAnonymousID, - Metadata, - RawCustomEvent, - CustomIssue, -} from '../common/messages.js'; -import * as _Messages from '../common/messages.js'; +import { UserID, UserAnonymousID, RawCustomEvent, CustomIssue } from './app/messages.js'; +import * as _Messages from './app/messages.js'; export const Messages = _Messages; import Connection from './modules/connection.js'; @@ -224,7 +218,7 @@ export default class API { setUserAnonymousID(id: string): void { if (typeof id === 'string' && this.app !== null) { - this.app.send(new UserAnonymousID(id)); + this.app.send(UserAnonymousID(id)); } } userAnonymousID(id: string): void { @@ -252,7 +246,7 @@ export default class API { } catch (e) { return; } - this.app.send(new RawCustomEvent(key, payload)); + this.app.send(RawCustomEvent(key, payload)); } } } @@ -264,7 +258,7 @@ export default class API { } catch (e) { return; } - this.app.send(new CustomIssue(key, payload)); + this.app.send(CustomIssue(key, payload)); } } diff --git a/tracker/tracker/src/main/modules/connection.ts b/tracker/tracker/src/main/modules/connection.ts index ecf9e9fe9..e5a089cb7 100644 --- a/tracker/tracker/src/main/modules/connection.ts +++ b/tracker/tracker/src/main/modules/connection.ts @@ -1,5 +1,5 @@ import App from '../app/index.js'; -import { ConnectionInformation } from '../../common/messages.js'; +import { ConnectionInformation } from '../app/messages.js'; export default function (app: App): void { const connection: @@ -18,10 +18,7 @@ export default function (app: App): void { const sendConnectionInformation = (): void => app.send( - new ConnectionInformation( - Math.round(connection.downlink * 1000), - connection.type || 'unknown', - ), + ConnectionInformation(Math.round(connection.downlink * 1000), connection.type || 'unknown'), ); sendConnectionInformation(); connection.addEventListener('change', sendConnectionInformation); diff --git a/tracker/tracker/src/main/modules/console.ts b/tracker/tracker/src/main/modules/console.ts index 33ad34957..8fbf2b71c 100644 --- a/tracker/tracker/src/main/modules/console.ts +++ b/tracker/tracker/src/main/modules/console.ts @@ -1,7 +1,7 @@ import type App from '../app/index.js'; import { hasTag } from '../app/guards.js'; import { IN_BROWSER } from '../utils.js'; -import { ConsoleLog } from '../../common/messages.js'; +import { ConsoleLog } from '../app/messages.js'; const printError: (e: Error) => string = IN_BROWSER && 'InstallTrigger' in window // detect Firefox @@ -109,7 +109,7 @@ export default function (app: App, opts: Partial): void { } const sendConsoleLog = app.safe((level: string, args: unknown[]): void => - app.send(new ConsoleLog(level, printf(args))), + app.send(ConsoleLog(level, printf(args))), ); let n: number; diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 53c805722..46c563daf 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -1,5 +1,5 @@ import type App from '../app/index.js'; -import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../../common/messages.js'; +import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from '../app/messages.js'; import { hasTag } from '../app/guards.js'; export default function (app: App | null) { @@ -7,7 +7,7 @@ export default function (app: App | null) { return; } if (!window.CSSStyleSheet) { - app.send(new TechnicalInfo('no_stylesheet_prototype_in_window', '')); + app.send(TechnicalInfo('no_stylesheet_prototype_in_window', '')); return; } @@ -15,8 +15,8 @@ export default function (app: App | null) { const sendMessage = typeof rule === 'string' ? (nodeID: number) => - app.send(new CSSInsertRuleURLBased(nodeID, rule, index, app.getBaseHref())) - : (nodeID: number) => app.send(new CSSDeleteRule(nodeID, index)); + app.send(CSSInsertRuleURLBased(nodeID, rule, index, app.getBaseHref())) + : (nodeID: number) => app.send(CSSDeleteRule(nodeID, index)); // TODO: Extend messages to maintain nested rules (CSSGroupingRule prototype, as well as CSSKeyframesRule) if (stylesheet.ownerNode == null) { throw new Error('Owner Node not found'); diff --git a/tracker/tracker/src/main/modules/exception.ts b/tracker/tracker/src/main/modules/exception.ts index 2cb66885e..a7f6a4f1e 100644 --- a/tracker/tracker/src/main/modules/exception.ts +++ b/tracker/tracker/src/main/modules/exception.ts @@ -1,6 +1,6 @@ import type App from '../app/index.js'; -import type Message from '../../common/messages.js'; -import { JSException } from '../../common/messages.js'; +import type Message from '../app/messages.js'; +import { JSException } from '../app/messages.js'; import ErrorStackParser from 'error-stack-parser'; export interface Options { @@ -32,7 +32,7 @@ export function getExceptionMessage(error: Error, fallbackStack: Array= 1e5 || app.sanitizer.isMasked(id)) { sendPlaceholder(id, this); } else { - app.send(new SetNodeAttribute(id, 'src', resolvedSrc)); + app.send(SetNodeAttribute(id, 'src', resolvedSrc)); if (srcset) { const resolvedSrcset = srcset .split(',') .map((str) => resolveURL(str)) .join(','); - app.send(new SetNodeAttribute(id, 'srcset', resolvedSrcset)); + app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset)); } } }); @@ -74,11 +70,11 @@ export default function (app: App): void { } if (mutation.attributeName === 'src') { const src = target.src; - app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref())); + app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref())); } if (mutation.attributeName === 'srcset') { const srcset = target.srcset; - app.send(new SetNodeAttribute(id, 'srcset', srcset)); + app.send(SetNodeAttribute(id, 'srcset', srcset)); } } } diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index 26c593c98..3153c6c61 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -1,7 +1,7 @@ import type App from '../app/index.js'; import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from '../utils.js'; import { hasTag } from '../app/guards.js'; -import { SetInputTarget, SetInputValue, SetInputChecked } from '../../common/messages.js'; +import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.js'; const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date']; @@ -97,7 +97,7 @@ export default function (app: App, opts: Partial): void { function sendInputTarget(id: number, node: TextEditableElement): void { const label = getInputLabel(node); if (label !== '') { - app.send(new SetInputTarget(id, label)); + app.send(SetInputTarget(id, label)); } } function sendInputValue(id: number, node: TextEditableElement | HTMLSelectElement): void { @@ -126,7 +126,7 @@ export default function (app: App, opts: Partial): void { break; } - app.send(new SetInputValue(id, value, mask)); + app.send(SetInputValue(id, value, mask)); } const inputValues: Map = new Map(); @@ -165,7 +165,7 @@ export default function (app: App, opts: Partial): void { } if (checked !== node.checked) { checkableValues.set(id, node.checked); - app.send(new SetInputChecked(id, node.checked)); + app.send(SetInputChecked(id, node.checked)); } }); }); @@ -191,7 +191,7 @@ export default function (app: App, opts: Partial): void { } if (isCheckable(node)) { checkableValues.set(id, node.checked); - app.send(new SetInputChecked(id, node.checked)); + app.send(SetInputChecked(id, node.checked)); return; } }), diff --git a/tracker/tracker/src/main/modules/longtasks.ts b/tracker/tracker/src/main/modules/longtasks.ts index 8ac5f789a..e8cbb8bb0 100644 --- a/tracker/tracker/src/main/modules/longtasks.ts +++ b/tracker/tracker/src/main/modules/longtasks.ts @@ -1,5 +1,5 @@ import type App from '../app/index.js'; -import { LongTask } from '../../common/messages.js'; +import { LongTask } from '../app/messages.js'; // https://w3c.github.io/performance-timeline/#the-performanceentry-interface interface TaskAttributionTiming extends PerformanceEntry { @@ -45,7 +45,7 @@ export default function (app: App): void { } app.send( - new LongTask( + LongTask( entry.startTime + performance.timing.navigationStart, entry.duration, Math.max(contexts.indexOf(entry.name), 0), diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index c46ed84ea..1cc775742 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -1,7 +1,7 @@ import type App from '../app/index.js'; import { hasTag, isSVGElement } from '../app/guards.js'; import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js'; -import { MouseMove, MouseClick } from '../../common/messages.js'; +import { MouseMove, MouseClick } from '../app/messages.js'; import { getInputLabel } from './input.js'; function _getSelector(target: Element): string { @@ -115,7 +115,7 @@ export default function (app: App): void { const sendMouseMove = (): void => { if (mousePositionChanged) { - app.send(new MouseMove(mousePositionX, mousePositionY)); + app.send(MouseMove(mousePositionX, mousePositionY)); mousePositionChanged = false; } }; @@ -151,7 +151,7 @@ export default function (app: App): void { if (id !== undefined) { sendMouseMove(); app.send( - new MouseClick( + MouseClick( id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), diff --git a/tracker/tracker/src/main/modules/performance.ts b/tracker/tracker/src/main/modules/performance.ts index c921e45eb..1aeb11c66 100644 --- a/tracker/tracker/src/main/modules/performance.ts +++ b/tracker/tracker/src/main/modules/performance.ts @@ -1,6 +1,6 @@ import type App from '../app/index.js'; import { IN_BROWSER } from '../utils.js'; -import { PerformanceTrack } from '../../common/messages.js'; +import { PerformanceTrack } from '../app/messages.js'; type Perf = { memory: { @@ -60,7 +60,7 @@ export default function (app: App, opts: Partial): void { return; } app.send( - new PerformanceTrack( + PerformanceTrack( frames, ticks, perf.memory.totalJSHeapSize || 0, diff --git a/tracker/tracker/src/main/modules/scroll.ts b/tracker/tracker/src/main/modules/scroll.ts index 656408824..5892fad4b 100644 --- a/tracker/tracker/src/main/modules/scroll.ts +++ b/tracker/tracker/src/main/modules/scroll.ts @@ -1,5 +1,5 @@ import type App from '../app/index.js'; -import { SetViewportScroll, SetNodeScroll } from '../../common/messages.js'; +import { SetViewportScroll, SetNodeScroll } from '../app/messages.js'; import { isElementNode } from '../app/guards.js'; export default function (app: App): void { @@ -8,7 +8,7 @@ export default function (app: App): void { const sendSetViewportScroll = app.safe((): void => app.send( - new SetViewportScroll( + SetViewportScroll( window.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft) || @@ -24,7 +24,7 @@ export default function (app: App): void { const sendSetNodeScroll = app.safe((s: [number, number], node: Node): void => { const id = app.nodes.getID(node); if (id !== undefined) { - app.send(new SetNodeScroll(id, s[0], s[1])); + app.send(SetNodeScroll(id, s[0], s[1])); } }); diff --git a/tracker/tracker/src/main/modules/timing.ts b/tracker/tracker/src/main/modules/timing.ts index 1ca670562..bb5d0cf49 100644 --- a/tracker/tracker/src/main/modules/timing.ts +++ b/tracker/tracker/src/main/modules/timing.ts @@ -1,7 +1,7 @@ import type App from '../app/index.js'; import { hasTag } from '../app/guards.js'; import { isURL } from '../utils.js'; -import { ResourceTiming, PageLoadTiming, PageRenderTiming } from '../../common/messages.js'; +import { ResourceTiming, PageLoadTiming, PageRenderTiming } from '../app/messages.js'; // Inspired by https://github.com/WPO-Foundation/RUM-SpeedIndex/blob/master/src/rum-speedindex.js @@ -109,7 +109,7 @@ export default function (app: App, opts: Partial): void { resources[entry.name] = entry.startTime + entry.duration; } app.send( - new ResourceTiming( + ResourceTiming( entry.startTime + performance.timing.navigationStart, entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, @@ -175,7 +175,7 @@ export default function (app: App, opts: Partial): void { loadEventEnd, } = performance.timing; app.send( - new PageLoadTiming( + PageLoadTiming( requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, @@ -236,7 +236,7 @@ export default function (app: App, opts: Partial): void { ) : 0; app.send( - new PageRenderTiming( + PageRenderTiming( speedIndex, firstContentfulPaint > visuallyComplete ? firstContentfulPaint : visuallyComplete, timeToInteractive, diff --git a/tracker/tracker/src/main/modules/viewport.ts b/tracker/tracker/src/main/modules/viewport.ts index b576b5384..8264b82b2 100644 --- a/tracker/tracker/src/main/modules/viewport.ts +++ b/tracker/tracker/src/main/modules/viewport.ts @@ -1,5 +1,5 @@ import type App from '../app/index.js'; -import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../../common/messages.js'; +import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../app/messages.js'; export default function (app: App): void { let url: string, width: number, height: number; @@ -9,7 +9,7 @@ export default function (app: App): void { const { URL } = document; if (URL !== url) { url = URL; - app.send(new SetPageLocation(url, document.referrer, navigationStart)); + app.send(SetPageLocation(url, document.referrer, navigationStart)); navigationStart = 0; } }); @@ -19,14 +19,14 @@ export default function (app: App): void { if (innerWidth !== width || innerHeight !== height) { width = innerWidth; height = innerHeight; - app.send(new SetViewportSize(width, height)); + app.send(SetViewportSize(width, height)); } }); const sendSetPageVisibility = document.hidden === undefined ? Function.prototype - : app.safe(() => app.send(new SetPageVisibility(document.hidden))); + : app.safe(() => app.send(SetPageVisibility(document.hidden))); app.attachStartCallback(() => { url = ''; diff --git a/tracker/tracker/src/webworker/BatchWriter.ts b/tracker/tracker/src/webworker/BatchWriter.ts index be0aed933..26fe37c0a 100644 --- a/tracker/tracker/src/webworker/BatchWriter.ts +++ b/tracker/tracker/src/webworker/BatchWriter.ts @@ -1,33 +1,62 @@ import type Message from '../common/messages.js'; -import PrimitiveWriter from './PrimitiveWriter.js'; -import { BatchMeta, Timestamp } from '../common/messages.js'; +import * as Messages from '../common/messages.js'; +import MessageEncoder from './MessageEncoder.js'; +import PrimitiveEncoder from './PrimitiveEncoder.js'; + +const SIZE_RESERVED = 2; +const MAX_M_SIZE = (1 << (SIZE_RESERVED * 8)) - 1; export default class BatchWriter { private nextIndex = 0; private beaconSize = 2 * 1e5; // Default 200kB - private writer = new PrimitiveWriter(this.beaconSize); + private encoder = new MessageEncoder(this.beaconSize); + private readonly sizeEncoder = new PrimitiveEncoder(SIZE_RESERVED); private isEmpty = true; constructor( private readonly pageNo: number, private timestamp: number, + private url: string, private readonly onBatch: (batch: Uint8Array) => void, ) { this.prepare(); } private prepare(): void { - if (!this.writer.isEmpty()) { + if (!this.encoder.isEmpty()) { return; } - new BatchMeta(this.pageNo, this.nextIndex, this.timestamp).encode(this.writer); + // MBTODO: move service-messages creation to webworker + const batchMetadata: Messages.BatchMetadata = [ + Messages.Type.BatchMetadata, + 1, + this.pageNo, + this.nextIndex, + this.timestamp, + this.url, + ]; + this.encoder.encode(batchMetadata); } private write(message: Message): boolean { - const wasWritten = message.encode(this.writer); + const e = this.encoder; + if (!e.uint(message[0]) || !e.skip(SIZE_RESERVED)) { + return false; + } + const startOffset = e.getCurrentOffset(); + const wasWritten = e.encode(message); if (wasWritten) { + const endOffset = e.getCurrentOffset(); + const size = endOffset - startOffset; + if (size > MAX_M_SIZE || !this.sizeEncoder.uint(size)) { + console.warn('OpenReplay: max message size overflow.'); + return false; + } + this.sizeEncoder.checkpoint(); // TODO: separate checkpoint logic to an Encoder-inherit class + e.set(this.sizeEncoder.flush(), startOffset - SIZE_RESERVED); + + e.checkpoint(); this.isEmpty = false; - this.writer.checkpoint(); this.nextIndex++; } return wasWritten; @@ -39,21 +68,24 @@ export default class BatchWriter { } writeMessage(message: Message) { - if (message instanceof Timestamp) { - this.timestamp = (message).timestamp; + if (message[0] === Messages.Type.Timestamp) { + this.timestamp = message[1]; // .timestamp + } + if (message[0] === Messages.Type.SetPageLocation) { + this.url = message[1]; // .url } while (!this.write(message)) { this.finaliseBatch(); if (this.beaconSize === this.beaconSizeLimit) { console.warn('OpenReplay: beacon size overflow. Skipping large message.'); - this.writer.reset(); + this.encoder.reset(); this.prepare(); this.isEmpty = true; return; } // MBTODO: tempWriter for one message? this.beaconSize = Math.min(this.beaconSize * 2, this.beaconSizeLimit); - this.writer = new PrimitiveWriter(this.beaconSize); + this.encoder = new MessageEncoder(this.beaconSize); this.prepare(); this.isEmpty = true; } @@ -63,12 +95,12 @@ export default class BatchWriter { if (this.isEmpty) { return; } - this.onBatch(this.writer.flush()); + this.onBatch(this.encoder.flush()); this.prepare(); this.isEmpty = true; } clean() { - this.writer.reset(); + this.encoder.reset(); } } diff --git a/tracker/tracker/src/webworker/MessageEncoder.ts b/tracker/tracker/src/webworker/MessageEncoder.ts new file mode 100644 index 000000000..e0b99c810 --- /dev/null +++ b/tracker/tracker/src/webworker/MessageEncoder.ts @@ -0,0 +1,264 @@ +// Auto-generated, do not edit + +import * as Messages from '../common/messages.js'; +import Message from '../common/messages.js'; +import PrimitiveEncoder from './PrimitiveEncoder.js'; + +export default class MessageEncoder extends PrimitiveEncoder { + encode(msg: Message): boolean { + switch (msg[0]) { + case Messages.Type.BatchMetadata: + return ( + this.uint(msg[1]) && + this.uint(msg[2]) && + this.uint(msg[3]) && + this.int(msg[4]) && + this.string(msg[5]) + ); + break; + + case Messages.Type.PartitionedMessage: + return this.uint(msg[1]) && this.uint(msg[2]); + break; + + case Messages.Type.Timestamp: + return this.uint(msg[1]); + break; + + case Messages.Type.SetPageLocation: + return this.string(msg[1]) && this.string(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.SetViewportSize: + return this.uint(msg[1]) && this.uint(msg[2]); + break; + + case Messages.Type.SetViewportScroll: + return this.int(msg[1]) && this.int(msg[2]); + break; + + case Messages.Type.CreateDocument: + return true; + break; + + case Messages.Type.CreateElementNode: + return ( + this.uint(msg[1]) && + this.uint(msg[2]) && + this.uint(msg[3]) && + this.string(msg[4]) && + this.boolean(msg[5]) + ); + break; + + case Messages.Type.CreateTextNode: + return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.MoveNode: + return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.RemoveNode: + return this.uint(msg[1]); + break; + + case Messages.Type.SetNodeAttribute: + return this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3]); + break; + + case Messages.Type.RemoveNodeAttribute: + return this.uint(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.SetNodeData: + return this.uint(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.SetNodeScroll: + return this.uint(msg[1]) && this.int(msg[2]) && this.int(msg[3]); + break; + + case Messages.Type.SetInputTarget: + return this.uint(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.SetInputValue: + return this.uint(msg[1]) && this.string(msg[2]) && this.int(msg[3]); + break; + + case Messages.Type.SetInputChecked: + return this.uint(msg[1]) && this.boolean(msg[2]); + break; + + case Messages.Type.MouseMove: + return this.uint(msg[1]) && this.uint(msg[2]); + break; + + case Messages.Type.ConsoleLog: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.PageLoadTiming: + return ( + this.uint(msg[1]) && + this.uint(msg[2]) && + this.uint(msg[3]) && + this.uint(msg[4]) && + this.uint(msg[5]) && + this.uint(msg[6]) && + this.uint(msg[7]) && + this.uint(msg[8]) && + this.uint(msg[9]) + ); + break; + + case Messages.Type.PageRenderTiming: + return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.JSException: + return this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]); + break; + + case Messages.Type.RawCustomEvent: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.UserID: + return this.string(msg[1]); + break; + + case Messages.Type.UserAnonymousID: + return this.string(msg[1]); + break; + + case Messages.Type.Metadata: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.CSSInsertRule: + return this.uint(msg[1]) && this.string(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.CSSDeleteRule: + return this.uint(msg[1]) && this.uint(msg[2]); + break; + + case Messages.Type.Fetch: + return ( + this.string(msg[1]) && + this.string(msg[2]) && + this.string(msg[3]) && + this.string(msg[4]) && + this.uint(msg[5]) && + this.uint(msg[6]) && + this.uint(msg[7]) + ); + break; + + case Messages.Type.Profiler: + return ( + this.string(msg[1]) && this.uint(msg[2]) && this.string(msg[3]) && this.string(msg[4]) + ); + break; + + case Messages.Type.OTable: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.StateAction: + return this.string(msg[1]); + break; + + case Messages.Type.Redux: + return this.string(msg[1]) && this.string(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.Vuex: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.MobX: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.NgRx: + return this.string(msg[1]) && this.string(msg[2]) && this.uint(msg[3]); + break; + + case Messages.Type.GraphQL: + return ( + this.string(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) + ); + break; + + case Messages.Type.PerformanceTrack: + return this.int(msg[1]) && this.int(msg[2]) && this.uint(msg[3]) && this.uint(msg[4]); + break; + + case Messages.Type.ResourceTiming: + return ( + this.uint(msg[1]) && + this.uint(msg[2]) && + this.uint(msg[3]) && + this.uint(msg[4]) && + this.uint(msg[5]) && + this.uint(msg[6]) && + this.string(msg[7]) && + this.string(msg[8]) + ); + break; + + case Messages.Type.ConnectionInformation: + return this.uint(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.SetPageVisibility: + return this.boolean(msg[1]); + break; + + case Messages.Type.LongTask: + return ( + this.uint(msg[1]) && + this.uint(msg[2]) && + this.uint(msg[3]) && + this.uint(msg[4]) && + this.string(msg[5]) && + this.string(msg[6]) && + this.string(msg[7]) + ); + break; + + case Messages.Type.SetNodeAttributeURLBased: + return ( + this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3]) && this.string(msg[4]) + ); + break; + + case Messages.Type.SetCSSDataURLBased: + return this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3]); + break; + + case Messages.Type.TechnicalInfo: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.CustomIssue: + return this.string(msg[1]) && this.string(msg[2]); + break; + + case Messages.Type.CSSInsertRuleURLBased: + return this.uint(msg[1]) && this.string(msg[2]) && this.uint(msg[3]) && this.string(msg[4]); + break; + + case Messages.Type.MouseClick: + return this.uint(msg[1]) && this.uint(msg[2]) && this.string(msg[3]) && this.string(msg[4]); + break; + + case Messages.Type.CreateIFrameDocument: + return this.uint(msg[1]) && this.uint(msg[2]); + break; + } + } +} diff --git a/tracker/tracker/src/webworker/PrimitiveWriter.ts b/tracker/tracker/src/webworker/PrimitiveEncoder.ts similarity index 93% rename from tracker/tracker/src/webworker/PrimitiveWriter.ts rename to tracker/tracker/src/webworker/PrimitiveEncoder.ts index 87924bf75..2d591a6ed 100644 --- a/tracker/tracker/src/webworker/PrimitiveWriter.ts +++ b/tracker/tracker/src/webworker/PrimitiveEncoder.ts @@ -53,19 +53,29 @@ const textEncoder: { encode(str: string): Uint8Array } = }, }; -export default class PrimitiveWriter { +export default class PrimitiveEncoder { private offset = 0; private checkpointOffset = 0; private readonly data: Uint8Array; constructor(private readonly size: number) { this.data = new Uint8Array(size); } + getCurrentOffset(): number { + return this.offset; + } checkpoint() { this.checkpointOffset = this.offset; } isEmpty(): boolean { return this.offset === 0; } + skip(n: number): boolean { + this.offset += n; + return this.offset <= this.size; + } + set(bytes: Uint8Array, offset: number) { + this.data.set(bytes, offset); + } boolean(value: boolean): boolean { this.data[this.offset++] = +value; return this.offset <= this.size; diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index a22e75303..4a8e4290f 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -1,7 +1,7 @@ import type Message from '../common/messages.js'; -import { WorkerMessageData } from '../common/webworker.js'; +import { Type as MType } from '../common/messages.js'; +import { WorkerMessageData } from '../common/interaction.js'; -import { classes, SetPageVisibility } from '../common/messages.js'; import QueueSender from './QueueSender.js'; import BatchWriter from './BatchWriter.js'; @@ -66,13 +66,11 @@ self.onmessage = ({ data }: MessageEvent): any => { } const w = writer; // Message[] - data.forEach((data) => { - // @ts-ignore - const message: Message = new (classes.get(data._id))(); + data.forEach((message) => { Object.assign(message, data); - if (message instanceof SetPageVisibility) { - // @ts-ignore - if ((message).hidden) { + if (message[0] === MType.SetPageVisibility) { + if (message[1]) { + // .hidden restartTimeoutID = setTimeout(() => self.postMessage('restart'), 30 * 60 * 1000); } else { clearTimeout(restartTimeoutID); @@ -102,6 +100,7 @@ self.onmessage = ({ data }: MessageEvent): any => { writer = new BatchWriter( data.pageNo, data.timestamp, + data.url, // onBatch (batch) => sender && sender.push(batch), );