mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {AtpSessionData, AtpSessionEvent} from '@atproto/api'
2import {sha256} from 'js-sha256'
3import {Statsig} from 'statsig-react-native-expo'
4
5import {Schema} from '../persisted'
6import {Action, State} from './reducer'
7import {SessionAccount} from './types'
8
9type Reducer = (state: State, action: Action) => State
10
11type Log =
12 | {
13 type: 'reducer:init'
14 state: State
15 }
16 | {
17 type: 'reducer:call'
18 action: Action
19 prevState: State
20 nextState: State
21 }
22 | {
23 type: 'method:start'
24 method:
25 | 'createAccount'
26 | 'login'
27 | 'logout'
28 | 'resumeSession'
29 | 'removeAccount'
30 account?: SessionAccount
31 }
32 | {
33 type: 'method:end'
34 method:
35 | 'createAccount'
36 | 'login'
37 | 'logout'
38 | 'resumeSession'
39 | 'removeAccount'
40 account?: SessionAccount
41 }
42 | {
43 type: 'persisted:broadcast'
44 data: Schema['session']
45 }
46 | {
47 type: 'persisted:receive'
48 data: Schema['session']
49 }
50 | {
51 type: 'agent:switch'
52 prevAgent: object
53 nextAgent: object
54 }
55 | {
56 type: 'agent:patch'
57 agent: object
58 prevSession: AtpSessionData | undefined
59 nextSession: AtpSessionData | undefined
60 }
61
62export function wrapSessionReducerForLogging(reducer: Reducer): Reducer {
63 return function loggingWrapper(prevState: State, action: Action): State {
64 const nextState = reducer(prevState, action)
65 addSessionDebugLog({type: 'reducer:call', prevState, action, nextState})
66 return nextState
67 }
68}
69
70let nextMessageIndex = 0
71const MAX_SLICE_LENGTH = 1000
72
73// Not gated.
74export function addSessionErrorLog(did: string, event: AtpSessionEvent) {
75 try {
76 if (!Statsig.initializeCalled() || !Statsig.getStableID()) {
77 return
78 }
79 const stack = (new Error().stack ?? '').slice(0, MAX_SLICE_LENGTH)
80 Statsig.logEvent('session:error', null, {
81 did,
82 event,
83 stack,
84 })
85 } catch (e) {
86 console.error(e)
87 }
88}
89
90export function addSessionDebugLog(log: Log) {
91 try {
92 if (!Statsig.initializeCalled() || !Statsig.getStableID()) {
93 // Drop these logs for now.
94 return
95 }
96 if (!Statsig.checkGate('debug_session')) {
97 return
98 }
99 const messageIndex = nextMessageIndex++
100 const {type, ...content} = log
101 let payload = JSON.stringify(content, replacer)
102
103 let nextSliceIndex = 0
104 while (payload.length > 0) {
105 const sliceIndex = nextSliceIndex++
106 const slice = payload.slice(0, MAX_SLICE_LENGTH)
107 payload = payload.slice(MAX_SLICE_LENGTH)
108 Statsig.logEvent('session:debug', null, {
109 realmId,
110 messageIndex: String(messageIndex),
111 messageType: type,
112 sliceIndex: String(sliceIndex),
113 slice,
114 })
115 }
116 } catch (e) {
117 console.error(e)
118 }
119}
120
121let agentIds = new WeakMap<object, string>()
122let realmId = Math.random().toString(36).slice(2)
123let nextAgentId = 1
124
125function getAgentId(agent: object) {
126 let id = agentIds.get(agent)
127 if (id === undefined) {
128 id = realmId + '::' + nextAgentId++
129 agentIds.set(agent, id)
130 }
131 return id
132}
133
134function replacer(key: string, value: unknown) {
135 if (typeof value === 'object' && value != null && 'api' in value) {
136 return getAgentId(value)
137 }
138 if (
139 key === 'service' ||
140 key === 'email' ||
141 key === 'emailConfirmed' ||
142 key === 'emailAuthFactor' ||
143 key === 'pdsUrl'
144 ) {
145 return undefined
146 }
147 if (
148 typeof value === 'string' &&
149 (key === 'refreshJwt' || key === 'accessJwt')
150 ) {
151 return sha256(value)
152 }
153 return value
154}