@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
@@ -0,0 +1,570 @@
1
+
2
+ import { SessionType } from '@multiplayer-app/session-recorder-common'
3
+
4
+ import { TracerReactNativeSDK } from './otel'
5
+ import { RecorderReactNativeSDK } from './recorder'
6
+
7
+ import {
8
+ ISession,
9
+ SessionState,
10
+ ISessionRecorder,
11
+ SessionRecorderConfigs,
12
+ SessionRecorderOptions
13
+ } from './types'
14
+ import { getFormattedDate, isSessionActive, getNavigatorInfo } from './utils'
15
+ import { setMaxCapturingHttpPayloadSize, setShouldRecordHttpData } from './patch/xhr'
16
+ import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig } from './config'
17
+
18
+ import { StorageService } from './services/storage.service'
19
+ import { ApiService, StartSessionRequest, StopSessionRequest } from './services/api.service'
20
+
21
+ // Utility functions for React Native
22
+
23
+
24
+ export class SessionRecorder implements ISessionRecorder {
25
+ private _isInitialized = false
26
+ private _configs: SessionRecorderConfigs | null = null
27
+ private _apiService = new ApiService()
28
+ private _tracer = new TracerReactNativeSDK()
29
+ private _recorder = new RecorderReactNativeSDK()
30
+ private _storageService = new StorageService()
31
+ private _startRequestController: AbortController | null = null
32
+
33
+ // Session ID and state are stored in AsyncStorage
34
+ private _sessionId: string | null = null
35
+ get sessionId(): string | null {
36
+ return this._sessionId
37
+ }
38
+ set sessionId(sessionId: string | null) {
39
+ this._sessionId = sessionId
40
+ if (sessionId) {
41
+ this._storageService.saveSessionId(sessionId)
42
+ }
43
+ }
44
+
45
+ private _sessionType: SessionType = SessionType.PLAIN
46
+ get sessionType(): SessionType {
47
+ return this._sessionType
48
+ }
49
+ set sessionType(sessionType: SessionType) {
50
+ this._sessionType = sessionType
51
+ this._storageService.saveSessionType(sessionType)
52
+ }
53
+
54
+ get continuousRecording(): boolean {
55
+ return this.sessionType === SessionType.CONTINUOUS
56
+ }
57
+
58
+ private _sessionState: SessionState | null = null
59
+ get sessionState(): SessionState | null {
60
+ return this._sessionState || SessionState.stopped
61
+ }
62
+ set sessionState(state: SessionState | null) {
63
+ this._sessionState = state
64
+ if (state) {
65
+ this._storageService.saveSessionState(state)
66
+ }
67
+ }
68
+
69
+ private _session: ISession | null = null
70
+ get session(): ISession | null {
71
+ return this._session
72
+ }
73
+ set session(session: ISession | null) {
74
+ this._session = session
75
+ if (session) {
76
+ this._storageService.saveSessionObject(session)
77
+ }
78
+ }
79
+
80
+ private _sessionAttributes: Record<string, any> | null = null
81
+ get sessionAttributes(): Record<string, any> {
82
+ return this._sessionAttributes || {}
83
+ }
84
+ set sessionAttributes(attributes: Record<string, any> | null) {
85
+ this._sessionAttributes = attributes
86
+ }
87
+
88
+ /**
89
+ * Error message getter and setter
90
+ */
91
+ public get error(): string {
92
+ return this._error || ''
93
+ }
94
+
95
+ public set error(v: string) {
96
+ this._error = v
97
+ }
98
+ private _error: string = ''
99
+
100
+ /**
101
+ * React Native doesn't have HTML elements, so we return null
102
+ */
103
+ public get sessionWidgetButtonElement(): any {
104
+ return null
105
+ }
106
+
107
+ /**
108
+ * Initialize debugger with default or custom configurations
109
+ */
110
+ constructor() {
111
+ // Initialize with stored session data if available
112
+ this._loadStoredSessionData()
113
+ }
114
+
115
+ private async _loadStoredSessionData(): Promise<void> {
116
+ try {
117
+ const storedData = await this._storageService.getAllSessionData()
118
+
119
+ if (isSessionActive(storedData.sessionObject, storedData.sessionType === SessionType.CONTINUOUS)) {
120
+ this.session = storedData.sessionObject
121
+ this.sessionId = storedData.sessionId
122
+ this.sessionType = storedData.sessionType || SessionType.PLAIN
123
+ this.sessionState = storedData.sessionState
124
+ } else {
125
+ this.session = null
126
+ this.sessionId = null
127
+ this.sessionState = null
128
+ this.sessionType = SessionType.PLAIN
129
+ }
130
+ } catch (error) {
131
+ console.error('Failed to load stored session data:', error)
132
+ this.session = null
133
+ this.sessionId = null
134
+ this.sessionState = null
135
+ this.sessionType = SessionType.PLAIN
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Initialize the session debugger
141
+ * @param configs - custom configurations for session debugger
142
+ */
143
+ public async init(configs: SessionRecorderOptions): Promise<void> {
144
+ this._configs = getSessionRecorderConfig({ ...this._configs, ...configs })
145
+
146
+ this._isInitialized = true
147
+ this._checkOperation('init')
148
+
149
+ await StorageService.initialize()
150
+
151
+ setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize || DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE)
152
+ setShouldRecordHttpData(!this._configs.captureBody, this._configs.captureHeaders)
153
+
154
+ this._tracer.init(this._configs)
155
+ this._apiService.init(this._configs)
156
+
157
+ if (this._configs.apiKey) {
158
+ this._recorder.init(this._configs)
159
+ }
160
+
161
+ if (this.sessionId && (this.sessionState === SessionState.started || this.sessionState === SessionState.paused)) {
162
+ this._start()
163
+ }
164
+
165
+ }
166
+
167
+ /**
168
+ * Start a new session
169
+ * @param type - the type of session to start
170
+ * @param session - the session to start
171
+ */
172
+ public async start(type: SessionType = SessionType.PLAIN, session?: ISession): Promise<void> {
173
+ this._checkOperation('start')
174
+ // If continuous recording is disabled, force plain mode
175
+ if (type === SessionType.CONTINUOUS && !this._configs?.showContinuousRecording) {
176
+ type = SessionType.PLAIN
177
+ }
178
+ this.sessionType = type
179
+ this._startRequestController = new AbortController()
180
+ if (session) {
181
+ this._setupSessionAndStart(session, true)
182
+ } else {
183
+ await this._createSessionAndStart()
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Stop the current session with an optional comment
189
+ * @param comment - user-provided comment to include in session session attributes
190
+ */
191
+ public async stop(comment?: string): Promise<void> {
192
+ try {
193
+ this._checkOperation('stop')
194
+ this._stop()
195
+ if (this.continuousRecording) {
196
+ await this._apiService.stopContinuousDebugSession(this.sessionId!)
197
+ this.sessionType = SessionType.PLAIN
198
+ } else {
199
+ const request: StopSessionRequest = {
200
+ sessionAttributes: { comment },
201
+ stoppedAt: Date.now(),
202
+ }
203
+ const response = await this._apiService.stopSession(this.sessionId!, request)
204
+ }
205
+ this._clearSession()
206
+ } catch (error: any) {
207
+ this.error = error.message
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Pause the current session
213
+ */
214
+ public async pause(): Promise<void> {
215
+ try {
216
+ this._checkOperation('pause')
217
+ this._pause()
218
+ } catch (error: any) {
219
+ this.error = error.message
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Resume the current session
225
+ */
226
+ public async resume(): Promise<void> {
227
+ try {
228
+ this._checkOperation('resume')
229
+ this._resume()
230
+ } catch (error: any) {
231
+ this.error = error.message
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Cancel the current session
237
+ */
238
+ public async cancel(): Promise<void> {
239
+ try {
240
+ this._checkOperation('cancel')
241
+ this._stop()
242
+ if (this.continuousRecording) {
243
+ await this._apiService.stopContinuousDebugSession(this.sessionId!)
244
+ this.sessionType = SessionType.PLAIN
245
+ } else {
246
+ await this._apiService.cancelSession(this.sessionId!)
247
+ }
248
+ this._clearSession()
249
+ } catch (error: any) {
250
+ this.error = error.message
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Save the continuous recording session
256
+ */
257
+ public async save(): Promise<any> {
258
+ try {
259
+ this._checkOperation('save')
260
+ if (!this.continuousRecording || !this._configs?.showContinuousRecording) {
261
+ return
262
+ }
263
+
264
+ const res = await this._apiService.saveContinuousDebugSession(
265
+ this.sessionId!,
266
+ {
267
+ sessionAttributes: this.sessionAttributes,
268
+ resourceAttributes: getNavigatorInfo(),
269
+ stoppedAt: Date.now(),
270
+ name: this.sessionAttributes.userName
271
+ ? `${this.sessionAttributes.userName}'s session on ${getFormattedDate(
272
+ Date.now(),
273
+ { month: 'short', day: 'numeric' },
274
+ )}`
275
+ : `Session on ${getFormattedDate(Date.now())}`,
276
+ },
277
+ )
278
+
279
+ return res
280
+ } catch (error: any) {
281
+ this.error = error.message
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Set the session attributes
287
+ * @param attributes - the attributes to set
288
+ */
289
+ public setSessionAttributes(attributes: Record<string, any>): void {
290
+ this._sessionAttributes = attributes
291
+ }
292
+
293
+ /**
294
+ * Set a custom click handler for the recording button
295
+ * @param handler - function that will be invoked when the button is clicked
296
+ */
297
+ public set recordingButtonClickHandler(handler: () => boolean | void) {
298
+ // React Native doesn't have HTML elements, so this is a no-op
299
+ }
300
+
301
+ /**
302
+ * @description Check if session should be started/stopped automatically
303
+ * @param {ISession} [sessionPayload]
304
+ * @returns {Promise<void>}
305
+ */
306
+ public async checkRemoteContinuousSession(
307
+ sessionPayload?: Omit<ISession, '_id' | 'shortId'>,
308
+ ): Promise<void> {
309
+ this._checkOperation('autoStartRemoteContinuousSession')
310
+ if (!this._configs?.showContinuousRecording) {
311
+ return
312
+ }
313
+ const payload = {
314
+ sessionAttributes: {
315
+ ...this.sessionAttributes,
316
+ ...(sessionPayload?.sessionAttributes || {}),
317
+ },
318
+ resourceAttributes: {
319
+ ...getNavigatorInfo(),
320
+ ...(sessionPayload?.resourceAttributes || {}),
321
+ },
322
+ }
323
+
324
+ const { state } = await this._apiService.checkRemoteSession(payload)
325
+
326
+ if (state == 'START') {
327
+ if (this.sessionState !== SessionState.started) {
328
+ await this.start(SessionType.CONTINUOUS)
329
+ }
330
+ } else if (state == 'STOP') {
331
+ if (this.sessionState !== SessionState.stopped) {
332
+ await this.stop()
333
+ }
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Create a new session and start it
339
+ */
340
+ private async _createSessionAndStart(): Promise<void> {
341
+ const signal = this._startRequestController?.signal
342
+ try {
343
+ const payload = {
344
+ sessionAttributes: this.sessionAttributes,
345
+ resourceAttributes: getNavigatorInfo(),
346
+ name: this.sessionAttributes.userName
347
+ ? `${this.sessionAttributes.userName}'s session on ${getFormattedDate(Date.now(), { month: 'short', day: 'numeric' })}`
348
+ : `Session on ${getFormattedDate(Date.now())}`,
349
+ }
350
+ const request: StartSessionRequest = !this.continuousRecording ?
351
+ payload : { debugSessionData: payload }
352
+
353
+ const session = this.continuousRecording
354
+ ? await this._apiService.startContinuousDebugSession(request, signal)
355
+ : await this._apiService.startSession(request, signal)
356
+
357
+ if (session) {
358
+ session.sessionType = this.continuousRecording
359
+ ? SessionType.CONTINUOUS
360
+ : SessionType.PLAIN
361
+ this._setupSessionAndStart(session, false)
362
+ }
363
+ } catch (error: any) {
364
+ this.error = error.message
365
+ if (this.continuousRecording) {
366
+ this.sessionType = SessionType.PLAIN
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Start tracing and recording for the session
373
+ */
374
+ private _start(): void {
375
+ this.sessionState = SessionState.started
376
+ if (this.sessionId) {
377
+ this._tracer.start(this.sessionId, this.sessionType)
378
+ this._recorder.start(this.sessionId, this.sessionType)
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Stop tracing and recording for the session
384
+ */
385
+ private _stop(): void {
386
+ this.sessionState = SessionState.stopped
387
+ this._tracer.shutdown()
388
+ this._recorder.stop()
389
+ }
390
+
391
+ /**
392
+ * Pause the session tracing and recording
393
+ */
394
+ private _pause(): void {
395
+ this._tracer.shutdown()
396
+ this._recorder.stop()
397
+ this.sessionState = SessionState.paused
398
+ }
399
+
400
+ /**
401
+ * Resume the session tracing and recording
402
+ */
403
+ private _resume(): void {
404
+ if (this.sessionId) {
405
+ this._tracer.setSessionId(this.sessionId, this.sessionType)
406
+ this._recorder.start(this.sessionId, this.sessionType)
407
+ }
408
+ this.sessionState = SessionState.started
409
+ }
410
+
411
+ private _setupSessionAndStart(session: ISession, configureExporters: boolean = true): void {
412
+ if (configureExporters && session.tempApiKey) {
413
+ this._configs!.apiKey = session.tempApiKey
414
+ this._recorder.init(this._configs!)
415
+ this._tracer.init(this._configs!)
416
+ this._apiService.updateConfigs({ apiKey: this._configs!.apiKey })
417
+ }
418
+
419
+ this._setSession(session)
420
+ this._start()
421
+ }
422
+
423
+ /**
424
+ * Set the session ID in storage
425
+ * @param sessionId - the session ID to set or clear
426
+ */
427
+ private _setSession(
428
+ session: ISession,
429
+ ): void {
430
+ this.session = { ...session, createdAt: session.createdAt || new Date().toISOString() }
431
+ this.sessionId = session?.shortId || session?._id
432
+ }
433
+
434
+ private _clearSession(): void {
435
+ this.session = null
436
+ this.sessionId = null
437
+ this.sessionState = SessionState.stopped
438
+ this._storageService.clearSessionData()
439
+ }
440
+
441
+ /**
442
+ * Check the operation validity based on the session state and action
443
+ * @param action - action being checked ('init', 'start', 'stop', 'cancel', 'pause', 'resume')
444
+ */
445
+ private _checkOperation(
446
+ action:
447
+ | 'init'
448
+ | 'start'
449
+ | 'stop'
450
+ | 'cancel'
451
+ | 'pause'
452
+ | 'resume'
453
+ | 'save'
454
+ | 'autoStartRemoteContinuousSession',
455
+ payload?: any,
456
+ ): void {
457
+ if (!this._isInitialized) {
458
+ throw new Error(
459
+ 'Configuration not initialized. Call init() before performing any actions.',
460
+ )
461
+ }
462
+ switch (action) {
463
+ case 'start':
464
+ if (this.sessionState === SessionState.started) {
465
+ throw new Error('Session is already started.')
466
+ }
467
+ break
468
+ case 'stop':
469
+ if (this.sessionState !== SessionState.paused && this.sessionState !== SessionState.started) {
470
+ throw new Error('Cannot stop. Session is not currently started.')
471
+ }
472
+ break
473
+ case 'cancel':
474
+ if (this.sessionState === SessionState.stopped) {
475
+ throw new Error('Cannot cancel. Session has already been stopped.')
476
+ }
477
+ break
478
+ case 'pause':
479
+ if (this.sessionState !== SessionState.started) {
480
+ throw new Error('Cannot pause. Session is not running.')
481
+ }
482
+ break
483
+ case 'resume':
484
+ if (this.sessionState !== SessionState.paused) {
485
+ throw new Error('Cannot resume. Session is not paused.')
486
+ }
487
+ break
488
+ case 'save':
489
+ if (!this.continuousRecording) {
490
+ throw new Error('Cannot save continuous recording session. Continuous recording is not enabled.')
491
+ }
492
+ if (this.sessionState !== SessionState.started) {
493
+ throw new Error('Cannot save continuous recording session. Session is not started.')
494
+ }
495
+ break
496
+ case 'autoStartRemoteContinuousSession':
497
+ if (this.sessionState !== SessionState.stopped) {
498
+ throw new Error('Cannot start remote continuous session. Session is not stopped.')
499
+ }
500
+ break
501
+ }
502
+ }
503
+
504
+ // Navigation methods
505
+ setNavigationRef(ref: any): void {
506
+ if (this._isInitialized) {
507
+ this._tracer.setNavigationRef(ref)
508
+ this._recorder.setNavigationRef(ref)
509
+ }
510
+ }
511
+
512
+ // Gesture methods
513
+ enableGestureTracking(): void {
514
+ if (this._isInitialized) {
515
+ this._tracer.enableGestureTracking()
516
+ }
517
+ }
518
+
519
+ disableGestureTracking(): void {
520
+ if (this._isInitialized) {
521
+ this._tracer.disableGestureTracking()
522
+ }
523
+ }
524
+
525
+ // Manual recording methods
526
+ recordTap(x: number, y: number, target?: string): void {
527
+ if (this._isInitialized) {
528
+ this._tracer.recordTap(x, y, target)
529
+ }
530
+ }
531
+
532
+ recordSwipe(direction: string, target?: string): void {
533
+ if (this._isInitialized) {
534
+ this._tracer.recordSwipe(direction, target)
535
+ }
536
+ }
537
+
538
+ recordNavigate(routeName: string, params?: Record<string, any>): void {
539
+ if (this._isInitialized) {
540
+ this._tracer.recordNavigate(routeName, params)
541
+ }
542
+ }
543
+
544
+ recordGoBack(): void {
545
+ if (this._isInitialized) {
546
+ this._tracer.recordGoBack()
547
+ }
548
+ }
549
+
550
+ // Session attributes
551
+ setSessionAttribute(key: string, value: any): void {
552
+ if (this._session) {
553
+ if (!this._session.sessionAttributes) {
554
+ this._session.sessionAttributes = {}
555
+ }
556
+ this._session.sessionAttributes[key] = value
557
+ this._session.updatedAt = new Date().toISOString()
558
+ }
559
+ }
560
+
561
+
562
+
563
+
564
+ captureException(error: Error, context?: Record<string, any>): void {
565
+ if (this._isInitialized) {
566
+ this._tracer.captureException(error, context)
567
+ }
568
+ }
569
+
570
+ }
@@ -1,88 +1,2 @@
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
- }
1
+ export * from './session-recorder'
2
+ export * from './session'