@multiplayer-app/session-recorder-react-native 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/babel.config.js +13 -0
- package/dist/config/masking.d.ts +30 -0
- package/dist/config/masking.js +1 -0
- package/dist/config/masking.js.map +1 -0
- package/dist/expo.d.ts +11 -0
- package/dist/expo.js +1 -0
- package/dist/expo.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/otel/helpers.d.ts +3 -0
- package/dist/otel/helpers.js +1 -0
- package/dist/otel/helpers.js.map +1 -0
- package/dist/otel/index.d.ts +40 -0
- package/dist/otel/index.js +1 -0
- package/dist/otel/index.js.map +1 -0
- package/dist/otel/instrumentations/gestureInstrumentation.d.ts +15 -0
- package/dist/otel/instrumentations/gestureInstrumentation.js +1 -0
- package/dist/otel/instrumentations/gestureInstrumentation.js.map +1 -0
- package/dist/otel/instrumentations/index.d.ts +5 -0
- package/dist/otel/instrumentations/index.js +1 -0
- package/dist/otel/instrumentations/index.js.map +1 -0
- package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +8 -0
- package/dist/otel/instrumentations/reactNativeInstrumentation.js +1 -0
- package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +1 -0
- package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +12 -0
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js +1 -0
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +1 -0
- package/dist/recorder/gestureRecorder.d.ts +42 -0
- package/dist/recorder/gestureRecorder.js +1 -0
- package/dist/recorder/gestureRecorder.js.map +1 -0
- package/dist/recorder/index.d.ts +16 -0
- package/dist/recorder/index.js +1 -0
- package/dist/recorder/index.js.map +1 -0
- package/dist/recorder/navigationTracker.d.ts +43 -0
- package/dist/recorder/navigationTracker.js +1 -0
- package/dist/recorder/navigationTracker.js.map +1 -0
- package/dist/recorder/screenRecorder.d.ts +46 -0
- package/dist/recorder/screenRecorder.js +1 -0
- package/dist/recorder/screenRecorder.js.map +1 -0
- package/dist/services/api.service.d.ts +20 -0
- package/dist/services/api.service.js +1 -0
- package/dist/services/api.service.js.map +1 -0
- package/dist/services/storage.service.d.ts +23 -0
- package/dist/services/storage.service.js +1 -0
- package/dist/services/storage.service.js.map +1 -0
- package/dist/sessionRecorder.d.ts +54 -0
- package/dist/sessionRecorder.js +1 -0
- package/dist/sessionRecorder.js.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/platform.d.ts +9 -0
- package/dist/utils/platform.js +1 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/version.js.map +1 -0
- package/examples/sample-expo-app/README.md +142 -0
- package/examples/sample-expo-app/app/(tabs)/_layout.tsx +60 -0
- package/examples/sample-expo-app/app/(tabs)/explore.tsx +110 -0
- package/examples/sample-expo-app/app/(tabs)/index.tsx +125 -0
- package/examples/sample-expo-app/app/(tabs)/posts.tsx +96 -0
- package/examples/sample-expo-app/app/(tabs)/users.tsx +131 -0
- package/examples/sample-expo-app/app/+not-found.tsx +32 -0
- package/examples/sample-expo-app/app/_layout.tsx +53 -0
- package/examples/sample-expo-app/app/post/[id].tsx +199 -0
- package/examples/sample-expo-app/app/user/[id].tsx +270 -0
- package/examples/sample-expo-app/app.json +42 -0
- package/examples/sample-expo-app/assets/fonts/SpaceMono-Regular.ttf +0 -0
- package/examples/sample-expo-app/assets/images/adaptive-icon.png +0 -0
- package/examples/sample-expo-app/assets/images/favicon.png +0 -0
- package/examples/sample-expo-app/assets/images/icon.png +0 -0
- package/examples/sample-expo-app/assets/images/partial-react-logo.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo@2x.png +0 -0
- package/examples/sample-expo-app/assets/images/react-logo@3x.png +0 -0
- package/examples/sample-expo-app/assets/images/splash-icon.png +0 -0
- package/examples/sample-expo-app/components/Collapsible.tsx +45 -0
- package/examples/sample-expo-app/components/ErrorView.tsx +52 -0
- package/examples/sample-expo-app/components/ExternalLink.tsx +24 -0
- package/examples/sample-expo-app/components/HapticTab.tsx +18 -0
- package/examples/sample-expo-app/components/HelloWave.tsx +40 -0
- package/examples/sample-expo-app/components/LoadingSpinner.tsx +34 -0
- package/examples/sample-expo-app/components/ParallaxScrollView.tsx +82 -0
- package/examples/sample-expo-app/components/ThemedText.tsx +60 -0
- package/examples/sample-expo-app/components/ThemedView.tsx +14 -0
- package/examples/sample-expo-app/components/ui/IconSymbol.ios.tsx +32 -0
- package/examples/sample-expo-app/components/ui/IconSymbol.tsx +41 -0
- package/examples/sample-expo-app/components/ui/TabBarBackground.ios.tsx +19 -0
- package/examples/sample-expo-app/components/ui/TabBarBackground.tsx +6 -0
- package/examples/sample-expo-app/constants/Colors.ts +26 -0
- package/examples/sample-expo-app/eslint.config.js +10 -0
- package/examples/sample-expo-app/hooks/useApi.ts +41 -0
- package/examples/sample-expo-app/hooks/useColorScheme.ts +1 -0
- package/examples/sample-expo-app/hooks/useColorScheme.web.ts +21 -0
- package/examples/sample-expo-app/hooks/useThemeColor.ts +21 -0
- package/examples/sample-expo-app/metro.config.js +26 -0
- package/examples/sample-expo-app/package-lock.json +26296 -0
- package/examples/sample-expo-app/package.json +59 -0
- package/examples/sample-expo-app/scripts/reset-project.js +112 -0
- package/examples/sample-expo-app/services/api.ts +98 -0
- package/examples/sample-expo-app/tsconfig.json +17 -0
- package/examples/sample-expo-app/utils/navigation.ts +19 -0
- package/package.json +98 -0
- package/src/config/masking.ts +78 -0
- package/src/expo.ts +41 -0
- package/src/index.ts +20 -0
- package/src/otel/helpers.ts +21 -0
- package/src/otel/index.ts +348 -0
- package/src/otel/instrumentations/gestureInstrumentation.ts +141 -0
- package/src/otel/instrumentations/index.ts +86 -0
- package/src/otel/instrumentations/reactNativeInstrumentation.ts +164 -0
- package/src/otel/instrumentations/reactNavigationInstrumentation.ts +114 -0
- package/src/recorder/gestureRecorder.ts +429 -0
- package/src/recorder/index.ts +71 -0
- package/src/recorder/navigationTracker.ts +447 -0
- package/src/recorder/screenRecorder.ts +411 -0
- package/src/services/api.service.ts +78 -0
- package/src/services/storage.service.ts +130 -0
- package/src/sessionRecorder.ts +367 -0
- package/src/types/expo.d.ts +23 -0
- package/src/types/index.ts +88 -0
- package/src/utils/platform.ts +75 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { SessionRecorderOptions, RecorderConfig, TracerReactNativeConfig } from './types'
|
|
2
|
+
import { TracerReactNativeSDK } from './otel'
|
|
3
|
+
import { RecorderReactNativeSDK } from './recorder'
|
|
4
|
+
import { ApiService } from './services/api.service'
|
|
5
|
+
import { StorageService } from './services/storage.service'
|
|
6
|
+
import {
|
|
7
|
+
SessionType,
|
|
8
|
+
} from '@multiplayer-app/session-recorder-common'
|
|
9
|
+
|
|
10
|
+
// Define these locally since they're not exported from common
|
|
11
|
+
export enum SessionState {
|
|
12
|
+
started = '2',
|
|
13
|
+
paused = '1',
|
|
14
|
+
stopped = '0',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ISession {
|
|
18
|
+
id: string
|
|
19
|
+
shortId: string
|
|
20
|
+
type: SessionType
|
|
21
|
+
state: SessionState
|
|
22
|
+
createdAt: string
|
|
23
|
+
updatedAt: string
|
|
24
|
+
tempApiKey?: string
|
|
25
|
+
metadata?: Record<string, any>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class SessionRecorder {
|
|
29
|
+
private _configs: SessionRecorderOptions | null = null
|
|
30
|
+
private _tracer: TracerReactNativeSDK
|
|
31
|
+
private _recorder: RecorderReactNativeSDK
|
|
32
|
+
private _apiService: ApiService
|
|
33
|
+
private _storageService: StorageService
|
|
34
|
+
private _session: ISession | null = null
|
|
35
|
+
private _isInitialized = false
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this._tracer = new TracerReactNativeSDK()
|
|
39
|
+
this._recorder = new RecorderReactNativeSDK()
|
|
40
|
+
this._apiService = new ApiService()
|
|
41
|
+
this._storageService = new StorageService()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async init(options: SessionRecorderOptions): Promise<void> {
|
|
45
|
+
if (this._isInitialized) {
|
|
46
|
+
console.warn('SessionRecorder already initialized')
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this._configs = options
|
|
51
|
+
|
|
52
|
+
// Initialize tracer
|
|
53
|
+
const tracerConfig: TracerReactNativeConfig = {
|
|
54
|
+
apiKey: options.apiKey,
|
|
55
|
+
application: options.application,
|
|
56
|
+
version: options.version,
|
|
57
|
+
environment: options.environment,
|
|
58
|
+
exporterEndpoint: options.exporterEndpoint,
|
|
59
|
+
ignoreUrls: options.ignoreUrls,
|
|
60
|
+
captureBody: options.captureBody,
|
|
61
|
+
captureHeaders: options.captureHeaders,
|
|
62
|
+
sampleTraceRatio: options.sampleTraceRatio,
|
|
63
|
+
httpMasking: options.httpMasking,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this._tracer.init(tracerConfig)
|
|
67
|
+
|
|
68
|
+
// Initialize recorder
|
|
69
|
+
const recorderConfig: RecorderConfig = {
|
|
70
|
+
apiKey: options.apiKey,
|
|
71
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
72
|
+
recordScreen: options.recordScreen,
|
|
73
|
+
recordGestures: options.recordGestures,
|
|
74
|
+
recordNavigation: options.recordNavigation,
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._recorder.init(recorderConfig)
|
|
79
|
+
|
|
80
|
+
// Initialize API service
|
|
81
|
+
this._apiService.init(options)
|
|
82
|
+
|
|
83
|
+
this._isInitialized = true
|
|
84
|
+
console.log('SessionRecorder initialized successfully')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async start(sessionId?: string, sessionType: SessionType = SessionType.PLAIN): Promise<void> {
|
|
88
|
+
if (!this._isInitialized) {
|
|
89
|
+
throw new Error('SessionRecorder not initialized. Call init() first.')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!this._configs) {
|
|
93
|
+
throw new Error('Configuration not found')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Create or use existing session
|
|
98
|
+
if (sessionId) {
|
|
99
|
+
this._session = {
|
|
100
|
+
id: sessionId,
|
|
101
|
+
shortId: sessionId.substring(0, 8),
|
|
102
|
+
type: sessionType,
|
|
103
|
+
state: SessionState.started,
|
|
104
|
+
createdAt: new Date().toISOString(),
|
|
105
|
+
updatedAt: new Date().toISOString(),
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// Start new session via API
|
|
109
|
+
this._session = await this._apiService.startSession({
|
|
110
|
+
application: this._configs.application,
|
|
111
|
+
version: this._configs.version,
|
|
112
|
+
environment: this._configs.environment,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!this._session) {
|
|
117
|
+
throw new Error('Failed to create session')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Set session ID in tracer
|
|
121
|
+
this._tracer.setSessionId(this._session.id, this._session.type)
|
|
122
|
+
|
|
123
|
+
// Start recording
|
|
124
|
+
this._recorder.start(this._session.id, this._session.type)
|
|
125
|
+
|
|
126
|
+
// Save session data
|
|
127
|
+
await this._storageService.saveSessionId(this._session.id)
|
|
128
|
+
await this._storageService.saveSessionType(this._session.type)
|
|
129
|
+
await this._storageService.saveSessionState(this._session.state)
|
|
130
|
+
await this._storageService.saveSessionObject(this._session)
|
|
131
|
+
|
|
132
|
+
console.log('Session started:', this._session.id)
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Failed to start session:', error)
|
|
135
|
+
throw error
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async stop(): Promise<void> {
|
|
140
|
+
if (!this._isInitialized || !this._session) {
|
|
141
|
+
console.warn('Session not active')
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
// Stop recording
|
|
147
|
+
this._recorder.stop()
|
|
148
|
+
|
|
149
|
+
// Update session state
|
|
150
|
+
this._session.state = SessionState.stopped
|
|
151
|
+
this._session.updatedAt = new Date().toISOString()
|
|
152
|
+
|
|
153
|
+
// Save updated session
|
|
154
|
+
await this._storageService.saveSessionState(this._session.state)
|
|
155
|
+
await this._storageService.saveSessionObject(this._session)
|
|
156
|
+
|
|
157
|
+
// Stop session via API
|
|
158
|
+
await this._apiService.stopSession({
|
|
159
|
+
sessionId: this._session.id,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
console.log('Session stopped:', this._session.id)
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('Failed to stop session:', error)
|
|
165
|
+
throw error
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async pause(): Promise<void> {
|
|
170
|
+
if (!this._isInitialized || !this._session) {
|
|
171
|
+
console.warn('Session not active')
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
// Pause recording
|
|
177
|
+
this._recorder.pause()
|
|
178
|
+
|
|
179
|
+
// Update session state
|
|
180
|
+
this._session.state = SessionState.paused
|
|
181
|
+
this._session.updatedAt = new Date().toISOString()
|
|
182
|
+
|
|
183
|
+
// Save updated session
|
|
184
|
+
await this._storageService.saveSessionState(this._session.state)
|
|
185
|
+
await this._storageService.saveSessionObject(this._session)
|
|
186
|
+
|
|
187
|
+
console.log('Session paused:', this._session.id)
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('Failed to pause session:', error)
|
|
190
|
+
throw error
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async resume(): Promise<void> {
|
|
195
|
+
if (!this._isInitialized || !this._session) {
|
|
196
|
+
console.warn('Session not active')
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
// Resume recording
|
|
202
|
+
this._recorder.resume()
|
|
203
|
+
|
|
204
|
+
// Update session state
|
|
205
|
+
this._session.state = SessionState.started
|
|
206
|
+
this._session.updatedAt = new Date().toISOString()
|
|
207
|
+
|
|
208
|
+
// Save updated session
|
|
209
|
+
await this._storageService.saveSessionState(this._session.state)
|
|
210
|
+
await this._storageService.saveSessionObject(this._session)
|
|
211
|
+
|
|
212
|
+
console.log('Session resumed:', this._session.id)
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('Failed to resume session:', error)
|
|
215
|
+
throw error
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async cancel(): Promise<void> {
|
|
220
|
+
if (!this._isInitialized || !this._session) {
|
|
221
|
+
console.warn('Session not active')
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
// Stop recording
|
|
227
|
+
this._recorder.stop()
|
|
228
|
+
|
|
229
|
+
// Clear session data
|
|
230
|
+
await this._storageService.clearSessionData()
|
|
231
|
+
|
|
232
|
+
// Cancel session via API
|
|
233
|
+
await this._apiService.stopSession({
|
|
234
|
+
sessionId: this._session.id,
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
this._session = null
|
|
238
|
+
console.log('Session cancelled')
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('Failed to cancel session:', error)
|
|
241
|
+
throw error
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async save(): Promise<void> {
|
|
246
|
+
if (!this._isInitialized || !this._session) {
|
|
247
|
+
console.warn('Session not active')
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
// Save session via API
|
|
253
|
+
await this._apiService.saveSession(this._session.id)
|
|
254
|
+
|
|
255
|
+
console.log('Session saved:', this._session.id)
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error('Failed to save session:', error)
|
|
258
|
+
throw error
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Navigation methods
|
|
263
|
+
setNavigationRef(ref: any): void {
|
|
264
|
+
if (this._isInitialized) {
|
|
265
|
+
this._tracer.setNavigationRef(ref)
|
|
266
|
+
this._recorder.setNavigationRef(ref)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Gesture methods
|
|
271
|
+
enableGestureTracking(): void {
|
|
272
|
+
if (this._isInitialized) {
|
|
273
|
+
this._tracer.enableGestureTracking()
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
disableGestureTracking(): void {
|
|
278
|
+
if (this._isInitialized) {
|
|
279
|
+
this._tracer.disableGestureTracking()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Manual recording methods
|
|
284
|
+
recordTap(x: number, y: number, target?: string): void {
|
|
285
|
+
if (this._isInitialized) {
|
|
286
|
+
this._tracer.recordTap(x, y, target)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
recordSwipe(direction: string, target?: string): void {
|
|
291
|
+
if (this._isInitialized) {
|
|
292
|
+
this._tracer.recordSwipe(direction, target)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
recordNavigate(routeName: string, params?: Record<string, any>): void {
|
|
297
|
+
if (this._isInitialized) {
|
|
298
|
+
this._tracer.recordNavigate(routeName, params)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
recordGoBack(): void {
|
|
303
|
+
if (this._isInitialized) {
|
|
304
|
+
this._tracer.recordGoBack()
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Session attributes
|
|
309
|
+
setSessionAttribute(key: string, value: any): void {
|
|
310
|
+
if (this._session) {
|
|
311
|
+
if (!this._session.metadata) {
|
|
312
|
+
this._session.metadata = {}
|
|
313
|
+
}
|
|
314
|
+
this._session.metadata[key] = value
|
|
315
|
+
this._session.updatedAt = new Date().toISOString()
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
getSessionAttribute(key: string): any {
|
|
320
|
+
return this._session?.metadata?.[key]
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Get current session
|
|
324
|
+
getCurrentSession(): ISession | null {
|
|
325
|
+
return this._session
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Get session data from storage
|
|
329
|
+
async getStoredSessionData(): Promise<{
|
|
330
|
+
sessionId: string | null
|
|
331
|
+
sessionType: SessionType | null
|
|
332
|
+
sessionState: SessionState | null
|
|
333
|
+
sessionObject: ISession | null
|
|
334
|
+
}> {
|
|
335
|
+
return await this._storageService.getAllSessionData()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Performance monitoring methods
|
|
339
|
+
startTrace(name: string, attributes?: Record<string, any>): any {
|
|
340
|
+
if (this._isInitialized) {
|
|
341
|
+
return this._tracer.startTrace(name, attributes)
|
|
342
|
+
}
|
|
343
|
+
return null
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
endTrace(span: any, status?: any): void {
|
|
347
|
+
if (this._isInitialized && span) {
|
|
348
|
+
this._tracer.endTrace(span, status)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
captureException(error: Error, context?: Record<string, any>): void {
|
|
353
|
+
if (this._isInitialized) {
|
|
354
|
+
this._tracer.captureException(error, context)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Shutdown
|
|
359
|
+
async shutdown(): Promise<void> {
|
|
360
|
+
if (this._isInitialized) {
|
|
361
|
+
await this.stop()
|
|
362
|
+
await this._tracer.shutdown()
|
|
363
|
+
this._isInitialized = false
|
|
364
|
+
console.log('SessionRecorder shutdown')
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SessionRecorderOptions, PlatformInfo } from './index'
|
|
2
|
+
|
|
3
|
+
declare module '@multiplayer-app/session-recorder-react-native' {
|
|
4
|
+
export interface ExpoSessionRecorderOptions extends SessionRecorderOptions {
|
|
5
|
+
platform: 'expo'
|
|
6
|
+
expoVersion?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ExpoPlatformInfo {
|
|
10
|
+
isExpo: true
|
|
11
|
+
isReactNative: true
|
|
12
|
+
platform: 'ios' | 'android' | 'web'
|
|
13
|
+
expoVersion: string
|
|
14
|
+
deviceType: 'expo'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isExpoEnvironment(): boolean
|
|
18
|
+
export function getPlatformAttributes(): Record<string, any>
|
|
19
|
+
export function detectPlatform(): PlatformInfo | ExpoPlatformInfo
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Expo-specific exports
|
|
23
|
+
export * from './index'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { SessionType } from '@multiplayer-app/session-recorder-common'
|
|
2
|
+
import { HttpMaskingConfig } from '../config/masking'
|
|
3
|
+
|
|
4
|
+
export enum SessionState {
|
|
5
|
+
started = '2',
|
|
6
|
+
paused = '1',
|
|
7
|
+
stopped = '0',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ISession {
|
|
11
|
+
id: string
|
|
12
|
+
shortId: string
|
|
13
|
+
type: SessionType
|
|
14
|
+
state: SessionState
|
|
15
|
+
createdAt: string
|
|
16
|
+
updatedAt: string
|
|
17
|
+
tempApiKey?: string
|
|
18
|
+
metadata?: Record<string, any>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
export interface SessionRecorderOptions {
|
|
24
|
+
apiKey: string
|
|
25
|
+
version: string
|
|
26
|
+
application: string
|
|
27
|
+
environment: string
|
|
28
|
+
exporterEndpoint?: string
|
|
29
|
+
apiBaseUrl?: string
|
|
30
|
+
ignoreUrls?: Array<string | RegExp>
|
|
31
|
+
showWidget?: boolean
|
|
32
|
+
recordScreen?: boolean
|
|
33
|
+
recordGestures?: boolean
|
|
34
|
+
recordNavigation?: boolean
|
|
35
|
+
sampleTraceRatio?: number
|
|
36
|
+
captureBody?: boolean
|
|
37
|
+
captureHeaders?: boolean
|
|
38
|
+
maxCapturingHttpPayloadSize?: number
|
|
39
|
+
usePostMessageFallback?: boolean
|
|
40
|
+
httpMasking?: HttpMaskingConfig
|
|
41
|
+
platform?: 'react-native' | 'expo'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TracerReactNativeConfig {
|
|
45
|
+
apiKey: string
|
|
46
|
+
application: string
|
|
47
|
+
version: string
|
|
48
|
+
environment: string
|
|
49
|
+
exporterEndpoint?: string
|
|
50
|
+
ignoreUrls?: Array<string | RegExp>
|
|
51
|
+
propagateTraceHeaderCorsUrls?: Array<string | RegExp>
|
|
52
|
+
captureBody?: boolean
|
|
53
|
+
captureHeaders?: boolean
|
|
54
|
+
sampleTraceRatio?: number
|
|
55
|
+
httpMasking?: HttpMaskingConfig
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface RecorderConfig {
|
|
59
|
+
apiKey: string
|
|
60
|
+
apiBaseUrl?: string
|
|
61
|
+
usePostMessageFallback?: boolean
|
|
62
|
+
recordScreen?: boolean
|
|
63
|
+
recordGestures?: boolean
|
|
64
|
+
recordNavigation?: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface GestureEvent {
|
|
68
|
+
type: 'tap' | 'swipe' | 'pinch' | 'pan' | 'longPress' | 'doubleTap' | 'rotate' | 'fling' | 'multiTouch' | 'scroll' | 'zoom' | 'gestureSequence' | 'gestureError' | 'gesturePerformance'
|
|
69
|
+
timestamp: number
|
|
70
|
+
target?: string
|
|
71
|
+
coordinates?: { x: number; y: number }
|
|
72
|
+
metadata?: Record<string, any>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface NavigationEvent {
|
|
76
|
+
type: 'navigate' | 'goBack' | 'reset'
|
|
77
|
+
timestamp: number
|
|
78
|
+
routeName?: string
|
|
79
|
+
params?: Record<string, any>
|
|
80
|
+
metadata?: Record<string, any>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ScreenEvent {
|
|
84
|
+
type: 'screenCapture'
|
|
85
|
+
timestamp: number
|
|
86
|
+
dataUrl?: string
|
|
87
|
+
metadata?: Record<string, any>
|
|
88
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export interface PlatformInfo {
|
|
2
|
+
isExpo: boolean
|
|
3
|
+
isReactNative: boolean
|
|
4
|
+
platform: 'ios' | 'android' | 'web' | 'unknown'
|
|
5
|
+
platformVersion?: string
|
|
6
|
+
expoVersion?: string
|
|
7
|
+
deviceType: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function detectPlatform(): PlatformInfo {
|
|
11
|
+
try {
|
|
12
|
+
// Check if we're in an Expo environment
|
|
13
|
+
const expoConstants = require('expo-constants')
|
|
14
|
+
const isExpo = !!expoConstants.default?.expoVersion || !!expoConstants.expoVersion
|
|
15
|
+
|
|
16
|
+
if (isExpo) {
|
|
17
|
+
const expoVersion = expoConstants.default?.expoVersion || expoConstants.expoVersion
|
|
18
|
+
const platform = expoConstants.default?.platform || expoConstants.platform
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
isExpo: true,
|
|
22
|
+
isReactNative: true,
|
|
23
|
+
platform: platform?.ios ? 'ios' : platform?.android ? 'android' : 'unknown',
|
|
24
|
+
expoVersion,
|
|
25
|
+
deviceType: 'expo',
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fallback to React Native detection
|
|
30
|
+
const { Platform } = require('react-native')
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
isExpo: false,
|
|
34
|
+
isReactNative: true,
|
|
35
|
+
platform: Platform.OS as 'ios' | 'android' | 'web' | 'unknown',
|
|
36
|
+
platformVersion: Platform.Version?.toString(),
|
|
37
|
+
deviceType: Platform.OS,
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Fallback for web or other environments
|
|
41
|
+
return {
|
|
42
|
+
isExpo: false,
|
|
43
|
+
isReactNative: false,
|
|
44
|
+
platform: 'unknown',
|
|
45
|
+
deviceType: 'unknown',
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getPlatformAttributes(): Record<string, any> {
|
|
51
|
+
const platformInfo = detectPlatform()
|
|
52
|
+
|
|
53
|
+
const attributes: Record<string, any> = {
|
|
54
|
+
'platform': platformInfo.isExpo ? 'expo' : 'react-native',
|
|
55
|
+
'device.type': platformInfo.deviceType,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (platformInfo.platformVersion) {
|
|
59
|
+
attributes['platform.version'] = platformInfo.platformVersion
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (platformInfo.expoVersion) {
|
|
63
|
+
attributes['expo.version'] = platformInfo.expoVersion
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return attributes
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function isExpoEnvironment(): boolean {
|
|
70
|
+
return detectPlatform().isExpo
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isReactNativeEnvironment(): boolean {
|
|
74
|
+
return detectPlatform().isReactNative
|
|
75
|
+
}
|
package/src/version.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const version = "0.0.1-alpha.1"
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDir": "./src",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"jsx": "react-native",
|
|
7
|
+
"incremental": false,
|
|
8
|
+
"lib": ["ESNext"],
|
|
9
|
+
"module": "ES2015",
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"noEmitOnError": true,
|
|
12
|
+
"outDir": "./dist",
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"strict": true,
|
|
16
|
+
"target": "ES2018",
|
|
17
|
+
"allowSyntheticDefaultImports": true,
|
|
18
|
+
"resolveJsonModule": true,
|
|
19
|
+
"isolatedModules": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
|
22
|
+
"exclude": ["node_modules", "dist", "examples"],
|
|
23
|
+
"files": ["src/index.ts", "src/expo.ts"]
|
|
24
|
+
}
|