@multiplayer-app/session-recorder-react-native 0.0.1-alpha.1 → 0.0.1-alpha.3

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.
Files changed (166) hide show
  1. package/dist/config/constants.d.ts +19 -0
  2. package/dist/config/constants.js +1 -0
  3. package/dist/config/constants.js.map +1 -0
  4. package/dist/config/defaults.d.ts +4 -0
  5. package/dist/config/defaults.js +1 -0
  6. package/dist/config/defaults.js.map +1 -0
  7. package/dist/config/index.d.ts +5 -0
  8. package/dist/config/index.js +1 -0
  9. package/dist/config/index.js.map +1 -0
  10. package/dist/config/masking.d.ts +2 -30
  11. package/dist/config/masking.js +1 -1
  12. package/dist/config/masking.js.map +1 -1
  13. package/dist/config/session-recorder.d.ts +2 -0
  14. package/dist/config/session-recorder.js +1 -0
  15. package/dist/config/session-recorder.js.map +1 -0
  16. package/dist/config/validators.d.ts +10 -0
  17. package/dist/config/validators.js +1 -0
  18. package/dist/config/validators.js.map +1 -0
  19. package/dist/expo.d.ts +2 -2
  20. package/dist/expo.js +1 -1
  21. package/dist/expo.js.map +1 -1
  22. package/dist/exporters.d.ts +3 -0
  23. package/dist/exporters.js +1 -0
  24. package/dist/exporters.js.map +1 -0
  25. package/dist/index.d.ts +3 -9
  26. package/dist/index.js +1 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/otel/helpers.d.ts +45 -3
  29. package/dist/otel/helpers.js +1 -1
  30. package/dist/otel/helpers.js.map +1 -1
  31. package/dist/otel/index.d.ts +9 -5
  32. package/dist/otel/index.js +1 -1
  33. package/dist/otel/index.js.map +1 -1
  34. package/dist/otel/instrumentations/index.js +1 -1
  35. package/dist/otel/instrumentations/index.js.map +1 -1
  36. package/dist/patch/index.d.ts +1 -0
  37. package/dist/patch/index.js +1 -0
  38. package/dist/patch/index.js.map +1 -0
  39. package/dist/patch/xhr.d.ts +2 -0
  40. package/dist/patch/xhr.js +1 -0
  41. package/dist/patch/xhr.js.map +1 -0
  42. package/dist/recorder/gestureRecorder.js +1 -1
  43. package/dist/recorder/gestureRecorder.js.map +1 -1
  44. package/dist/recorder/navigationTracker.js +1 -1
  45. package/dist/recorder/navigationTracker.js.map +1 -1
  46. package/dist/recorder/screenRecorder.d.ts +1 -0
  47. package/dist/recorder/screenRecorder.js +1 -1
  48. package/dist/recorder/screenRecorder.js.map +1 -1
  49. package/dist/services/api.service.d.ts +62 -10
  50. package/dist/services/api.service.js +1 -1
  51. package/dist/services/api.service.js.map +1 -1
  52. package/dist/services/storage.service.d.ts +23 -16
  53. package/dist/services/storage.service.js +1 -1
  54. package/dist/services/storage.service.js.map +1 -1
  55. package/dist/session-recorder.d.ts +131 -0
  56. package/dist/session-recorder.js +1 -0
  57. package/dist/session-recorder.js.map +1 -0
  58. package/dist/sessionRecorder.d.ts +113 -34
  59. package/dist/sessionRecorder.js +1 -1
  60. package/dist/sessionRecorder.js.map +1 -1
  61. package/dist/types/index.d.ts +2 -81
  62. package/dist/types/index.js +1 -1
  63. package/dist/types/index.js.map +1 -1
  64. package/dist/types/session-recorder.d.ts +366 -0
  65. package/dist/types/session-recorder.js +1 -0
  66. package/dist/types/session-recorder.js.map +1 -0
  67. package/dist/types/session.d.ts +59 -0
  68. package/dist/types/session.js +1 -0
  69. package/dist/types/session.js.map +1 -0
  70. package/dist/utils/index.d.ts +5 -0
  71. package/dist/utils/index.js +1 -0
  72. package/dist/utils/index.js.map +1 -0
  73. package/dist/utils/platform.d.ts +5 -0
  74. package/dist/utils/platform.js +1 -1
  75. package/dist/utils/platform.js.map +1 -1
  76. package/dist/utils/request-utils.d.ts +21 -0
  77. package/dist/utils/request-utils.js +1 -0
  78. package/dist/utils/request-utils.js.map +1 -0
  79. package/dist/utils/session.d.ts +5 -0
  80. package/dist/utils/session.js +1 -0
  81. package/dist/utils/session.js.map +1 -0
  82. package/dist/utils/time.d.ts +4 -0
  83. package/dist/utils/time.js +1 -0
  84. package/dist/utils/time.js.map +1 -0
  85. package/dist/utils/type-utils.d.ts +16 -0
  86. package/dist/utils/type-utils.js +1 -0
  87. package/dist/utils/type-utils.js.map +1 -0
  88. package/dist/version.d.ts +1 -1
  89. package/dist/version.js +1 -1
  90. package/package.json +3 -3
  91. package/src/config/constants.ts +60 -0
  92. package/src/config/defaults.ts +82 -0
  93. package/src/config/index.ts +6 -0
  94. package/src/config/masking.ts +10 -61
  95. package/src/config/session-recorder.ts +55 -0
  96. package/src/config/validators.ts +31 -0
  97. package/src/expo.ts +2 -22
  98. package/src/index.ts +3 -15
  99. package/src/otel/helpers.ts +247 -11
  100. package/src/otel/index.ts +109 -76
  101. package/src/otel/instrumentations/index.ts +8 -8
  102. package/src/patch/index.ts +1 -0
  103. package/src/patch/xhr.ts +142 -0
  104. package/src/recorder/gestureRecorder.ts +12 -12
  105. package/src/recorder/navigationTracker.ts +10 -10
  106. package/src/recorder/screenRecorder.ts +26 -26
  107. package/src/services/api.service.ts +169 -37
  108. package/src/services/storage.service.ts +101 -74
  109. package/src/session-recorder.ts +570 -0
  110. package/src/types/index.ts +2 -88
  111. package/src/types/session-recorder.ts +423 -0
  112. package/src/types/session.ts +65 -0
  113. package/src/utils/index.ts +6 -0
  114. package/src/utils/platform.ts +13 -0
  115. package/src/utils/request-utils.ts +61 -0
  116. package/src/utils/session.ts +18 -0
  117. package/src/utils/time.ts +17 -0
  118. package/src/utils/type-utils.ts +75 -0
  119. package/src/version.ts +1 -1
  120. package/examples/sample-expo-app/README.md +0 -142
  121. package/examples/sample-expo-app/app/(tabs)/_layout.tsx +0 -60
  122. package/examples/sample-expo-app/app/(tabs)/explore.tsx +0 -110
  123. package/examples/sample-expo-app/app/(tabs)/index.tsx +0 -125
  124. package/examples/sample-expo-app/app/(tabs)/posts.tsx +0 -96
  125. package/examples/sample-expo-app/app/(tabs)/users.tsx +0 -131
  126. package/examples/sample-expo-app/app/+not-found.tsx +0 -32
  127. package/examples/sample-expo-app/app/_layout.tsx +0 -53
  128. package/examples/sample-expo-app/app/post/[id].tsx +0 -199
  129. package/examples/sample-expo-app/app/user/[id].tsx +0 -270
  130. package/examples/sample-expo-app/app.json +0 -42
  131. package/examples/sample-expo-app/assets/fonts/SpaceMono-Regular.ttf +0 -0
  132. package/examples/sample-expo-app/assets/images/adaptive-icon.png +0 -0
  133. package/examples/sample-expo-app/assets/images/favicon.png +0 -0
  134. package/examples/sample-expo-app/assets/images/icon.png +0 -0
  135. package/examples/sample-expo-app/assets/images/partial-react-logo.png +0 -0
  136. package/examples/sample-expo-app/assets/images/react-logo.png +0 -0
  137. package/examples/sample-expo-app/assets/images/react-logo@2x.png +0 -0
  138. package/examples/sample-expo-app/assets/images/react-logo@3x.png +0 -0
  139. package/examples/sample-expo-app/assets/images/splash-icon.png +0 -0
  140. package/examples/sample-expo-app/components/Collapsible.tsx +0 -45
  141. package/examples/sample-expo-app/components/ErrorView.tsx +0 -52
  142. package/examples/sample-expo-app/components/ExternalLink.tsx +0 -24
  143. package/examples/sample-expo-app/components/HapticTab.tsx +0 -18
  144. package/examples/sample-expo-app/components/HelloWave.tsx +0 -40
  145. package/examples/sample-expo-app/components/LoadingSpinner.tsx +0 -34
  146. package/examples/sample-expo-app/components/ParallaxScrollView.tsx +0 -82
  147. package/examples/sample-expo-app/components/ThemedText.tsx +0 -60
  148. package/examples/sample-expo-app/components/ThemedView.tsx +0 -14
  149. package/examples/sample-expo-app/components/ui/IconSymbol.ios.tsx +0 -32
  150. package/examples/sample-expo-app/components/ui/IconSymbol.tsx +0 -41
  151. package/examples/sample-expo-app/components/ui/TabBarBackground.ios.tsx +0 -19
  152. package/examples/sample-expo-app/components/ui/TabBarBackground.tsx +0 -6
  153. package/examples/sample-expo-app/constants/Colors.ts +0 -26
  154. package/examples/sample-expo-app/eslint.config.js +0 -10
  155. package/examples/sample-expo-app/hooks/useApi.ts +0 -41
  156. package/examples/sample-expo-app/hooks/useColorScheme.ts +0 -1
  157. package/examples/sample-expo-app/hooks/useColorScheme.web.ts +0 -21
  158. package/examples/sample-expo-app/hooks/useThemeColor.ts +0 -21
  159. package/examples/sample-expo-app/metro.config.js +0 -26
  160. package/examples/sample-expo-app/package-lock.json +0 -26296
  161. package/examples/sample-expo-app/package.json +0 -59
  162. package/examples/sample-expo-app/scripts/reset-project.js +0 -112
  163. package/examples/sample-expo-app/services/api.ts +0 -98
  164. package/examples/sample-expo-app/tsconfig.json +0 -17
  165. package/examples/sample-expo-app/utils/navigation.ts +0 -19
  166. package/src/sessionRecorder.ts +0 -367
@@ -11,6 +11,7 @@ export class ScreenRecorder {
11
11
  private captureQuality: number = 0.8
12
12
  private captureFormat: 'png' | 'jpg' | 'webp' = 'png'
13
13
  private screenDimensions: { width: number; height: number } | null = null
14
+ private currentScreen: string | null = null
14
15
 
15
16
  init(config: RecorderConfig): void {
16
17
  this.config = config
@@ -22,13 +23,13 @@ export class ScreenRecorder {
22
23
  this.events = []
23
24
  this.captureCount = 0
24
25
  this._startPeriodicCapture()
25
- console.log('Screen recording started')
26
+ // Screen recording started
26
27
  }
27
28
 
28
29
  stop(): void {
29
30
  this.isRecording = false
30
31
  this._stopPeriodicCapture()
31
- console.log('Screen recording stopped')
32
+ // Screen recording stopped
32
33
  }
33
34
 
34
35
  pause(): void {
@@ -46,7 +47,7 @@ export class ScreenRecorder {
46
47
  const { Dimensions } = require('react-native')
47
48
  this.screenDimensions = Dimensions.get('window')
48
49
  } catch (error) {
49
- console.warn('Failed to get screen dimensions:', error)
50
+ // Failed to get screen dimensions - silently continue
50
51
  this.screenDimensions = { width: 375, height: 667 } // Default fallback
51
52
  }
52
53
  }
@@ -79,6 +80,7 @@ export class ScreenRecorder {
79
80
 
80
81
  if (screenData) {
81
82
  const screenEvent: ScreenEvent = {
83
+ screenName: this.currentScreen || 'unknown',
82
84
  type: 'screenCapture',
83
85
  timestamp: Date.now(),
84
86
  dataUrl: screenData,
@@ -98,7 +100,7 @@ export class ScreenRecorder {
98
100
  this._recordOpenTelemetrySpan(screenEvent)
99
101
  }
100
102
  } catch (error) {
101
- console.error('Failed to capture screen:', error)
103
+ // Failed to capture screen - silently continue
102
104
  this._recordScreenCaptureError(error instanceof Error ? error : new Error(String(error)))
103
105
  }
104
106
  }
@@ -113,17 +115,17 @@ export class ScreenRecorder {
113
115
  return await this._captureWithViewShot()
114
116
  }
115
117
  } catch (error) {
116
- console.warn('react-native-view-shot not available:', error)
118
+ // react-native-view-shot not available - silently continue
117
119
  }
118
120
 
119
121
  try {
120
122
  // Try react-native-screenshot
121
- // const Screenshot = require('react-native-screenshot')
122
- // if (Screenshot && Screenshot.takeScreenshot) {
123
- // return await this._captureWithScreenshot()
124
- // }
123
+ const Screenshot = require('react-native-screenshot')
124
+ if (Screenshot && Screenshot.takeScreenshot) {
125
+ return await this._captureWithScreenshot()
126
+ }
125
127
  } catch (error) {
126
- console.warn('react-native-screenshot not available:', error)
128
+ // react-native-screenshot not available - silently continue
127
129
  }
128
130
 
129
131
  // Fallback to placeholder
@@ -148,23 +150,23 @@ export class ScreenRecorder {
148
150
  return await ViewShot.captureRef(rootViewRef, options)
149
151
  }
150
152
  } catch (error) {
151
- console.warn('Failed to capture with ViewShot:', error)
153
+ // Failed to capture with ViewShot - silently continue
152
154
  }
153
155
  return null
154
156
  }
155
157
 
156
158
  private async _captureWithScreenshot(): Promise<string | null> {
157
159
  try {
158
- // const Screenshot = require('react-native-screenshot')
160
+ const Screenshot = require('react-native-screenshot')
159
161
 
160
162
  const options = {
161
163
  format: this.captureFormat,
162
164
  quality: this.captureQuality,
163
165
  }
164
166
 
165
- // return await Screenshot.takeScreenshot(options)
167
+ return await Screenshot.takeScreenshot(options)
166
168
  } catch (error) {
167
- console.warn('Failed to capture with Screenshot:', error)
169
+ // Failed to capture with Screenshot - silently continue
168
170
  }
169
171
  return null
170
172
  }
@@ -176,7 +178,7 @@ export class ScreenRecorder {
176
178
  const appName = AppRegistry.getAppKeys()[0]
177
179
  return appName ? { current: null } : null
178
180
  } catch (error) {
179
- console.warn('Failed to get root view ref:', error)
181
+ // Failed to get root view ref - silently continue
180
182
  return null
181
183
  }
182
184
  }
@@ -206,12 +208,8 @@ export class ScreenRecorder {
206
208
  }
207
209
 
208
210
  private _sendEvent(event: ScreenEvent): void {
209
- console.log('Screen event recorded:', {
210
- type: event.type,
211
- timestamp: event.timestamp,
212
- captureCount: event.metadata?.captureCount,
213
- captureTime: event.metadata?.captureTime,
214
- })
211
+ // Screen event recorded
212
+ // Send event to backend or store locally
215
213
  }
216
214
 
217
215
  private _recordOpenTelemetrySpan(event: ScreenEvent): void {
@@ -233,7 +231,7 @@ export class ScreenRecorder {
233
231
  span.setStatus({ code: SpanStatusCode.OK })
234
232
  span.end()
235
233
  } catch (error) {
236
- console.warn('Failed to record OpenTelemetry span for screen:', error)
234
+ // Failed to record OpenTelemetry span for screen - silently continue
237
235
  }
238
236
  }
239
237
 
@@ -252,14 +250,14 @@ export class ScreenRecorder {
252
250
  span.recordException(error)
253
251
  span.end()
254
252
  } catch (spanError) {
255
- console.warn('Failed to record error span:', spanError)
253
+ // Failed to record error span - silently continue
256
254
  }
257
255
  }
258
256
 
259
257
  // Manual screen capture methods
260
258
  async captureScreenNow(): Promise<string | null> {
261
259
  if (!this.isRecording) {
262
- console.warn('Screen recording not active')
260
+ // Screen recording not active - silently continue
263
261
  return null
264
262
  }
265
263
 
@@ -281,7 +279,7 @@ export class ScreenRecorder {
281
279
 
282
280
  return await ViewShot.captureRef(elementRef, captureOptions)
283
281
  } catch (error) {
284
- console.error('Failed to capture specific element:', error)
282
+ // Failed to capture specific element - silently continue
285
283
  return null
286
284
  }
287
285
  }
@@ -314,6 +312,7 @@ export class ScreenRecorder {
314
312
  // Performance monitoring
315
313
  recordScreenPerformance(screenName: string, loadTime: number): void {
316
314
  const event: ScreenEvent = {
315
+ screenName,
317
316
  type: 'screenCapture',
318
317
  timestamp: Date.now(),
319
318
  metadata: {
@@ -333,6 +332,7 @@ export class ScreenRecorder {
333
332
  // Error tracking
334
333
  recordScreenError(error: Error, screenName?: string): void {
335
334
  const event: ScreenEvent = {
335
+ screenName: screenName || 'unknown',
336
336
  type: 'screenCapture',
337
337
  timestamp: Date.now(),
338
338
  metadata: {
@@ -406,6 +406,6 @@ export class ScreenRecorder {
406
406
  shutdown(): void {
407
407
  this.stop()
408
408
  this.clearEvents()
409
- console.log('Screen recorder shutdown')
409
+ // Screen recorder shutdown
410
410
  }
411
411
  }
@@ -1,4 +1,4 @@
1
- import { ISession, SessionRecorderOptions } from '../types'
1
+ import { SessionRecorderOptions, IResourceAttributes, ISessionAttributes } from '../types'
2
2
 
3
3
  // Type definitions for fetch API
4
4
  type RequestInit = {
@@ -9,70 +9,202 @@ type RequestInit = {
9
9
  }
10
10
 
11
11
  export interface StartSessionRequest {
12
- application: string
13
- version: string
14
- environment: string
15
- metadata?: Record<string, any>
12
+ name?: string
13
+ stoppedAt?: string | number
14
+ sessionAttributes?: ISessionAttributes
15
+ resourceAttributes?: IResourceAttributes
16
+ debugSessionData?: Record<string, any>
16
17
  }
17
18
 
18
19
  export interface StopSessionRequest {
19
- sessionId: string
20
- comment?: string
20
+ sessionAttributes?: ISessionAttributes
21
+ stoppedAt: string | number
21
22
  }
22
23
 
23
24
  export class ApiService {
24
25
  private config?: SessionRecorderOptions
25
26
  private baseUrl: string = 'https://api.multiplayer.app'
26
27
 
28
+ constructor() {
29
+ this.config = {
30
+ apiKey: '',
31
+ apiBaseUrl: '',
32
+ exporterEndpoint: '',
33
+ version: '',
34
+ application: '',
35
+ environment: '',
36
+ } as SessionRecorderOptions
37
+ }
38
+
27
39
  init(config: SessionRecorderOptions): void {
28
- this.config = config
40
+ this.config = {
41
+ ...this.config,
42
+ ...config,
43
+ }
29
44
  if (config.apiBaseUrl) {
30
45
  this.baseUrl = config.apiBaseUrl
31
46
  }
32
47
  }
33
48
 
34
- private async makeRequest<T>(
35
- endpoint: string,
36
- options: RequestInit = {},
37
- ): Promise<T> {
38
- if (!this.config?.apiKey) {
39
- throw new Error('API key not configured')
49
+ /**
50
+ * Update the API service configuration
51
+ * @param config - Partial configuration to update
52
+ */
53
+ public updateConfigs(config: Partial<SessionRecorderOptions>) {
54
+ if (this.config) {
55
+ this.config = { ...this.config, ...config }
40
56
  }
57
+ }
41
58
 
42
- const url = `${this.baseUrl}${endpoint}`
43
- const response = await fetch(url, {
44
- ...options,
59
+ /**
60
+ * Make a request to the session debugger API
61
+ * @param path - API endpoint path (relative to the base URL)
62
+ * @param method - HTTP method (GET, POST, PATCH, etc.)
63
+ * @param body - request payload
64
+ * @param signal - AbortSignal to set request's signal
65
+ */
66
+ private async makeRequest(
67
+ path: string,
68
+ method: string,
69
+ body?: any,
70
+ signal?: AbortSignal,
71
+ ): Promise<any> {
72
+ const url = `${this.baseUrl}/v0/radar${path}`
73
+ const params = {
74
+ method,
75
+ body: body ? JSON.stringify(body) : null,
45
76
  headers: {
46
77
  'Content-Type': 'application/json',
47
- 'Authorization': `Bearer ${this.config.apiKey}`,
48
- ...options.headers,
78
+ ...(this.config?.apiKey && { 'X-Api-Key': this.config.apiKey }),
49
79
  },
50
- })
80
+ }
81
+
82
+ try {
83
+ const response = await fetch(url, {
84
+ ...params,
85
+ signal,
86
+ })
87
+
88
+ if (!response.ok) {
89
+ throw new Error('Network response was not ok: ' + response.statusText)
90
+ }
91
+
92
+ if (response.status === 204) {
93
+ return null
94
+ }
51
95
 
52
- if (!response.ok) {
53
- throw new Error(`API request failed: ${response.statusText}`)
96
+ return await response.json()
97
+ } catch (error: any) {
98
+ if (error?.name === 'AbortError') {
99
+ throw new Error('Request aborted')
100
+ }
101
+ throw new Error('Error making request: ' + error.message)
54
102
  }
103
+ }
104
+
105
+ /**
106
+ * Start a new debug session
107
+ * @param request - Session start request data
108
+ * @param signal - Optional AbortSignal for request cancellation
109
+ */
110
+ async startSession(
111
+ request: StartSessionRequest,
112
+ signal?: AbortSignal,
113
+ ): Promise<any> {
114
+ return this.makeRequest(
115
+ '/debug-sessions/start',
116
+ 'POST',
117
+ request,
118
+ signal,
119
+ )
120
+ }
121
+
122
+ /**
123
+ * Stop an active debug session
124
+ * @param sessionId - ID of the session to stop
125
+ * @param request - Session stop request data
126
+ */
127
+ async stopSession(
128
+ sessionId: string,
129
+ request: StopSessionRequest,
130
+ ): Promise<any> {
131
+ return this.makeRequest(
132
+ `/debug-sessions/${sessionId}/stop`,
133
+ 'PATCH',
134
+ request,
135
+ )
136
+ }
137
+
138
+ /**
139
+ * Cancel an active debug session
140
+ * @param sessionId - ID of the session to cancel
141
+ */
142
+ async cancelSession(sessionId: string): Promise<any> {
143
+ return this.makeRequest(
144
+ `/debug-sessions/${sessionId}/cancel`,
145
+ 'DELETE',
146
+ )
147
+ }
55
148
 
56
- return response.json()
149
+ /**
150
+ * Start a new continuous debug session
151
+ * @param request - Session start request data
152
+ * @param signal - Optional AbortSignal for request cancellation
153
+ */
154
+ async startContinuousDebugSession(
155
+ request: StartSessionRequest,
156
+ signal?: AbortSignal,
157
+ ): Promise<any> {
158
+ return this.makeRequest(
159
+ '/continuous-debug-sessions/start',
160
+ 'POST',
161
+ request,
162
+ signal,
163
+ )
57
164
  }
58
165
 
59
- async startSession(request: StartSessionRequest): Promise<ISession> {
60
- return this.makeRequest<ISession>('/sessions', {
61
- method: 'POST',
62
- body: JSON.stringify(request),
63
- })
166
+ /**
167
+ * Save a continuous debug session
168
+ * @param sessionId - ID of the session to save
169
+ * @param request - Session save request data
170
+ * @param signal - Optional AbortSignal for request cancellation
171
+ */
172
+ async saveContinuousDebugSession(
173
+ sessionId: string,
174
+ request: StartSessionRequest,
175
+ signal?: AbortSignal,
176
+ ): Promise<any> {
177
+ return this.makeRequest(
178
+ `/continuous-debug-sessions/${sessionId}/save`,
179
+ 'POST',
180
+ request,
181
+ signal,
182
+ )
64
183
  }
65
184
 
66
- async stopSession(request: StopSessionRequest): Promise<void> {
67
- return this.makeRequest<void>(`/sessions/${request.sessionId}/stop`, {
68
- method: 'POST',
69
- body: JSON.stringify({ comment: request.comment }),
70
- })
185
+ /**
186
+ * Stop an active continuous debug session
187
+ * @param sessionId - ID of the session to stop
188
+ */
189
+ async stopContinuousDebugSession(sessionId: string): Promise<any> {
190
+ return this.makeRequest(
191
+ `/continuous-debug-sessions/${sessionId}/cancel`,
192
+ 'DELETE',
193
+ )
71
194
  }
72
195
 
73
- async saveSession(sessionId: string): Promise<ISession> {
74
- return this.makeRequest<ISession>(`/sessions/${sessionId}/save`, {
75
- method: 'POST',
76
- })
196
+ /**
197
+ * Check debug session should be started remotely
198
+ */
199
+ async checkRemoteSession(
200
+ requestBody: StartSessionRequest,
201
+ signal?: AbortSignal,
202
+ ): Promise<{ state: 'START' | 'STOP' }> {
203
+ return this.makeRequest(
204
+ '/remote-debug-session/check',
205
+ 'POST',
206
+ requestBody,
207
+ signal,
208
+ )
77
209
  }
78
210
  }
@@ -1,6 +1,13 @@
1
1
  import AsyncStorage from '@react-native-async-storage/async-storage'
2
- import { ISession, SessionState } from '../types'
3
2
  import { SessionType } from '@multiplayer-app/session-recorder-common'
3
+ import { ISession, SessionState } from '../types'
4
+
5
+ interface CacheData {
6
+ sessionId: string | null
7
+ sessionType: SessionType | null
8
+ sessionState: SessionState | null
9
+ sessionObject: ISession | null
10
+ }
4
11
 
5
12
  export class StorageService {
6
13
  private static readonly SESSION_ID_KEY = 'session_id'
@@ -8,123 +15,143 @@ export class StorageService {
8
15
  private static readonly SESSION_STATE_KEY = 'session_state'
9
16
  private static readonly SESSION_OBJECT_KEY = 'session_object'
10
17
 
11
- async saveSessionId(sessionId: string): Promise<void> {
12
- try {
13
- await AsyncStorage.setItem(StorageService.SESSION_ID_KEY, sessionId)
14
- } catch (error) {
15
- console.error('Failed to save session ID:', error)
16
- throw error
17
- }
18
+ private static cache: CacheData = {
19
+ sessionId: null,
20
+ sessionType: null,
21
+ sessionState: null,
22
+ sessionObject: null,
23
+ }
24
+
25
+ private static cacheInitialized = false
26
+
27
+ constructor() {
28
+ StorageService.initialize()
18
29
  }
19
30
 
20
- async getSessionId(): Promise<string | null> {
31
+ private static async initializeCache(): Promise<void> {
32
+ if (StorageService.cacheInitialized) return
33
+
21
34
  try {
22
- return await AsyncStorage.getItem(StorageService.SESSION_ID_KEY)
35
+ const [sessionId, sessionType, sessionState, sessionObject] = await Promise.all([
36
+ AsyncStorage.getItem(StorageService.SESSION_ID_KEY),
37
+ AsyncStorage.getItem(StorageService.SESSION_TYPE_KEY),
38
+ AsyncStorage.getItem(StorageService.SESSION_STATE_KEY),
39
+ AsyncStorage.getItem(StorageService.SESSION_OBJECT_KEY),
40
+ ])
41
+
42
+ StorageService.cache = {
43
+ sessionId,
44
+ sessionType: sessionType as SessionType | null,
45
+ sessionState: sessionState as SessionState | null,
46
+ sessionObject: sessionObject ? JSON.parse(sessionObject) : null,
47
+ }
48
+ StorageService.cacheInitialized = true
23
49
  } catch (error) {
24
- console.error('Failed to get session ID:', error)
25
- return null
50
+ // Failed to initialize cache - silently continue
51
+ StorageService.cacheInitialized = true // Mark as initialized to prevent retries
26
52
  }
27
53
  }
28
54
 
29
- async saveSessionType(sessionType: SessionType): Promise<void> {
55
+ saveSessionId(sessionId: string): void {
30
56
  try {
31
- await AsyncStorage.setItem(StorageService.SESSION_TYPE_KEY, sessionType)
57
+ StorageService.cache.sessionId = sessionId
58
+ AsyncStorage.setItem(StorageService.SESSION_ID_KEY, sessionId).catch(error => {
59
+ // Failed to persist session ID - silently continue
60
+ })
32
61
  } catch (error) {
33
- console.error('Failed to save session type:', error)
62
+ // Failed to save session ID - silently continue
34
63
  throw error
35
64
  }
36
65
  }
37
66
 
38
- async getSessionType(): Promise<SessionType | null> {
39
- try {
40
- const type = await AsyncStorage.getItem(StorageService.SESSION_TYPE_KEY)
41
- return type as SessionType | null
42
- } catch (error) {
43
- console.error('Failed to get session type:', error)
44
- return null
45
- }
67
+ getSessionId(): string | null {
68
+ return StorageService.cache.sessionId
46
69
  }
47
70
 
48
- async saveSessionState(state: SessionState): Promise<void> {
71
+ saveSessionType(sessionType: SessionType): void {
49
72
  try {
50
- await AsyncStorage.setItem(StorageService.SESSION_STATE_KEY, state)
73
+ StorageService.cache.sessionType = sessionType
74
+ AsyncStorage.setItem(StorageService.SESSION_TYPE_KEY, sessionType).catch(error => {
75
+ // Failed to persist session type - silently continue
76
+ })
51
77
  } catch (error) {
52
- console.error('Failed to save session state:', error)
78
+ // Failed to save session type - silently continue
53
79
  throw error
54
80
  }
55
81
  }
56
82
 
57
- async getSessionState(): Promise<SessionState | null> {
58
- try {
59
- const state = await AsyncStorage.getItem(StorageService.SESSION_STATE_KEY)
60
- return state as SessionState | null
61
- } catch (error) {
62
- console.error('Failed to get session state:', error)
63
- return null
64
- }
83
+ getSessionType(): SessionType | null {
84
+ return StorageService.cache.sessionType
65
85
  }
66
86
 
67
- async saveSessionObject(session: ISession): Promise<void> {
87
+ saveSessionState(state: SessionState): void {
68
88
  try {
69
- await AsyncStorage.setItem(StorageService.SESSION_OBJECT_KEY, JSON.stringify(session))
89
+ StorageService.cache.sessionState = state
90
+ AsyncStorage.setItem(StorageService.SESSION_STATE_KEY, state).catch(error => {
91
+ // Failed to persist session state - silently continue
92
+ })
70
93
  } catch (error) {
71
- console.error('Failed to save session object:', error)
94
+ // Failed to save session state - silently continue
72
95
  throw error
73
96
  }
74
97
  }
75
98
 
76
- async getSessionObject(): Promise<ISession | null> {
99
+ getSessionState(): SessionState | null {
100
+ return StorageService.cache.sessionState
101
+ }
102
+
103
+ saveSessionObject(session: ISession): void {
77
104
  try {
78
- const sessionData = await AsyncStorage.getItem(StorageService.SESSION_OBJECT_KEY)
79
- return sessionData ? JSON.parse(sessionData) : null
105
+ StorageService.cache.sessionObject = session
106
+ AsyncStorage.setItem(StorageService.SESSION_OBJECT_KEY, JSON.stringify(session)).catch(error => {
107
+ // Failed to persist session object - silently continue
108
+ })
80
109
  } catch (error) {
81
- console.error('Failed to get session object:', error)
82
- return null
110
+ // Failed to save session object - silently continue
111
+ throw error
83
112
  }
84
113
  }
85
114
 
86
- async clearSessionData(): Promise<void> {
115
+ getSessionObject(): ISession | null {
116
+ return StorageService.cache.sessionObject
117
+ }
118
+
119
+ clearSessionData(): void {
87
120
  try {
88
- await AsyncStorage.multiRemove([
121
+ // Clear cache immediately
122
+ StorageService.cache = {
123
+ sessionId: null,
124
+ sessionType: null,
125
+ sessionState: null,
126
+ sessionObject: null,
127
+ }
128
+
129
+ // Clear persistent storage asynchronously
130
+ AsyncStorage.multiRemove([
89
131
  StorageService.SESSION_ID_KEY,
90
132
  StorageService.SESSION_TYPE_KEY,
91
133
  StorageService.SESSION_STATE_KEY,
92
134
  StorageService.SESSION_OBJECT_KEY,
93
- ])
135
+ ]).catch(error => {
136
+ // Failed to clear session data from storage - silently continue
137
+ })
94
138
  } catch (error) {
95
- console.error('Failed to clear session data:', error)
139
+ // Failed to clear session data - silently continue
96
140
  throw error
97
141
  }
98
142
  }
99
143
 
100
- async getAllSessionData(): Promise<{
101
- sessionId: string | null
102
- sessionType: SessionType | null
103
- sessionState: SessionState | null
104
- sessionObject: ISession | null
105
- }> {
106
- try {
107
- const [sessionId, sessionType, sessionState, sessionObject] = await Promise.all([
108
- this.getSessionId(),
109
- this.getSessionType(),
110
- this.getSessionState(),
111
- this.getSessionObject(),
112
- ])
113
-
114
- return {
115
- sessionId,
116
- sessionType,
117
- sessionState,
118
- sessionObject,
119
- }
120
- } catch (error) {
121
- console.error('Failed to get all session data:', error)
122
- return {
123
- sessionId: null,
124
- sessionType: null,
125
- sessionState: null,
126
- sessionObject: null,
127
- }
144
+ getAllSessionData(): CacheData {
145
+ return {
146
+ sessionId: StorageService.cache.sessionId,
147
+ sessionType: StorageService.cache.sessionType,
148
+ sessionState: StorageService.cache.sessionState,
149
+ sessionObject: StorageService.cache.sessionObject,
128
150
  }
129
151
  }
152
+
153
+ // Initialize cache on first use - call this method when the service is first used
154
+ static async initialize(): Promise<void> {
155
+ await StorageService.initializeCache()
156
+ }
130
157
  }