feat(tracker): add support for multi tab sessions

This commit is contained in:
nick-delirium 2023-05-05 12:19:51 +02:00
parent 9cedd798be
commit 54e95e252d
24 changed files with 318 additions and 33 deletions

View file

@ -2,7 +2,7 @@
package messages
func IsReplayerType(id int) bool {
return 1 != id && 3 != id && 17 != id && 23 != id && 24 != id && 25 != id && 26 != id && 27 != id && 28 != id && 29 != id && 30 != id && 31 != id && 32 != id && 42 != id && 56 != id && 62 != id && 63 != id && 64 != id && 66 != id && 78 != id && 80 != id && 81 != id && 82 != id && 112 != id && 115 != id && 125 != id && 126 != id && 127 != id && 107 != id && 91 != id && 92 != id && 94 != id && 95 != id && 97 != id && 98 != id && 99 != id && 101 != id && 104 != id && 110 != id && 111 != id
return 1 != id && 3 != id && 17 != id && 23 != id && 24 != id && 25 != id && 26 != id && 27 != id && 28 != id && 29 != id && 30 != id && 31 != id && 32 != id && 42 != id && 56 != id && 62 != id && 63 != id && 64 != id && 66 != id && 78 != id && 80 != id && 81 != id && 82 != id && 112 != id && 115 != id && 117 != id && 125 != id && 126 != id && 127 != id && 107 != id && 91 != id && 92 != id && 94 != id && 95 != id && 97 != id && 98 != id && 99 != id && 101 != id && 104 != id && 110 != id && 111 != id
}
func IsIOSType(id int) bool {
@ -10,5 +10,5 @@ func IsIOSType(id int) bool {
}
func IsDOMType(id int) bool {
return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id
return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 118 == id || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id
}

View file

@ -74,13 +74,15 @@ const (
MsgJSException = 78
MsgZustand = 79
MsgBatchMeta = 80
MsgBatchMetadata = 81
MsgBatchMetadataDeprecated = 81
MsgPartitionedMessage = 82
MsgInputChange = 112
MsgSelectionChange = 113
MsgMouseThrashing = 114
MsgUnbindNodes = 115
MsgResourceTiming = 116
MsgBatchMetadata = 117
MsgTabChange = 118
MsgIssueEvent = 125
MsgSessionEnd = 126
MsgSessionSearch = 127
@ -1974,7 +1976,7 @@ func (msg *BatchMeta) TypeID() int {
return 80
}
type BatchMetadata struct {
type BatchMetadataDeprecated struct {
message
Version uint64
PageNo uint64
@ -1983,7 +1985,7 @@ type BatchMetadata struct {
Location string
}
func (msg *BatchMetadata) Encode() []byte {
func (msg *BatchMetadataDeprecated) Encode() []byte {
buf := make([]byte, 51+len(msg.Location))
buf[0] = 81
p := 1
@ -1995,11 +1997,11 @@ func (msg *BatchMetadata) Encode() []byte {
return buf[:p]
}
func (msg *BatchMetadata) Decode() Message {
func (msg *BatchMetadataDeprecated) Decode() Message {
return msg
}
func (msg *BatchMetadata) TypeID() int {
func (msg *BatchMetadataDeprecated) TypeID() int {
return 81
}
@ -2163,6 +2165,58 @@ func (msg *ResourceTiming) TypeID() int {
return 116
}
type BatchMetadata struct {
message
Version uint64
PageNo uint64
FirstIndex uint64
Timestamp int64
Location string
TabId string
}
func (msg *BatchMetadata) Encode() []byte {
buf := make([]byte, 61+len(msg.Location)+len(msg.TabId))
buf[0] = 117
p := 1
p = WriteUint(msg.Version, buf, p)
p = WriteUint(msg.PageNo, buf, p)
p = WriteUint(msg.FirstIndex, buf, p)
p = WriteInt(msg.Timestamp, buf, p)
p = WriteString(msg.Location, buf, p)
p = WriteString(msg.TabId, buf, p)
return buf[:p]
}
func (msg *BatchMetadata) Decode() Message {
return msg
}
func (msg *BatchMetadata) TypeID() int {
return 117
}
type TabChange struct {
message
TabId string
}
func (msg *TabChange) Encode() []byte {
buf := make([]byte, 11+len(msg.TabId))
buf[0] = 118
p := 1
p = WriteString(msg.TabId, buf, p)
return buf[:p]
}
func (msg *TabChange) Decode() Message {
return msg
}
func (msg *TabChange) TypeID() int {
return 118
}
type IssueEvent struct {
message
MessageID uint64

View file

@ -1188,9 +1188,9 @@ func DecodeBatchMeta(reader BytesReader) (Message, error) {
return msg, err
}
func DecodeBatchMetadata(reader BytesReader) (Message, error) {
func DecodeBatchMetadataDeprecated(reader BytesReader) (Message, error) {
var err error = nil
msg := &BatchMetadata{}
msg := &BatchMetadataDeprecated{}
if msg.Version, err = reader.ReadUint(); err != nil {
return nil, err
}
@ -1314,6 +1314,39 @@ func DecodeResourceTiming(reader BytesReader) (Message, error) {
return msg, err
}
func DecodeBatchMetadata(reader BytesReader) (Message, error) {
var err error = nil
msg := &BatchMetadata{}
if msg.Version, err = reader.ReadUint(); err != nil {
return nil, err
}
if msg.PageNo, err = reader.ReadUint(); err != nil {
return nil, err
}
if msg.FirstIndex, err = reader.ReadUint(); err != nil {
return nil, err
}
if msg.Timestamp, err = reader.ReadInt(); err != nil {
return nil, err
}
if msg.Location, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.TabId, err = reader.ReadString(); err != nil {
return nil, err
}
return msg, err
}
func DecodeTabChange(reader BytesReader) (Message, error) {
var err error = nil
msg := &TabChange{}
if msg.TabId, err = reader.ReadString(); err != nil {
return nil, err
}
return msg, err
}
func DecodeIssueEvent(reader BytesReader) (Message, error) {
var err error = nil
msg := &IssueEvent{}
@ -1914,7 +1947,7 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
case 80:
return DecodeBatchMeta(reader)
case 81:
return DecodeBatchMetadata(reader)
return DecodeBatchMetadataDeprecated(reader)
case 82:
return DecodePartitionedMessage(reader)
case 112:
@ -1927,6 +1960,10 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
return DecodeUnbindNodes(reader)
case 116:
return DecodeResourceTiming(reader)
case 117:
return DecodeBatchMetadata(reader)
case 118:
return DecodeTabChange(reader)
case 125:
return DecodeIssueEvent(reader)
case 126:

View file

@ -71,7 +71,7 @@ class CreateDocument(Message):
__id__ = 7
def __init__(self, ):
pass
class CreateElementNode(Message):
@ -689,7 +689,7 @@ class BatchMeta(Message):
self.timestamp = timestamp
class BatchMetadata(Message):
class BatchMetadataDeprecated(Message):
__id__ = 81
def __init__(self, version, page_no, first_index, timestamp, location):
@ -759,6 +759,25 @@ class ResourceTiming(Message):
self.cached = cached
class BatchMetadata(Message):
__id__ = 117
def __init__(self, version, page_no, first_index, timestamp, location, tab_id):
self.version = version
self.page_no = page_no
self.first_index = first_index
self.timestamp = timestamp
self.location = location
self.tab_id = tab_id
class TabChange(Message):
__id__ = 118
def __init__(self, tab_id):
self.tab_id = tab_id
class IssueEvent(Message):
__id__ = 125

View file

@ -634,7 +634,7 @@ class MessageCodec(Codec):
)
if message_id == 81:
return BatchMetadata(
return BatchMetadataDeprecated(
version=self.read_uint(reader),
page_no=self.read_uint(reader),
first_index=self.read_uint(reader),
@ -689,6 +689,21 @@ class MessageCodec(Codec):
cached=self.read_boolean(reader)
)
if message_id == 117:
return BatchMetadata(
version=self.read_uint(reader),
page_no=self.read_uint(reader),
first_index=self.read_uint(reader),
timestamp=self.read_int(reader),
location=self.read_string(reader),
tab_id=self.read_string(reader)
)
if message_id == 118:
return TabChange(
tab_id=self.read_string(reader)
)
if message_id == 125:
return IssueEvent(
message_id=self.read_uint(reader),

View file

@ -673,6 +673,14 @@ export default class RawMessageReader extends PrimitiveReader {
};
}
case 118: {
const tabId = this.readString(); if (tabId === null) { return resetPointer() }
return {
tp: MType.TabChange,
tabId,
};
}
case 90: {
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
const projectID = this.readUint(); if (projectID === null) { return resetPointer() }

View file

@ -3,7 +3,7 @@
import { MType } from './raw.gen'
const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,54,55,57,58,59,60,61,67,69,70,71,72,73,74,75,76,77,113,114,90,93,96,100,102,103,105]
const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,54,55,57,58,59,60,61,67,69,70,71,72,73,74,75,76,77,113,114,118,90,93,96,100,102,103,105]
export function isDOMType(t: MType) {
return DOM_TYPES.includes(t)
}

View file

@ -58,6 +58,7 @@ import type {
RawSelectionChange,
RawMouseThrashing,
RawResourceTiming,
RawTabChange,
RawIosSessionStart,
RawIosCustomEvent,
RawIosScreenChanges,
@ -178,6 +179,8 @@ export type MouseThrashing = RawMouseThrashing & Timed
export type ResourceTiming = RawResourceTiming & Timed
export type TabChange = RawTabChange & Timed
export type IosSessionStart = RawIosSessionStart & Timed
export type IosCustomEvent = RawIosCustomEvent & Timed

View file

@ -56,6 +56,7 @@ export const enum MType {
SelectionChange = 113,
MouseThrashing = 114,
ResourceTiming = 116,
TabChange = 118,
IosSessionStart = 90,
IosCustomEvent = 93,
IosScreenChanges = 96,
@ -447,6 +448,11 @@ export interface RawResourceTiming {
cached: boolean,
}
export interface RawTabChange {
tp: MType.TabChange,
tabId: string,
}
export interface RawIosSessionStart {
tp: MType.IosSessionStart,
timestamp: number,
@ -518,4 +524,4 @@ export interface RawIosNetworkCall {
}
export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequest | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall;
export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequest | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall;

View file

@ -57,6 +57,7 @@ export const TP_MAP = {
113: MType.SelectionChange,
114: MType.MouseThrashing,
116: MType.ResourceTiming,
118: MType.TabChange,
90: MType.IosSessionStart,
93: MType.IosCustomEvent,
96: MType.IosScreenChanges,

View file

@ -414,7 +414,7 @@ type TrZustand = [
state: string,
]
type TrBatchMetadata = [
type TrBatchMetadataDeprecated = [
type: 81,
version: number,
pageNo: number,
@ -470,8 +470,23 @@ type TrResourceTiming = [
cached: boolean,
]
type TrBatchMetadata = [
type: 117,
version: number,
pageNo: number,
firstIndex: number,
timestamp: number,
location: string,
tabId: string,
]
export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequest | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming
type TrTabChange = [
type: 118,
tabId: string,
]
export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequest | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadataDeprecated | TrPartitionedMessage | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrBatchMetadata | TrTabChange
export default function translate(tMsg: TrackerMessage): RawMessage | null {
switch(tMsg[0]) {
@ -940,6 +955,13 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
}
}
case 118: {
return {
tp: MType.TabChange,
tabId: tMsg[1],
}
}
default:
return null
}

View file

@ -432,7 +432,7 @@ message 80, 'BatchMeta', :replayer => false, :tracker => false do
end
# since tracker 3.6.0 TODO: for webworker only
message 81, 'BatchMetadata', :replayer => false do
message 81, 'BatchMetadataDeprecated', :replayer => false do
uint 'Version'
uint 'PageNo'
uint 'FirstIndex'
@ -484,6 +484,19 @@ message 116, 'ResourceTiming', :replayer => :devtools do
boolean 'Cached'
end
message 117, 'BatchMetadata', :replayer => false do
uint 'Version'
uint 'PageNo'
uint 'FirstIndex'
int 'Timestamp'
string 'Location'
string 'TabId'
end
message 118, 'TabChange' do
string 'TabId'
end
## Backend-only
message 125, 'IssueEvent', :replayer => false, :tracker => false do
uint 'MessageID'

View file

@ -1,3 +1,7 @@
# 8.0.0
- **[breaking]** support for multi-tab sessions
# 7.0.3
- Prevent auto restart after manual stop

View file

@ -11,6 +11,7 @@ type Start = {
pageNo: number
timestamp: number
url: string
tabId: string
} & Options
type Auth = {

View file

@ -61,13 +61,15 @@ export declare const enum Type {
AdoptedSSRemoveOwner = 77,
JSException = 78,
Zustand = 79,
BatchMetadata = 81,
BatchMetadataDeprecated = 81,
PartitionedMessage = 82,
InputChange = 112,
SelectionChange = 113,
MouseThrashing = 114,
UnbindNodes = 115,
ResourceTiming = 116,
BatchMetadata = 117,
TabChange = 118,
}
@ -480,8 +482,8 @@ export type Zustand = [
/*state:*/ string,
]
export type BatchMetadata = [
/*type:*/ Type.BatchMetadata,
export type BatchMetadataDeprecated = [
/*type:*/ Type.BatchMetadataDeprecated,
/*version:*/ number,
/*pageNo:*/ number,
/*firstIndex:*/ number,
@ -536,6 +538,21 @@ export type ResourceTiming = [
/*cached:*/ boolean,
]
export type BatchMetadata = [
/*type:*/ Type.BatchMetadata,
/*version:*/ number,
/*pageNo:*/ number,
/*firstIndex:*/ number,
/*timestamp:*/ number,
/*location:*/ string,
/*tabId:*/ string,
]
type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequest | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming
export type TabChange = [
/*type:*/ Type.TabChange,
/*tabId:*/ string,
]
type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequest | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadataDeprecated | PartitionedMessage | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | BatchMetadata | TabChange
export default Message

View file

@ -64,6 +64,7 @@ type AppOptions = {
session_reset_key: string
session_token_key: string
session_pageno_key: string
session_tabid_key: string
local_uuid_key: string
ingestPoint: string
resourceBaseHref: string | null // resourceHref?
@ -124,6 +125,7 @@ export default class App {
session_token_key: '__openreplay_token',
session_pageno_key: '__openreplay_pageno',
session_reset_key: '__openreplay_reset',
session_tabid_key: '__openreplay_tabid',
local_uuid_key: '__openreplay_uuid',
ingestPoint: DEFAULT_INGEST_POINT,
resourceBaseHref: null,
@ -455,6 +457,7 @@ export default class App {
url: document.URL,
connAttemptCount: this.options.connAttemptCount,
connAttemptGap: this.options.connAttemptGap,
tabId: this.session.getTabId(),
})
const lsReset = this.sessionStorage.getItem(this.options.session_reset_key) !== null

View file

@ -764,15 +764,15 @@ export function Zustand(
]
}
export function BatchMetadata(
export function BatchMetadataDeprecated(
version: number,
pageNo: number,
firstIndex: number,
timestamp: number,
location: string,
): Messages.BatchMetadata {
): Messages.BatchMetadataDeprecated {
return [
Messages.Type.BatchMetadata,
Messages.Type.BatchMetadataDeprecated,
version,
pageNo,
firstIndex,
@ -869,3 +869,31 @@ export function ResourceTiming(
]
}
export function BatchMetadata(
version: number,
pageNo: number,
firstIndex: number,
timestamp: number,
location: string,
tabId: string,
): Messages.BatchMetadata {
return [
Messages.Type.BatchMetadata,
version,
pageNo,
firstIndex,
timestamp,
location,
tabId,
]
}
export function TabChange(
tabId: string,
): Messages.TabChange {
return [
Messages.Type.TabChange,
tabId,
]
}

View file

@ -1,4 +1,5 @@
import type App from './index.js'
import { generateRandomId } from '../utils.js'
interface SessionInfo {
sessionID: string | undefined
@ -12,6 +13,7 @@ type OnUpdateCallback = (i: Partial<SessionInfo>) => void
export type Options = {
session_token_key: string
session_pageno_key: string
session_tabid_key: string
}
export default class Session {
@ -21,8 +23,11 @@ export default class Session {
private readonly callbacks: OnUpdateCallback[] = []
private timestamp = 0
private projectID: string | undefined
private readonly tabId: string
constructor(private readonly app: App, private readonly options: Options) {}
constructor(private readonly app: App, private readonly options: Options) {
this.createTabId()
}
attachUpdateCallback(cb: OnUpdateCallback) {
this.callbacks.push(cb)
@ -61,6 +66,7 @@ export default class Session {
this.metadata[key] = value
this.handleUpdate({ metadata: { [key]: value } })
}
setUserID(userID: string) {
this.userID = userID
this.handleUpdate({ userID })
@ -86,10 +92,11 @@ export default class Session {
}
getSessionToken(): string | undefined {
return this.app.sessionStorage.getItem(this.options.session_token_key) || undefined
return this.app.localStorage.getItem(this.options.session_token_key) || undefined
}
setSessionToken(token: string): void {
this.app.sessionStorage.setItem(this.options.session_token_key, token)
this.app.localStorage.setItem(this.options.session_token_key, token)
}
applySessionHash(hash: string) {
@ -102,7 +109,7 @@ export default class Session {
if (!pageNoStr || !token) {
return
}
this.app.sessionStorage.setItem(this.options.session_token_key, token)
this.app.localStorage.setItem(this.options.session_token_key, token)
this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNoStr)
}
@ -115,6 +122,14 @@ export default class Session {
return encodeURI(String(pageNo) + '&' + token)
}
public getTabId(): string {
return this.tabId
}
private createTabId() {
this.app.sessionStorage.setItem(this.options.session_tabid_key, generateRandomId(16))
}
getInfo(): SessionInfo {
return {
sessionID: this.sessionID,
@ -126,7 +141,7 @@ export default class Session {
}
reset(): void {
this.app.sessionStorage.removeItem(this.options.session_token_key)
this.app.localStorage.removeItem(this.options.session_token_key)
this.metadata = {}
this.userID = null
this.sessionID = undefined

View file

@ -25,6 +25,7 @@ import Fonts from './modules/fonts.js'
import Network from './modules/network.js'
import ConstructedStyleSheets from './modules/constructedStyleSheets.js'
import Selection from './modules/selection.js'
import Tabs from './modules/tabs.js'
import { IN_BROWSER, deprecationWarn, DOCS_HOST } from './utils.js'
import type { Options as AppOptions } from './app/index.js'
@ -136,6 +137,7 @@ export default class API {
Fonts(app)
Network(app, options.network)
Selection(app)
Tabs(app)
;(window as any).__OPENREPLAY__ = this
if (options.autoResetOnWindowOpen) {

View file

@ -0,0 +1,12 @@
import type App from '../app/index.js'
import { TabChange } from '../app/messages.gen.js'
export default function (app: App): void {
function changeTab() {
if (!document.hidden) app.safe(() => app.send(TabChange(app.session.getTabId())))
}
if (document.hidden !== undefined) {
app.attachEventListener(document, 'visibilitychange', changeTab as EventListener, false, false)
}
}

View file

@ -100,3 +100,16 @@ export function canAccessIframe(iframe: HTMLIFrameElement) {
return false
}
}
function dec2hex(dec: number) {
return dec.toString(16).padStart(2, '0')
}
export function generateRandomId(len: number) {
const arr: Uint8Array = new Uint8Array((len || 40) / 2)
// msCrypto = IE11
// @ts-ignore
const safeCrypto = window.crypto || window.msCrypto
safeCrypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}

View file

@ -19,6 +19,7 @@ export default class BatchWriter {
private timestamp: number,
private url: string,
private readonly onBatch: (batch: Uint8Array) => void,
private tabId: string,
) {
this.prepare()
}
@ -50,6 +51,7 @@ export default class BatchWriter {
this.nextIndex,
this.timestamp,
this.url,
this.tabId,
]
this.writeType(batchMetadata)
this.writeFields(batchMetadata)

View file

@ -246,7 +246,7 @@ export default class MessageEncoder extends PrimitiveEncoder {
return this.string(msg[1]) && this.string(msg[2])
break
case Messages.Type.BatchMetadata:
case Messages.Type.BatchMetadataDeprecated:
return this.uint(msg[1]) && this.uint(msg[2]) && this.uint(msg[3]) && this.int(msg[4]) && this.string(msg[5])
break
@ -274,6 +274,14 @@ export default class MessageEncoder extends PrimitiveEncoder {
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]) && this.uint(msg[9]) && this.boolean(msg[10])
break
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]) && this.string(msg[6])
break
case Messages.Type.TabChange:
return this.string(msg[1])
break
}
}

View file

@ -21,8 +21,9 @@ const AUTO_SEND_INTERVAL = 10 * 1000
let sender: QueueSender | null = null
let writer: BatchWriter | null = null
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let workerStatus: WorkerStatus = WorkerStatus.NotActive
// let afterSleepRestarts = 0
function finalize(): void {
if (!writer) {
return
@ -73,7 +74,7 @@ let sendIntervalID: ReturnType<typeof setInterval> | null = null
let restartTimeoutID: ReturnType<typeof setTimeout>
// @ts-ignore
self.onmessage = ({ data }: any): any => {
self.onmessage = ({ data }: { data: ToWorkerData }): any => {
if (data == null) {
finalize()
return
@ -146,6 +147,7 @@ self.onmessage = ({ data }: any): any => {
data.timestamp,
data.url,
(batch) => sender && sender.push(batch),
data.tabId,
)
if (sendIntervalID === null) {
sendIntervalID = setInterval(finalize, AUTO_SEND_INTERVAL)