@multiplayer-app/session-recorder-react-native 1.3.22 → 1.3.32

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 (49) hide show
  1. package/README.md +22 -13
  2. package/lib/module/config/defaults.js +2 -2
  3. package/lib/module/config/defaults.js.map +1 -1
  4. package/lib/module/config/session-recorder.js.map +1 -1
  5. package/lib/module/otel/CrashBufferSpanProcessor.js +14 -13
  6. package/lib/module/otel/CrashBufferSpanProcessor.js.map +1 -1
  7. package/lib/module/otel/index.js +93 -71
  8. package/lib/module/otel/index.js.map +1 -1
  9. package/lib/module/recorder/index.js +1 -1
  10. package/lib/module/recorder/index.js.map +1 -1
  11. package/lib/module/services/api.service.js.map +1 -1
  12. package/lib/module/services/crashBuffer.service.js +51 -19
  13. package/lib/module/services/crashBuffer.service.js.map +1 -1
  14. package/lib/module/services/socket.service.js +2 -2
  15. package/lib/module/services/socket.service.js.map +1 -1
  16. package/lib/module/session-recorder.js +29 -78
  17. package/lib/module/session-recorder.js.map +1 -1
  18. package/lib/module/types/session-recorder.js.map +1 -1
  19. package/lib/module/utils/rrweb-events.js +1 -1
  20. package/lib/module/utils/rrweb-events.js.map +1 -1
  21. package/lib/typescript/src/config/defaults.d.ts.map +1 -1
  22. package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
  23. package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts +3 -4
  24. package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts.map +1 -1
  25. package/lib/typescript/src/otel/index.d.ts +2 -5
  26. package/lib/typescript/src/otel/index.d.ts.map +1 -1
  27. package/lib/typescript/src/services/api.service.d.ts +2 -1
  28. package/lib/typescript/src/services/api.service.d.ts.map +1 -1
  29. package/lib/typescript/src/services/crashBuffer.service.d.ts +4 -23
  30. package/lib/typescript/src/services/crashBuffer.service.d.ts.map +1 -1
  31. package/lib/typescript/src/services/socket.service.d.ts +4 -1
  32. package/lib/typescript/src/services/socket.service.d.ts.map +1 -1
  33. package/lib/typescript/src/session-recorder.d.ts +0 -3
  34. package/lib/typescript/src/session-recorder.d.ts.map +1 -1
  35. package/lib/typescript/src/types/session-recorder.d.ts +6 -13
  36. package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
  37. package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
  38. package/package.json +2 -2
  39. package/src/config/defaults.ts +29 -29
  40. package/src/config/session-recorder.ts +8 -2
  41. package/src/otel/CrashBufferSpanProcessor.ts +17 -24
  42. package/src/otel/index.ts +124 -76
  43. package/src/recorder/index.ts +1 -1
  44. package/src/services/api.service.ts +2 -1
  45. package/src/services/crashBuffer.service.ts +73 -31
  46. package/src/services/socket.service.ts +5 -2
  47. package/src/session-recorder.ts +43 -98
  48. package/src/types/session-recorder.ts +15 -18
  49. package/src/utils/rrweb-events.ts +3 -3
@@ -180,95 +180,32 @@ class SessionRecorder
180
180
  }
181
181
  }
182
182
 
183
- public async flushBuffer(payload?: { reason?: string }): Promise<any> {
184
- if (!this._configs?.buffering?.enabled) return null;
185
- if (this._isFlushingBuffer) return null;
186
- if (this.sessionState !== SessionState.stopped || this.sessionId)
187
- return null;
188
-
189
- const windowMs = Math.max(
190
- 10_000,
191
- (this._configs.buffering.windowMinutes || 2) * 60 * 1000
192
- );
193
-
194
- this._isFlushingBuffer = true;
195
- try {
196
- const reason = payload?.reason || 'manual';
197
- await this._crashBuffer.setAttrs({
198
- sessionAttributes: this.sessionAttributes,
199
- resourceAttributes: getNavigatorInfo(),
200
- userAttributes: this._userAttributes,
201
- });
202
-
203
- const snapshot = await this._crashBuffer.snapshot(windowMs);
204
- if (
205
- snapshot.rrwebEvents.length === 0 &&
206
- snapshot.otelSpans.length === 0
207
- ) {
208
- return null;
209
- }
210
-
211
- const request: StartSessionRequest = {
212
- name: `${this._configs.application} ${getFormattedDate(new Date())}`,
213
- stoppedAt: new Date().toISOString(),
214
- sessionAttributes: this.sessionAttributes,
215
- resourceAttributes: getNavigatorInfo(),
216
- ...(this._userAttributes
217
- ? { userAttributes: this._userAttributes }
218
- : {}),
219
- debugSessionData: {
220
- meta: {
221
- reason,
222
- windowMs: snapshot.windowMs,
223
- fromTs: snapshot.fromTs,
224
- toTs: snapshot.toTs,
225
- },
226
- events: snapshot.rrwebEvents,
227
- spans: snapshot.otelSpans.map((s) => s.span),
228
- attrs: snapshot.attrs,
229
- },
230
- };
231
-
232
- try {
233
- const res = await this._apiService.startSession(request);
234
- await this._crashBuffer.clear();
235
- return res;
236
- } catch (_e) {
237
- // swallow: flush is best-effort; never throw into app code
238
- return null;
239
- }
240
- } finally {
241
- this._isFlushingBuffer = false;
242
- }
243
- }
244
-
245
183
  private async _flushBuffer(sessionId: string): Promise<any> {
246
- if (!this._configs?.buffering?.enabled) return null;
247
- if (this._isFlushingBuffer) return null;
248
- if (this.sessionState !== SessionState.stopped || this.sessionId)
184
+ if (
185
+ !sessionId ||
186
+ !this._crashBuffer ||
187
+ this._isFlushingBuffer ||
188
+ !this._configs?.buffering?.enabled ||
189
+ this.sessionState !== SessionState.stopped
190
+ ) {
249
191
  return null;
250
-
251
- const windowMs = Math.max(
252
- 10_000,
253
- (this._configs.buffering.windowMinutes || 2) * 60 * 1000
254
- );
192
+ }
255
193
 
256
194
  this._isFlushingBuffer = true;
257
195
  try {
258
- const snapshot = await this._crashBuffer.snapshot(windowMs);
259
- if (
260
- snapshot.rrwebEvents.length === 0 &&
261
- snapshot.otelSpans.length === 0
262
- ) {
196
+ const { events, spans, startedAt, stoppedAt } =
197
+ await this._crashBuffer.snapshot();
198
+ if (events.length === 0 && spans.length === 0) {
263
199
  return null;
264
200
  }
265
- const spans = snapshot.otelSpans.map((s) => s.span);
266
- const events = snapshot.rrwebEvents.map((e) => e.event);
267
201
  await Promise.all([
268
- this._tracer.exportTraces(spans),
269
- this._apiService.exportEvents(sessionId, { events }),
202
+ this._tracer.exportTraces(spans.map((s) => s.span)),
203
+ this._apiService.exportEvents(sessionId, {
204
+ events: events.map((e) => e.event),
205
+ }),
270
206
  this._apiService.updateSessionAttributes(sessionId, {
271
- name: this._getSessionName(),
207
+ startedAt: new Date(startedAt).toISOString(),
208
+ stoppedAt: new Date(stoppedAt).toISOString(),
272
209
  sessionAttributes: this.sessionAttributes,
273
210
  resourceAttributes: getNavigatorInfo(),
274
211
  userAttributes: this._userAttributes || undefined,
@@ -285,7 +222,7 @@ class SessionRecorder
285
222
  private async _createExceptionSession(span: any): Promise<void> {
286
223
  try {
287
224
  const session = await this._apiService.createErrorSession({ span });
288
- if (session) {
225
+ if (session?._id) {
289
226
  void this._flushBuffer(session._id);
290
227
  }
291
228
  } catch (_ignored) {
@@ -341,7 +278,7 @@ class SessionRecorder
341
278
  const bufferEnabled = Boolean(this._configs.buffering?.enabled);
342
279
  const windowMs = Math.max(
343
280
  10_000,
344
- (this._configs.buffering?.windowMinutes || 2) * 60 * 1000
281
+ (this._configs.buffering?.windowMinutes || 0.5) * 60 * 1000
345
282
  );
346
283
  this._tracer.setCrashBuffer(
347
284
  bufferEnabled ? this._crashBuffer : undefined,
@@ -365,8 +302,13 @@ class SessionRecorder
365
302
  );
366
303
 
367
304
  this._crashBuffer.on('error-span-appended', (payload) => {
368
- if (this.sessionState !== SessionState.stopped || this.sessionId) return;
369
- if (!payload.span) return;
305
+ if (
306
+ !payload.span ||
307
+ this.sessionId ||
308
+ this.sessionState !== SessionState.stopped
309
+ ) {
310
+ return;
311
+ }
370
312
  this._createExceptionSession(payload.span);
371
313
  });
372
314
 
@@ -387,21 +329,20 @@ class SessionRecorder
387
329
  }
388
330
 
389
331
  private _startBufferOnlyRecording(): void {
390
- if (!this._configs?.buffering?.enabled) return;
391
- if (this.sessionState !== SessionState.stopped || this.sessionId) return;
332
+ if (
333
+ this.sessionId ||
334
+ !this._crashBuffer ||
335
+ !this._configs?.buffering?.enabled ||
336
+ this.sessionState !== SessionState.stopped
337
+ ) {
338
+ return;
339
+ }
392
340
 
393
341
  const windowMs = Math.max(
394
342
  10_000,
395
- (this._configs.buffering.windowMinutes || 2) * 60 * 1000
343
+ (this._configs.buffering.windowMinutes || 0.5) * 60 * 1000
396
344
  );
397
345
 
398
- // Best-effort: persist current attrs so flush has context.
399
- this._crashBuffer.setAttrs({
400
- sessionAttributes: this.sessionAttributes,
401
- resourceAttributes: getNavigatorInfo(),
402
- userAttributes: this._userAttributes,
403
- });
404
-
405
346
  // Wire buffer into tracer + recorder (only used when sessionId is null).
406
347
  this._tracer.setCrashBuffer(this._crashBuffer, windowMs);
407
348
  this._recorder.init(this._configs, this._socketService, this._crashBuffer, {
@@ -448,9 +389,7 @@ class SessionRecorder
448
389
  });
449
390
 
450
391
  this._socketService.on(SESSION_SAVE_BUFFER_EVENT, (payload: any) => {
451
- if (payload?.debugSession?._id) {
452
- void this._flushBuffer(payload.debugSession._id);
453
- }
392
+ this._flushBuffer(payload?.debugSession?._id);
454
393
  });
455
394
  }
456
395
 
@@ -628,7 +567,13 @@ class SessionRecorder
628
567
  return;
629
568
  }
630
569
  this._userAttributes = userAttributes;
631
- this._socketService.setUser(this._userAttributes);
570
+
571
+ const data = {
572
+ userAttributes: this._userAttributes,
573
+ clientId: this._tracer.clientId,
574
+ };
575
+
576
+ this._socketService.setUser(data);
632
577
  }
633
578
 
634
579
  /**
@@ -1,8 +1,11 @@
1
1
  import { type Span } from '@opentelemetry/api';
2
- import { type ISession, type SessionType, type IUserAttributes } from '@multiplayer-app/session-recorder-common';
2
+ import {
3
+ type ISession,
4
+ type SessionType,
5
+ type IUserAttributes,
6
+ } from '@multiplayer-app/session-recorder-common';
3
7
  import { type PropagateTraceHeaderCorsUrls } from '@opentelemetry/sdk-trace-web';
4
8
 
5
-
6
9
  // WidgetButtonPlacement moved to configs.ts
7
10
 
8
11
  export enum SessionState {
@@ -157,11 +160,11 @@ export interface SessionRecorderOptions {
157
160
  };
158
161
 
159
162
  /**
160
- * @description
161
- * If true, webSocket will be used to manage remote recording sessions.
162
- * @default true
163
- */
164
- useWebsocket?: boolean
163
+ * @description
164
+ * If true, webSocket will be used to manage remote recording sessions.
165
+ * @default true
166
+ */
167
+ useWebsocket?: boolean;
165
168
 
166
169
  /**
167
170
  * (Optional) Client-side crash buffer configuration.
@@ -169,11 +172,11 @@ export interface SessionRecorderOptions {
169
172
  * even if the user did not start a manual/continuous recording.
170
173
  */
171
174
  buffering?: {
172
- /** Enable/disable buffering. @default true */
173
- enabled?: boolean
174
- /** Rolling window size (minutes). @default 1 */
175
- windowMinutes?: number
176
- }
175
+ /** Enable/disable buffering. @default false */
176
+ enabled?: boolean;
177
+ /** Rolling window size (minutes). @default 0.5 */
178
+ windowMinutes?: number;
179
+ };
177
180
  }
178
181
 
179
182
  /**
@@ -361,12 +364,6 @@ export interface ISessionRecorder {
361
364
  * Capture an exception and send it as an error trace
362
365
  */
363
366
  captureException(error: unknown, errorInfo?: Record<string, any>): void;
364
-
365
- /**
366
- * Flush the local crash buffer by creating a debug session and uploading buffered data.
367
- * No-op if a live recording is currently active.
368
- */
369
- flushBuffer(payload?: { reason?: string }): Promise<any>;
370
367
  }
371
368
 
372
369
  /**
@@ -1,11 +1,11 @@
1
1
  import { Dimensions } from 'react-native';
2
2
  import {
3
- EventType,
4
- type eventWithTime,
5
3
  NodeType,
6
- type serializedNodeWithId,
4
+ EventType,
7
5
  IncrementalSource,
8
6
  type mutationData,
7
+ type eventWithTime,
8
+ type serializedNodeWithId,
9
9
  } from '@rrweb/types';
10
10
  import { getAppMetadata } from './platform';
11
11