From f360d8416dae2305a1b9ea6ba01030b7f3490576 Mon Sep 17 00:00:00 2001 From: sylenien Date: Thu, 8 Sep 2022 15:06:18 +0200 Subject: [PATCH 1/6] feat(tracker): add zustand support --- backend/pkg/messages/filters.go | 2 +- backend/pkg/messages/messages.go | 36 +++ backend/pkg/messages/read-message.go | 15 ++ ee/connectors/msgcodec/messages.py | 16 +- ee/connectors/msgcodec/msgcodec.py | 21 +- .../messages/RawMessageReader.ts | 10 + .../MessageDistributor/messages/message.ts | 3 + .../player/MessageDistributor/messages/raw.ts | 8 +- .../messages/tracker-legacy.ts | 1 + .../MessageDistributor/messages/tracker.ts | 16 +- mobs/messages.rb | 21 +- tracker/tracker-vuex/package.json | 5 +- tracker/tracker-vuex/src/index.ts | 2 +- tracker/tracker-zustand/.gitignore | 6 + tracker/tracker-zustand/.npmignore | 5 + tracker/tracker-zustand/LICENSE | 19 ++ tracker/tracker-zustand/README.md | 48 ++++ tracker/tracker-zustand/package.json | 35 +++ tracker/tracker-zustand/src/index.ts | 62 +++++ tracker/tracker-zustand/src/syncod/chars.ts | 18 ++ tracker/tracker-zustand/src/syncod/encoder.ts | 220 ++++++++++++++++++ tracker/tracker-zustand/src/syncod/index.ts | 5 + tracker/tracker-zustand/src/syncod/sha1.ts | 104 +++++++++ tracker/tracker-zustand/tsconfig-cjs.json | 8 + tracker/tracker-zustand/tsconfig.json | 12 + tracker/tracker/package.json | 2 +- tracker/tracker/src/common/messages.gen.ts | 9 +- tracker/tracker/src/main/app/messages.gen.ts | 11 + .../src/webworker/MessageEncoder.gen.ts | 4 + 29 files changed, 690 insertions(+), 34 deletions(-) create mode 100644 tracker/tracker-zustand/.gitignore create mode 100644 tracker/tracker-zustand/.npmignore create mode 100644 tracker/tracker-zustand/LICENSE create mode 100644 tracker/tracker-zustand/README.md create mode 100644 tracker/tracker-zustand/package.json create mode 100644 tracker/tracker-zustand/src/index.ts create mode 100644 tracker/tracker-zustand/src/syncod/chars.ts create mode 100644 tracker/tracker-zustand/src/syncod/encoder.ts create mode 100644 tracker/tracker-zustand/src/syncod/index.ts create mode 100644 tracker/tracker-zustand/src/syncod/sha1.ts create mode 100644 tracker/tracker-zustand/tsconfig-cjs.json create mode 100644 tracker/tracker-zustand/tsconfig.json diff --git a/backend/pkg/messages/filters.go b/backend/pkg/messages/filters.go index c28e07742..e79d1d987 100644 --- a/backend/pkg/messages/filters.go +++ b/backend/pkg/messages/filters.go @@ -2,7 +2,7 @@ package messages func IsReplayerType(id int) bool { - return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 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 + 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 || 79 == id || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id } func IsIOSType(id int) bool { diff --git a/backend/pkg/messages/messages.go b/backend/pkg/messages/messages.go index 27712cb1f..8cdb95722 100644 --- a/backend/pkg/messages/messages.go +++ b/backend/pkg/messages/messages.go @@ -156,6 +156,8 @@ const ( MsgAdoptedSSRemoveOwner = 77 + MsgZustand = 79 + MsgIOSBatchMeta = 107 MsgIOSSessionStart = 90 @@ -3038,6 +3040,40 @@ func (msg *AdoptedSSRemoveOwner) TypeID() int { return 77 } +type Zustand struct { + message + Mutation string + State string +} + +func (msg *Zustand) Encode() []byte { + buf := make([]byte, 21+len(msg.Mutation)+len(msg.State)) + buf[0] = 79 + p := 1 + p = WriteString(msg.Mutation, buf, p) + p = WriteString(msg.State, buf, p) + return buf[:p] +} + +func (msg *Zustand) 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 *Zustand) Decode() Message { + return msg +} + +func (msg *Zustand) TypeID() int { + return 79 +} + type IOSBatchMeta struct { message Timestamp uint64 diff --git a/backend/pkg/messages/read-message.go b/backend/pkg/messages/read-message.go index 2b12601d9..1b0f579af 100644 --- a/backend/pkg/messages/read-message.go +++ b/backend/pkg/messages/read-message.go @@ -1306,6 +1306,18 @@ func DecodeAdoptedSSRemoveOwner(reader io.Reader) (Message, error) { return msg, err } +func DecodeZustand(reader io.Reader) (Message, error) { + var err error = nil + msg := &Zustand{} + if msg.Mutation, err = ReadString(reader); err != nil { + return nil, err + } + if msg.State, err = ReadString(reader); err != nil { + return nil, err + } + return msg, err +} + func DecodeIOSBatchMeta(reader io.Reader) (Message, error) { var err error = nil msg := &IOSBatchMeta{} @@ -1939,6 +1951,9 @@ func ReadMessage(t uint64, reader io.Reader) (Message, error) { case 77: return DecodeAdoptedSSRemoveOwner(reader) + case 79: + return DecodeZustand(reader) + case 107: return DecodeIOSBatchMeta(reader) diff --git a/ee/connectors/msgcodec/messages.py b/ee/connectors/msgcodec/messages.py index f645e2995..e1fe393a4 100644 --- a/ee/connectors/msgcodec/messages.py +++ b/ee/connectors/msgcodec/messages.py @@ -63,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 @@ -106,7 +99,6 @@ class CreateDocument(Message): __id__ = 7 def __init__(self, ): - pass @@ -752,6 +744,14 @@ class AdoptedSSRemoveOwner(Message): self.id = id +class Zustand(Message): + __id__ = 79 + + def __init__(self, mutation, state): + self.mutation = mutation + self.state = state + + class IOSBatchMeta(Message): __id__ = 107 diff --git a/ee/connectors/msgcodec/msgcodec.py b/ee/connectors/msgcodec/msgcodec.py index 76468682a..d53c3e75d 100644 --- a/ee/connectors/msgcodec/msgcodec.py +++ b/ee/connectors/msgcodec/msgcodec.py @@ -2,6 +2,7 @@ from msgcodec.codec import Codec from msgcodec.messages import * +from typing import List import io class MessageCodec(Codec): @@ -42,7 +43,7 @@ class MessageCodec(Codec): raise UnicodeDecodeError(f"Error while decoding message key (SessionID) from {b}\n{e}") return decoded - def decode_detailed(self, b: bytes): + def decode_detailed(self, b: bytes) -> List[Message]: reader = io.BytesIO(b) messages_list = list() messages_list.append(self.handler(reader, 0)) @@ -61,7 +62,7 @@ class MessageCodec(Codec): break return messages_list - def handler(self, reader: io.BytesIO, mode=0): + def handler(self, reader: io.BytesIO, mode=0) -> Message: message_id = self.read_message_id(reader) if mode == 1: # We skip the three bytes representing the length of message. It can be used to skip unwanted messages @@ -71,9 +72,10 @@ class MessageCodec(Codec): # Old format with no bytes for message length return self.read_head_message(reader, message_id) else: - raise IOError() + raise IOError() + + def read_head_message(self, reader: io.BytesIO, message_id) -> Message: - def read_head_message(self, reader: io.BytesIO, message_id: int): if message_id == 80: return BatchMeta( page_no=self.read_uint(reader), @@ -121,11 +123,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) @@ -665,6 +662,12 @@ class MessageCodec(Codec): id=self.read_uint(reader) ) + if message_id == 79: + return Zustand( + mutation=self.read_string(reader), + state=self.read_string(reader) + ) + if message_id == 107: return IOSBatchMeta( timestamp=self.read_uint(reader), diff --git a/frontend/app/player/MessageDistributor/messages/RawMessageReader.ts b/frontend/app/player/MessageDistributor/messages/RawMessageReader.ts index 4536a7c0e..d925d8bcc 100644 --- a/frontend/app/player/MessageDistributor/messages/RawMessageReader.ts +++ b/frontend/app/player/MessageDistributor/messages/RawMessageReader.ts @@ -527,6 +527,16 @@ export default class RawMessageReader extends PrimitiveReader { }; } + case 79: { + const mutation = this.readString(); if (mutation === null) { return resetPointer() } + const state = this.readString(); if (state === null) { return resetPointer() } + return { + tp: "zustand", + mutation, + state, + }; + } + case 90: { const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() } const projectID = this.readUint(); if (projectID === null) { return resetPointer() } diff --git a/frontend/app/player/MessageDistributor/messages/message.ts b/frontend/app/player/MessageDistributor/messages/message.ts index 490f817ea..397bbfaa4 100644 --- a/frontend/app/player/MessageDistributor/messages/message.ts +++ b/frontend/app/player/MessageDistributor/messages/message.ts @@ -47,6 +47,7 @@ import type { RawAdoptedSsDeleteRule, RawAdoptedSsAddOwner, RawAdoptedSsRemoveOwner, + RawZustand, RawIosSessionStart, RawIosCustomEvent, RawIosScreenChanges, @@ -147,6 +148,8 @@ export type AdoptedSsAddOwner = RawAdoptedSsAddOwner & Timed export type AdoptedSsRemoveOwner = RawAdoptedSsRemoveOwner & Timed +export type Zustand = RawZustand & Timed + export type IosSessionStart = RawIosSessionStart & Timed export type IosCustomEvent = RawIosCustomEvent & Timed diff --git a/frontend/app/player/MessageDistributor/messages/raw.ts b/frontend/app/player/MessageDistributor/messages/raw.ts index a546ca799..c7a133440 100644 --- a/frontend/app/player/MessageDistributor/messages/raw.ts +++ b/frontend/app/player/MessageDistributor/messages/raw.ts @@ -300,6 +300,12 @@ export interface RawAdoptedSsRemoveOwner { id: number, } +export interface RawZustand { + tp: "zustand", + mutation: string, + state: string, +} + export interface RawIosSessionStart { tp: "ios_session_start", timestamp: number, @@ -371,4 +377,4 @@ export interface 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; +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 | RawZustand | RawIosSessionStart | RawIosCustomEvent | RawIosScreenChanges | RawIosClickEvent | RawIosPerformanceEvent | RawIosLog | RawIosNetworkCall; diff --git a/frontend/app/player/MessageDistributor/messages/tracker-legacy.ts b/frontend/app/player/MessageDistributor/messages/tracker-legacy.ts index c89f8a47c..088731bbc 100644 --- a/frontend/app/player/MessageDistributor/messages/tracker-legacy.ts +++ b/frontend/app/player/MessageDistributor/messages/tracker-legacy.ts @@ -60,6 +60,7 @@ export const TP_MAP = { 75: "adopted_ss_delete_rule", 76: "adopted_ss_add_owner", 77: "adopted_ss_remove_owner", + 79: "zustand", 90: "ios_session_start", 93: "ios_custom_event", 96: "ios_screen_changes", diff --git a/frontend/app/player/MessageDistributor/messages/tracker.ts b/frontend/app/player/MessageDistributor/messages/tracker.ts index 34493f32c..865b0ce11 100644 --- a/frontend/app/player/MessageDistributor/messages/tracker.ts +++ b/frontend/app/player/MessageDistributor/messages/tracker.ts @@ -382,8 +382,14 @@ type TrAdoptedSSRemoveOwner = [ id: number, ] +type TrZustand = [ + type: 79, + mutation: string, + state: string, +] -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 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 | TrZustand export default function translate(tMsg: TrackerMessage): RawMessage | null { switch(tMsg[0]) { @@ -750,6 +756,14 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null { } } + case 79: { + return { + tp: "zustand", + mutation: tMsg[1], + state: tMsg[2], + } + } + default: return null } diff --git a/mobs/messages.rb b/mobs/messages.rb index 61f141121..09acdb6f2 100644 --- a/mobs/messages.rb +++ b/mobs/messages.rb @@ -1,7 +1,7 @@ -# Special one for Batch Metadata. 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', :replayer => false, :tracker => false do +message 80, 'BatchMeta', :replayer => false, :tracker => false do uint 'PageNo' uint 'FirstIndex' int 'Timestamp' @@ -421,7 +421,7 @@ message 70, 'CreateIFrameDocument' do uint 'FrameID' uint 'ID' end - + #Since 3.6.0 AdoptedStyleSheets message 71, 'AdoptedSSReplaceURLBased' do uint 'SheetID' @@ -432,26 +432,31 @@ message 72, 'AdoptedSSReplace', :tracker => false do uint 'SheetID' string 'Text' end -message 73, 'AdoptedSSInsertRuleURLBased' do +message 73, 'AdoptedSSInsertRuleURLBased' do uint 'SheetID' string 'Rule' uint 'Index' string 'BaseURL' end -message 74, 'AdoptedSSInsertRule', :tracker => false do +message 74, 'AdoptedSSInsertRule', :tracker => false do uint 'SheetID' string 'Rule' uint 'Index' end -message 75, 'AdoptedSSDeleteRule' do +message 75, 'AdoptedSSDeleteRule' do uint 'SheetID' uint 'Index' end -message 76, 'AdoptedSSAddOwner' do +message 76, 'AdoptedSSAddOwner' do uint 'SheetID' uint 'ID' end -message 77, 'AdoptedSSRemoveOwner' do +message 77, 'AdoptedSSRemoveOwner' do uint 'SheetID' uint 'ID' end + +message 79, 'Zustand' do + string 'Mutation' + string 'State' +end diff --git a/tracker/tracker-vuex/package.json b/tracker/tracker-vuex/package.json index 69f43df64..1fd8a8c19 100644 --- a/tracker/tracker-vuex/package.json +++ b/tracker/tracker-vuex/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-vuex", "description": "Tracker plugin for Vuex state recording", - "version": "4.0.0", + "version": "4.0.1", "keywords": [ "vuex", "logging", @@ -23,8 +23,7 @@ }, "dependencies": {}, "peerDependencies": { - "@openreplay/tracker": "^3.4.8", - "@ngrx/store": ">=4" + "@openreplay/tracker": "^3.4.8" }, "devDependencies": { "@openreplay/tracker": "^3.4.8", diff --git a/tracker/tracker-vuex/src/index.ts b/tracker/tracker-vuex/src/index.ts index f8d8c1dba..a7a92cba0 100644 --- a/tracker/tracker-vuex/src/index.ts +++ b/tracker/tracker-vuex/src/index.ts @@ -52,7 +52,7 @@ export default function(opts: Partial = {}) { const randomId = Math.random().toString(36).substring(2, 9) store.subscribe((mutation, storeState) => { state[storeName || randomId] = state - processMutationAndState(app, options, encoder, mutation, storeState); + processMutationAndState(app, options, encoder, mutation, state); }); } diff --git a/tracker/tracker-zustand/.gitignore b/tracker/tracker-zustand/.gitignore new file mode 100644 index 000000000..6ddaccbb5 --- /dev/null +++ b/tracker/tracker-zustand/.gitignore @@ -0,0 +1,6 @@ +node_modules +npm-debug.log +lib +cjs +.cache +*.DS_Store \ No newline at end of file diff --git a/tracker/tracker-zustand/.npmignore b/tracker/tracker-zustand/.npmignore new file mode 100644 index 000000000..4ea62cab6 --- /dev/null +++ b/tracker/tracker-zustand/.npmignore @@ -0,0 +1,5 @@ +src +tsconfig-cjs.json +tsconfig.json +.prettierrc.json +.cache \ No newline at end of file diff --git a/tracker/tracker-zustand/LICENSE b/tracker/tracker-zustand/LICENSE new file mode 100644 index 000000000..15669f768 --- /dev/null +++ b/tracker/tracker-zustand/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 Asayer, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tracker/tracker-zustand/README.md b/tracker/tracker-zustand/README.md new file mode 100644 index 000000000..83bd0318b --- /dev/null +++ b/tracker/tracker-zustand/README.md @@ -0,0 +1,48 @@ +# OpenReplay Tracker Vuex plugin +A Vuex plugin for OpenReplay Tracker. This plugin allows you to see the application state during session replay. + +## Installation +```bash +npm i @openreplay/tracker-vuex +``` + +## Usage +Initialize the `@openreplay/tracker` package as usual and load the plugin into it. +Then put the generated plugin into your `plugins` field of your store. + +```js +import Vuex from 'vuex' +import Tracker from '@openreplay/tracker'; +import trackerVuex from '@openreplay/tracker-vuex'; + +const tracker = new Tracker({ + projectKey: YOUR_PROJECT_KEY, +}); + +const store = new Vuex.Store({ + // ... + plugins: [tracker.plugin(trackerVuex())], +}); +``` + +You can customize the plugin behavior with options to sanitize your data. They are similar to the ones from the standard `createLogger` plugin. + +```js +trackerVuex({ + filter (mutation, state) { + // returns `true` if a mutation should be logged + // `mutation` is a `{ type, payload }` + return mutation.type !== "aBlacklistedMutation"; + }, + transformer (state) { + // transform the state before logging it. + // for example return only a specific sub-tree + return state.subTree; + }, + mutationTransformer (mutation) { + // mutations are logged in the format of `{ type, payload }` + // we can format it any way we want. + return mutation.type; + }, +}) +``` diff --git a/tracker/tracker-zustand/package.json b/tracker/tracker-zustand/package.json new file mode 100644 index 000000000..60fa6a74f --- /dev/null +++ b/tracker/tracker-zustand/package.json @@ -0,0 +1,35 @@ +{ + "name": "@openreplay/tracker-zustand", + "description": "Tracker plugin for Zustand state recording", + "version": "1.0.0", + "keywords": [ + "zustand", + "state", + "logging", + "replay" + ], + "author": "Nikita Melnikov ", + "contributors": [ + "Aleksandr K " + ], + "license": "MIT", + "type": "module", + "main": "./lib/index.js", + "scripts": { + "lint": "prettier --write 'src/**/*.ts' && tsc --noEmit", + "build": "npm run build-es && npm run build-cjs", + "build-es": "rm -Rf lib && tsc", + "build-cjs": "rm -Rf cjs && tsc --project tsconfig-cjs.json && echo '{ \"type\": \"commonjs\" }' > cjs/package.json && replace-in-files cjs/* --string='@openreplay/tracker' --replacement='@openreplay/tracker/cjs'", + "prepublishOnly": "npm run build" + }, + "dependencies": {}, + "peerDependencies": { + "@openreplay/tracker": "^4.0.0" + }, + "devDependencies": { + "@openreplay/tracker": "file:../tracker", + "prettier": "^1.18.2", + "replace-in-files-cli": "^1.0.0", + "typescript": "^4.6.0-dev.20211126" + } +} diff --git a/tracker/tracker-zustand/src/index.ts b/tracker/tracker-zustand/src/index.ts new file mode 100644 index 000000000..bf9186fb1 --- /dev/null +++ b/tracker/tracker-zustand/src/index.ts @@ -0,0 +1,62 @@ +import { App, Messages } from "@openreplay/tracker"; +import { Encoder, sha1 } from "./syncod/index.js"; + +export interface Options { + filter: (mutation: any, state: any) => boolean; + transformer: (state: any) => any; + mutationTransformer: (mutation: any) => any; +} + +function processMutationAndState( + app: App, + options: Options, + encoder: Encoder, + mutation: string[], + state: Record +) { + if (options.filter(mutation, state)) { + try { + const _mutation = encoder.encode(options.mutationTransformer(mutation)); + const _state = encoder.encode(options.transformer(state)); + const _table = encoder.commit(); + for (let key in _table) app.send(Messages.OTable(key, _table[key])); + app.send(Messages.Zustand(_mutation, _state)); + } catch (e) { + encoder.clear(); + app.debug.error(e) + } + } +} + +export default function(opts: Partial = {}) { + const options: Options = Object.assign( + { + filter: () => true, + transformer: state => state, + mutationTransformer: mutation => mutation, + }, + opts + ); + return (app: App | null) => { + if (app === null) { + return Function.prototype; + } + const encoder = new Encoder(sha1, 50); + const state = {}; + return (storeName: string = Math.random().toString(36).substring(2, 9)) => + (config: Function) => + (set: (...args: any) => void, get: () => Record, api: any) => + config( + (...args) => { + set(...args) + const newState = get(); + state[storeName] = newState + const triggeredActions = args.map(action => action.toString?.()) + + processMutationAndState(app, options, encoder, triggeredActions, state) + }, + get, + api + ) + }; +} diff --git a/tracker/tracker-zustand/src/syncod/chars.ts b/tracker/tracker-zustand/src/syncod/chars.ts new file mode 100644 index 000000000..ed0d4d0c5 --- /dev/null +++ b/tracker/tracker-zustand/src/syncod/chars.ts @@ -0,0 +1,18 @@ +const chars = {}; + +[ + "DEL", + "UNDEF", + "TRUE", + "FALSE", + "NUMBER", + "BIGINT", + "FUNCTION", + "STRING", + "SYMBOL", + "NULL", + "OBJECT", + "ARRAY" +].forEach((k, i) => (chars[k] = String.fromCharCode(i + 0xe000))); + +export default chars; diff --git a/tracker/tracker-zustand/src/syncod/encoder.ts b/tracker/tracker-zustand/src/syncod/encoder.ts new file mode 100644 index 000000000..db531a0b2 --- /dev/null +++ b/tracker/tracker-zustand/src/syncod/encoder.ts @@ -0,0 +1,220 @@ +import _ from "./chars.js"; +// @ts-ignore + +// @ts-ignore +export default class Encoder { +// @ts-ignore + constructor(hash, slen = Infinity) { +// @ts-ignore + this._hash = hash; +// @ts-ignore + this._slen = slen; +// @ts-ignore + this._refmap = new Map(); +// @ts-ignore + this._refset = new Set(); +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + _ref_str(str) { +// @ts-ignore + if (str.length < this._slen && str.indexOf(_.DEL) === -1) { +// @ts-ignore + return str; +// @ts-ignore + } +// @ts-ignore + let ref = this._refmap.get(str); +// @ts-ignore + if (ref === undefined) { +// @ts-ignore + ref = this._hash(str); +// @ts-ignore + this._refmap.set(str, ref); +// @ts-ignore + } +// @ts-ignore + return ref; +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + _encode_prim(obj) { +// @ts-ignore + switch (typeof obj) { +// @ts-ignore + case "undefined": +// @ts-ignore + return _.UNDEF; +// @ts-ignore + case "boolean": +// @ts-ignore + return obj ? _.TRUE : _.FALSE; +// @ts-ignore + case "number": +// @ts-ignore + return _.NUMBER + obj.toString(); +// @ts-ignore + case "bigint": +// @ts-ignore + return _.BIGINT + obj.toString(); +// @ts-ignore + case "function": +// @ts-ignore + return _.FUNCTION; +// @ts-ignore + case "string": +// @ts-ignore + return _.STRING + this._ref_str(obj); +// @ts-ignore + case "symbol": +// @ts-ignore + return _.SYMBOL + this._ref_str(obj.toString().slice(7, -1)); +// @ts-ignore + } +// @ts-ignore + if (obj === null) { +// @ts-ignore + return _.NULL; +// @ts-ignore + } +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + _encode_obj(obj, ref = this._refmap.get(obj)) { +// @ts-ignore + return (Array.isArray(obj) ? _.ARRAY : _.OBJECT) + ref; +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + _encode_term(obj) { +// @ts-ignore + return this._encode_prim(obj) || this._encode_obj(obj); +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + _encode_deep(obj, depth) { +// @ts-ignore + const enc = this._encode_prim(obj); +// @ts-ignore + if (enc !== undefined) { +// @ts-ignore + return enc; +// @ts-ignore + } +// @ts-ignore + const ref = this._refmap.get(obj); +// @ts-ignore + switch (typeof ref) { +// @ts-ignore + case "number": +// @ts-ignore + return (depth - ref).toString(); +// @ts-ignore + case "string": +// @ts-ignore + return this._encode_obj(obj, ref); +// @ts-ignore + } +// @ts-ignore + this._refmap.set(obj, depth); +// @ts-ignore + const hash = this._hash( +// @ts-ignore + (Array.isArray(obj) +// @ts-ignore + ? obj.map(v => this._encode_deep(v, depth + 1)) +// @ts-ignore + : Object.keys(obj) +// @ts-ignore + .sort() +// @ts-ignore + .map( +// @ts-ignore + k => +// @ts-ignore + this._ref_str(k) + _.DEL + this._encode_deep(obj[k], depth + 1) +// @ts-ignore + ) +// @ts-ignore + ).join(_.DEL) +// @ts-ignore + ); +// @ts-ignore + this._refmap.set(obj, hash); +// @ts-ignore + return this._encode_obj(obj, hash); +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + encode(obj) { +// @ts-ignore + return this._encode_deep(obj, 0); +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + commit() { +// @ts-ignore + const dict = {}; +// @ts-ignore + this._refmap.forEach((ref, obj) => { +// @ts-ignore + if (this._refset.has(ref)) { +// @ts-ignore + return; +// @ts-ignore + } +// @ts-ignore + this._refset.add(ref); +// @ts-ignore + if (typeof obj !== "string") { +// @ts-ignore + obj = (Array.isArray(obj) +// @ts-ignore + ? obj.map(v => this._encode_term(v)) +// @ts-ignore + : Object.keys(obj).map( +// @ts-ignore + k => this._ref_str(k) + _.DEL + this._encode_term(obj[k]) +// @ts-ignore + ) +// @ts-ignore + ).join(_.DEL); +// @ts-ignore + } +// @ts-ignore + dict[ref] = obj; +// @ts-ignore + }); +// @ts-ignore + this._refmap.clear(); +// @ts-ignore + return dict; +// @ts-ignore + } +// @ts-ignore + +// @ts-ignore + clear() { +// @ts-ignore + this._refmap.clear(); +// @ts-ignore + this._refset.clear(); +// @ts-ignore + } +// @ts-ignore +} +// @ts-ignore diff --git a/tracker/tracker-zustand/src/syncod/index.ts b/tracker/tracker-zustand/src/syncod/index.ts new file mode 100644 index 000000000..de6f7c64c --- /dev/null +++ b/tracker/tracker-zustand/src/syncod/index.ts @@ -0,0 +1,5 @@ +// TODO: SSR solution for all asayer libraries +import Encoder from "./encoder.js"; +import sha1 from "./sha1.js"; + +export { Encoder, sha1 }; diff --git a/tracker/tracker-zustand/src/syncod/sha1.ts b/tracker/tracker-zustand/src/syncod/sha1.ts new file mode 100644 index 000000000..addacc2cd --- /dev/null +++ b/tracker/tracker-zustand/src/syncod/sha1.ts @@ -0,0 +1,104 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +function core_sha1(x, len) { + x[len >> 5] |= 0x80 << (24 - (len % 32)); + x[(((len + 64) >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); + var t = safe_add( + safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j)) + ); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +function sha1_ft(t, b, c, d) { + if (t < 20) return (b & c) | (~b & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +function sha1_kt(t) { + return t < 20 + ? 1518500249 + : t < 40 + ? 1859775393 + : t < 60 + ? -1894007588 + : -899497514; +} + +function safe_add(x, y) { + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); +} + +function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); +} + +function str2binb(str) { + var bin = Array(); + var mask = (1 << 16) - 1; + for (var i = 0; i < str.length * 16; i += 16) + bin[i >> 5] |= (str.charCodeAt(i / 16) & mask) << (32 - 16 - (i % 32)); + return bin; +} + +function binb2b64(binarray) { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) { + var triplet = + (((binarray[i >> 2] >> (8 * (3 - (i % 4)))) & 0xff) << 16) | + (((binarray[(i + 1) >> 2] >> (8 * (3 - ((i + 1) % 4)))) & 0xff) << 8) | + ((binarray[(i + 2) >> 2] >> (8 * (3 - ((i + 2) % 4)))) & 0xff); + for (var j = 0; j < 4; j++) { + if (i * 8 + j * 6 <= binarray.length * 32) + str += tab.charAt((triplet >> (6 * (3 - j))) & 0x3f); + } + } + return str; +} + +export default function(s) { + return binb2b64(core_sha1(str2binb(s), s.length * 16)); +} diff --git a/tracker/tracker-zustand/tsconfig-cjs.json b/tracker/tracker-zustand/tsconfig-cjs.json new file mode 100644 index 000000000..2e91a035b --- /dev/null +++ b/tracker/tracker-zustand/tsconfig-cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "./cjs", + "declaration": false + }, +} \ No newline at end of file diff --git a/tracker/tracker-zustand/tsconfig.json b/tracker/tracker-zustand/tsconfig.json new file mode 100644 index 000000000..0c5b8d1b3 --- /dev/null +++ b/tracker/tracker-zustand/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "noImplicitThis": true, + "strictNullChecks": true, + "alwaysStrict": true, + "target": "es6", + "module": "es6", + "moduleResolution": "nodenext", + "declaration": true, + "outDir": "./lib" + } +} diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 59ca4bb50..5699bcdf9 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "4.0.0", + "version": "4.0.1", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/common/messages.gen.ts b/tracker/tracker/src/common/messages.gen.ts index 3b45ec023..107416711 100644 --- a/tracker/tracker/src/common/messages.gen.ts +++ b/tracker/tracker/src/common/messages.gen.ts @@ -56,6 +56,7 @@ export declare const enum Type { AdoptedSSDeleteRule = 75, AdoptedSSAddOwner = 76, AdoptedSSRemoveOwner = 77, + Zustand = 79, } @@ -438,6 +439,12 @@ export type AdoptedSSRemoveOwner = [ /*id:*/ number, ] +export type Zustand = [ + /*type:*/ Type.Zustand, + /*mutation:*/ string, + /*state:*/ 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 | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner + +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 | Zustand export default Message diff --git a/tracker/tracker/src/main/app/messages.gen.ts b/tracker/tracker/src/main/app/messages.gen.ts index 05f4f2894..7bbbeb615 100644 --- a/tracker/tracker/src/main/app/messages.gen.ts +++ b/tracker/tracker/src/main/app/messages.gen.ts @@ -707,3 +707,14 @@ export function AdoptedSSRemoveOwner( ] } +export function Zustand( + mutation: string, + state: string, +): Messages.Zustand { + return [ + Messages.Type.Zustand, + mutation, + state, + ] +} + diff --git a/tracker/tracker/src/webworker/MessageEncoder.gen.ts b/tracker/tracker/src/webworker/MessageEncoder.gen.ts index c38fa77a8..78151ba9f 100644 --- a/tracker/tracker/src/webworker/MessageEncoder.gen.ts +++ b/tracker/tracker/src/webworker/MessageEncoder.gen.ts @@ -229,6 +229,10 @@ export default class MessageEncoder extends PrimitiveEncoder { return this.uint(msg[1]) && this.uint(msg[2]) break + case Messages.Type.Zustand: + return this.string(msg[1]) && this.string(msg[2]) + break + } } From 7e22c1a260de95cf9ff4f3b31146feca8e134219 Mon Sep 17 00:00:00 2001 From: sylenien Date: Thu, 8 Sep 2022 15:09:19 +0200 Subject: [PATCH 2/6] fix(tracker): rm tracker change --- tracker/tracker/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 5699bcdf9..59ca4bb50 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "4.0.1", + "version": "4.0.0", "keywords": [ "logging", "replay" From 65f40d2f3d14ee7710c416b42c7b119c67abcfd0 Mon Sep 17 00:00:00 2001 From: sylenien Date: Thu, 8 Sep 2022 15:09:46 +0200 Subject: [PATCH 3/6] fix(tracker): rm tracker change --- tracker/tracker/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 59ca4bb50..5699bcdf9 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "4.0.0", + "version": "4.0.1", "keywords": [ "logging", "replay" From 0d8dd27ac1ca7ae951a19cdaa992e9f22693f862 Mon Sep 17 00:00:00 2001 From: sylenien Date: Fri, 9 Sep 2022 11:37:12 +0200 Subject: [PATCH 4/6] feat(ui): zustand support for the replayer --- .../components/Session_/Player/Controls/Controls.js | 4 ++++ frontend/app/components/Session_/Storage/Storage.js | 4 ++++ frontend/app/player/MessageDistributor/Lists.ts | 2 +- .../player/MessageDistributor/MessageDistributor.ts | 10 ++++++++-- frontend/app/player/store/selectors.js | 6 +++++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/Session_/Player/Controls/Controls.js b/frontend/app/components/Session_/Player/Controls/Controls.js index 0601e092f..1dcbbaa21 100644 --- a/frontend/app/components/Session_/Player/Controls/Controls.js +++ b/frontend/app/components/Session_/Player/Controls/Controls.js @@ -48,6 +48,8 @@ function getStorageIconName(type) { return 'vendors/vuex'; case STORAGE_TYPES.NGRX: return 'vendors/ngrx'; + case STORAGE_TYPES.ZUSTAND: + return 'vendors/zustand'; case STORAGE_TYPES.NONE: return 'store'; } @@ -73,6 +75,8 @@ function getStorageName(type) { return 'VUEX'; case STORAGE_TYPES.NGRX: return 'NGRX'; + case STORAGE_TYPES.ZUSTAND: + return 'ZUSTAND'; case STORAGE_TYPES.NONE: return 'STATE'; } diff --git a/frontend/app/components/Session_/Storage/Storage.js b/frontend/app/components/Session_/Storage/Storage.js index b1cf53dfc..62e9eda19 100644 --- a/frontend/app/components/Session_/Storage/Storage.js +++ b/frontend/app/components/Session_/Storage/Storage.js @@ -121,6 +121,9 @@ export default class Storage extends React.PureComponent { const { type, listNow, list } = this.props; let src; let name; + + // ZUSTAND TODO + console.log(item, type) switch(type) { case STORAGE_TYPES.REDUX: case STORAGE_TYPES.NGRX: @@ -208,6 +211,7 @@ export default class Storage extends React.PureComponent { {'Inspect your application state while you’re replaying your users sessions. OpenReplay supports '} Redux{', '} VueX{', '} + {/* ZUSTAND TODO */} MobX{' and '} NgRx.

diff --git a/frontend/app/player/MessageDistributor/Lists.ts b/frontend/app/player/MessageDistributor/Lists.ts index 0173b2667..a65eff52f 100644 --- a/frontend/app/player/MessageDistributor/Lists.ts +++ b/frontend/app/player/MessageDistributor/Lists.ts @@ -1,7 +1,7 @@ import type { Message } from './messages' import ListWalker from './managers/ListWalker'; -export const LIST_NAMES = ["redux", "mobx", "vuex", "ngrx", "graphql", "exceptions", "profiles", "longtasks"] as const; +export const LIST_NAMES = ["redux", "mobx", "vuex", "zustand", "ngrx", "graphql", "exceptions", "profiles", "longtasks"] as const; export const INITIAL_STATE = {} LIST_NAMES.forEach(name => { diff --git a/frontend/app/player/MessageDistributor/MessageDistributor.ts b/frontend/app/player/MessageDistributor/MessageDistributor.ts index 2d73081fb..8c47debe2 100644 --- a/frontend/app/player/MessageDistributor/MessageDistributor.ts +++ b/frontend/app/player/MessageDistributor/MessageDistributor.ts @@ -123,7 +123,7 @@ export default class MessageDistributor extends StatedScreen { // TODO: fix types for events, remove immutable js eventList.forEach((e: Record) => { if (e.type === EVENT_TYPES.LOCATION) { //TODO type system - this.locationEventManager.append(e); + this.locationEventManager.append(e); } }); this.session.errors.forEach((e: Record) => { @@ -233,7 +233,7 @@ export default class MessageDistributor extends StatedScreen { this.waitingForFiles = false this.setMessagesLoading(false) }) - + }) } @@ -479,6 +479,12 @@ export default class MessageDistributor extends StatedScreen { this.lists.vuex.append(decoded); } break; + case "zustand": + decoded = this.decodeMessage(msg, ["state", "mutation"]) + logger.log(decoded) + if (decoded != null) { + this.lists.zustand.append(decoded) + } case "mob_x": decoded = this.decodeMessage(msg, ["payload"]); logger.log(decoded) diff --git a/frontend/app/player/store/selectors.js b/frontend/app/player/store/selectors.js index 1a6f0dc2d..36750b56d 100644 --- a/frontend/app/player/store/selectors.js +++ b/frontend/app/player/store/selectors.js @@ -2,6 +2,7 @@ const REDUX = "redux"; const MOBX = "mobx"; const VUEX = "vuex"; const NGRX = "ngrx"; +const ZUSTAND = 'zustand'; const NONE = 0; @@ -10,6 +11,7 @@ export const STORAGE_TYPES = { MOBX, VUEX, NGRX, + ZUSTAND, NONE, }; @@ -24,6 +26,8 @@ export function selectStorageType(state) { return MOBX; } else if (state.ngrxList.length > 0) { return NGRX; + } else if (state.zustandList.length > 0) { + return ZUSTAND; } return NONE; } @@ -41,4 +45,4 @@ export function selectStorageListNow(state) { return state[`${key}ListNow`] || []; } return []; -} \ No newline at end of file +} From 3d86fcf635085ebc86541b392eb4c2293320a6e5 Mon Sep 17 00:00:00 2001 From: sylenien Date: Fri, 9 Sep 2022 12:57:53 +0200 Subject: [PATCH 5/6] feat(ui): zustand actions tab --- .../components/Session_/Storage/Storage.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/frontend/app/components/Session_/Storage/Storage.js b/frontend/app/components/Session_/Storage/Storage.js index 62e9eda19..9bd6c0174 100644 --- a/frontend/app/components/Session_/Storage/Storage.js +++ b/frontend/app/components/Session_/Storage/Storage.js @@ -138,16 +138,23 @@ export default class Storage extends React.PureComponent { src = item.payload; name = `@${item.type} ${src && src.type}`; break; + case STORAGE_TYPES.ZUSTAND: + src = null; + name = item.mutation.join('') } return (
- + {src === null ? ( +
{name}
+ ) : ( + + )}
{ i + 1 < listNow.length &&