Merge pull request #694 from openreplay/adopted-style-sheets
Tracker 3.6.0 and message schema update * feat (tracker, backend, player): Adopted Style Sheets maintenance * refactor(tracker,player): compact messages representation (as array) * feat (tracker,backend): Use real sessionStart timestamp decoded from token on start * fix (frontend/assist): Fix activity timeouts logic in assist * fix (tracker): maintain scroll, mousemove, mouseclick, exceptions inside iFrames * fix (tracker): img module url resolving * fix (tracker): critical bug in observer (missing nodes) * feat (tracker): sessionHash returned on stop can be used for continuing session on start
This commit is contained in:
commit
f19a7df354
53 changed files with 2606 additions and 913 deletions
1
backend/cmd/assets/file
Normal file
1
backend/cmd/assets/file
Normal file
|
|
@ -0,0 +1 @@
|
|||
GROUP_CACHE=from_file
|
||||
92
backend/cmd/db/values.yaml
Normal file
92
backend/cmd/db/values.yaml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
chalice:
|
||||
env:
|
||||
jwt_secret: SetARandomStringHere
|
||||
clickhouse:
|
||||
enabled: false
|
||||
fromVersion: v1.6.0
|
||||
global:
|
||||
domainName: openreplay.local
|
||||
email:
|
||||
emailFrom: OpenReplay<do-not-reply@openreplay.com>
|
||||
emailHost: ""
|
||||
emailPassword: ""
|
||||
emailPort: "587"
|
||||
emailSslCert: ""
|
||||
emailSslKey: ""
|
||||
emailUseSsl: "false"
|
||||
emailUseTls: "true"
|
||||
emailUser: ""
|
||||
enterpriseEditionLicense: ""
|
||||
ingress:
|
||||
controller:
|
||||
config:
|
||||
enable-real-ip: true
|
||||
force-ssl-redirect: false
|
||||
max-worker-connections: 0
|
||||
proxy-body-size: 10m
|
||||
ssl-redirect: false
|
||||
extraArgs:
|
||||
default-ssl-certificate: app/openreplay-ssl
|
||||
ingressClass: openreplay
|
||||
ingressClassResource:
|
||||
name: openreplay
|
||||
service:
|
||||
externalTrafficPolicy: Local
|
||||
kafka:
|
||||
kafkaHost: kafka.db.svc.cluster.local
|
||||
kafkaPort: "9092"
|
||||
kafkaUseSsl: "false"
|
||||
zookeeperHost: databases-zookeeper.svc.cluster.local
|
||||
zookeeperNonTLSPort: 2181
|
||||
postgresql:
|
||||
postgresqlDatabase: postgres
|
||||
postgresqlHost: postgresql.db.svc.cluster.local
|
||||
postgresqlPassword: changeMePassword
|
||||
postgresqlPort: "5432"
|
||||
postgresqlUser: postgres
|
||||
redis:
|
||||
redisHost: redis-master.db.svc.cluster.local
|
||||
redisPort: "6379"
|
||||
s3:
|
||||
accessKey: changeMeMinioAccessKey
|
||||
assetsBucket: sessions-assets
|
||||
endpoint: http://minio.db.svc.cluster.local:9000
|
||||
recordingsBucket: mobs
|
||||
region: us-east-1
|
||||
secretKey: changeMeMinioPassword
|
||||
sourcemapsBucket: sourcemaps
|
||||
ingress-nginx:
|
||||
controller:
|
||||
config:
|
||||
enable-real-ip: true
|
||||
force-ssl-redirect: false
|
||||
max-worker-connections: 0
|
||||
proxy-body-size: 10m
|
||||
ssl-redirect: false
|
||||
extraArgs:
|
||||
default-ssl-certificate: app/openreplay-ssl
|
||||
ingressClass: openreplay
|
||||
ingressClassResource:
|
||||
name: openreplay
|
||||
service:
|
||||
externalTrafficPolicy: Local
|
||||
kafka:
|
||||
kafkaHost: kafka.db.svc.cluster.local
|
||||
kafkaPort: "9092"
|
||||
kafkaUseSsl: "false"
|
||||
zookeeperHost: databases-zookeeper.svc.cluster.local
|
||||
zookeeperNonTLSPort: 2181
|
||||
minio:
|
||||
global:
|
||||
minio:
|
||||
accessKey: changeMeMinioAccessKey
|
||||
secretKey: changeMeMinioPassword
|
||||
postgresql:
|
||||
postgresqlDatabase: postgres
|
||||
postgresqlHost: postgresql.db.svc.cluster.local
|
||||
postgresqlPassword: changeMePassword
|
||||
postgresqlPort: "5432"
|
||||
postgresqlUser: postgres
|
||||
redis:
|
||||
redisHost: redis-master.db.svc.cluster.local
|
||||
redisPort: "6379"
|
||||
|
|
@ -57,6 +57,21 @@ func (e *AssetsCache) ParseAssets(sessID uint64, msg messages.Message) messages.
|
|||
}
|
||||
newMsg.SetMeta(msg.Meta())
|
||||
return newMsg
|
||||
case *messages.AdoptedSSReplaceURLBased:
|
||||
newMsg := &messages.AdoptedSSReplace{
|
||||
SheetID: m.SheetID,
|
||||
Text: e.handleCSS(sessID, m.BaseURL, m.Text),
|
||||
}
|
||||
newMsg.SetMeta(msg.Meta())
|
||||
return newMsg
|
||||
case *messages.AdoptedSSInsertRuleURLBased:
|
||||
newMsg := &messages.AdoptedSSInsertRule{
|
||||
SheetID: m.SheetID,
|
||||
Index: m.Index,
|
||||
Rule: e.handleCSS(sessID, m.BaseURL, m.Rule),
|
||||
}
|
||||
newMsg.SetMeta(msg.Meta())
|
||||
return newMsg
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package messages
|
||||
|
||||
func IsReplayerType(id int) bool {
|
||||
return 0 == id || 2 == 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 || 22 == id || 37 == id || 38 == id || 39 == id || 40 == id || 41 == id || 44 == id || 45 == id || 46 == id || 47 == id || 48 == id || 49 == id || 54 == id || 55 == id || 59 == id || 69 == id || 70 == 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 || 22 == id || 37 == id || 38 == id || 39 == id || 40 == id || 41 == id || 44 == id || 45 == id || 46 == id || 47 == id || 48 == id || 49 == id || 54 == id || 55 == 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 || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id
|
||||
}
|
||||
|
||||
func IsIOSType(id int) bool {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ const (
|
|||
|
||||
MsgSessionStart = 1
|
||||
|
||||
MsgSessionDisconnect = 2
|
||||
|
||||
MsgSessionEnd = 3
|
||||
|
||||
MsgSetPageLocation = 4
|
||||
|
|
@ -144,6 +142,20 @@ const (
|
|||
|
||||
MsgCreateIFrameDocument = 70
|
||||
|
||||
MsgAdoptedSSReplaceURLBased = 71
|
||||
|
||||
MsgAdoptedSSReplace = 72
|
||||
|
||||
MsgAdoptedSSInsertRuleURLBased = 73
|
||||
|
||||
MsgAdoptedSSInsertRule = 74
|
||||
|
||||
MsgAdoptedSSDeleteRule = 75
|
||||
|
||||
MsgAdoptedSSAddOwner = 76
|
||||
|
||||
MsgAdoptedSSRemoveOwner = 77
|
||||
|
||||
MsgIOSBatchMeta = 107
|
||||
|
||||
MsgIOSSessionStart = 90
|
||||
|
|
@ -387,38 +399,6 @@ func (msg *SessionStart) TypeID() int {
|
|||
return 1
|
||||
}
|
||||
|
||||
type SessionDisconnect struct {
|
||||
message
|
||||
Timestamp uint64
|
||||
}
|
||||
|
||||
func (msg *SessionDisconnect) Encode() []byte {
|
||||
buf := make([]byte, 11)
|
||||
buf[0] = 2
|
||||
p := 1
|
||||
p = WriteUint(msg.Timestamp, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *SessionDisconnect) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *SessionDisconnect) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *SessionDisconnect) TypeID() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
type SessionEnd struct {
|
||||
message
|
||||
Timestamp uint64
|
||||
|
|
@ -2812,6 +2792,252 @@ func (msg *CreateIFrameDocument) TypeID() int {
|
|||
return 70
|
||||
}
|
||||
|
||||
type AdoptedSSReplaceURLBased struct {
|
||||
message
|
||||
SheetID uint64
|
||||
Text string
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplaceURLBased) Encode() []byte {
|
||||
buf := make([]byte, 31+len(msg.Text)+len(msg.BaseURL))
|
||||
buf[0] = 71
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteString(msg.Text, buf, p)
|
||||
p = WriteString(msg.BaseURL, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplaceURLBased) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplaceURLBased) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplaceURLBased) TypeID() int {
|
||||
return 71
|
||||
}
|
||||
|
||||
type AdoptedSSReplace struct {
|
||||
message
|
||||
SheetID uint64
|
||||
Text string
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplace) Encode() []byte {
|
||||
buf := make([]byte, 21+len(msg.Text))
|
||||
buf[0] = 72
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteString(msg.Text, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplace) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplace) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSReplace) TypeID() int {
|
||||
return 72
|
||||
}
|
||||
|
||||
type AdoptedSSInsertRuleURLBased struct {
|
||||
message
|
||||
SheetID uint64
|
||||
Rule string
|
||||
Index uint64
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRuleURLBased) Encode() []byte {
|
||||
buf := make([]byte, 41+len(msg.Rule)+len(msg.BaseURL))
|
||||
buf[0] = 73
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteString(msg.Rule, buf, p)
|
||||
p = WriteUint(msg.Index, buf, p)
|
||||
p = WriteString(msg.BaseURL, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRuleURLBased) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRuleURLBased) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRuleURLBased) TypeID() int {
|
||||
return 73
|
||||
}
|
||||
|
||||
type AdoptedSSInsertRule struct {
|
||||
message
|
||||
SheetID uint64
|
||||
Rule string
|
||||
Index uint64
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRule) Encode() []byte {
|
||||
buf := make([]byte, 31+len(msg.Rule))
|
||||
buf[0] = 74
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteString(msg.Rule, buf, p)
|
||||
p = WriteUint(msg.Index, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRule) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRule) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSInsertRule) TypeID() int {
|
||||
return 74
|
||||
}
|
||||
|
||||
type AdoptedSSDeleteRule struct {
|
||||
message
|
||||
SheetID uint64
|
||||
Index uint64
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSDeleteRule) Encode() []byte {
|
||||
buf := make([]byte, 21)
|
||||
buf[0] = 75
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteUint(msg.Index, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSDeleteRule) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSDeleteRule) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSDeleteRule) TypeID() int {
|
||||
return 75
|
||||
}
|
||||
|
||||
type AdoptedSSAddOwner struct {
|
||||
message
|
||||
SheetID uint64
|
||||
ID uint64
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSAddOwner) Encode() []byte {
|
||||
buf := make([]byte, 21)
|
||||
buf[0] = 76
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteUint(msg.ID, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSAddOwner) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSAddOwner) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSAddOwner) TypeID() int {
|
||||
return 76
|
||||
}
|
||||
|
||||
type AdoptedSSRemoveOwner struct {
|
||||
message
|
||||
SheetID uint64
|
||||
ID uint64
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSRemoveOwner) Encode() []byte {
|
||||
buf := make([]byte, 21)
|
||||
buf[0] = 77
|
||||
p := 1
|
||||
p = WriteUint(msg.SheetID, buf, p)
|
||||
p = WriteUint(msg.ID, buf, p)
|
||||
return buf[:p]
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSRemoveOwner) EncodeWithIndex() []byte {
|
||||
encoded := msg.Encode()
|
||||
if IsIOSType(msg.TypeID()) {
|
||||
return encoded
|
||||
}
|
||||
data := make([]byte, len(encoded)+8)
|
||||
copy(data[8:], encoded[:])
|
||||
binary.LittleEndian.PutUint64(data[0:], msg.Meta().Index)
|
||||
return data
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSRemoveOwner) Decode() Message {
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *AdoptedSSRemoveOwner) TypeID() int {
|
||||
return 77
|
||||
}
|
||||
|
||||
type IOSBatchMeta struct {
|
||||
message
|
||||
Timestamp uint64
|
||||
|
|
|
|||
|
|
@ -117,15 +117,6 @@ func DecodeSessionStart(reader io.Reader) (Message, error) {
|
|||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeSessionDisconnect(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &SessionDisconnect{}
|
||||
if msg.Timestamp, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeSessionEnd(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &SessionEnd{}
|
||||
|
|
@ -1219,6 +1210,102 @@ func DecodeCreateIFrameDocument(reader io.Reader) (Message, error) {
|
|||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSReplaceURLBased(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSReplaceURLBased{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Text, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.BaseURL, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSReplace(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSReplace{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Text, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSInsertRuleURLBased(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSInsertRuleURLBased{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Rule, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Index, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.BaseURL, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSInsertRule(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSInsertRule{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Rule, err = ReadString(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Index, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSDeleteRule(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSDeleteRule{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Index, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSAddOwner(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSAddOwner{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.ID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeAdoptedSSRemoveOwner(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &AdoptedSSRemoveOwner{}
|
||||
if msg.SheetID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.ID, err = ReadUint(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
func DecodeIOSBatchMeta(reader io.Reader) (Message, error) {
|
||||
var err error = nil
|
||||
msg := &IOSBatchMeta{}
|
||||
|
|
@ -1639,9 +1726,6 @@ func ReadMessage(t uint64, reader io.Reader) (Message, error) {
|
|||
case 1:
|
||||
return DecodeSessionStart(reader)
|
||||
|
||||
case 2:
|
||||
return DecodeSessionDisconnect(reader)
|
||||
|
||||
case 3:
|
||||
return DecodeSessionEnd(reader)
|
||||
|
||||
|
|
@ -1834,6 +1918,27 @@ func ReadMessage(t uint64, reader io.Reader) (Message, error) {
|
|||
case 70:
|
||||
return DecodeCreateIFrameDocument(reader)
|
||||
|
||||
case 71:
|
||||
return DecodeAdoptedSSReplaceURLBased(reader)
|
||||
|
||||
case 72:
|
||||
return DecodeAdoptedSSReplace(reader)
|
||||
|
||||
case 73:
|
||||
return DecodeAdoptedSSInsertRuleURLBased(reader)
|
||||
|
||||
case 74:
|
||||
return DecodeAdoptedSSInsertRule(reader)
|
||||
|
||||
case 75:
|
||||
return DecodeAdoptedSSDeleteRule(reader)
|
||||
|
||||
case 76:
|
||||
return DecodeAdoptedSSAddOwner(reader)
|
||||
|
||||
case 77:
|
||||
return DecodeAdoptedSSRemoveOwner(reader)
|
||||
|
||||
case 107:
|
||||
return DecodeIOSBatchMeta(reader)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,25 @@ class BatchMeta(Message):
|
|||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class BatchMetadata(Message):
|
||||
__id__ = 81
|
||||
|
||||
def __init__(self, version, page_no, first_index, timestamp, location):
|
||||
self.version = version
|
||||
self.page_no = page_no
|
||||
self.first_index = first_index
|
||||
self.timestamp = timestamp
|
||||
self.location = location
|
||||
|
||||
|
||||
class PartitionedMessage(Message):
|
||||
__id__ = 82
|
||||
|
||||
def __init__(self, part_no, part_total):
|
||||
self.part_no = part_no
|
||||
self.part_total = part_total
|
||||
|
||||
|
||||
class Timestamp(Message):
|
||||
__id__ = 0
|
||||
|
||||
|
|
@ -44,13 +63,6 @@ class SessionStart(Message):
|
|||
self.user_id = user_id
|
||||
|
||||
|
||||
class SessionDisconnect(Message):
|
||||
__id__ = 2
|
||||
|
||||
def __init__(self, timestamp):
|
||||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class SessionEnd(Message):
|
||||
__id__ = 3
|
||||
|
||||
|
|
@ -637,13 +649,6 @@ class CustomIssue(Message):
|
|||
self.payload = payload
|
||||
|
||||
|
||||
class PageClose(Message):
|
||||
__id__ = 65
|
||||
|
||||
def __init__(self, ):
|
||||
|
||||
|
||||
|
||||
class AssetCache(Message):
|
||||
__id__ = 66
|
||||
|
||||
|
|
@ -679,6 +684,66 @@ class CreateIFrameDocument(Message):
|
|||
self.id = id
|
||||
|
||||
|
||||
class AdoptedSSReplaceURLBased(Message):
|
||||
__id__ = 71
|
||||
|
||||
def __init__(self, sheet_id, text, base_url):
|
||||
self.sheet_id = sheet_id
|
||||
self.text = text
|
||||
self.base_url = base_url
|
||||
|
||||
|
||||
class AdoptedSSReplace(Message):
|
||||
__id__ = 72
|
||||
|
||||
def __init__(self, sheet_id, text):
|
||||
self.sheet_id = sheet_id
|
||||
self.text = text
|
||||
|
||||
|
||||
class AdoptedSSInsertRuleURLBased(Message):
|
||||
__id__ = 73
|
||||
|
||||
def __init__(self, sheet_id, rule, index, base_url):
|
||||
self.sheet_id = sheet_id
|
||||
self.rule = rule
|
||||
self.index = index
|
||||
self.base_url = base_url
|
||||
|
||||
|
||||
class AdoptedSSInsertRule(Message):
|
||||
__id__ = 74
|
||||
|
||||
def __init__(self, sheet_id, rule, index):
|
||||
self.sheet_id = sheet_id
|
||||
self.rule = rule
|
||||
self.index = index
|
||||
|
||||
|
||||
class AdoptedSSDeleteRule(Message):
|
||||
__id__ = 75
|
||||
|
||||
def __init__(self, sheet_id, index):
|
||||
self.sheet_id = sheet_id
|
||||
self.index = index
|
||||
|
||||
|
||||
class AdoptedSSAddOwner(Message):
|
||||
__id__ = 76
|
||||
|
||||
def __init__(self, sheet_id, id):
|
||||
self.sheet_id = sheet_id
|
||||
self.id = id
|
||||
|
||||
|
||||
class AdoptedSSRemoveOwner(Message):
|
||||
__id__ = 77
|
||||
|
||||
def __init__(self, sheet_id, id):
|
||||
self.sheet_id = sheet_id
|
||||
self.id = id
|
||||
|
||||
|
||||
class IOSBatchMeta(Message):
|
||||
__id__ = 107
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,21 @@ class MessageCodec(Codec):
|
|||
timestamp=self.read_int(reader)
|
||||
)
|
||||
|
||||
if message_id == 81:
|
||||
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)
|
||||
)
|
||||
|
||||
if message_id == 82:
|
||||
return PartitionedMessage(
|
||||
part_no=self.read_uint(reader),
|
||||
part_total=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 0:
|
||||
return Timestamp(
|
||||
timestamp=self.read_uint(reader)
|
||||
|
|
@ -51,11 +66,6 @@ class MessageCodec(Codec):
|
|||
user_id=self.read_string(reader)
|
||||
)
|
||||
|
||||
if message_id == 2:
|
||||
return SessionDisconnect(
|
||||
timestamp=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 3:
|
||||
return SessionEnd(
|
||||
timestamp=self.read_uint(reader)
|
||||
|
|
@ -522,11 +532,6 @@ class MessageCodec(Codec):
|
|||
payload=self.read_string(reader)
|
||||
)
|
||||
|
||||
if message_id == 65:
|
||||
return PageClose(
|
||||
|
||||
)
|
||||
|
||||
if message_id == 66:
|
||||
return AssetCache(
|
||||
url=self.read_string(reader)
|
||||
|
|
@ -554,6 +559,52 @@ class MessageCodec(Codec):
|
|||
id=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 71:
|
||||
return AdoptedSSReplaceURLBased(
|
||||
sheet_id=self.read_uint(reader),
|
||||
text=self.read_string(reader),
|
||||
base_url=self.read_string(reader)
|
||||
)
|
||||
|
||||
if message_id == 72:
|
||||
return AdoptedSSReplace(
|
||||
sheet_id=self.read_uint(reader),
|
||||
text=self.read_string(reader)
|
||||
)
|
||||
|
||||
if message_id == 73:
|
||||
return AdoptedSSInsertRuleURLBased(
|
||||
sheet_id=self.read_uint(reader),
|
||||
rule=self.read_string(reader),
|
||||
index=self.read_uint(reader),
|
||||
base_url=self.read_string(reader)
|
||||
)
|
||||
|
||||
if message_id == 74:
|
||||
return AdoptedSSInsertRule(
|
||||
sheet_id=self.read_uint(reader),
|
||||
rule=self.read_string(reader),
|
||||
index=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 75:
|
||||
return AdoptedSSDeleteRule(
|
||||
sheet_id=self.read_uint(reader),
|
||||
index=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 76:
|
||||
return AdoptedSSAddOwner(
|
||||
sheet_id=self.read_uint(reader),
|
||||
id=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 77:
|
||||
return AdoptedSSRemoveOwner(
|
||||
sheet_id=self.read_uint(reader),
|
||||
id=self.read_uint(reader)
|
||||
)
|
||||
|
||||
if message_id == 107:
|
||||
return IOSBatchMeta(
|
||||
timestamp=self.read_uint(reader),
|
||||
|
|
|
|||
|
|
@ -17,22 +17,6 @@ export interface Props {
|
|||
|
||||
function ChatWindow({ userId, incomeStream, localStream, endCall, isPrestart }: Props) {
|
||||
const [localVideoEnabled, setLocalVideoEnabled] = useState(false)
|
||||
const [remoteVideoEnabled, setRemoteVideoEnabled] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!incomeStream || incomeStream.length === 0) { return }
|
||||
const iid = setInterval(() => {
|
||||
const settings = incomeStream.map(stream => stream.getVideoTracks()[0]?.getSettings()).filter(Boolean)
|
||||
const isDummyVideoTrack = settings.length > 0 ? (settings.every(s => s.width === 2 || s.frameRate === 0 || s.frameRate === undefined)) : true
|
||||
const shouldBeEnabled = !isDummyVideoTrack
|
||||
if (shouldBeEnabled !== localVideoEnabled) {
|
||||
setRemoteVideoEnabled(shouldBeEnabled)
|
||||
}
|
||||
}, 1000)
|
||||
return () => clearInterval(iid)
|
||||
}, [ incomeStream, localVideoEnabled ])
|
||||
|
||||
const minimize = !localVideoEnabled && !remoteVideoEnabled
|
||||
|
||||
return (
|
||||
<Draggable handle=".handle" bounds="body">
|
||||
|
|
@ -43,15 +27,18 @@ function ChatWindow({ userId, incomeStream, localStream, endCall, isPrestart }:
|
|||
<div className="handle flex items-center p-2 cursor-move select-none border-b">
|
||||
<div className={stl.headerTitle}>
|
||||
<b>Talking to </b> {userId ? userId : 'Anonymous User'}
|
||||
<br />
|
||||
{incomeStream && incomeStream.length > 2 ? ' (+ other agents in the call)' : ''}
|
||||
</div>
|
||||
<Counter startTime={new Date().getTime() } className="text-sm ml-auto" />
|
||||
</div>
|
||||
<div className={cn(stl.videoWrapper, {'hidden' : minimize}, 'relative')}>
|
||||
{!incomeStream && <div className={stl.noVideo}>Error obtaining incoming streams</div>}
|
||||
{incomeStream && incomeStream.map(stream => <VideoContainer stream={ stream } />)}
|
||||
<div className="absolute bottom-0 right-0 z-50">
|
||||
<VideoContainer stream={ localStream ? localStream.stream : null } muted width={50} />
|
||||
<div className={cn(stl.videoWrapper, 'relative')} style={{ minHeight: localVideoEnabled ? 52 : undefined}}>
|
||||
{incomeStream
|
||||
? incomeStream.map(stream => <React.Fragment key={stream.id}><VideoContainer stream={ stream } /></React.Fragment>) : (
|
||||
<div className={stl.noVideo}>Error obtaining incoming streams</div>
|
||||
)}
|
||||
<div className={cn("absolute bottom-0 right-0 z-50", localVideoEnabled ? "" : "!hidden")}>
|
||||
<VideoContainer stream={ localStream ? localStream.stream : null } muted height={50} />
|
||||
</div>
|
||||
</div>
|
||||
<ChatControls videoEnabled={localVideoEnabled} setVideoEnabled={setLocalVideoEnabled} stream={localStream} endCall={endCall} isPrestart={isPrestart} />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
background-color: white;
|
||||
border: solid thin $gray-light;
|
||||
border-radius: 3px;
|
||||
position: fixed;
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
|
||||
.videoWrapper {
|
||||
height: 180px;
|
||||
overflow: hidden;
|
||||
max-height: 280px;
|
||||
display: flex;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ function onError(e) {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
userId: String;
|
||||
userId: string;
|
||||
toggleChatWindow: (state) => void;
|
||||
calling: CallingState;
|
||||
annotating: boolean;
|
||||
|
|
@ -69,7 +69,12 @@ function AssistActions({
|
|||
}, [peerConnectionStatus]);
|
||||
|
||||
const addIncomeStream = (stream: MediaStream) => {
|
||||
setIncomeStream(oldState => [...oldState, stream]);
|
||||
setIncomeStream(oldState => {
|
||||
if (!oldState.find(existingStream => existingStream.id === stream.id)) {
|
||||
return [...oldState, stream]
|
||||
}
|
||||
return oldState
|
||||
});
|
||||
}
|
||||
|
||||
function call(agentIds?: string[]) {
|
||||
|
|
|
|||
|
|
@ -6,18 +6,30 @@ interface Props {
|
|||
width?: number
|
||||
}
|
||||
|
||||
function VideoContainer({ stream, muted = false, width = 280 }: Props) {
|
||||
function VideoContainer({ stream, muted = false, height = 280 }: Props) {
|
||||
const ref = useRef<HTMLVideoElement>(null);
|
||||
const [isEnabled, setEnabled] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.srcObject = stream;
|
||||
}
|
||||
}, [ ref.current, stream ])
|
||||
}, [ ref.current, stream, stream.getVideoTracks()[0]?.getSettings().width ])
|
||||
|
||||
useEffect(() => {
|
||||
if (!stream) { return }
|
||||
const iid = setInterval(() => {
|
||||
const settings = stream.getVideoTracks()[0]?.getSettings()
|
||||
const isDummyVideoTrack = settings.width === 2 || settings.frameRate === 0
|
||||
const shouldBeEnabled = !isDummyVideoTrack
|
||||
isEnabled !== shouldBeEnabled ? setEnabled(shouldBeEnabled) : null;
|
||||
}, 1000)
|
||||
return () => clearInterval(iid)
|
||||
}, [ stream, isEnabled ])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<video autoPlay ref={ ref } muted={ muted } style={{ width: width }} />
|
||||
<div className="flex-1" style={{ display: isEnabled ? undefined : 'none', border: "1px solid grey" }}>
|
||||
<video autoPlay ref={ ref } muted={ muted } style={{ height: height }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export function getStatusText(status: ConnectionStatus): string {
|
|||
return "Connected. Waiting for the data... (The tab might be inactive)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface State {
|
||||
calling: CallingState;
|
||||
peerConnectionStatus: ConnectionStatus;
|
||||
|
|
@ -77,11 +77,11 @@ const MAX_RECONNECTION_COUNT = 4;
|
|||
export default class AssistManager {
|
||||
private timeTravelJump = false;
|
||||
private jumped = false;
|
||||
|
||||
|
||||
constructor(private session: any, private md: MessageDistributor, private config: any) {}
|
||||
|
||||
private setStatus(status: ConnectionStatus) {
|
||||
if (getState().peerConnectionStatus === ConnectionStatus.Disconnected &&
|
||||
if (getState().peerConnectionStatus === ConnectionStatus.Disconnected &&
|
||||
status !== ConnectionStatus.Connected) {
|
||||
return
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ export default class AssistManager {
|
|||
if (document.hidden) {
|
||||
this.socketCloseTimeout = setTimeout(() => {
|
||||
const state = getState()
|
||||
if (document.hidden &&
|
||||
if (document.hidden &&
|
||||
(state.calling === CallingState.NoCall && state.remoteControl === RemoteControlStatus.Enabled)) {
|
||||
this.socket?.close()
|
||||
}
|
||||
|
|
@ -124,8 +124,16 @@ export default class AssistManager {
|
|||
const jmr = new JSONRawMessageReader()
|
||||
const reader = new MStreamReader(jmr)
|
||||
let waitingForMessages = true
|
||||
let showDisconnectTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
let disconnectTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
let inactiveTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
function clearDisconnectTimeout() {
|
||||
disconnectTimeout && clearTimeout(disconnectTimeout)
|
||||
disconnectTimeout = undefined
|
||||
}
|
||||
function clearInactiveTimeout() {
|
||||
inactiveTimeout && clearTimeout(inactiveTimeout)
|
||||
inactiveTimeout = undefined
|
||||
}
|
||||
|
||||
const now = +new Date()
|
||||
update({ assistStart: now })
|
||||
|
|
@ -187,17 +195,17 @@ export default class AssistManager {
|
|||
id === socket.id && this.toggleRemoteControl(false)
|
||||
})
|
||||
socket.on('SESSION_RECONNECTED', () => {
|
||||
showDisconnectTimeout && clearTimeout(showDisconnectTimeout)
|
||||
inactiveTimeout && clearTimeout(inactiveTimeout)
|
||||
clearDisconnectTimeout()
|
||||
clearInactiveTimeout()
|
||||
this.setStatus(ConnectionStatus.Connected)
|
||||
})
|
||||
|
||||
socket.on('UPDATE_SESSION', (data) => {
|
||||
showDisconnectTimeout && clearTimeout(showDisconnectTimeout)
|
||||
socket.on('UPDATE_SESSION', ({ active }) => {
|
||||
clearDisconnectTimeout()
|
||||
!inactiveTimeout && this.setStatus(ConnectionStatus.Connected)
|
||||
if (typeof data.active === "boolean") {
|
||||
if (data.active) {
|
||||
inactiveTimeout && clearTimeout(inactiveTimeout)
|
||||
if (typeof active === "boolean") {
|
||||
clearInactiveTimeout()
|
||||
if (active) {
|
||||
this.setStatus(ConnectionStatus.Connected)
|
||||
} else {
|
||||
inactiveTimeout = setTimeout(() => this.setStatus(ConnectionStatus.Inactive), 5000)
|
||||
|
|
@ -206,8 +214,8 @@ export default class AssistManager {
|
|||
})
|
||||
socket.on('SESSION_DISCONNECTED', e => {
|
||||
waitingForMessages = true
|
||||
showDisconnectTimeout && clearTimeout(showDisconnectTimeout)
|
||||
showDisconnectTimeout = setTimeout(() => {
|
||||
clearDisconnectTimeout()
|
||||
disconnectTimeout = setTimeout(() => {
|
||||
if (this.cleaned) { return }
|
||||
this.setStatus(ConnectionStatus.Disconnected)
|
||||
}, 30000)
|
||||
|
|
@ -230,7 +238,7 @@ export default class AssistManager {
|
|||
})
|
||||
socket.on('call_end', this.onRemoteCallEnd)
|
||||
|
||||
document.addEventListener('visibilitychange', this.onVisChange)
|
||||
document.addEventListener('visibilitychange', this.onVisChange)
|
||||
|
||||
})
|
||||
}
|
||||
|
|
@ -254,14 +262,14 @@ export default class AssistManager {
|
|||
private onMouseClick = (e: MouseEvent): void => {
|
||||
if (!this.socket) { return; }
|
||||
if (getState().annotating) { return; } // ignore clicks while annotating
|
||||
|
||||
|
||||
const data = this.md.getInternalViewportCoordinates(e)
|
||||
// const el = this.md.getElementFromPoint(e); // requires requestiong node_id from domManager
|
||||
const el = this.md.getElementFromInternalPoint(data)
|
||||
if (el instanceof HTMLElement) {
|
||||
el.focus()
|
||||
el.oninput = e => {
|
||||
if (el instanceof HTMLTextAreaElement
|
||||
if (el instanceof HTMLTextAreaElement
|
||||
|| el instanceof HTMLInputElement
|
||||
) {
|
||||
this.socket && this.socket.emit("input", el.value)
|
||||
|
|
@ -346,7 +354,7 @@ export default class AssistManager {
|
|||
console.log('getting call from', call.peer)
|
||||
call.answer(this.callArgs.localStream.stream)
|
||||
this.callConnection.push(call)
|
||||
|
||||
|
||||
this.callArgs.localStream.onVideoTrack(vTrack => {
|
||||
const sender = call.peerConnection.getSenders().find(s => s.track?.kind === "video")
|
||||
if (!sender) {
|
||||
|
|
@ -355,12 +363,12 @@ export default class AssistManager {
|
|||
}
|
||||
sender.replaceTrack(vTrack)
|
||||
})
|
||||
|
||||
|
||||
call.on('stream', stream => {
|
||||
this.callArgs && this.callArgs.onStream(stream)
|
||||
});
|
||||
// call.peerConnection.addEventListener("track", e => console.log('newtrack',e.track))
|
||||
|
||||
|
||||
call.on("close", this.onRemoteCallEnd)
|
||||
call.on("error", (e) => {
|
||||
console.error("PeerJS error (on call):", e)
|
||||
|
|
@ -377,7 +385,7 @@ export default class AssistManager {
|
|||
|
||||
//call-reconnection connected
|
||||
// if (['peer-unavailable', 'network', 'webrtc'].includes(e.type)) {
|
||||
// this.setStatus(this.connectionAttempts++ < MAX_RECONNECTION_COUNT
|
||||
// this.setStatus(this.connectionAttempts++ < MAX_RECONNECTION_COUNT
|
||||
// ? ConnectionStatus.Connecting
|
||||
// : ConnectionStatus.Disconnected);
|
||||
// Reconnect...
|
||||
|
|
@ -420,15 +428,15 @@ export default class AssistManager {
|
|||
localStream: LocalStream,
|
||||
onStream: (s: MediaStream)=>void,
|
||||
onCallEnd: () => void,
|
||||
onReject: () => void,
|
||||
onReject: () => void,
|
||||
onError?: ()=> void,
|
||||
} | null = null
|
||||
|
||||
public setCallArgs(
|
||||
localStream: LocalStream,
|
||||
onStream: (s: MediaStream)=>void,
|
||||
onCallEnd: () => void,
|
||||
onReject: () => void,
|
||||
localStream: LocalStream,
|
||||
onStream: (s: MediaStream)=>void,
|
||||
onCallEnd: () => void,
|
||||
onReject: () => void,
|
||||
onError?: ()=> void,
|
||||
) {
|
||||
this.callArgs = {
|
||||
|
|
@ -451,7 +459,7 @@ export default class AssistManager {
|
|||
}
|
||||
}
|
||||
|
||||
/** Connecting to the other agents that are already
|
||||
/** Connecting to the other agents that are already
|
||||
* in the call with the user
|
||||
*/
|
||||
public addPeerCall(thirdPartyPeers: string[]) {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import type { Message, SetNodeScroll, CreateElementNode } from '../../messages';
|
|||
|
||||
import ListWalker from '../ListWalker';
|
||||
import StylesManager, { rewriteNodeStyleSheet } from './StylesManager';
|
||||
import { VElement, VText, VFragment, VDocument, VNode, VStyleElement } from './VirtualDOM';
|
||||
import { VElement, VText, VShadowRoot, VDocument, VNode, VStyleElement } from './VirtualDOM';
|
||||
import type { StyleElement } from './VirtualDOM';
|
||||
|
||||
|
||||
type HTMLElementWithValue = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
|
||||
const IGNORED_ATTRS = [ "autocomplete", "name" ];
|
||||
const IGNORED_ATTRS = [ "autocomplete" ];
|
||||
const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/; // regexp costs ~
|
||||
|
||||
|
||||
|
|
@ -24,10 +24,32 @@ const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/; // regexp costs ~
|
|||
// .replace(/\-webkit\-/g, "")
|
||||
// }
|
||||
|
||||
function insertRule(sheet: CSSStyleSheet, msg: { rule: string, index: number }) {
|
||||
try {
|
||||
sheet.insertRule(msg.rule, msg.index)
|
||||
} catch (e) {
|
||||
logger.warn(e, msg)
|
||||
try {
|
||||
sheet.insertRule(msg.rule)
|
||||
} catch (e) {
|
||||
logger.warn("Cannot insert rule.", e, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteRule(sheet: CSSStyleSheet, msg: { index: number }) {
|
||||
try {
|
||||
sheet.deleteRule(msg.index)
|
||||
} catch (e) {
|
||||
logger.warn(e, msg)
|
||||
}
|
||||
}
|
||||
|
||||
export default class DOMManager extends ListWalker<Message> {
|
||||
private vTexts: Map<number, VText> = new Map() // map vs object here?
|
||||
private vElements: Map<number, VElement> = new Map()
|
||||
private vRoots: Map<number, VFragment | VDocument> = new Map()
|
||||
private vRoots: Map<number, VShadowRoot | VDocument> = new Map()
|
||||
private styleSheets: Map<number, CSSStyleSheet> = new Map()
|
||||
|
||||
|
||||
private upperBodyId: number = -1;
|
||||
|
|
@ -42,6 +64,7 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
) {
|
||||
super()
|
||||
this.stylesManager = new StylesManager(screen)
|
||||
logger.log(this.vElements)
|
||||
}
|
||||
|
||||
append(m: Message): void {
|
||||
|
|
@ -117,6 +140,7 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
let node: Node | undefined
|
||||
let vn: VNode | undefined
|
||||
let doc: Document | null
|
||||
let styleSheet: CSSStyleSheet | undefined
|
||||
switch (msg.tp) {
|
||||
case "create_document":
|
||||
doc = this.screen.document;
|
||||
|
|
@ -134,7 +158,9 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
this.vElements = new Map([[0, vn]])
|
||||
const vDoc = new VDocument(doc)
|
||||
vDoc.insertChildAt(vn, 0)
|
||||
this.vRoots = new Map([[-1, vDoc]]) // todo: start from 0 (sync logic with tracker)
|
||||
this.vRoots = new Map([[0, vDoc]]) // watchout: id==0 for both Document and documentElement
|
||||
// this is done for the AdoptedCSS logic
|
||||
// todo: start from 0 (sync logic with tracker)
|
||||
this.stylesManager.reset()
|
||||
return
|
||||
case "create_text_node":
|
||||
|
|
@ -175,6 +201,10 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
let { name, value } = msg;
|
||||
vn = this.vElements.get(msg.id)
|
||||
if (!vn) { logger.error("Node not found", msg); return }
|
||||
if (vn.node.tagName === "INPUT" && name === "name") {
|
||||
// Otherwise binds local autocomplete values (maybe should ignore on the tracker level)
|
||||
return
|
||||
}
|
||||
if (name === "href" && vn.node.tagName === "LINK") {
|
||||
// @ts-ignore ?global ENV type // It've been done on backend (remove after testing in saas)
|
||||
// if (value.startsWith(window.env.ASSETS_HOST || window.location.origin + '/assets')) {
|
||||
|
|
@ -242,18 +272,7 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
logger.warn("Non-style node in CSS rules message (or sheet is null)", msg, vn);
|
||||
return
|
||||
}
|
||||
vn.onStyleSheet(sheet => {
|
||||
try {
|
||||
sheet.insertRule(msg.rule, msg.index)
|
||||
} catch (e) {
|
||||
logger.warn(e, msg)
|
||||
try {
|
||||
sheet.insertRule(msg.rule)
|
||||
} catch (e) {
|
||||
logger.warn("Cannot insert rule.", e, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
vn.onStyleSheet(sheet => insertRule(sheet, msg))
|
||||
return
|
||||
case "css_delete_rule":
|
||||
vn = this.vElements.get(msg.id)
|
||||
|
|
@ -262,35 +281,27 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
logger.warn("Non-style node in CSS rules message (or sheet is null)", msg, vn);
|
||||
return
|
||||
}
|
||||
vn.onStyleSheet(sheet => {
|
||||
try {
|
||||
sheet.deleteRule(msg.index)
|
||||
} catch (e) {
|
||||
logger.warn(e, msg)
|
||||
}
|
||||
})
|
||||
vn.onStyleSheet(sheet => deleteRule(sheet, msg))
|
||||
return
|
||||
case "create_i_frame_document":
|
||||
vn = this.vElements.get(msg.frameID)
|
||||
if (!vn) { logger.error("Node not found", msg); return }
|
||||
vn.enforceInsertion()
|
||||
const host = vn.node
|
||||
if (host instanceof HTMLIFrameElement) {
|
||||
const vDoc = new VDocument()
|
||||
this.vRoots.set(msg.id, vDoc)
|
||||
host.onload = () => {
|
||||
const doc = host.contentDocument
|
||||
if (!doc) {
|
||||
logger.warn("No iframe doc onload", msg, host)
|
||||
return
|
||||
}
|
||||
vDoc.setDocument(doc)
|
||||
vDoc.applyChanges()
|
||||
}
|
||||
const doc = host.contentDocument
|
||||
if (!doc) {
|
||||
logger.warn("No iframe doc onload", msg, host)
|
||||
return
|
||||
}
|
||||
vDoc.setDocument(doc)
|
||||
return;
|
||||
} else if (host instanceof Element) { // shadow DOM
|
||||
try {
|
||||
const shadowRoot = host.attachShadow({ mode: 'open' })
|
||||
vn = new VFragment(shadowRoot)
|
||||
vn = new VShadowRoot(shadowRoot)
|
||||
this.vRoots.set(msg.id, vn)
|
||||
} catch(e) {
|
||||
logger.warn("Can not attach shadow dom", e, msg)
|
||||
|
|
@ -299,6 +310,60 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
logger.warn("Context message host is not Element", msg)
|
||||
}
|
||||
return
|
||||
case "adopted_ss_insert_rule":
|
||||
styleSheet = this.styleSheets.get(msg.sheetID)
|
||||
if (!styleSheet) {
|
||||
logger.warn("No stylesheet was created for ", msg)
|
||||
return
|
||||
}
|
||||
insertRule(styleSheet, msg)
|
||||
return
|
||||
case "adopted_ss_delete_rule":
|
||||
styleSheet = this.styleSheets.get(msg.sheetID)
|
||||
if (!styleSheet) {
|
||||
logger.warn("No stylesheet was created for ", msg)
|
||||
return
|
||||
}
|
||||
deleteRule(styleSheet, msg)
|
||||
return
|
||||
case "adopted_ss_replace":
|
||||
styleSheet = this.styleSheets.get(msg.sheetID)
|
||||
if (!styleSheet) {
|
||||
logger.warn("No stylesheet was created for ", msg)
|
||||
return
|
||||
}
|
||||
// @ts-ignore
|
||||
styleSheet.replaceSync(msg.text)
|
||||
return
|
||||
case "adopted_ss_add_owner":
|
||||
vn = this.vRoots.get(msg.id)
|
||||
if (!vn) { logger.error("Node not found", msg); return }
|
||||
styleSheet = this.styleSheets.get(msg.sheetID)
|
||||
if (!styleSheet) {
|
||||
let context: typeof globalThis
|
||||
const rootNode = vn.node
|
||||
if (rootNode.nodeType === Node.DOCUMENT_NODE) {
|
||||
context = (rootNode as Document).defaultView
|
||||
} else {
|
||||
context = (rootNode as ShadowRoot).ownerDocument.defaultView
|
||||
}
|
||||
styleSheet = new context.CSSStyleSheet()
|
||||
this.styleSheets.set(msg.sheetID, styleSheet)
|
||||
}
|
||||
//@ts-ignore
|
||||
vn.node.adoptedStyleSheets = [...vn.node.adoptedStyleSheets, styleSheet]
|
||||
return
|
||||
case "adopted_ss_remove_owner":
|
||||
styleSheet = this.styleSheets.get(msg.sheetID)
|
||||
if (!styleSheet) {
|
||||
logger.warn("No stylesheet was created for ", msg)
|
||||
return
|
||||
}
|
||||
vn = this.vRoots.get(msg.id)
|
||||
if (!vn) { logger.error("Node not found", msg); return }
|
||||
//@ts-ignore
|
||||
vn.node.adoptedStyleSheets = [...vn.node.adoptedStyleSheets].filter(s => s !== styleSheet)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
type VChild = VElement | VText
|
||||
|
||||
export type VNode = VDocument | VFragment | VElement | VText
|
||||
export type VNode = VDocument | VShadowRoot | VElement | VText
|
||||
|
||||
abstract class VParent {
|
||||
abstract node: Node | null
|
||||
|
|
@ -67,6 +67,7 @@ export class VDocument extends VParent {
|
|||
return
|
||||
}
|
||||
const child = this.children[0]
|
||||
if (!child) { return }
|
||||
child.applyChanges()
|
||||
const htmlNode = child.node
|
||||
if (htmlNode.parentNode !== this.node) {
|
||||
|
|
@ -75,8 +76,8 @@ export class VDocument extends VParent {
|
|||
}
|
||||
}
|
||||
|
||||
export class VFragment extends VParent {
|
||||
constructor(public readonly node: DocumentFragment) { super() }
|
||||
export class VShadowRoot extends VParent {
|
||||
constructor(public readonly node: ShadowRoot) { super() }
|
||||
}
|
||||
|
||||
export class VElement extends VParent {
|
||||
|
|
@ -122,31 +123,32 @@ export class VElement extends VParent {
|
|||
type StyleSheetCallback = (s: CSSStyleSheet) => void
|
||||
export type StyleElement = HTMLStyleElement | SVGStyleElement
|
||||
export class VStyleElement extends VElement {
|
||||
// private loaded = false
|
||||
private loaded = false
|
||||
private stylesheetCallbacks: StyleSheetCallback[] = []
|
||||
constructor(public readonly node: StyleElement) {
|
||||
super(node) // Is it compiled correctly or with 2 node assignments?
|
||||
// node.onload = () => {
|
||||
// const sheet = node.sheet
|
||||
// if (sheet) {
|
||||
// this.stylesheetCallbacks.forEach(cb => cb(sheet))
|
||||
// } else {
|
||||
// console.warn("Style onload: sheet is null")
|
||||
// }
|
||||
// this.loaded = true
|
||||
// }
|
||||
node.onload = () => {
|
||||
const sheet = node.sheet
|
||||
if (sheet) {
|
||||
this.stylesheetCallbacks.forEach(cb => cb(sheet))
|
||||
this.stylesheetCallbacks = []
|
||||
} else {
|
||||
console.warn("Style onload: sheet is null")
|
||||
}
|
||||
this.loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
onStyleSheet(cb: StyleSheetCallback) {
|
||||
// if (this.loaded) {
|
||||
if (this.loaded) {
|
||||
if (!this.node.sheet) {
|
||||
console.warn("Style tag is loaded, but sheet is null")
|
||||
return
|
||||
}
|
||||
cb(this.node.sheet)
|
||||
// } else {
|
||||
// this.stylesheetCallbacks.push(cb)
|
||||
// }
|
||||
} else {
|
||||
this.stylesheetCallbacks.push(cb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,11 +79,11 @@ export default class WindowNodeCounter {
|
|||
|
||||
moveNode(id: number, parentId: number) {
|
||||
if (!this.nodes[ id ]) {
|
||||
console.error(`Wrong! Node with id ${ id } not found.`)
|
||||
console.warn(`Node Counter: Node with id ${ id } not found.`)
|
||||
return
|
||||
}
|
||||
if (!this.nodes[ parentId ]) {
|
||||
console.error(`Wrong! Node with id ${ parentId } (parentId) not found.`)
|
||||
console.warn(`Node Counter: Node with id ${ parentId } (parentId) not found.`)
|
||||
return
|
||||
}
|
||||
this.nodes[ id ].moveNode(this.nodes[ parentId ])
|
||||
|
|
|
|||
|
|
@ -1,18 +1,100 @@
|
|||
import type { RawMessage } from './raw'
|
||||
import type {
|
||||
RawMessage,
|
||||
RawSetNodeAttributeURLBased,
|
||||
RawSetNodeAttribute,
|
||||
RawSetCssDataURLBased,
|
||||
RawSetCssData,
|
||||
RawCssInsertRuleURLBased,
|
||||
RawCssInsertRule,
|
||||
RawAdoptedSsInsertRuleURLBased,
|
||||
RawAdoptedSsInsertRule,
|
||||
RawAdoptedSsReplaceURLBased,
|
||||
RawAdoptedSsReplace,
|
||||
} from './raw'
|
||||
import type { TrackerMessage } from './tracker'
|
||||
import translate from './tracker'
|
||||
import { TP_MAP } from './tracker-legacy'
|
||||
import { resolveURL, resolveCSS } from './urlResolve'
|
||||
|
||||
|
||||
function legacyTranslate(msg: any): RawMessage | null {
|
||||
const type = TP_MAP[msg._id as keyof typeof TP_MAP]
|
||||
if (!type) {
|
||||
return null
|
||||
}
|
||||
msg.tp = type
|
||||
delete msg._id
|
||||
return msg as RawMessage
|
||||
}
|
||||
|
||||
|
||||
// TODO: commonURLBased logic for feilds
|
||||
const resolvers = {
|
||||
"set_node_attribute_url_based": (msg: RawSetNodeAttributeURLBased): RawSetNodeAttribute =>
|
||||
({
|
||||
...msg,
|
||||
value: msg.name === 'src' || msg.name === 'href'
|
||||
? resolveURL(msg.baseURL, msg.value)
|
||||
: (msg.name === 'style'
|
||||
? resolveCSS(msg.baseURL, msg.value)
|
||||
: msg.value
|
||||
),
|
||||
tp: "set_node_attribute",
|
||||
}),
|
||||
"set_css_data_url_based": (msg: RawSetCssDataURLBased): RawSetCssData =>
|
||||
({
|
||||
...msg,
|
||||
data: resolveCSS(msg.baseURL, msg.data),
|
||||
tp: "set_css_data",
|
||||
}),
|
||||
"css_insert_rule_url_based": (msg: RawCssInsertRuleURLBased): RawCssInsertRule =>
|
||||
({
|
||||
...msg,
|
||||
rule: resolveCSS(msg.baseURL, msg.rule),
|
||||
tp: "css_insert_rule",
|
||||
}),
|
||||
"adopted_ss_insert_rule_url_based": (msg: RawAdoptedSsInsertRuleURLBased): RawAdoptedSsInsertRule =>
|
||||
({
|
||||
...msg,
|
||||
rule: resolveCSS(msg.baseURL, msg.rule),
|
||||
tp: "adopted_ss_insert_rule",
|
||||
}),
|
||||
"adopted_ss_replace_url_based": (msg: RawAdoptedSsReplaceURLBased): RawAdoptedSsReplace =>
|
||||
({
|
||||
...msg,
|
||||
text: resolveCSS(msg.baseURL, msg.text),
|
||||
tp: "adopted_ss_replace"
|
||||
})
|
||||
} as const
|
||||
|
||||
type ResolvableType = keyof typeof resolvers
|
||||
type ResolvableRawMessage = RawMessage & { tp: ResolvableType }
|
||||
|
||||
function isResolvable(msg: RawMessage): msg is ResolvableRawMessage {
|
||||
//@ts-ignore
|
||||
return resolvers[msg.tp] !== undefined
|
||||
}
|
||||
|
||||
import { TP_MAP } from './raw'
|
||||
|
||||
export default class JSONRawMessageReader {
|
||||
constructor(private messages: any[] = []){}
|
||||
append(messages: any[]) {
|
||||
constructor(private messages: TrackerMessage[] = []){}
|
||||
append(messages: TrackerMessage[]) {
|
||||
this.messages = this.messages.concat(messages)
|
||||
}
|
||||
readMessage(): RawMessage | null {
|
||||
const msg = this.messages.shift()
|
||||
let msg = this.messages.shift()
|
||||
if (!msg) { return null }
|
||||
msg.tp = TP_MAP[msg._id]
|
||||
delete msg._id
|
||||
return msg as RawMessage
|
||||
const rawMsg = Array.isArray(msg)
|
||||
? translate(msg)
|
||||
: legacyTranslate(msg)
|
||||
if (!rawMsg) {
|
||||
return this.readMessage()
|
||||
}
|
||||
if (isResolvable(rawMsg)) {
|
||||
//@ts-ignore ??? too complex typscript...
|
||||
return resolvers[rawMsg.tp](rawMsg)
|
||||
}
|
||||
return rawMsg
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,68 +1,25 @@
|
|||
import type { Message } from './message'
|
||||
import type {
|
||||
RawMessage,
|
||||
RawSetNodeAttributeURLBased,
|
||||
RawSetNodeAttribute,
|
||||
RawSetCssDataURLBased,
|
||||
RawSetCssData,
|
||||
RawCssInsertRuleURLBased,
|
||||
RawCssInsertRule,
|
||||
} from './raw'
|
||||
import type { RawMessage } from './raw'
|
||||
import RawMessageReader from './RawMessageReader'
|
||||
import { resolveURL, resolveCSS } from './urlResolve'
|
||||
|
||||
interface RawMessageReaderI {
|
||||
readMessage(): RawMessage | null
|
||||
}
|
||||
|
||||
const resolveMsg = {
|
||||
"set_node_attribute_url_based": (msg: RawSetNodeAttributeURLBased): RawSetNodeAttribute =>
|
||||
({
|
||||
...msg,
|
||||
value: msg.name === 'src' || msg.name === 'href'
|
||||
? resolveURL(msg.baseURL, msg.value)
|
||||
: (msg.name === 'style'
|
||||
? resolveCSS(msg.baseURL, msg.value)
|
||||
: msg.value
|
||||
),
|
||||
tp: "set_node_attribute",
|
||||
}),
|
||||
"set_css_data_url_based": (msg: RawSetCssDataURLBased): RawSetCssData =>
|
||||
({
|
||||
...msg,
|
||||
data: resolveCSS(msg.baseURL, msg.data),
|
||||
tp: "set_css_data",
|
||||
}),
|
||||
"css_insert_rule_url_based": (msg: RawCssInsertRuleURLBased): RawCssInsertRule =>
|
||||
({
|
||||
...msg,
|
||||
rule: resolveCSS(msg.baseURL, msg.rule),
|
||||
tp: "css_insert_rule",
|
||||
})
|
||||
}
|
||||
|
||||
export default class MStreamReader {
|
||||
constructor(private readonly r: RawMessageReaderI = new RawMessageReader()){}
|
||||
constructor(private readonly r: RawMessageReaderI = new RawMessageReader(), private startTs: number = 0){}
|
||||
|
||||
// append(buf: Uint8Array) {
|
||||
// this.r.append(buf)
|
||||
// }
|
||||
|
||||
private t0: number = 0
|
||||
private t: number = 0
|
||||
private idx: number = 0
|
||||
readNext(): Message | null {
|
||||
let msg = this.r.readMessage()
|
||||
if (msg === null) { return null }
|
||||
if (msg.tp === "timestamp" || msg.tp === "batch_meta") {
|
||||
this.t0 = this.t0 || msg.timestamp
|
||||
this.t = msg.timestamp - this.t0
|
||||
if (msg.tp === "timestamp") {
|
||||
this.startTs = this.startTs || msg.timestamp
|
||||
this.t = msg.timestamp - this.startTs
|
||||
return this.readNext()
|
||||
}
|
||||
|
||||
// why typescript doesn't work here?
|
||||
msg = (resolveMsg[msg.tp] || ((m:RawMessage)=>m))(msg)
|
||||
|
||||
return Object.assign(msg, {
|
||||
time: this.t,
|
||||
_index: this.idx++,
|
||||
|
|
|
|||
|
|
@ -17,18 +17,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
|
||||
switch (tp) {
|
||||
|
||||
case 80: {
|
||||
const pageNo = this.readUint(); if (pageNo === null) { return resetPointer() }
|
||||
const firstIndex = this.readUint(); if (firstIndex === null) { return resetPointer() }
|
||||
const timestamp = this.readInt(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "batch_meta",
|
||||
pageNo,
|
||||
firstIndex,
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
case 0: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
|
|
@ -37,14 +25,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 2: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "session_disconnect",
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
case 4: {
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const referrer = this.readString(); if (referrer === null) { return resetPointer() }
|
||||
|
|
@ -187,16 +167,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 17: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const label = this.readString(); if (label === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "set_input_target",
|
||||
id,
|
||||
label,
|
||||
};
|
||||
}
|
||||
|
||||
case 18: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
|
|
@ -239,90 +209,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 23: {
|
||||
const requestStart = this.readUint(); if (requestStart === null) { return resetPointer() }
|
||||
const responseStart = this.readUint(); if (responseStart === null) { return resetPointer() }
|
||||
const responseEnd = this.readUint(); if (responseEnd === null) { return resetPointer() }
|
||||
const domContentLoadedEventStart = this.readUint(); if (domContentLoadedEventStart === null) { return resetPointer() }
|
||||
const domContentLoadedEventEnd = this.readUint(); if (domContentLoadedEventEnd === null) { return resetPointer() }
|
||||
const loadEventStart = this.readUint(); if (loadEventStart === null) { return resetPointer() }
|
||||
const loadEventEnd = this.readUint(); if (loadEventEnd === null) { return resetPointer() }
|
||||
const firstPaint = this.readUint(); if (firstPaint === null) { return resetPointer() }
|
||||
const firstContentfulPaint = this.readUint(); if (firstContentfulPaint === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "page_load_timing",
|
||||
requestStart,
|
||||
responseStart,
|
||||
responseEnd,
|
||||
domContentLoadedEventStart,
|
||||
domContentLoadedEventEnd,
|
||||
loadEventStart,
|
||||
loadEventEnd,
|
||||
firstPaint,
|
||||
firstContentfulPaint,
|
||||
};
|
||||
}
|
||||
|
||||
case 24: {
|
||||
const speedIndex = this.readUint(); if (speedIndex === null) { return resetPointer() }
|
||||
const visuallyComplete = this.readUint(); if (visuallyComplete === null) { return resetPointer() }
|
||||
const timeToInteractive = this.readUint(); if (timeToInteractive === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "page_render_timing",
|
||||
speedIndex,
|
||||
visuallyComplete,
|
||||
timeToInteractive,
|
||||
};
|
||||
}
|
||||
|
||||
case 25: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const message = this.readString(); if (message === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "js_exception",
|
||||
name,
|
||||
message,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 27: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "raw_custom_event",
|
||||
name,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 28: {
|
||||
const id = this.readString(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "user_id",
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 29: {
|
||||
const id = this.readString(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "user_anonymous_id",
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 30: {
|
||||
const key = this.readString(); if (key === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "metadata",
|
||||
key,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 37: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
|
|
@ -389,14 +275,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 42: {
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "state_action",
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
case 44: {
|
||||
const action = this.readString(); if (action === null) { return resetPointer() }
|
||||
const state = this.readString(); if (state === null) { return resetPointer() }
|
||||
|
|
@ -469,28 +347,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 53: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const duration = this.readUint(); if (duration === null) { return resetPointer() }
|
||||
const ttfb = this.readUint(); if (ttfb === null) { return resetPointer() }
|
||||
const headerSize = this.readUint(); if (headerSize === null) { return resetPointer() }
|
||||
const encodedBodySize = this.readUint(); if (encodedBodySize === null) { return resetPointer() }
|
||||
const decodedBodySize = this.readUint(); if (decodedBodySize === null) { return resetPointer() }
|
||||
const url = this.readString(); if (url === null) { return resetPointer() }
|
||||
const initiator = this.readString(); if (initiator === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "resource_timing",
|
||||
timestamp,
|
||||
duration,
|
||||
ttfb,
|
||||
headerSize,
|
||||
encodedBodySize,
|
||||
decodedBodySize,
|
||||
url,
|
||||
initiator,
|
||||
};
|
||||
}
|
||||
|
||||
case 54: {
|
||||
const downlink = this.readUint(); if (downlink === null) { return resetPointer() }
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
|
|
@ -555,34 +411,6 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 63: {
|
||||
const type = this.readString(); if (type === null) { return resetPointer() }
|
||||
const value = this.readString(); if (value === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "technical_info",
|
||||
type,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
case 64: {
|
||||
const name = this.readString(); if (name === null) { return resetPointer() }
|
||||
const payload = this.readString(); if (payload === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "custom_issue",
|
||||
name,
|
||||
payload,
|
||||
};
|
||||
}
|
||||
|
||||
case 65: {
|
||||
|
||||
return {
|
||||
tp: "page_close",
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
case 67: {
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
|
|
@ -621,6 +449,84 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
};
|
||||
}
|
||||
|
||||
case 71: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const text = this.readString(); if (text === null) { return resetPointer() }
|
||||
const baseURL = this.readString(); if (baseURL === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_replace_url_based",
|
||||
sheetID,
|
||||
text,
|
||||
baseURL,
|
||||
};
|
||||
}
|
||||
|
||||
case 72: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const text = this.readString(); if (text === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_replace",
|
||||
sheetID,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
case 73: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
const baseURL = this.readString(); if (baseURL === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_insert_rule_url_based",
|
||||
sheetID,
|
||||
rule,
|
||||
index,
|
||||
baseURL,
|
||||
};
|
||||
}
|
||||
|
||||
case 74: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const rule = this.readString(); if (rule === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_insert_rule",
|
||||
sheetID,
|
||||
rule,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 75: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const index = this.readUint(); if (index === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_delete_rule",
|
||||
sheetID,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
case 76: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_add_owner",
|
||||
sheetID,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 77: {
|
||||
const sheetID = this.readUint(); if (sheetID === null) { return resetPointer() }
|
||||
const id = this.readUint(); if (id === null) { return resetPointer() }
|
||||
return {
|
||||
tp: "adopted_ss_remove_owner",
|
||||
sheetID,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
case 90: {
|
||||
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
|
||||
const projectID = this.readUint(); if (projectID === null) { return resetPointer() }
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
import type { Timed } from './timed'
|
||||
import type { RawMessage } from './raw'
|
||||
import type {
|
||||
RawBatchMeta,
|
||||
RawTimestamp,
|
||||
RawSessionDisconnect,
|
||||
RawSetPageLocation,
|
||||
RawSetViewportSize,
|
||||
RawSetViewportScroll,
|
||||
|
|
@ -19,42 +17,36 @@ import type {
|
|||
RawSetNodeData,
|
||||
RawSetCssData,
|
||||
RawSetNodeScroll,
|
||||
RawSetInputTarget,
|
||||
RawSetInputValue,
|
||||
RawSetInputChecked,
|
||||
RawMouseMove,
|
||||
RawConsoleLog,
|
||||
RawPageLoadTiming,
|
||||
RawPageRenderTiming,
|
||||
RawJsException,
|
||||
RawRawCustomEvent,
|
||||
RawUserID,
|
||||
RawUserAnonymousID,
|
||||
RawMetadata,
|
||||
RawCssInsertRule,
|
||||
RawCssDeleteRule,
|
||||
RawFetch,
|
||||
RawProfiler,
|
||||
RawOTable,
|
||||
RawStateAction,
|
||||
RawRedux,
|
||||
RawVuex,
|
||||
RawMobX,
|
||||
RawNgRx,
|
||||
RawGraphQl,
|
||||
RawPerformanceTrack,
|
||||
RawResourceTiming,
|
||||
RawConnectionInformation,
|
||||
RawSetPageVisibility,
|
||||
RawLongTask,
|
||||
RawSetNodeAttributeURLBased,
|
||||
RawSetCssDataURLBased,
|
||||
RawTechnicalInfo,
|
||||
RawCustomIssue,
|
||||
RawPageClose,
|
||||
RawCssInsertRuleURLBased,
|
||||
RawMouseClick,
|
||||
RawCreateIFrameDocument,
|
||||
RawAdoptedSsReplaceURLBased,
|
||||
RawAdoptedSsReplace,
|
||||
RawAdoptedSsInsertRuleURLBased,
|
||||
RawAdoptedSsInsertRule,
|
||||
RawAdoptedSsDeleteRule,
|
||||
RawAdoptedSsAddOwner,
|
||||
RawAdoptedSsRemoveOwner,
|
||||
RawIosSessionStart,
|
||||
RawIosCustomEvent,
|
||||
RawIosScreenChanges,
|
||||
|
|
@ -67,12 +59,8 @@ import type {
|
|||
export type Message = RawMessage & Timed
|
||||
|
||||
|
||||
export type BatchMeta = RawBatchMeta & Timed
|
||||
|
||||
export type Timestamp = RawTimestamp & Timed
|
||||
|
||||
export type SessionDisconnect = RawSessionDisconnect & Timed
|
||||
|
||||
export type SetPageLocation = RawSetPageLocation & Timed
|
||||
|
||||
export type SetViewportSize = RawSetViewportSize & Timed
|
||||
|
|
@ -99,8 +87,6 @@ export type SetCssData = RawSetCssData & Timed
|
|||
|
||||
export type SetNodeScroll = RawSetNodeScroll & Timed
|
||||
|
||||
export type SetInputTarget = RawSetInputTarget & Timed
|
||||
|
||||
export type SetInputValue = RawSetInputValue & Timed
|
||||
|
||||
export type SetInputChecked = RawSetInputChecked & Timed
|
||||
|
|
@ -109,20 +95,6 @@ export type MouseMove = RawMouseMove & Timed
|
|||
|
||||
export type ConsoleLog = RawConsoleLog & Timed
|
||||
|
||||
export type PageLoadTiming = RawPageLoadTiming & Timed
|
||||
|
||||
export type PageRenderTiming = RawPageRenderTiming & Timed
|
||||
|
||||
export type JsException = RawJsException & Timed
|
||||
|
||||
export type RawCustomEvent = RawRawCustomEvent & Timed
|
||||
|
||||
export type UserID = RawUserID & Timed
|
||||
|
||||
export type UserAnonymousID = RawUserAnonymousID & Timed
|
||||
|
||||
export type Metadata = RawMetadata & Timed
|
||||
|
||||
export type CssInsertRule = RawCssInsertRule & Timed
|
||||
|
||||
export type CssDeleteRule = RawCssDeleteRule & Timed
|
||||
|
|
@ -133,8 +105,6 @@ export type Profiler = RawProfiler & Timed
|
|||
|
||||
export type OTable = RawOTable & Timed
|
||||
|
||||
export type StateAction = RawStateAction & Timed
|
||||
|
||||
export type Redux = RawRedux & Timed
|
||||
|
||||
export type Vuex = RawVuex & Timed
|
||||
|
|
@ -147,8 +117,6 @@ export type GraphQl = RawGraphQl & Timed
|
|||
|
||||
export type PerformanceTrack = RawPerformanceTrack & Timed
|
||||
|
||||
export type ResourceTiming = RawResourceTiming & Timed
|
||||
|
||||
export type ConnectionInformation = RawConnectionInformation & Timed
|
||||
|
||||
export type SetPageVisibility = RawSetPageVisibility & Timed
|
||||
|
|
@ -159,18 +127,26 @@ export type SetNodeAttributeURLBased = RawSetNodeAttributeURLBased & Timed
|
|||
|
||||
export type SetCssDataURLBased = RawSetCssDataURLBased & Timed
|
||||
|
||||
export type TechnicalInfo = RawTechnicalInfo & Timed
|
||||
|
||||
export type CustomIssue = RawCustomIssue & Timed
|
||||
|
||||
export type PageClose = RawPageClose & Timed
|
||||
|
||||
export type CssInsertRuleURLBased = RawCssInsertRuleURLBased & Timed
|
||||
|
||||
export type MouseClick = RawMouseClick & Timed
|
||||
|
||||
export type CreateIFrameDocument = RawCreateIFrameDocument & Timed
|
||||
|
||||
export type AdoptedSsReplaceURLBased = RawAdoptedSsReplaceURLBased & Timed
|
||||
|
||||
export type AdoptedSsReplace = RawAdoptedSsReplace & Timed
|
||||
|
||||
export type AdoptedSsInsertRuleURLBased = RawAdoptedSsInsertRuleURLBased & Timed
|
||||
|
||||
export type AdoptedSsInsertRule = RawAdoptedSsInsertRule & Timed
|
||||
|
||||
export type AdoptedSsDeleteRule = RawAdoptedSsDeleteRule & Timed
|
||||
|
||||
export type AdoptedSsAddOwner = RawAdoptedSsAddOwner & Timed
|
||||
|
||||
export type AdoptedSsRemoveOwner = RawAdoptedSsRemoveOwner & Timed
|
||||
|
||||
export type IosSessionStart = RawIosSessionStart & Timed
|
||||
|
||||
export type IosCustomEvent = RawIosCustomEvent & Timed
|
||||
|
|
|
|||
|
|
@ -1,85 +1,11 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
export const TP_MAP = {
|
||||
80: "batch_meta",
|
||||
0: "timestamp",
|
||||
2: "session_disconnect",
|
||||
4: "set_page_location",
|
||||
5: "set_viewport_size",
|
||||
6: "set_viewport_scroll",
|
||||
7: "create_document",
|
||||
8: "create_element_node",
|
||||
9: "create_text_node",
|
||||
10: "move_node",
|
||||
11: "remove_node",
|
||||
12: "set_node_attribute",
|
||||
13: "remove_node_attribute",
|
||||
14: "set_node_data",
|
||||
15: "set_css_data",
|
||||
16: "set_node_scroll",
|
||||
17: "set_input_target",
|
||||
18: "set_input_value",
|
||||
19: "set_input_checked",
|
||||
20: "mouse_move",
|
||||
22: "console_log",
|
||||
23: "page_load_timing",
|
||||
24: "page_render_timing",
|
||||
25: "js_exception",
|
||||
27: "raw_custom_event",
|
||||
28: "user_id",
|
||||
29: "user_anonymous_id",
|
||||
30: "metadata",
|
||||
37: "css_insert_rule",
|
||||
38: "css_delete_rule",
|
||||
39: "fetch",
|
||||
40: "profiler",
|
||||
41: "o_table",
|
||||
42: "state_action",
|
||||
44: "redux",
|
||||
45: "vuex",
|
||||
46: "mob_x",
|
||||
47: "ng_rx",
|
||||
48: "graph_ql",
|
||||
49: "performance_track",
|
||||
53: "resource_timing",
|
||||
54: "connection_information",
|
||||
55: "set_page_visibility",
|
||||
59: "long_task",
|
||||
60: "set_node_attribute_url_based",
|
||||
61: "set_css_data_url_based",
|
||||
63: "technical_info",
|
||||
64: "custom_issue",
|
||||
65: "page_close",
|
||||
67: "css_insert_rule_url_based",
|
||||
69: "mouse_click",
|
||||
70: "create_i_frame_document",
|
||||
90: "ios_session_start",
|
||||
93: "ios_custom_event",
|
||||
96: "ios_screen_changes",
|
||||
100: "ios_click_event",
|
||||
102: "ios_performance_event",
|
||||
103: "ios_log",
|
||||
105: "ios_network_call",
|
||||
}
|
||||
|
||||
|
||||
export interface RawBatchMeta {
|
||||
tp: "batch_meta",
|
||||
pageNo: number,
|
||||
firstIndex: number,
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawTimestamp {
|
||||
tp: "timestamp",
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawSessionDisconnect {
|
||||
tp: "session_disconnect",
|
||||
timestamp: number,
|
||||
}
|
||||
|
||||
export interface RawSetPageLocation {
|
||||
tp: "set_page_location",
|
||||
url: string,
|
||||
|
|
@ -164,12 +90,6 @@ export interface RawSetNodeScroll {
|
|||
y: number,
|
||||
}
|
||||
|
||||
export interface RawSetInputTarget {
|
||||
tp: "set_input_target",
|
||||
id: number,
|
||||
label: string,
|
||||
}
|
||||
|
||||
export interface RawSetInputValue {
|
||||
tp: "set_input_value",
|
||||
id: number,
|
||||
|
|
@ -195,55 +115,6 @@ export interface RawConsoleLog {
|
|||
value: string,
|
||||
}
|
||||
|
||||
export interface RawPageLoadTiming {
|
||||
tp: "page_load_timing",
|
||||
requestStart: number,
|
||||
responseStart: number,
|
||||
responseEnd: number,
|
||||
domContentLoadedEventStart: number,
|
||||
domContentLoadedEventEnd: number,
|
||||
loadEventStart: number,
|
||||
loadEventEnd: number,
|
||||
firstPaint: number,
|
||||
firstContentfulPaint: number,
|
||||
}
|
||||
|
||||
export interface RawPageRenderTiming {
|
||||
tp: "page_render_timing",
|
||||
speedIndex: number,
|
||||
visuallyComplete: number,
|
||||
timeToInteractive: number,
|
||||
}
|
||||
|
||||
export interface RawJsException {
|
||||
tp: "js_exception",
|
||||
name: string,
|
||||
message: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawRawCustomEvent {
|
||||
tp: "raw_custom_event",
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawUserID {
|
||||
tp: "user_id",
|
||||
id: string,
|
||||
}
|
||||
|
||||
export interface RawUserAnonymousID {
|
||||
tp: "user_anonymous_id",
|
||||
id: string,
|
||||
}
|
||||
|
||||
export interface RawMetadata {
|
||||
tp: "metadata",
|
||||
key: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawCssInsertRule {
|
||||
tp: "css_insert_rule",
|
||||
id: number,
|
||||
|
|
@ -282,11 +153,6 @@ export interface RawOTable {
|
|||
value: string,
|
||||
}
|
||||
|
||||
export interface RawStateAction {
|
||||
tp: "state_action",
|
||||
type: string,
|
||||
}
|
||||
|
||||
export interface RawRedux {
|
||||
tp: "redux",
|
||||
action: string,
|
||||
|
|
@ -329,18 +195,6 @@ export interface RawPerformanceTrack {
|
|||
usedJSHeapSize: number,
|
||||
}
|
||||
|
||||
export interface RawResourceTiming {
|
||||
tp: "resource_timing",
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
ttfb: number,
|
||||
headerSize: number,
|
||||
encodedBodySize: number,
|
||||
decodedBodySize: number,
|
||||
url: string,
|
||||
initiator: string,
|
||||
}
|
||||
|
||||
export interface RawConnectionInformation {
|
||||
tp: "connection_information",
|
||||
downlink: number,
|
||||
|
|
@ -378,23 +232,6 @@ export interface RawSetCssDataURLBased {
|
|||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawTechnicalInfo {
|
||||
tp: "technical_info",
|
||||
type: string,
|
||||
value: string,
|
||||
}
|
||||
|
||||
export interface RawCustomIssue {
|
||||
tp: "custom_issue",
|
||||
name: string,
|
||||
payload: string,
|
||||
}
|
||||
|
||||
export interface RawPageClose {
|
||||
tp: "page_close",
|
||||
|
||||
}
|
||||
|
||||
export interface RawCssInsertRuleURLBased {
|
||||
tp: "css_insert_rule_url_based",
|
||||
id: number,
|
||||
|
|
@ -417,6 +254,52 @@ export interface RawCreateIFrameDocument {
|
|||
id: number,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsReplaceURLBased {
|
||||
tp: "adopted_ss_replace_url_based",
|
||||
sheetID: number,
|
||||
text: string,
|
||||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsReplace {
|
||||
tp: "adopted_ss_replace",
|
||||
sheetID: number,
|
||||
text: string,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsInsertRuleURLBased {
|
||||
tp: "adopted_ss_insert_rule_url_based",
|
||||
sheetID: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsInsertRule {
|
||||
tp: "adopted_ss_insert_rule",
|
||||
sheetID: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsDeleteRule {
|
||||
tp: "adopted_ss_delete_rule",
|
||||
sheetID: number,
|
||||
index: number,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsAddOwner {
|
||||
tp: "adopted_ss_add_owner",
|
||||
sheetID: number,
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface RawAdoptedSsRemoveOwner {
|
||||
tp: "adopted_ss_remove_owner",
|
||||
sheetID: number,
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface RawIosSessionStart {
|
||||
tp: "ios_session_start",
|
||||
timestamp: number,
|
||||
|
|
@ -488,4 +371,4 @@ export interface RawIosNetworkCall {
|
|||
}
|
||||
|
||||
|
||||
export type RawMessage = RawBatchMeta | RawTimestamp | RawSessionDisconnect | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputTarget | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawConsoleLog | RawPageLoadTiming | RawPageRenderTiming | RawJsException | RawRawCustomEvent | RawUserID | RawUserAnonymousID | RawMetadata | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawStateAction | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawResourceTiming | RawConnectionInformation | RawSetPageVisibility | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawTechnicalInfo | RawCustomIssue | RawPageClose | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | 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 | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawConnectionInformation | RawSetPageVisibility | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
// @ts-nocheck
|
||||
// Auto-generated, do not edit
|
||||
|
||||
export const TP_MAP = {
|
||||
81: "batch_metadata",
|
||||
82: "partitioned_message",
|
||||
0: "timestamp",
|
||||
4: "set_page_location",
|
||||
5: "set_viewport_size",
|
||||
6: "set_viewport_scroll",
|
||||
7: "create_document",
|
||||
8: "create_element_node",
|
||||
9: "create_text_node",
|
||||
10: "move_node",
|
||||
11: "remove_node",
|
||||
12: "set_node_attribute",
|
||||
13: "remove_node_attribute",
|
||||
14: "set_node_data",
|
||||
15: "set_css_data",
|
||||
16: "set_node_scroll",
|
||||
17: "set_input_target",
|
||||
18: "set_input_value",
|
||||
19: "set_input_checked",
|
||||
20: "mouse_move",
|
||||
22: "console_log",
|
||||
23: "page_load_timing",
|
||||
24: "page_render_timing",
|
||||
25: "js_exception",
|
||||
27: "raw_custom_event",
|
||||
28: "user_id",
|
||||
29: "user_anonymous_id",
|
||||
30: "metadata",
|
||||
37: "css_insert_rule",
|
||||
38: "css_delete_rule",
|
||||
39: "fetch",
|
||||
40: "profiler",
|
||||
41: "o_table",
|
||||
42: "state_action",
|
||||
44: "redux",
|
||||
45: "vuex",
|
||||
46: "mob_x",
|
||||
47: "ng_rx",
|
||||
48: "graph_ql",
|
||||
49: "performance_track",
|
||||
53: "resource_timing",
|
||||
54: "connection_information",
|
||||
55: "set_page_visibility",
|
||||
59: "long_task",
|
||||
60: "set_node_attribute_url_based",
|
||||
61: "set_css_data_url_based",
|
||||
63: "technical_info",
|
||||
64: "custom_issue",
|
||||
67: "css_insert_rule_url_based",
|
||||
69: "mouse_click",
|
||||
70: "create_i_frame_document",
|
||||
71: "adopted_ss_replace_url_based",
|
||||
72: "adopted_ss_replace",
|
||||
73: "adopted_ss_insert_rule_url_based",
|
||||
74: "adopted_ss_insert_rule",
|
||||
75: "adopted_ss_delete_rule",
|
||||
76: "adopted_ss_add_owner",
|
||||
77: "adopted_ss_remove_owner",
|
||||
90: "ios_session_start",
|
||||
93: "ios_custom_event",
|
||||
96: "ios_screen_changes",
|
||||
100: "ios_click_event",
|
||||
102: "ios_performance_event",
|
||||
103: "ios_log",
|
||||
105: "ios_network_call",
|
||||
} as const
|
||||
|
||||
|
||||
757
frontend/app/player/MessageDistributor/messages/tracker.ts
Normal file
757
frontend/app/player/MessageDistributor/messages/tracker.ts
Normal file
|
|
@ -0,0 +1,757 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
import type { RawMessage } from './raw'
|
||||
|
||||
|
||||
type TrBatchMetadata = [
|
||||
type: 81,
|
||||
version: number,
|
||||
pageNo: number,
|
||||
firstIndex: number,
|
||||
timestamp: number,
|
||||
location: string,
|
||||
]
|
||||
|
||||
type TrPartitionedMessage = [
|
||||
type: 82,
|
||||
partNo: number,
|
||||
partTotal: number,
|
||||
]
|
||||
|
||||
type TrTimestamp = [
|
||||
type: 0,
|
||||
timestamp: number,
|
||||
]
|
||||
|
||||
type TrSetPageLocation = [
|
||||
type: 4,
|
||||
url: string,
|
||||
referrer: string,
|
||||
navigationStart: number,
|
||||
]
|
||||
|
||||
type TrSetViewportSize = [
|
||||
type: 5,
|
||||
width: number,
|
||||
height: number,
|
||||
]
|
||||
|
||||
type TrSetViewportScroll = [
|
||||
type: 6,
|
||||
x: number,
|
||||
y: number,
|
||||
]
|
||||
|
||||
type TrCreateDocument = [
|
||||
type: 7,
|
||||
|
||||
]
|
||||
|
||||
type TrCreateElementNode = [
|
||||
type: 8,
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
tag: string,
|
||||
svg: boolean,
|
||||
]
|
||||
|
||||
type TrCreateTextNode = [
|
||||
type: 9,
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
]
|
||||
|
||||
type TrMoveNode = [
|
||||
type: 10,
|
||||
id: number,
|
||||
parentID: number,
|
||||
index: number,
|
||||
]
|
||||
|
||||
type TrRemoveNode = [
|
||||
type: 11,
|
||||
id: number,
|
||||
]
|
||||
|
||||
type TrSetNodeAttribute = [
|
||||
type: 12,
|
||||
id: number,
|
||||
name: string,
|
||||
value: string,
|
||||
]
|
||||
|
||||
type TrRemoveNodeAttribute = [
|
||||
type: 13,
|
||||
id: number,
|
||||
name: string,
|
||||
]
|
||||
|
||||
type TrSetNodeData = [
|
||||
type: 14,
|
||||
id: number,
|
||||
data: string,
|
||||
]
|
||||
|
||||
type TrSetNodeScroll = [
|
||||
type: 16,
|
||||
id: number,
|
||||
x: number,
|
||||
y: number,
|
||||
]
|
||||
|
||||
type TrSetInputTarget = [
|
||||
type: 17,
|
||||
id: number,
|
||||
label: string,
|
||||
]
|
||||
|
||||
type TrSetInputValue = [
|
||||
type: 18,
|
||||
id: number,
|
||||
value: string,
|
||||
mask: number,
|
||||
]
|
||||
|
||||
type TrSetInputChecked = [
|
||||
type: 19,
|
||||
id: number,
|
||||
checked: boolean,
|
||||
]
|
||||
|
||||
type TrMouseMove = [
|
||||
type: 20,
|
||||
x: number,
|
||||
y: number,
|
||||
]
|
||||
|
||||
type TrConsoleLog = [
|
||||
type: 22,
|
||||
level: string,
|
||||
value: string,
|
||||
]
|
||||
|
||||
type TrPageLoadTiming = [
|
||||
type: 23,
|
||||
requestStart: number,
|
||||
responseStart: number,
|
||||
responseEnd: number,
|
||||
domContentLoadedEventStart: number,
|
||||
domContentLoadedEventEnd: number,
|
||||
loadEventStart: number,
|
||||
loadEventEnd: number,
|
||||
firstPaint: number,
|
||||
firstContentfulPaint: number,
|
||||
]
|
||||
|
||||
type TrPageRenderTiming = [
|
||||
type: 24,
|
||||
speedIndex: number,
|
||||
visuallyComplete: number,
|
||||
timeToInteractive: number,
|
||||
]
|
||||
|
||||
type TrJSException = [
|
||||
type: 25,
|
||||
name: string,
|
||||
message: string,
|
||||
payload: string,
|
||||
]
|
||||
|
||||
type TrRawCustomEvent = [
|
||||
type: 27,
|
||||
name: string,
|
||||
payload: string,
|
||||
]
|
||||
|
||||
type TrUserID = [
|
||||
type: 28,
|
||||
id: string,
|
||||
]
|
||||
|
||||
type TrUserAnonymousID = [
|
||||
type: 29,
|
||||
id: string,
|
||||
]
|
||||
|
||||
type TrMetadata = [
|
||||
type: 30,
|
||||
key: string,
|
||||
value: string,
|
||||
]
|
||||
|
||||
type TrCSSInsertRule = [
|
||||
type: 37,
|
||||
id: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
]
|
||||
|
||||
type TrCSSDeleteRule = [
|
||||
type: 38,
|
||||
id: number,
|
||||
index: number,
|
||||
]
|
||||
|
||||
type TrFetch = [
|
||||
type: 39,
|
||||
method: string,
|
||||
url: string,
|
||||
request: string,
|
||||
response: string,
|
||||
status: number,
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
]
|
||||
|
||||
type TrProfiler = [
|
||||
type: 40,
|
||||
name: string,
|
||||
duration: number,
|
||||
args: string,
|
||||
result: string,
|
||||
]
|
||||
|
||||
type TrOTable = [
|
||||
type: 41,
|
||||
key: string,
|
||||
value: string,
|
||||
]
|
||||
|
||||
type TrStateAction = [
|
||||
type: 42,
|
||||
type: string,
|
||||
]
|
||||
|
||||
type TrRedux = [
|
||||
type: 44,
|
||||
action: string,
|
||||
state: string,
|
||||
duration: number,
|
||||
]
|
||||
|
||||
type TrVuex = [
|
||||
type: 45,
|
||||
mutation: string,
|
||||
state: string,
|
||||
]
|
||||
|
||||
type TrMobX = [
|
||||
type: 46,
|
||||
type: string,
|
||||
payload: string,
|
||||
]
|
||||
|
||||
type TrNgRx = [
|
||||
type: 47,
|
||||
action: string,
|
||||
state: string,
|
||||
duration: number,
|
||||
]
|
||||
|
||||
type TrGraphQL = [
|
||||
type: 48,
|
||||
operationKind: string,
|
||||
operationName: string,
|
||||
variables: string,
|
||||
response: string,
|
||||
]
|
||||
|
||||
type TrPerformanceTrack = [
|
||||
type: 49,
|
||||
frames: number,
|
||||
ticks: number,
|
||||
totalJSHeapSize: number,
|
||||
usedJSHeapSize: number,
|
||||
]
|
||||
|
||||
type TrResourceTiming = [
|
||||
type: 53,
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
ttfb: number,
|
||||
headerSize: number,
|
||||
encodedBodySize: number,
|
||||
decodedBodySize: number,
|
||||
url: string,
|
||||
initiator: string,
|
||||
]
|
||||
|
||||
type TrConnectionInformation = [
|
||||
type: 54,
|
||||
downlink: number,
|
||||
type: string,
|
||||
]
|
||||
|
||||
type TrSetPageVisibility = [
|
||||
type: 55,
|
||||
hidden: boolean,
|
||||
]
|
||||
|
||||
type TrLongTask = [
|
||||
type: 59,
|
||||
timestamp: number,
|
||||
duration: number,
|
||||
context: number,
|
||||
containerType: number,
|
||||
containerSrc: string,
|
||||
containerId: string,
|
||||
containerName: string,
|
||||
]
|
||||
|
||||
type TrSetNodeAttributeURLBased = [
|
||||
type: 60,
|
||||
id: number,
|
||||
name: string,
|
||||
value: string,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
type TrSetCSSDataURLBased = [
|
||||
type: 61,
|
||||
id: number,
|
||||
data: string,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
type TrTechnicalInfo = [
|
||||
type: 63,
|
||||
type: string,
|
||||
value: string,
|
||||
]
|
||||
|
||||
type TrCustomIssue = [
|
||||
type: 64,
|
||||
name: string,
|
||||
payload: string,
|
||||
]
|
||||
|
||||
type TrCSSInsertRuleURLBased = [
|
||||
type: 67,
|
||||
id: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
type TrMouseClick = [
|
||||
type: 69,
|
||||
id: number,
|
||||
hesitationTime: number,
|
||||
label: string,
|
||||
selector: string,
|
||||
]
|
||||
|
||||
type TrCreateIFrameDocument = [
|
||||
type: 70,
|
||||
frameID: number,
|
||||
id: number,
|
||||
]
|
||||
|
||||
type TrAdoptedSSReplaceURLBased = [
|
||||
type: 71,
|
||||
sheetID: number,
|
||||
text: string,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
type TrAdoptedSSInsertRuleURLBased = [
|
||||
type: 73,
|
||||
sheetID: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
type TrAdoptedSSDeleteRule = [
|
||||
type: 75,
|
||||
sheetID: number,
|
||||
index: number,
|
||||
]
|
||||
|
||||
type TrAdoptedSSAddOwner = [
|
||||
type: 76,
|
||||
sheetID: number,
|
||||
id: number,
|
||||
]
|
||||
|
||||
type TrAdoptedSSRemoveOwner = [
|
||||
type: 77,
|
||||
sheetID: number,
|
||||
id: number,
|
||||
]
|
||||
|
||||
|
||||
export type TrackerMessage = TrBatchMetadata | TrPartitionedMessage | TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrJSException | TrRawCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrResourceTiming | TrConnectionInformation | TrSetPageVisibility | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner
|
||||
|
||||
export default function translate(tMsg: TrackerMessage): RawMessage | null {
|
||||
switch(tMsg[0]) {
|
||||
|
||||
case 0: {
|
||||
return {
|
||||
tp: "timestamp",
|
||||
timestamp: tMsg[1],
|
||||
}
|
||||
}
|
||||
|
||||
case 4: {
|
||||
return {
|
||||
tp: "set_page_location",
|
||||
url: tMsg[1],
|
||||
referrer: tMsg[2],
|
||||
navigationStart: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 5: {
|
||||
return {
|
||||
tp: "set_viewport_size",
|
||||
width: tMsg[1],
|
||||
height: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 6: {
|
||||
return {
|
||||
tp: "set_viewport_scroll",
|
||||
x: tMsg[1],
|
||||
y: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 7: {
|
||||
return {
|
||||
tp: "create_document",
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
case 8: {
|
||||
return {
|
||||
tp: "create_element_node",
|
||||
id: tMsg[1],
|
||||
parentID: tMsg[2],
|
||||
index: tMsg[3],
|
||||
tag: tMsg[4],
|
||||
svg: tMsg[5],
|
||||
}
|
||||
}
|
||||
|
||||
case 9: {
|
||||
return {
|
||||
tp: "create_text_node",
|
||||
id: tMsg[1],
|
||||
parentID: tMsg[2],
|
||||
index: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 10: {
|
||||
return {
|
||||
tp: "move_node",
|
||||
id: tMsg[1],
|
||||
parentID: tMsg[2],
|
||||
index: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 11: {
|
||||
return {
|
||||
tp: "remove_node",
|
||||
id: tMsg[1],
|
||||
}
|
||||
}
|
||||
|
||||
case 12: {
|
||||
return {
|
||||
tp: "set_node_attribute",
|
||||
id: tMsg[1],
|
||||
name: tMsg[2],
|
||||
value: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 13: {
|
||||
return {
|
||||
tp: "remove_node_attribute",
|
||||
id: tMsg[1],
|
||||
name: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 14: {
|
||||
return {
|
||||
tp: "set_node_data",
|
||||
id: tMsg[1],
|
||||
data: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 16: {
|
||||
return {
|
||||
tp: "set_node_scroll",
|
||||
id: tMsg[1],
|
||||
x: tMsg[2],
|
||||
y: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 18: {
|
||||
return {
|
||||
tp: "set_input_value",
|
||||
id: tMsg[1],
|
||||
value: tMsg[2],
|
||||
mask: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 19: {
|
||||
return {
|
||||
tp: "set_input_checked",
|
||||
id: tMsg[1],
|
||||
checked: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 20: {
|
||||
return {
|
||||
tp: "mouse_move",
|
||||
x: tMsg[1],
|
||||
y: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 22: {
|
||||
return {
|
||||
tp: "console_log",
|
||||
level: tMsg[1],
|
||||
value: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 37: {
|
||||
return {
|
||||
tp: "css_insert_rule",
|
||||
id: tMsg[1],
|
||||
rule: tMsg[2],
|
||||
index: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 38: {
|
||||
return {
|
||||
tp: "css_delete_rule",
|
||||
id: tMsg[1],
|
||||
index: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 39: {
|
||||
return {
|
||||
tp: "fetch",
|
||||
method: tMsg[1],
|
||||
url: tMsg[2],
|
||||
request: tMsg[3],
|
||||
response: tMsg[4],
|
||||
status: tMsg[5],
|
||||
timestamp: tMsg[6],
|
||||
duration: tMsg[7],
|
||||
}
|
||||
}
|
||||
|
||||
case 40: {
|
||||
return {
|
||||
tp: "profiler",
|
||||
name: tMsg[1],
|
||||
duration: tMsg[2],
|
||||
args: tMsg[3],
|
||||
result: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 41: {
|
||||
return {
|
||||
tp: "o_table",
|
||||
key: tMsg[1],
|
||||
value: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 44: {
|
||||
return {
|
||||
tp: "redux",
|
||||
action: tMsg[1],
|
||||
state: tMsg[2],
|
||||
duration: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 45: {
|
||||
return {
|
||||
tp: "vuex",
|
||||
mutation: tMsg[1],
|
||||
state: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 46: {
|
||||
return {
|
||||
tp: "mob_x",
|
||||
type: tMsg[1],
|
||||
payload: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 47: {
|
||||
return {
|
||||
tp: "ng_rx",
|
||||
action: tMsg[1],
|
||||
state: tMsg[2],
|
||||
duration: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 48: {
|
||||
return {
|
||||
tp: "graph_ql",
|
||||
operationKind: tMsg[1],
|
||||
operationName: tMsg[2],
|
||||
variables: tMsg[3],
|
||||
response: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 49: {
|
||||
return {
|
||||
tp: "performance_track",
|
||||
frames: tMsg[1],
|
||||
ticks: tMsg[2],
|
||||
totalJSHeapSize: tMsg[3],
|
||||
usedJSHeapSize: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 54: {
|
||||
return {
|
||||
tp: "connection_information",
|
||||
downlink: tMsg[1],
|
||||
type: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 55: {
|
||||
return {
|
||||
tp: "set_page_visibility",
|
||||
hidden: tMsg[1],
|
||||
}
|
||||
}
|
||||
|
||||
case 59: {
|
||||
return {
|
||||
tp: "long_task",
|
||||
timestamp: tMsg[1],
|
||||
duration: tMsg[2],
|
||||
context: tMsg[3],
|
||||
containerType: tMsg[4],
|
||||
containerSrc: tMsg[5],
|
||||
containerId: tMsg[6],
|
||||
containerName: tMsg[7],
|
||||
}
|
||||
}
|
||||
|
||||
case 60: {
|
||||
return {
|
||||
tp: "set_node_attribute_url_based",
|
||||
id: tMsg[1],
|
||||
name: tMsg[2],
|
||||
value: tMsg[3],
|
||||
baseURL: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 61: {
|
||||
return {
|
||||
tp: "set_css_data_url_based",
|
||||
id: tMsg[1],
|
||||
data: tMsg[2],
|
||||
baseURL: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 67: {
|
||||
return {
|
||||
tp: "css_insert_rule_url_based",
|
||||
id: tMsg[1],
|
||||
rule: tMsg[2],
|
||||
index: tMsg[3],
|
||||
baseURL: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 69: {
|
||||
return {
|
||||
tp: "mouse_click",
|
||||
id: tMsg[1],
|
||||
hesitationTime: tMsg[2],
|
||||
label: tMsg[3],
|
||||
selector: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 70: {
|
||||
return {
|
||||
tp: "create_i_frame_document",
|
||||
frameID: tMsg[1],
|
||||
id: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 71: {
|
||||
return {
|
||||
tp: "adopted_ss_replace_url_based",
|
||||
sheetID: tMsg[1],
|
||||
text: tMsg[2],
|
||||
baseURL: tMsg[3],
|
||||
}
|
||||
}
|
||||
|
||||
case 73: {
|
||||
return {
|
||||
tp: "adopted_ss_insert_rule_url_based",
|
||||
sheetID: tMsg[1],
|
||||
rule: tMsg[2],
|
||||
index: tMsg[3],
|
||||
baseURL: tMsg[4],
|
||||
}
|
||||
}
|
||||
|
||||
case 75: {
|
||||
return {
|
||||
tp: "adopted_ss_delete_rule",
|
||||
sheetID: tMsg[1],
|
||||
index: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 76: {
|
||||
return {
|
||||
tp: "adopted_ss_add_owner",
|
||||
sheetID: tMsg[1],
|
||||
id: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
case 77: {
|
||||
return {
|
||||
tp: "adopted_ss_remove_owner",
|
||||
sheetID: tMsg[1],
|
||||
id: tMsg[2],
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,10 +26,9 @@ function cssUrlsIndex(css: string): Array<[number, number]> {
|
|||
const e = s + m[1].length;
|
||||
idxs.push([s, e])
|
||||
}
|
||||
return idxs.reverse();
|
||||
return idxs.reverse()
|
||||
}
|
||||
function unquote(str: string): [string, string] {
|
||||
str = str ? str.trim() : '';
|
||||
if (str.length <= 2) {
|
||||
return [str, ""]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ ruby run.rb
|
|||
|
||||
```
|
||||
|
||||
In order generated .go file to fit the go formatting style:
|
||||
In order format generated files run:
|
||||
```sh
|
||||
gofmt -w ../backend/pkg/messages/messages.go
|
||||
|
||||
sh format.sh
|
||||
```
|
||||
(Otherwise there will be changes in stage)
|
||||
|
|
|
|||
1
mobs/format.sh
Normal file
1
mobs/format.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
gofmt -w ../backend/pkg/messages
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
# Special one for Batch Meta. Message id could define the version
|
||||
# Special one for Batch Metadata. Message id could define the version
|
||||
|
||||
# Depricated since tracker 3.6.0 in favor of BatchMetadata
|
||||
message 80, 'BatchMeta', :tracker => false, :replayer => false do
|
||||
message 80, 'BatchMeta', :replayer => false, :tracker => false do
|
||||
uint 'PageNo'
|
||||
uint 'FirstIndex'
|
||||
int 'Timestamp'
|
||||
end
|
||||
|
||||
# since tracker 3.6.0
|
||||
# since tracker 3.6.0 TODO: for webworker only
|
||||
message 81, 'BatchMetadata', :replayer => false do
|
||||
uint 'Version'
|
||||
uint 'PageNo'
|
||||
|
|
@ -43,10 +44,8 @@ message 1, 'SessionStart', :tracker => false, :replayer => false do
|
|||
string 'UserCountry'
|
||||
string 'UserID'
|
||||
end
|
||||
# Depricated (not used) since OpenReplay tracker 3.0.0
|
||||
message 2, 'SessionDisconnect', :tracker => false do
|
||||
uint 'Timestamp'
|
||||
end
|
||||
# message 2, 'CreateDocument', do
|
||||
# end
|
||||
message 3, 'SessionEnd', :tracker => false, :replayer => false do
|
||||
uint 'Timestamp'
|
||||
end
|
||||
|
|
@ -63,6 +62,8 @@ message 6, 'SetViewportScroll' do
|
|||
int 'X'
|
||||
int 'Y'
|
||||
end
|
||||
# Depricated sinse tracker 3.6.0 in favor of CreateDocument(id=2)
|
||||
# in order to use Document as a default root node instead of the documentElement
|
||||
message 7, 'CreateDocument' do
|
||||
end
|
||||
message 8, 'CreateElementNode' do
|
||||
|
|
@ -124,7 +125,7 @@ message 20, 'MouseMove' do
|
|||
uint 'X'
|
||||
uint 'Y'
|
||||
end
|
||||
# Depricated since OpenReplay 1.2.0
|
||||
# Depricated since OpenReplay 1.2.0 (tracker version?)
|
||||
message 21, 'MouseClickDepricated', :tracker => false, :replayer => false do
|
||||
uint 'ID'
|
||||
uint 'HesitationTime'
|
||||
|
|
@ -371,14 +372,14 @@ message 59, 'LongTask' do
|
|||
string 'ContainerId'
|
||||
string 'ContainerName'
|
||||
end
|
||||
message 60, 'SetNodeAttributeURLBased', :replayer => false do
|
||||
message 60, 'SetNodeAttributeURLBased' do
|
||||
uint 'ID'
|
||||
string 'Name'
|
||||
string 'Value'
|
||||
string 'BaseURL'
|
||||
end
|
||||
# Might replace SetCSSData (although BaseURL is useless after rewriting)
|
||||
message 61, 'SetCSSDataURLBased', :replayer => false do
|
||||
message 61, 'SetCSSDataURLBased' do
|
||||
uint 'ID'
|
||||
string 'Data'
|
||||
string 'BaseURL'
|
||||
|
|
@ -402,7 +403,7 @@ end
|
|||
message 66, 'AssetCache', :replayer => false, :tracker => false do
|
||||
string 'URL'
|
||||
end
|
||||
message 67, 'CSSInsertRuleURLBased', :replayer => false do
|
||||
message 67, 'CSSInsertRuleURLBased' do
|
||||
uint 'ID'
|
||||
string 'Rule'
|
||||
uint 'Index'
|
||||
|
|
@ -419,4 +420,38 @@ end
|
|||
message 70, 'CreateIFrameDocument' do
|
||||
uint 'FrameID'
|
||||
uint 'ID'
|
||||
end
|
||||
end
|
||||
|
||||
#Since 3.6.0 AdoptedStyleSheets
|
||||
message 71, 'AdoptedSSReplaceURLBased' do
|
||||
uint 'SheetID'
|
||||
string 'Text'
|
||||
string 'BaseURL'
|
||||
end
|
||||
message 72, 'AdoptedSSReplace', :tracker => false do
|
||||
uint 'SheetID'
|
||||
string 'Text'
|
||||
end
|
||||
message 73, 'AdoptedSSInsertRuleURLBased' do
|
||||
uint 'SheetID'
|
||||
string 'Rule'
|
||||
uint 'Index'
|
||||
string 'BaseURL'
|
||||
end
|
||||
message 74, 'AdoptedSSInsertRule', :tracker => false do
|
||||
uint 'SheetID'
|
||||
string 'Rule'
|
||||
uint 'Index'
|
||||
end
|
||||
message 75, 'AdoptedSSDeleteRule' do
|
||||
uint 'SheetID'
|
||||
uint 'Index'
|
||||
end
|
||||
message 76, 'AdoptedSSAddOwner' do
|
||||
uint 'SheetID'
|
||||
uint 'ID'
|
||||
end
|
||||
message 77, 'AdoptedSSRemoveOwner' do
|
||||
uint 'SheetID'
|
||||
uint 'ID'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Auto-generated, do not edit
|
||||
package messages
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
<% $messages.each do |msg| %>
|
||||
Msg<%= msg.name %> = <%= msg.id %>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default class RawMessageReader extends PrimitiveReader {
|
|||
if (tp === null) { return resetPointer() }
|
||||
|
||||
switch (tp) {
|
||||
<% $messages.select { |msg| msg.tracker || msg.replayer }.each do |msg| %>
|
||||
<% $messages.select { |msg| msg.replayer }.each do |msg| %>
|
||||
case <%= msg.id %>: {
|
||||
<%= msg.attributes.map { |attr|
|
||||
" const #{attr.name.camel_case} = this.read#{attr.type.to_s.pascal_case}(); if (#{attr.name.camel_case} === null) { return resetPointer() }" }.join "\n" %>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
import type { Timed } from './timed'
|
||||
import type { RawMessage } from './raw'
|
||||
import type {
|
||||
<%= $messages.select { |msg| msg.tracker || msg.replayer }.map { |msg| " Raw#{msg.name.snake_case.pascal_case}," }.join "\n" %>
|
||||
<%= $messages.select { |msg| msg.replayer }.map { |msg| " Raw#{msg.name.snake_case.pascal_case}," }.join "\n" %>
|
||||
} from './raw'
|
||||
|
||||
export type Message = RawMessage & Timed
|
||||
|
||||
<% $messages.select { |msg| msg.tracker || msg.replayer }.each do |msg| %>
|
||||
<% $messages.select { |msg| msg.replayer }.each do |msg| %>
|
||||
export type <%= msg.name.snake_case.pascal_case %> = Raw<%= msg.name.snake_case.pascal_case %> & Timed
|
||||
<% end %>
|
||||
|
|
@ -1,14 +1,10 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
export const TP_MAP = {
|
||||
<%= $messages.select { |msg| msg.tracker || msg.replayer }.map { |msg| " #{msg.id}: \"#{msg.name.snake_case}\"," }.join "\n" %>
|
||||
}
|
||||
|
||||
<% $messages.select { |msg| msg.tracker || msg.replayer }.each do |msg| %>
|
||||
<% $messages.select { |msg| msg.replayer }.each do |msg| %>
|
||||
export interface Raw<%= msg.name.snake_case.pascal_case %> {
|
||||
tp: "<%= msg.name.snake_case %>",
|
||||
<%= msg.attributes.map { |attr| " #{attr.name.camel_case}: #{attr.type_js}," }.join "\n" %>
|
||||
}
|
||||
<% end %>
|
||||
|
||||
export type RawMessage = <%= $messages.select { |msg| msg.tracker || msg.replayer }.map { |msg| "Raw#{msg.name.snake_case.pascal_case}" }.join " | " %>;
|
||||
export type RawMessage = <%= $messages.select { |msg| msg.replayer }.map { |msg| "Raw#{msg.name.snake_case.pascal_case}" }.join " | " %>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
// @ts-nocheck
|
||||
// Auto-generated, do not edit
|
||||
|
||||
export const TP_MAP = {
|
||||
<%= $messages.select { |msg| msg.tracker || msg.replayer }.map { |msg| " #{msg.id}: \"#{msg.name.snake_case}\"," }.join "\n" %>
|
||||
} as const
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Auto-generated, do not edit
|
||||
|
||||
import type { RawMessage } from './raw'
|
||||
|
||||
<% $messages.select { |msg| msg.tracker }.each do |msg| %>
|
||||
type Tr<%= msg.name %> = [
|
||||
type: <%= msg.id %>,
|
||||
<%= msg.attributes.map { |attr| "#{attr.name.camel_case}: #{attr.type_js}," }.join "\n " %>
|
||||
]
|
||||
<% end %>
|
||||
|
||||
export type TrackerMessage = <%= $messages.select { |msg| msg.tracker }.map { |msg| "Tr#{msg.name}" }.join " | " %>
|
||||
|
||||
export default function translate(tMsg: TrackerMessage): RawMessage | null {
|
||||
switch(tMsg[0]) {
|
||||
<% $messages.select { |msg| msg.replayer & msg.tracker }.each do |msg| %>
|
||||
case <%= msg.id %>: {
|
||||
return {
|
||||
tp: "<%= msg.name.snake_case %>",
|
||||
<%= msg.attributes.map.with_index { |attr, i| "#{attr.name.camel_case}: tMsg[#{i+1}]," }.join "\n " %>
|
||||
}
|
||||
}
|
||||
<% end %>
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -60,11 +60,11 @@ export default class Assist {
|
|||
private agents: Record<string, Agent> = {}
|
||||
private readonly options: Options
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
options?: Partial<Options>,
|
||||
private readonly app: App,
|
||||
options?: Partial<Options>,
|
||||
private readonly noSecureMode: boolean = false,
|
||||
) {
|
||||
this.options = Object.assign({
|
||||
this.options = Object.assign({
|
||||
session_calling_peer_key: '__openreplay_calling_peer',
|
||||
session_control_peer_key: '__openreplay_control_peer',
|
||||
config: null,
|
||||
|
|
@ -91,12 +91,12 @@ export default class Assist {
|
|||
const observer = titleNode && new MutationObserver(() => {
|
||||
this.emit('UPDATE_SESSION', { pageTitle: document.title, })
|
||||
})
|
||||
app.attachStartCallback(() => {
|
||||
app.attachStartCallback(() => {
|
||||
if (this.assistDemandedRestart) { return }
|
||||
this.onStart()
|
||||
observer && observer.observe(titleNode, { subtree: true, characterData: true, childList: true, })
|
||||
})
|
||||
app.attachStopCallback(() => {
|
||||
app.attachStopCallback(() => {
|
||||
if (this.assistDemandedRestart) { return }
|
||||
this.clean()
|
||||
observer && observer.disconnect()
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ export enum Type {
|
|||
CSSInsertRuleURLBased = 67,
|
||||
MouseClick = 69,
|
||||
CreateIFrameDocument = 70,
|
||||
AdoptedSSReplaceURLBased = 71,
|
||||
AdoptedSSInsertRuleURLBased = 73,
|
||||
AdoptedSSDeleteRule = 75,
|
||||
AdoptedSSAddOwner = 76,
|
||||
AdoptedSSRemoveOwner = 77,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -400,6 +405,39 @@ export type CreateIFrameDocument = [
|
|||
id: number,
|
||||
]
|
||||
|
||||
export type AdoptedSSReplaceURLBased = [
|
||||
type: Type.AdoptedSSReplaceURLBased,
|
||||
sheetID: number,
|
||||
text: string,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
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 type AdoptedSSInsertRuleURLBased = [
|
||||
type: Type.AdoptedSSInsertRuleURLBased,
|
||||
sheetID: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
]
|
||||
|
||||
export type AdoptedSSDeleteRule = [
|
||||
type: Type.AdoptedSSDeleteRule,
|
||||
sheetID: number,
|
||||
index: number,
|
||||
]
|
||||
|
||||
export type AdoptedSSAddOwner = [
|
||||
type: Type.AdoptedSSAddOwner,
|
||||
sheetID: number,
|
||||
id: number,
|
||||
]
|
||||
|
||||
export type AdoptedSSRemoveOwner = [
|
||||
type: Type.AdoptedSSRemoveOwner,
|
||||
sheetID: 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 | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner
|
||||
export default Message
|
||||
|
|
|
|||
|
|
@ -10,7 +10,11 @@ export function isTextNode(node: Node): node is Text {
|
|||
return node.nodeType === Node.TEXT_NODE
|
||||
}
|
||||
|
||||
export function isRootNode(node: Node): boolean {
|
||||
export function isDocument(node: Node): node is Document {
|
||||
return node.nodeType === Node.DOCUMENT_NODE
|
||||
}
|
||||
|
||||
export function isRootNode(node: Node): node is Document | DocumentFragment {
|
||||
return node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type Message from './messages.gen.js'
|
||||
import { Timestamp, Metadata, UserID } from './messages.gen.js'
|
||||
import { timestamp, deprecationWarn } from '../utils.js'
|
||||
import { timestamp as now, deprecationWarn } from '../utils.js'
|
||||
import Nodes from './nodes.js'
|
||||
import Observer from './observer/top_observer.js'
|
||||
import Sanitizer from './sanitizer.js'
|
||||
|
|
@ -13,6 +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 SessOptions } from './session.js'
|
||||
import type { Options as WebworkerOptions, WorkerMessageData } from '../../common/interaction.js'
|
||||
|
||||
// TODO: Unify and clearly describe options logic
|
||||
|
|
@ -20,6 +21,7 @@ export interface StartOptions {
|
|||
userID?: string
|
||||
metadata?: Record<string, string>
|
||||
forceNew?: boolean
|
||||
sessionHash?: string
|
||||
}
|
||||
|
||||
interface OnStartInfo {
|
||||
|
|
@ -49,9 +51,9 @@ enum ActivityState {
|
|||
type AppOptions = {
|
||||
revID: string
|
||||
node_id: string
|
||||
session_reset_key: string
|
||||
session_token_key: string
|
||||
session_pageno_key: string
|
||||
session_reset_key: string
|
||||
local_uuid_key: string
|
||||
ingestPoint: string
|
||||
resourceBaseHref: string | null // resourceHref?
|
||||
|
|
@ -60,12 +62,13 @@ type AppOptions = {
|
|||
__is_snippet: boolean
|
||||
__debug_report_edp: string | null
|
||||
__debug__?: LoggerOptions
|
||||
localStorage: Storage
|
||||
sessionStorage: Storage
|
||||
localStorage: Storage | null
|
||||
sessionStorage: Storage | null
|
||||
|
||||
// @deprecated
|
||||
onStart?: StartCallback
|
||||
} & WebworkerOptions
|
||||
} & WebworkerOptions &
|
||||
SessOptions
|
||||
|
||||
export type Options = AppOptions & ObserverOptions & SanitizerOptions
|
||||
|
||||
|
|
@ -83,7 +86,7 @@ export default class App {
|
|||
readonly localStorage: Storage
|
||||
readonly sessionStorage: Storage
|
||||
private readonly messages: Array<Message> = []
|
||||
private readonly observer: Observer
|
||||
/* private */ readonly observer: Observer // non-privat for attachContextCallback
|
||||
private readonly startCallbacks: Array<StartCallback> = []
|
||||
private readonly stopCallbacks: Array<() => any> = []
|
||||
private readonly commitCallbacks: Array<CommitCallback> = []
|
||||
|
|
@ -92,11 +95,7 @@ export default class App {
|
|||
private activityState: ActivityState = ActivityState.NotActive
|
||||
private readonly version = 'TRACKER_VERSION' // TODO: version compatability check inside each plugin.
|
||||
private readonly worker?: Worker
|
||||
constructor(
|
||||
projectKey: string,
|
||||
sessionToken: string | null | undefined,
|
||||
options: Partial<Options>,
|
||||
) {
|
||||
constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>) {
|
||||
// if (options.onStart !== undefined) {
|
||||
// deprecationWarn("'onStart' option", "tracker.start().then(/* handle session info */)")
|
||||
// } ?? maybe onStart is good
|
||||
|
|
@ -129,7 +128,9 @@ export default class App {
|
|||
this.ticker.attach(() => this.commit())
|
||||
this.debug = new Logger(this.options.__debug__)
|
||||
this.notify = new Logger(this.options.verbose ? LogLevel.Warnings : LogLevel.Silent)
|
||||
this.session = new Session()
|
||||
this.localStorage = this.options.localStorage || window.localStorage
|
||||
this.sessionStorage = this.options.sessionStorage || window.sessionStorage
|
||||
this.session = new Session(this, this.options)
|
||||
this.session.attachUpdateCallback(({ userID, metadata }) => {
|
||||
if (userID != null) {
|
||||
// TODO: nullable userID
|
||||
|
|
@ -139,11 +140,10 @@ export default class App {
|
|||
Object.entries(metadata).forEach(([key, value]) => this.send(Metadata(key, value)))
|
||||
}
|
||||
})
|
||||
this.localStorage = this.options.localStorage
|
||||
this.sessionStorage = this.options.sessionStorage
|
||||
|
||||
// @depricated (use sessionHash on start instead)
|
||||
if (sessionToken != null) {
|
||||
this.sessionStorage.setItem(this.options.session_token_key, sessionToken)
|
||||
this.session.applySessionHash(sessionToken)
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -206,7 +206,7 @@ export default class App {
|
|||
}
|
||||
private commit(): void {
|
||||
if (this.worker && this.messages.length) {
|
||||
this.messages.unshift(Timestamp(timestamp()))
|
||||
this.messages.unshift(Timestamp(now()))
|
||||
this.worker.postMessage(this.messages)
|
||||
this.commitCallbacks.forEach((cb) => cb(this.messages))
|
||||
this.messages.length = 0
|
||||
|
|
@ -220,7 +220,7 @@ export default class App {
|
|||
fn.apply(this, args)
|
||||
} catch (e) {
|
||||
app._debug('safe_fn_call', e)
|
||||
// time: timestamp(),
|
||||
// time: now(),
|
||||
// name: e.name,
|
||||
// message: e.message,
|
||||
// stack: e.stack
|
||||
|
|
@ -256,19 +256,24 @@ export default class App {
|
|||
const reqVer = version.split(/[.-]/)
|
||||
const ver = this.version.split(/[.-]/)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (Number(ver[i]) < Number(reqVer[i]) || isNaN(Number(ver[i])) || isNaN(Number(reqVer[i]))) {
|
||||
if (isNaN(Number(ver[i])) || isNaN(Number(reqVer[i]))) {
|
||||
return false
|
||||
}
|
||||
if (Number(ver[i]) > Number(reqVer[i])) {
|
||||
return true
|
||||
}
|
||||
if (Number(ver[i]) < Number(reqVer[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private getStartInfo() {
|
||||
private getTrackerInfo() {
|
||||
return {
|
||||
userUUID: this.localStorage.getItem(this.options.local_uuid_key),
|
||||
projectKey: this.projectKey,
|
||||
revID: this.revID,
|
||||
timestamp: timestamp(), // shouldn't it be set once?
|
||||
trackerVersion: this.version,
|
||||
isSnippet: this.options.__is_snippet,
|
||||
}
|
||||
|
|
@ -276,14 +281,11 @@ export default class App {
|
|||
getSessionInfo() {
|
||||
return {
|
||||
...this.session.getInfo(),
|
||||
...this.getStartInfo(),
|
||||
...this.getTrackerInfo(),
|
||||
}
|
||||
}
|
||||
getSessionToken(): string | undefined {
|
||||
const token = this.sessionStorage.getItem(this.options.session_token_key)
|
||||
if (token !== null) {
|
||||
return token
|
||||
}
|
||||
return this.session.getSessionToken()
|
||||
}
|
||||
getSessionID(): string | undefined {
|
||||
return this.session.getInfo().sessionID || undefined
|
||||
|
|
@ -298,7 +300,7 @@ export default class App {
|
|||
if (typeof this.options.resourceBaseHref === 'string') {
|
||||
return this.options.resourceBaseHref
|
||||
} else if (typeof this.options.resourceBaseHref === 'object') {
|
||||
//switch between types
|
||||
//TODO: switch between types
|
||||
}
|
||||
if (document.baseURI) {
|
||||
return document.baseURI
|
||||
|
|
@ -343,21 +345,16 @@ export default class App {
|
|||
)
|
||||
}
|
||||
this.activityState = ActivityState.Starting
|
||||
|
||||
let pageNo = 0
|
||||
const pageNoStr = this.sessionStorage.getItem(this.options.session_pageno_key)
|
||||
if (pageNoStr != null) {
|
||||
pageNo = parseInt(pageNoStr)
|
||||
pageNo++
|
||||
if (startOpts.sessionHash) {
|
||||
this.session.applySessionHash(startOpts.sessionHash)
|
||||
}
|
||||
this.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString())
|
||||
|
||||
const startInfo = this.getStartInfo()
|
||||
const timestamp = now()
|
||||
const startWorkerMsg: WorkerMessageData = {
|
||||
type: 'start',
|
||||
pageNo,
|
||||
pageNo: this.session.incPageNo(),
|
||||
ingestPoint: this.options.ingestPoint,
|
||||
timestamp: startInfo.timestamp,
|
||||
timestamp,
|
||||
url: document.URL,
|
||||
connAttemptCount: this.options.connAttemptCount,
|
||||
connAttemptGap: this.options.connAttemptGap,
|
||||
|
|
@ -382,9 +379,10 @@ export default class App {
|
|||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...startInfo,
|
||||
...this.getTrackerInfo(),
|
||||
timestamp,
|
||||
userID: this.session.getInfo().userID,
|
||||
token: this.sessionStorage.getItem(this.options.session_token_key),
|
||||
token: this.session.getSessionToken(),
|
||||
deviceMemory,
|
||||
jsHeapSizeLimit,
|
||||
reset: startOpts.forceNew || sReset !== null,
|
||||
|
|
@ -407,17 +405,25 @@ export default class App {
|
|||
if (!this.worker) {
|
||||
return Promise.reject('no worker found after start request (this might not happen)')
|
||||
}
|
||||
const { token, userUUID, sessionID, beaconSizeLimit } = r
|
||||
const {
|
||||
token,
|
||||
userUUID,
|
||||
sessionID,
|
||||
beaconSizeLimit,
|
||||
startTimestamp, // real startTS, derived from sessionID
|
||||
} = r
|
||||
if (
|
||||
typeof token !== 'string' ||
|
||||
typeof userUUID !== 'string' ||
|
||||
//typeof startTimestamp !== 'number' ||
|
||||
//typeof sessionID !== 'string' ||
|
||||
(typeof beaconSizeLimit !== 'number' && typeof beaconSizeLimit !== 'undefined')
|
||||
) {
|
||||
return Promise.reject(`Incorrect server response: ${JSON.stringify(r)}`)
|
||||
}
|
||||
this.sessionStorage.setItem(this.options.session_token_key, token)
|
||||
this.session.setSessionToken(token)
|
||||
this.localStorage.setItem(this.options.local_uuid_key, userUUID)
|
||||
this.session.update({ sessionID }) // TODO: no no-explicit 'any'
|
||||
this.session.update({ sessionID, timestamp: startTimestamp || timestamp }) // TODO: no no-explicit 'any'
|
||||
const startWorkerMsg: WorkerMessageData = {
|
||||
type: 'auth',
|
||||
token,
|
||||
|
|
@ -441,8 +447,8 @@ export default class App {
|
|||
return SuccessfulStart(onStartInfo)
|
||||
})
|
||||
.catch((reason) => {
|
||||
this.sessionStorage.removeItem(this.options.session_token_key)
|
||||
this.stop()
|
||||
this.session.reset()
|
||||
if (reason === CANCELED) {
|
||||
return UnsuccessfulStart(CANCELED)
|
||||
}
|
||||
|
|
@ -468,7 +474,7 @@ export default class App {
|
|||
})
|
||||
}
|
||||
}
|
||||
stop(calledFromAPI = false, restarting = false): void {
|
||||
stop(stopWorker = true): void {
|
||||
if (this.activityState !== ActivityState.NotActive) {
|
||||
try {
|
||||
this.sanitizer.clear()
|
||||
|
|
@ -476,11 +482,8 @@ export default class App {
|
|||
this.nodes.clear()
|
||||
this.ticker.stop()
|
||||
this.stopCallbacks.forEach((cb) => cb())
|
||||
if (calledFromAPI) {
|
||||
this.session.reset()
|
||||
}
|
||||
this.notify.log('OpenReplay tracking stopped.')
|
||||
if (this.worker && !restarting) {
|
||||
if (this.worker && stopWorker) {
|
||||
this.worker.postMessage('stop')
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -489,7 +492,7 @@ export default class App {
|
|||
}
|
||||
}
|
||||
restart() {
|
||||
this.stop(false, true)
|
||||
this.stop(false)
|
||||
this.start({ forceNew: false })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -646,3 +646,64 @@ export function CreateIFrameDocument(
|
|||
]
|
||||
}
|
||||
|
||||
export function AdoptedSSReplaceURLBased(
|
||||
sheetID: number,
|
||||
text: string,
|
||||
baseURL: string,
|
||||
): Messages.AdoptedSSReplaceURLBased {
|
||||
return [
|
||||
Messages.Type.AdoptedSSReplaceURLBased,
|
||||
sheetID,
|
||||
text,
|
||||
baseURL,
|
||||
]
|
||||
}
|
||||
|
||||
export function AdoptedSSInsertRuleURLBased(
|
||||
sheetID: number,
|
||||
rule: string,
|
||||
index: number,
|
||||
baseURL: string,
|
||||
): Messages.AdoptedSSInsertRuleURLBased {
|
||||
return [
|
||||
Messages.Type.AdoptedSSInsertRuleURLBased,
|
||||
sheetID,
|
||||
rule,
|
||||
index,
|
||||
baseURL,
|
||||
]
|
||||
}
|
||||
|
||||
export function AdoptedSSDeleteRule(
|
||||
sheetID: number,
|
||||
index: number,
|
||||
): Messages.AdoptedSSDeleteRule {
|
||||
return [
|
||||
Messages.Type.AdoptedSSDeleteRule,
|
||||
sheetID,
|
||||
index,
|
||||
]
|
||||
}
|
||||
|
||||
export function AdoptedSSAddOwner(
|
||||
sheetID: number,
|
||||
id: number,
|
||||
): Messages.AdoptedSSAddOwner {
|
||||
return [
|
||||
Messages.Type.AdoptedSSAddOwner,
|
||||
sheetID,
|
||||
id,
|
||||
]
|
||||
}
|
||||
|
||||
export function AdoptedSSRemoveOwner(
|
||||
sheetID: number,
|
||||
id: number,
|
||||
): Messages.AdoptedSSRemoveOwner {
|
||||
return [
|
||||
Messages.Type.AdoptedSSRemoveOwner,
|
||||
sheetID,
|
||||
id,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ export default class Nodes {
|
|||
|
||||
constructor(private readonly node_id: string) {}
|
||||
|
||||
// Attached once per Tracker instance
|
||||
attachNodeCallback(nodeCallback: NodeCallback): void {
|
||||
this.nodeCallbacks.push(nodeCallback)
|
||||
}
|
||||
// TODO: what is the difference with app.attachEventListener. can we use only one of those?
|
||||
attachElementListener(type: string, node: Element, elementListener: EventListener): void {
|
||||
const id = this.getID(node)
|
||||
if (id === undefined) {
|
||||
|
|
|
|||
|
|
@ -43,17 +43,10 @@ function isObservable(node: Node): boolean {
|
|||
- use document as a 0-node in the upper context (should be updated in player at first)
|
||||
*/
|
||||
|
||||
/*
|
||||
Nikita:
|
||||
- rn we only send unbind event for parent (all child nodes will be cut in the live replay anyways)
|
||||
to prevent sending 1k+ unbinds for child nodes and making replay file bigger than it should be
|
||||
*/
|
||||
|
||||
enum RecentsType {
|
||||
New,
|
||||
Removed,
|
||||
Changed,
|
||||
RemovedChild,
|
||||
}
|
||||
|
||||
export default abstract class Observer {
|
||||
|
|
@ -76,7 +69,10 @@ export default abstract class Observer {
|
|||
}
|
||||
if (type === 'childList') {
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
this.bindTree(mutation.removedNodes[i], true)
|
||||
// Should be the same as bindTree(mutation.removedNodes[i]), but logic needs to be be untied
|
||||
if (isObservable(mutation.removedNodes[i])) {
|
||||
this.bindNode(mutation.removedNodes[i])
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
||||
this.bindTree(mutation.addedNodes[i])
|
||||
|
|
@ -183,16 +179,11 @@ export default abstract class Observer {
|
|||
if (isNew) {
|
||||
this.recents.set(id, RecentsType.New)
|
||||
} else if (this.recents.get(id) !== RecentsType.New) {
|
||||
// can we do just `else` here?
|
||||
this.recents.set(id, RecentsType.Removed)
|
||||
}
|
||||
}
|
||||
private unbindChildNode(node: Node): void {
|
||||
const [id] = this.app.nodes.registerNode(node)
|
||||
this.recents.set(id, RecentsType.RemovedChild)
|
||||
}
|
||||
|
||||
private bindTree(node: Node, isChildUnbinding = false): void {
|
||||
private bindTree(node: Node): void {
|
||||
if (!isObservable(node)) {
|
||||
return
|
||||
}
|
||||
|
|
@ -202,7 +193,7 @@ export default abstract class Observer {
|
|||
NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT,
|
||||
{
|
||||
acceptNode: (node) =>
|
||||
isIgnored(node) || (this.app.nodes.getID(node) !== undefined && !isChildUnbinding)
|
||||
isIgnored(node) || this.app.nodes.getID(node) !== undefined
|
||||
? NodeFilter.FILTER_REJECT
|
||||
: NodeFilter.FILTER_ACCEPT,
|
||||
},
|
||||
|
|
@ -210,18 +201,33 @@ export default abstract class Observer {
|
|||
false,
|
||||
)
|
||||
while (walker.nextNode()) {
|
||||
if (isChildUnbinding) {
|
||||
this.unbindChildNode(walker.currentNode)
|
||||
} else {
|
||||
this.bindNode(walker.currentNode)
|
||||
}
|
||||
this.bindNode(walker.currentNode)
|
||||
}
|
||||
}
|
||||
|
||||
private unbindNode(node: Node) {
|
||||
private unbindTree(node: Node) {
|
||||
const id = this.app.nodes.unregisterNode(node)
|
||||
if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
|
||||
// Sending RemoveNode only for parent to maintain
|
||||
this.app.send(RemoveNode(id))
|
||||
|
||||
// Unregistering all the children in order to clear the memory
|
||||
const walker = document.createTreeWalker(
|
||||
node,
|
||||
NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT,
|
||||
{
|
||||
acceptNode: (node) =>
|
||||
isIgnored(node) || this.app.nodes.getID(node) === undefined
|
||||
? NodeFilter.FILTER_REJECT
|
||||
: NodeFilter.FILTER_ACCEPT,
|
||||
},
|
||||
// @ts-ignore
|
||||
false,
|
||||
)
|
||||
while (walker.nextNode()) {
|
||||
this.app.nodes.unregisterNode(walker.currentNode)
|
||||
}
|
||||
// MBTODO: count and send RemovedNodesCount (for the page crash detection in heuristics)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,17 +245,17 @@ export default abstract class Observer {
|
|||
if (!hasTag(node, 'HTML') || !this.isTopContext) {
|
||||
if (parent === null) {
|
||||
// Sometimes one observation contains attribute mutations for the removimg node, which gets ignored here.
|
||||
// That shouldn't affect the visual rendering ( should it? )
|
||||
this.unbindNode(node)
|
||||
// That shouldn't affect the visual rendering ( should it? maybe when transition applied? )
|
||||
this.unbindTree(node)
|
||||
return false
|
||||
}
|
||||
parentID = this.app.nodes.getID(parent)
|
||||
if (parentID === undefined) {
|
||||
this.unbindNode(node)
|
||||
this.unbindTree(node)
|
||||
return false
|
||||
}
|
||||
if (!this.commitNode(parentID)) {
|
||||
this.unbindNode(node)
|
||||
this.unbindTree(node)
|
||||
return false
|
||||
}
|
||||
this.app.sanitizer.handleNode(id, parentID, node)
|
||||
|
|
@ -345,7 +351,8 @@ export default abstract class Observer {
|
|||
this.clear()
|
||||
}
|
||||
|
||||
// ISSSUE
|
||||
// ISSSUE (nodeToBinde should be the same as node. Look at the comment about 0-node at the beginning of the file.)
|
||||
// TODO: use one observer instance for all iframes/shadowRoots (composition instiad of inheritance)
|
||||
protected observeRoot(
|
||||
node: Node,
|
||||
beforeCommit: (id?: number) => unknown,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,18 @@ export interface Options {
|
|||
captureIFrames: boolean
|
||||
}
|
||||
|
||||
type ContextCallback = (context: Window & typeof globalThis) => void
|
||||
|
||||
// Le truc - for defining an absolute offset for (nested) iframe documents. (To track mouse movments)
|
||||
type Offset = { top: number; left: number }
|
||||
type PatchedDocument = Document & {
|
||||
__openreplay__getOffset: () => Offset
|
||||
}
|
||||
function isPatchedDocument(doc: Document): doc is PatchedDocument {
|
||||
// @ts-ignore
|
||||
return typeof doc.__openreplay__getOffset === 'function'
|
||||
}
|
||||
|
||||
const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () => new ShadowRoot()
|
||||
|
||||
export default class TopObserver extends Observer {
|
||||
|
|
@ -44,25 +56,53 @@ export default class TopObserver extends Observer {
|
|||
})
|
||||
}
|
||||
|
||||
private readonly contextCallbacks: Array<ContextCallback> = []
|
||||
|
||||
// Attached once per Tracker instance
|
||||
attachContextCallback(cb: ContextCallback) {
|
||||
this.contextCallbacks.push(cb)
|
||||
}
|
||||
|
||||
// Le truc
|
||||
getDocumentOffset(doc: Document): Offset {
|
||||
if (isPatchedDocument(doc)) {
|
||||
return doc.__openreplay__getOffset()
|
||||
}
|
||||
return { top: 0, left: 0 }
|
||||
}
|
||||
|
||||
private iframeObservers: IFrameObserver[] = []
|
||||
private handleIframe(iframe: HTMLIFrameElement): void {
|
||||
let doc: Document | null = null
|
||||
let win: Window | null = null
|
||||
const handle = this.app.safe(() => {
|
||||
const id = this.app.nodes.getID(iframe)
|
||||
if (id === undefined) {
|
||||
return
|
||||
} //log
|
||||
if (iframe.contentDocument === doc) {
|
||||
return
|
||||
} // How frequently can it happen?
|
||||
doc = iframe.contentDocument
|
||||
if (!doc || !iframe.contentWindow) {
|
||||
//log
|
||||
return
|
||||
}
|
||||
const observer = new IFrameObserver(this.app)
|
||||
const currentWin = iframe.contentWindow
|
||||
const currentDoc = iframe.contentDocument
|
||||
if (currentDoc && currentDoc !== doc) {
|
||||
const observer = new IFrameObserver(this.app)
|
||||
this.iframeObservers.push(observer)
|
||||
observer.observe(iframe)
|
||||
doc = currentDoc
|
||||
|
||||
this.iframeObservers.push(observer)
|
||||
observer.observe(iframe)
|
||||
// Le truc
|
||||
;(doc as PatchedDocument).__openreplay__getOffset = () => {
|
||||
const { top, left } = this.getDocumentOffset(iframe.ownerDocument)
|
||||
return {
|
||||
top: iframe.offsetTop + top,
|
||||
left: iframe.offsetLeft + left,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentWin && currentWin !== win) {
|
||||
//@ts-ignore https://github.com/microsoft/TypeScript/issues/41684
|
||||
this.contextCallbacks.forEach((cb) => cb(currentWin))
|
||||
win = currentWin
|
||||
}
|
||||
})
|
||||
iframe.addEventListener('load', handle) // why app.attachEventListener not working?
|
||||
handle()
|
||||
|
|
|
|||
|
|
@ -1,15 +1,26 @@
|
|||
import type App from './index.js'
|
||||
|
||||
interface SessionInfo {
|
||||
sessionID: string | null
|
||||
sessionID: string | undefined
|
||||
metadata: Record<string, string>
|
||||
userID: string | null
|
||||
timestamp: number
|
||||
}
|
||||
type OnUpdateCallback = (i: Partial<SessionInfo>) => void
|
||||
|
||||
export type Options = {
|
||||
session_token_key: string
|
||||
session_pageno_key: string
|
||||
}
|
||||
|
||||
export default class Session {
|
||||
private metadata: Record<string, string> = {}
|
||||
private userID: string | null = null
|
||||
private sessionID: string | null = null
|
||||
private sessionID: string | undefined
|
||||
private readonly callbacks: OnUpdateCallback[] = []
|
||||
private timestamp = 0
|
||||
|
||||
constructor(private readonly app: App, private options: Options) {}
|
||||
|
||||
attachUpdateCallback(cb: OnUpdateCallback) {
|
||||
this.callbacks.push(cb)
|
||||
|
|
@ -35,6 +46,9 @@ export default class Session {
|
|||
if (newInfo.sessionID !== undefined) {
|
||||
this.sessionID = newInfo.sessionID
|
||||
}
|
||||
if (newInfo.timestamp !== undefined) {
|
||||
this.timestamp = newInfo.timestamp
|
||||
}
|
||||
this.handleUpdate(newInfo)
|
||||
}
|
||||
|
||||
|
|
@ -47,17 +61,69 @@ export default class Session {
|
|||
this.handleUpdate({ userID })
|
||||
}
|
||||
|
||||
private getPageNumber(): number | undefined {
|
||||
const pageNoStr = this.app.sessionStorage.getItem(this.options.session_pageno_key)
|
||||
if (pageNoStr == null) {
|
||||
return undefined
|
||||
}
|
||||
return parseInt(pageNoStr)
|
||||
}
|
||||
|
||||
incPageNo(): number {
|
||||
let pageNo = this.getPageNumber()
|
||||
if (pageNo === undefined) {
|
||||
pageNo = 0
|
||||
} else {
|
||||
pageNo++
|
||||
}
|
||||
this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString())
|
||||
return pageNo
|
||||
}
|
||||
|
||||
getSessionToken(): string | undefined {
|
||||
return this.app.sessionStorage.getItem(this.options.session_token_key) || undefined
|
||||
}
|
||||
setSessionToken(token: string): void {
|
||||
this.app.sessionStorage.setItem(this.options.session_token_key, token)
|
||||
}
|
||||
|
||||
applySessionHash(hash: string) {
|
||||
const hashParts = decodeURI(hash).split('&')
|
||||
let token = hash
|
||||
let pageNoStr = '100500' // back-compat for sessionToken
|
||||
if (hashParts.length == 2) {
|
||||
;[token, pageNoStr] = hashParts
|
||||
}
|
||||
if (!pageNoStr || !token) {
|
||||
return
|
||||
}
|
||||
this.app.sessionStorage.setItem(this.options.session_token_key, token)
|
||||
this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNoStr)
|
||||
}
|
||||
|
||||
getSessionHash(): string | undefined {
|
||||
const pageNo = this.getPageNumber()
|
||||
const token = this.getSessionToken()
|
||||
if (pageNo === undefined || token === undefined) {
|
||||
return
|
||||
}
|
||||
return encodeURI(String(pageNo) + '&' + token)
|
||||
}
|
||||
|
||||
getInfo(): SessionInfo {
|
||||
return {
|
||||
sessionID: this.sessionID,
|
||||
metadata: this.metadata,
|
||||
userID: this.userID,
|
||||
timestamp: this.timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.app.sessionStorage.removeItem(this.options.session_token_key)
|
||||
this.metadata = {}
|
||||
this.userID = null
|
||||
this.sessionID = null
|
||||
this.sessionID = undefined
|
||||
this.timestamp = 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import Performance from './modules/performance.js'
|
|||
import Scroll from './modules/scroll.js'
|
||||
import Viewport from './modules/viewport.js'
|
||||
import CSSRules from './modules/cssrules.js'
|
||||
import AdoptedStyleSheets from './modules/adoptedStyleSheets.js'
|
||||
import { IN_BROWSER, deprecationWarn, DOCS_HOST } from './utils.js'
|
||||
|
||||
import type { Options as AppOptions } from './app/index.js'
|
||||
|
|
@ -69,10 +70,8 @@ function processOptions(obj: any): obj is Options {
|
|||
obj.projectKey = obj.projectKey.toString()
|
||||
}
|
||||
}
|
||||
if (typeof obj.sessionToken !== 'string' && obj.sessionToken != null) {
|
||||
console.warn(
|
||||
`OpenReplay: invalid options argument type. Please, check documentation on ${DOCS_HOST}${DOCS_SETUP}`,
|
||||
)
|
||||
if (obj.sessionToken != null) {
|
||||
deprecationWarn('`sessionToken` option', '`sessionHash` start() option', '/')
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -113,6 +112,7 @@ export default class API {
|
|||
if (app !== null) {
|
||||
Viewport(app)
|
||||
CSSRules(app)
|
||||
AdoptedStyleSheets(app)
|
||||
Connection(app)
|
||||
Console(app, options)
|
||||
Exception(app, options)
|
||||
|
|
@ -182,11 +182,14 @@ export default class API {
|
|||
// TODO: check argument type
|
||||
return this.app.start(startOpts)
|
||||
}
|
||||
stop(): void {
|
||||
stop(): string | undefined {
|
||||
if (this.app === null) {
|
||||
return
|
||||
}
|
||||
this.app.stop(true)
|
||||
this.app.stop()
|
||||
const sessionHash = this.app.session.getSessionHash()
|
||||
this.app.session.reset()
|
||||
return sessionHash
|
||||
}
|
||||
|
||||
getSessionToken(): string | null | undefined {
|
||||
|
|
|
|||
155
tracker/tracker/src/main/modules/adoptedStyleSheets.ts
Normal file
155
tracker/tracker/src/main/modules/adoptedStyleSheets.ts
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import type App from '../app/index.js'
|
||||
import {
|
||||
TechnicalInfo,
|
||||
AdoptedSSReplaceURLBased,
|
||||
AdoptedSSInsertRuleURLBased,
|
||||
AdoptedSSDeleteRule,
|
||||
AdoptedSSAddOwner,
|
||||
AdoptedSSRemoveOwner,
|
||||
} from '../app/messages.gen.js'
|
||||
import { isRootNode } from '../app/guards.js'
|
||||
|
||||
type StyleSheetOwner = (Document | ShadowRoot) & { adoptedStyleSheets: CSSStyleSheet[] }
|
||||
|
||||
function hasAdoptedSS(node: Node): node is StyleSheetOwner {
|
||||
return (
|
||||
isRootNode(node) &&
|
||||
// @ts-ignore
|
||||
!!node.adoptedStyleSheets
|
||||
)
|
||||
}
|
||||
|
||||
export default function (app: App | null) {
|
||||
if (app === null) {
|
||||
return
|
||||
}
|
||||
if (!hasAdoptedSS(document)) {
|
||||
app.attachStartCallback(() => {
|
||||
// MBTODO: pre-start sendQueue app
|
||||
app.send(TechnicalInfo('no_adopted_stylesheets', ''))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let nextID = 0xf
|
||||
const styleSheetIDMap: Map<CSSStyleSheet, number> = new Map()
|
||||
const adoptedStyleSheetsOwnings: Map<number, number[]> = new Map()
|
||||
|
||||
const updateAdoptedStyleSheets = (root: StyleSheetOwner) => {
|
||||
let nodeID = app.nodes.getID(root)
|
||||
if (root === document) {
|
||||
nodeID = 0 // main document doesn't have nodeID. ID count starts from the documentElement
|
||||
}
|
||||
if (!nodeID) {
|
||||
return
|
||||
}
|
||||
let pastOwning = adoptedStyleSheetsOwnings.get(nodeID)
|
||||
if (!pastOwning) {
|
||||
pastOwning = []
|
||||
}
|
||||
const nowOwning: number[] = []
|
||||
const styleSheets = root.adoptedStyleSheets
|
||||
for (const s of styleSheets) {
|
||||
let sheetID = styleSheetIDMap.get(s)
|
||||
const init = !sheetID
|
||||
if (!sheetID) {
|
||||
sheetID = ++nextID
|
||||
}
|
||||
nowOwning.push(sheetID)
|
||||
if (!pastOwning.includes(sheetID)) {
|
||||
app.send(AdoptedSSAddOwner(sheetID, nodeID))
|
||||
}
|
||||
if (init) {
|
||||
const rules = s.cssRules
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
app.send(AdoptedSSInsertRuleURLBased(sheetID, rules[i].cssText, i, app.getBaseHref()))
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const sheetID of pastOwning) {
|
||||
if (!nowOwning.includes(sheetID)) {
|
||||
app.send(AdoptedSSRemoveOwner(sheetID, nodeID))
|
||||
}
|
||||
}
|
||||
adoptedStyleSheetsOwnings.set(nodeID, nowOwning)
|
||||
}
|
||||
|
||||
function patchAdoptedStyleSheets(
|
||||
prototype: typeof Document.prototype | typeof ShadowRoot.prototype,
|
||||
) {
|
||||
const nativeAdoptedStyleSheetsDescriptor = Object.getOwnPropertyDescriptor(
|
||||
prototype,
|
||||
'adoptedStyleSheets',
|
||||
)
|
||||
if (nativeAdoptedStyleSheetsDescriptor) {
|
||||
Object.defineProperty(prototype, 'adoptedStyleSheets', {
|
||||
...nativeAdoptedStyleSheetsDescriptor,
|
||||
set: function (this: StyleSheetOwner, value) {
|
||||
// @ts-ignore
|
||||
const retVal = nativeAdoptedStyleSheetsDescriptor.set.call(this, value)
|
||||
updateAdoptedStyleSheets(this)
|
||||
return retVal
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const patchContext = (context: typeof globalThis): void => {
|
||||
patchAdoptedStyleSheets(context.Document.prototype)
|
||||
patchAdoptedStyleSheets(context.ShadowRoot.prototype)
|
||||
|
||||
//@ts-ignore TODO: configure ts (use necessary lib)
|
||||
const { insertRule, deleteRule, replace, replaceSync } = context.CSSStyleSheet.prototype
|
||||
|
||||
//@ts-ignore
|
||||
context.CSSStyleSheet.prototype.replace = function (text: string) {
|
||||
return replace.call(this, text).then((sheet: CSSStyleSheet) => {
|
||||
const sheetID = styleSheetIDMap.get(this)
|
||||
if (sheetID) {
|
||||
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()))
|
||||
}
|
||||
return sheet
|
||||
})
|
||||
}
|
||||
//@ts-ignore
|
||||
context.CSSStyleSheet.prototype.replaceSync = function (text: string) {
|
||||
const sheetID = styleSheetIDMap.get(this)
|
||||
if (sheetID) {
|
||||
app.send(AdoptedSSReplaceURLBased(sheetID, text, app.getBaseHref()))
|
||||
}
|
||||
return replaceSync.call(this, text)
|
||||
}
|
||||
context.CSSStyleSheet.prototype.insertRule = function (rule: string, index = 0) {
|
||||
const sheetID = styleSheetIDMap.get(this)
|
||||
if (sheetID) {
|
||||
app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref()))
|
||||
}
|
||||
return insertRule.call(this, rule, index)
|
||||
}
|
||||
context.CSSStyleSheet.prototype.deleteRule = function (index: number) {
|
||||
const sheetID = styleSheetIDMap.get(this)
|
||||
if (sheetID) {
|
||||
app.send(AdoptedSSDeleteRule(sheetID, index))
|
||||
}
|
||||
return deleteRule.call(this, index)
|
||||
}
|
||||
}
|
||||
|
||||
patchContext(window)
|
||||
app.observer.attachContextCallback(patchContext)
|
||||
|
||||
app.attachStopCallback(() => {
|
||||
styleSheetIDMap.clear()
|
||||
adoptedStyleSheetsOwnings.clear()
|
||||
})
|
||||
|
||||
// So far main Document is not triggered with nodeCallbacks
|
||||
app.attachStartCallback(() => {
|
||||
updateAdoptedStyleSheets(document as StyleSheetOwner)
|
||||
})
|
||||
app.nodes.attachNodeCallback((node: Node): void => {
|
||||
if (hasAdoptedSS(node)) {
|
||||
updateAdoptedStyleSheets(node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ export default function (app: App, opts: Partial<Options>): void {
|
|||
const patchConsole = (console: Console) =>
|
||||
options.consoleMethods!.forEach((method) => {
|
||||
if (consoleMethods.indexOf(method) === -1) {
|
||||
console.error(`OpenReplay: unsupported console method "${method}"`)
|
||||
app.debug.error(`OpenReplay: unsupported console method "${method}"`)
|
||||
return
|
||||
}
|
||||
const fn = (console as any)[method]
|
||||
|
|
@ -134,23 +134,8 @@ export default function (app: App, opts: Partial<Options>): void {
|
|||
sendConsoleLog(method, args)
|
||||
}
|
||||
})
|
||||
patchConsole(window.console)
|
||||
const patchContext = app.safe((context: typeof globalThis) => patchConsole(context.console))
|
||||
|
||||
app.nodes.attachNodeCallback(
|
||||
app.safe((node) => {
|
||||
if (hasTag(node, 'IFRAME')) {
|
||||
// TODO: newContextCallback
|
||||
let context = node.contentWindow
|
||||
if (context) {
|
||||
patchConsole((context as Window & typeof globalThis).console)
|
||||
}
|
||||
app.attachEventListener(node, 'load', () => {
|
||||
if (node.contentWindow !== context) {
|
||||
context = node.contentWindow
|
||||
patchConsole((context as Window & typeof globalThis).console)
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
patchContext(window)
|
||||
app.observer.attachContextCallback(patchContext)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,16 +27,20 @@ export default function (app: App | null) {
|
|||
} // else error?
|
||||
})
|
||||
|
||||
const { insertRule, deleteRule } = CSSStyleSheet.prototype
|
||||
const patchContext = (context: typeof globalThis) => {
|
||||
const { insertRule, deleteRule } = context.CSSStyleSheet.prototype
|
||||
context.CSSStyleSheet.prototype.insertRule = function (rule: string, index = 0) {
|
||||
processOperation(this, index, rule)
|
||||
return insertRule.call(this, rule, index)
|
||||
}
|
||||
context.CSSStyleSheet.prototype.deleteRule = function (index: number) {
|
||||
processOperation(this, index)
|
||||
return deleteRule.call(this, index)
|
||||
}
|
||||
}
|
||||
|
||||
CSSStyleSheet.prototype.insertRule = function (rule: string, index = 0) {
|
||||
processOperation(this, index, rule)
|
||||
return insertRule.call(this, rule, index)
|
||||
}
|
||||
CSSStyleSheet.prototype.deleteRule = function (index: number) {
|
||||
processOperation(this, index)
|
||||
return deleteRule.call(this, index)
|
||||
}
|
||||
patchContext(window)
|
||||
app.observer.attachContextCallback(patchContext)
|
||||
|
||||
app.nodes.attachNodeCallback((node: Node): void => {
|
||||
if (!hasTag(node, 'STYLE') || !node.sheet) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export function getExceptionMessage(error: Error, fallbackStack: Array<StackFram
|
|||
|
||||
export function getExceptionMessageFromEvent(
|
||||
e: ErrorEvent | PromiseRejectionEvent,
|
||||
context: typeof globalThis = window,
|
||||
): Message | null {
|
||||
if (e instanceof ErrorEvent) {
|
||||
if (e.error instanceof Error) {
|
||||
|
|
@ -49,7 +50,7 @@ export function getExceptionMessageFromEvent(
|
|||
}
|
||||
return JSException(name, message, JSON.stringify(getDefaultStack(e)))
|
||||
}
|
||||
} else if ('PromiseRejectionEvent' in window && e instanceof PromiseRejectionEvent) {
|
||||
} else if ('PromiseRejectionEvent' in context && e instanceof context.PromiseRejectionEvent) {
|
||||
if (e.reason instanceof Error) {
|
||||
return getExceptionMessage(e.reason, [])
|
||||
} else {
|
||||
|
|
@ -72,17 +73,18 @@ export default function (app: App, opts: Partial<Options>): void {
|
|||
},
|
||||
opts,
|
||||
)
|
||||
if (options.captureExceptions) {
|
||||
const handler = (e: ErrorEvent | PromiseRejectionEvent): void => {
|
||||
const msg = getExceptionMessageFromEvent(e)
|
||||
function patchContext(context: Window & typeof globalThis) {
|
||||
function handler(e: ErrorEvent | PromiseRejectionEvent): void {
|
||||
const msg = getExceptionMessageFromEvent(e, context)
|
||||
if (msg != null) {
|
||||
app.send(msg)
|
||||
}
|
||||
}
|
||||
|
||||
app.attachEventListener(window, 'unhandledrejection', (e: PromiseRejectionEvent): void =>
|
||||
handler(e),
|
||||
)
|
||||
app.attachEventListener(window, 'error', (e: ErrorEvent): void => handler(e))
|
||||
app.attachEventListener(context, 'unhandledrejection', handler)
|
||||
app.attachEventListener(context, 'error', handler)
|
||||
}
|
||||
if (options.captureExceptions) {
|
||||
app.observer.attachContextCallback(patchContext)
|
||||
patchContext(window)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,24 @@ export default function (app: App): void {
|
|||
}
|
||||
}
|
||||
|
||||
const sendImgSrc = app.safe(function (this: HTMLImageElement): void {
|
||||
const sendSrcset = function (id: number, img: HTMLImageElement): void {
|
||||
const { srcset } = img
|
||||
if (!srcset) {
|
||||
return
|
||||
}
|
||||
const resolvedSrcset = srcset
|
||||
.split(',')
|
||||
.map((str) => resolveURL(str))
|
||||
.join(',')
|
||||
app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset))
|
||||
}
|
||||
|
||||
const sendSrc = function (id: number, img: HTMLImageElement): void {
|
||||
const src = img.src
|
||||
app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()))
|
||||
}
|
||||
|
||||
const sendImgAttrs = app.safe(function (this: HTMLImageElement): void {
|
||||
const id = app.nodes.getID(this)
|
||||
if (id === undefined) {
|
||||
return
|
||||
|
|
@ -49,14 +66,8 @@ export default function (app: App): void {
|
|||
} else if (resolvedSrc.length >= 1e5 || app.sanitizer.isMasked(id)) {
|
||||
sendPlaceholder(id, this)
|
||||
} else {
|
||||
app.send(SetNodeAttribute(id, 'src', resolvedSrc))
|
||||
if (srcset) {
|
||||
const resolvedSrcset = srcset
|
||||
.split(',')
|
||||
.map((str) => resolveURL(str))
|
||||
.join(',')
|
||||
app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset))
|
||||
}
|
||||
sendSrc(id, this)
|
||||
sendSrcset(id, this)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -69,24 +80,26 @@ export default function (app: App): void {
|
|||
return
|
||||
}
|
||||
if (mutation.attributeName === 'src') {
|
||||
const src = target.src
|
||||
app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()))
|
||||
sendSrc(id, target)
|
||||
}
|
||||
if (mutation.attributeName === 'srcset') {
|
||||
const srcset = target.srcset
|
||||
app.send(SetNodeAttribute(id, 'srcset', srcset))
|
||||
sendSrcset(id, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.attachStopCallback(() => {
|
||||
observer.disconnect()
|
||||
})
|
||||
|
||||
app.nodes.attachNodeCallback((node: Node): void => {
|
||||
if (!hasTag(node, 'IMG')) {
|
||||
return
|
||||
}
|
||||
app.nodes.attachElementListener('error', node, sendImgSrc)
|
||||
app.nodes.attachElementListener('load', node, sendImgSrc)
|
||||
sendImgSrc.call(node)
|
||||
app.nodes.attachElementListener('error', node, sendImgAttrs.bind(node))
|
||||
app.nodes.attachElementListener('load', node, sendImgAttrs.bind(node))
|
||||
sendImgAttrs.call(node)
|
||||
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] })
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const labelElementFor: (element: TextEditableElement) => HTMLLabelElement | unde
|
|||
}
|
||||
const id = node.id
|
||||
if (id) {
|
||||
const labels = document.querySelectorAll('label[for="' + id + '"]')
|
||||
const labels = node.ownerDocument.querySelectorAll('label[for="' + id + '"]')
|
||||
if (labels !== null && labels.length === 1) {
|
||||
return labels[0] as HTMLLabelElement
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import type App from '../app/index.js'
|
||||
import { LongTask } from '../app/messages.gen.js'
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#the-performanceentry-interface
|
||||
interface TaskAttributionTiming extends PerformanceEntry {
|
||||
readonly containerType: string
|
||||
readonly containerSrc: string
|
||||
readonly containerId: string
|
||||
readonly containerName: string
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/longtasks/#performancelongtasktiming
|
||||
interface PerformanceLongTaskTiming extends PerformanceEntry {
|
||||
readonly attribution: ReadonlyArray<TaskAttributionTiming>
|
||||
}
|
||||
|
||||
export default function (app: App): void {
|
||||
if (!('PerformanceObserver' in window) || !('PerformanceLongTaskTiming' in window)) {
|
||||
return
|
||||
}
|
||||
|
||||
const contexts: string[] = [
|
||||
'unknown',
|
||||
'self',
|
||||
'same-origin-ancestor',
|
||||
'same-origin-descendant',
|
||||
'same-origin',
|
||||
'cross-origin-ancestor',
|
||||
'cross-origin-descendant',
|
||||
'cross-origin-unreachable',
|
||||
'multiple-contexts',
|
||||
]
|
||||
const containerTypes: string[] = ['window', 'iframe', 'embed', 'object']
|
||||
function longTask(entry: PerformanceLongTaskTiming): void {
|
||||
let type = '',
|
||||
src = '',
|
||||
id = '',
|
||||
name = ''
|
||||
const container = entry.attribution[0]
|
||||
if (container != null) {
|
||||
type = container.containerType
|
||||
name = container.containerName
|
||||
id = container.containerId
|
||||
src = container.containerSrc
|
||||
}
|
||||
|
||||
app.send(
|
||||
LongTask(
|
||||
entry.startTime + performance.timing.navigationStart,
|
||||
entry.duration,
|
||||
Math.max(contexts.indexOf(entry.name), 0),
|
||||
Math.max(containerTypes.indexOf(type), 0),
|
||||
name,
|
||||
id,
|
||||
src,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const observer: PerformanceObserver = new PerformanceObserver((list) =>
|
||||
list.getEntries().forEach(longTask),
|
||||
)
|
||||
observer.observe({ entryTypes: ['longtask'] })
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import type App from '../app/index.js'
|
||||
import { hasTag, isSVGElement } from '../app/guards.js'
|
||||
import { hasTag, isSVGElement, isDocument } from '../app/guards.js'
|
||||
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js'
|
||||
import { MouseMove, MouseClick } from '../app/messages.gen.js'
|
||||
import { getInputLabel } from './input.js'
|
||||
|
||||
function _getSelector(target: Element): string {
|
||||
function _getSelector(target: Element, document: Document): string {
|
||||
let el: Element | null = target
|
||||
let selector: string | null = null
|
||||
do {
|
||||
|
|
@ -37,18 +37,18 @@ function isClickable(element: Element): boolean {
|
|||
element.getAttribute('role') === 'button'
|
||||
)
|
||||
//|| element.className.includes("btn")
|
||||
// MBTODO: intersect addEventListener
|
||||
// MBTODO: intersept addEventListener
|
||||
}
|
||||
|
||||
//TODO: fix (typescript doesn't allow work when the guard is inside the function)
|
||||
function getTarget(target: EventTarget | null): Element | null {
|
||||
//TODO: fix (typescript is not sure about target variable after assignation of svg)
|
||||
function getTarget(target: EventTarget | null, document: Document): Element | null {
|
||||
if (target instanceof Element) {
|
||||
return _getTarget(target)
|
||||
return _getTarget(target, document)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function _getTarget(target: Element): Element | null {
|
||||
function _getTarget(target: Element, document: Document): Element | null {
|
||||
let element: Element | null = target
|
||||
while (element !== null && element !== document.documentElement) {
|
||||
if (hasOpenreplayAttribute(element, 'masked')) {
|
||||
|
|
@ -120,48 +120,58 @@ export default function (app: App): void {
|
|||
}
|
||||
}
|
||||
|
||||
const selectorMap: { [id: number]: string } = {}
|
||||
function getSelector(id: number, target: Element): string {
|
||||
return (selectorMap[id] = selectorMap[id] || _getSelector(target))
|
||||
const patchDocument = (document: Document) => {
|
||||
const selectorMap: { [id: number]: string } = {}
|
||||
function getSelector(id: number, target: Element): string {
|
||||
return (selectorMap[id] = selectorMap[id] || _getSelector(target, document))
|
||||
}
|
||||
|
||||
app.attachEventListener(document.documentElement, 'mouseover', (e: MouseEvent): void => {
|
||||
const target = getTarget(e.target, document)
|
||||
if (target !== mouseTarget) {
|
||||
mouseTarget = target
|
||||
mouseTargetTime = performance.now()
|
||||
}
|
||||
})
|
||||
app.attachEventListener(
|
||||
document,
|
||||
'mousemove',
|
||||
(e: MouseEvent): void => {
|
||||
const { top, left } = app.observer.getDocumentOffset(document)
|
||||
mousePositionX = e.clientX + left
|
||||
mousePositionY = e.clientY + top
|
||||
mousePositionChanged = true
|
||||
},
|
||||
false,
|
||||
)
|
||||
app.attachEventListener(document, 'click', (e: MouseEvent): void => {
|
||||
const target = getTarget(e.target, document)
|
||||
if ((!e.clientX && !e.clientY) || target === null) {
|
||||
return
|
||||
}
|
||||
const id = app.nodes.getID(target)
|
||||
if (id !== undefined) {
|
||||
sendMouseMove()
|
||||
app.send(
|
||||
MouseClick(
|
||||
id,
|
||||
mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0,
|
||||
getTargetLabel(target),
|
||||
getSelector(id, target),
|
||||
),
|
||||
true,
|
||||
)
|
||||
}
|
||||
mouseTarget = null
|
||||
})
|
||||
}
|
||||
|
||||
app.attachEventListener(document.documentElement, 'mouseover', (e: MouseEvent): void => {
|
||||
const target = getTarget(e.target)
|
||||
if (target !== mouseTarget) {
|
||||
mouseTarget = target
|
||||
mouseTargetTime = performance.now()
|
||||
app.nodes.attachNodeCallback((node) => {
|
||||
if (isDocument(node)) {
|
||||
patchDocument(node)
|
||||
}
|
||||
})
|
||||
app.attachEventListener(
|
||||
document,
|
||||
'mousemove',
|
||||
(e: MouseEvent): void => {
|
||||
mousePositionX = e.clientX
|
||||
mousePositionY = e.clientY
|
||||
mousePositionChanged = true
|
||||
},
|
||||
false,
|
||||
)
|
||||
app.attachEventListener(document, 'click', (e: MouseEvent): void => {
|
||||
const target = getTarget(e.target)
|
||||
if ((!e.clientX && !e.clientY) || target === null) {
|
||||
return
|
||||
}
|
||||
const id = app.nodes.getID(target)
|
||||
if (id !== undefined) {
|
||||
sendMouseMove()
|
||||
app.send(
|
||||
MouseClick(
|
||||
id,
|
||||
mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0,
|
||||
getTargetLabel(target),
|
||||
getSelector(id, target),
|
||||
),
|
||||
true,
|
||||
)
|
||||
}
|
||||
mouseTarget = null
|
||||
})
|
||||
patchDocument(document)
|
||||
|
||||
app.ticker.attach(sendMouseMove, 10)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import type App from '../app/index.js'
|
||||
import { SetViewportScroll, SetNodeScroll } from '../app/messages.gen.js'
|
||||
import { isElementNode } from '../app/guards.js'
|
||||
import { isElementNode, isRootNode } from '../app/guards.js'
|
||||
|
||||
export default function (app: App): void {
|
||||
let documentScroll = false
|
||||
const nodeScroll: Map<Element, [number, number]> = new Map()
|
||||
const nodeScroll: Map<Node, [number, number]> = new Map()
|
||||
|
||||
function setNodeScroll(target: EventTarget | null) {
|
||||
if (target instanceof Element) {
|
||||
nodeScroll.set(target, [target.scrollLeft, target.scrollTop])
|
||||
}
|
||||
}
|
||||
|
||||
const sendSetViewportScroll = app.safe((): void =>
|
||||
app.send(
|
||||
|
|
@ -38,18 +44,21 @@ export default function (app: App): void {
|
|||
app.nodes.attachNodeCallback((node, isStart) => {
|
||||
if (isStart && isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
|
||||
nodeScroll.set(node, [node.scrollLeft, node.scrollTop])
|
||||
} else if (isRootNode(node)) {
|
||||
// scroll is not-composed event (https://javascript.info/shadow-dom-events)
|
||||
app.attachEventListener(node, 'scroll', (e: Event): void => {
|
||||
setNodeScroll(e.target)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.attachEventListener(window, 'scroll', (e: Event): void => {
|
||||
app.attachEventListener(document, 'scroll', (e: Event): void => {
|
||||
const target = e.target
|
||||
if (target === document) {
|
||||
documentScroll = true
|
||||
return
|
||||
}
|
||||
if (target instanceof Element) {
|
||||
nodeScroll.set(target, [target.scrollLeft, target.scrollTop])
|
||||
}
|
||||
setNodeScroll(target)
|
||||
})
|
||||
|
||||
app.ticker.attach(
|
||||
|
|
|
|||
|
|
@ -209,6 +209,26 @@ export default class MessageEncoder extends PrimitiveEncoder {
|
|||
return this.uint(msg[1]) && this.uint(msg[2])
|
||||
break
|
||||
|
||||
case Messages.Type.AdoptedSSReplaceURLBased:
|
||||
return this.uint(msg[1]) && this.string(msg[2]) && this.string(msg[3])
|
||||
break
|
||||
|
||||
case Messages.Type.AdoptedSSInsertRuleURLBased:
|
||||
return this.uint(msg[1]) && this.string(msg[2]) && this.uint(msg[3]) && this.string(msg[4])
|
||||
break
|
||||
|
||||
case Messages.Type.AdoptedSSDeleteRule:
|
||||
return this.uint(msg[1]) && this.uint(msg[2])
|
||||
break
|
||||
|
||||
case Messages.Type.AdoptedSSAddOwner:
|
||||
return this.uint(msg[1]) && this.uint(msg[2])
|
||||
break
|
||||
|
||||
case Messages.Type.AdoptedSSRemoveOwner:
|
||||
return this.uint(msg[1]) && this.uint(msg[2])
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue