@multiplayer-app/session-recorder-react-native 1.3.16 → 1.3.21
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/lib/module/config/constants.js +3 -0
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js +5 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/session-recorder.js +5 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/otel/CrashBufferSpanProcessor.js +41 -0
- package/lib/module/otel/CrashBufferSpanProcessor.js.map +1 -0
- package/lib/module/otel/index.js +28 -9
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/recorder/index.js +15 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/services/api.service.js +24 -2
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/crashBuffer.service.js +248 -0
- package/lib/module/services/crashBuffer.service.js.map +1 -0
- package/lib/module/services/socket.service.js +9 -2
- package/lib/module/services/socket.service.js.map +1 -1
- package/lib/module/session-recorder.js +152 -6
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/typescript/src/config/constants.d.ts +1 -0
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts +18 -0
- package/lib/typescript/src/otel/CrashBufferSpanProcessor.d.ts.map +1 -0
- package/lib/typescript/src/otel/index.d.ts +8 -0
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts +8 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts +27 -2
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/crashBuffer.service.d.ts +46 -0
- package/lib/typescript/src/services/crashBuffer.service.d.ts.map +1 -0
- package/lib/typescript/src/services/socket.service.d.ts +4 -3
- package/lib/typescript/src/services/socket.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts +8 -0
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +18 -0
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/config/constants.ts +3 -0
- package/src/config/defaults.ts +5 -0
- package/src/config/session-recorder.ts +5 -0
- package/src/otel/CrashBufferSpanProcessor.ts +61 -0
- package/src/otel/index.ts +90 -34
- package/src/recorder/index.ts +30 -3
- package/src/services/api.service.ts +68 -13
- package/src/services/crashBuffer.service.ts +327 -0
- package/src/services/socket.service.ts +36 -22
- package/src/session-recorder.ts +226 -19
- package/src/types/session-recorder.ts +18 -0
package/src/session-recorder.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionType,
|
|
3
3
|
type ISession,
|
|
4
|
-
type IUserAttributes
|
|
4
|
+
type IUserAttributes,
|
|
5
5
|
} from '@multiplayer-app/session-recorder-common';
|
|
6
6
|
import { Observable } from 'lib0/observable';
|
|
7
7
|
import { type eventWithTime } from '@rrweb/types';
|
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
SESSION_STOPPED_EVENT,
|
|
22
22
|
REMOTE_SESSION_RECORDING_START,
|
|
23
|
-
REMOTE_SESSION_RECORDING_STOP
|
|
23
|
+
REMOTE_SESSION_RECORDING_STOP,
|
|
24
|
+
SESSION_SAVE_BUFFER_EVENT,
|
|
24
25
|
} from './config';
|
|
25
26
|
import { getFormattedDate, isSessionActive, getNavigatorInfo } from './utils';
|
|
26
27
|
import {
|
|
@@ -31,6 +32,7 @@ import { BASE_CONFIG, getSessionRecorderConfig } from './config';
|
|
|
31
32
|
|
|
32
33
|
import { StorageService } from './services/storage.service';
|
|
33
34
|
import { NetworkService } from './services/network.service';
|
|
35
|
+
import { CrashBufferService } from './services/crashBuffer.service';
|
|
34
36
|
import {
|
|
35
37
|
ApiService,
|
|
36
38
|
type StartSessionRequest,
|
|
@@ -42,7 +44,8 @@ type SessionRecorderEvents = 'state-change' | 'init';
|
|
|
42
44
|
|
|
43
45
|
class SessionRecorder
|
|
44
46
|
extends Observable<SessionRecorderEvents>
|
|
45
|
-
implements ISessionRecorder, EventRecorder
|
|
47
|
+
implements ISessionRecorder, EventRecorder
|
|
48
|
+
{
|
|
46
49
|
private _configs: SessionRecorderConfigs;
|
|
47
50
|
private _apiService = new ApiService();
|
|
48
51
|
private _socketService = new SocketService();
|
|
@@ -50,6 +53,8 @@ class SessionRecorder
|
|
|
50
53
|
private _recorder = new RecorderReactNativeSDK();
|
|
51
54
|
private _storageService = StorageService.getInstance();
|
|
52
55
|
private _networkService = NetworkService.getInstance();
|
|
56
|
+
private _crashBuffer = CrashBufferService.getInstance();
|
|
57
|
+
private _isFlushingBuffer: boolean = false;
|
|
53
58
|
private _startRequestController: AbortController | null = null;
|
|
54
59
|
|
|
55
60
|
// Whether the session recorder is initialized
|
|
@@ -162,7 +167,10 @@ class SessionRecorder
|
|
|
162
167
|
/**
|
|
163
168
|
* Capture an exception manually and send it as an error trace.
|
|
164
169
|
*/
|
|
165
|
-
public captureException(
|
|
170
|
+
public captureException(
|
|
171
|
+
error: unknown,
|
|
172
|
+
errorInfo?: Record<string, any>
|
|
173
|
+
): void {
|
|
166
174
|
try {
|
|
167
175
|
const normalizedError = this._normalizeError(error);
|
|
168
176
|
const normalizedErrorInfo = this._normalizeErrorInfo(errorInfo);
|
|
@@ -172,6 +180,119 @@ class SessionRecorder
|
|
|
172
180
|
}
|
|
173
181
|
}
|
|
174
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
|
+
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)
|
|
249
|
+
return null;
|
|
250
|
+
|
|
251
|
+
const windowMs = Math.max(
|
|
252
|
+
10_000,
|
|
253
|
+
(this._configs.buffering.windowMinutes || 2) * 60 * 1000
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
this._isFlushingBuffer = true;
|
|
257
|
+
try {
|
|
258
|
+
const snapshot = await this._crashBuffer.snapshot(windowMs);
|
|
259
|
+
if (
|
|
260
|
+
snapshot.rrwebEvents.length === 0 &&
|
|
261
|
+
snapshot.otelSpans.length === 0
|
|
262
|
+
) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const spans = snapshot.otelSpans.map((s) => s.span);
|
|
266
|
+
const events = snapshot.rrwebEvents.map((e) => e.event);
|
|
267
|
+
await Promise.all([
|
|
268
|
+
this._tracer.exportTraces(spans),
|
|
269
|
+
this._apiService.exportEvents(sessionId, { events }),
|
|
270
|
+
this._apiService.updateSessionAttributes(sessionId, {
|
|
271
|
+
name: this._getSessionName(),
|
|
272
|
+
sessionAttributes: this.sessionAttributes,
|
|
273
|
+
resourceAttributes: getNavigatorInfo(),
|
|
274
|
+
userAttributes: this._userAttributes || undefined,
|
|
275
|
+
}),
|
|
276
|
+
]);
|
|
277
|
+
} catch (_e) {
|
|
278
|
+
// swallow: flush is best-effort; never throw into app code
|
|
279
|
+
} finally {
|
|
280
|
+
await this._crashBuffer.clear();
|
|
281
|
+
this._isFlushingBuffer = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private async _createExceptionSession(span: any): Promise<void> {
|
|
286
|
+
try {
|
|
287
|
+
const session = await this._apiService.createErrorSession({ span });
|
|
288
|
+
if (session) {
|
|
289
|
+
void this._flushBuffer(session._id);
|
|
290
|
+
}
|
|
291
|
+
} catch (_ignored) {
|
|
292
|
+
// best-effort
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
175
296
|
private async _loadStoredSessionData(): Promise<void> {
|
|
176
297
|
try {
|
|
177
298
|
await StorageService.initialize();
|
|
@@ -216,14 +337,39 @@ class SessionRecorder
|
|
|
216
337
|
this._configs.captureHeaders
|
|
217
338
|
);
|
|
218
339
|
|
|
340
|
+
// Crash buffer wiring (RN): set BEFORE tracer init so early spans don't get exported.
|
|
341
|
+
const bufferEnabled = Boolean(this._configs.buffering?.enabled);
|
|
342
|
+
const windowMs = Math.max(
|
|
343
|
+
10_000,
|
|
344
|
+
(this._configs.buffering?.windowMinutes || 2) * 60 * 1000
|
|
345
|
+
);
|
|
346
|
+
this._tracer.setCrashBuffer(
|
|
347
|
+
bufferEnabled ? this._crashBuffer : undefined,
|
|
348
|
+
windowMs
|
|
349
|
+
);
|
|
350
|
+
|
|
219
351
|
this._tracer.init(this._configs);
|
|
220
352
|
this._apiService.init(this._configs);
|
|
221
353
|
this._socketService.init({
|
|
222
354
|
apiKey: this._configs.apiKey,
|
|
223
355
|
socketUrl: this._configs.apiBaseUrl,
|
|
224
|
-
keepAlive: this._configs.useWebsocket
|
|
356
|
+
keepAlive: this._configs.useWebsocket,
|
|
357
|
+
clientId: this._tracer.clientId,
|
|
225
358
|
});
|
|
226
|
-
|
|
359
|
+
|
|
360
|
+
this._recorder.init(
|
|
361
|
+
this._configs,
|
|
362
|
+
this._socketService,
|
|
363
|
+
bufferEnabled ? this._crashBuffer : undefined,
|
|
364
|
+
{ enabled: bufferEnabled, windowMs }
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
this._crashBuffer.on('error-span-appended', (payload) => {
|
|
368
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return;
|
|
369
|
+
if (!payload.span) return;
|
|
370
|
+
this._createExceptionSession(payload.span);
|
|
371
|
+
});
|
|
372
|
+
|
|
227
373
|
await this._networkService.init();
|
|
228
374
|
this._setupNetworkCallbacks();
|
|
229
375
|
this._registerSocketServiceListeners();
|
|
@@ -234,10 +380,42 @@ class SessionRecorder
|
|
|
234
380
|
this.sessionState === SessionState.paused)
|
|
235
381
|
) {
|
|
236
382
|
this._start();
|
|
383
|
+
} else {
|
|
384
|
+
this._startBufferOnlyRecording();
|
|
237
385
|
}
|
|
238
386
|
this.emit('init', []);
|
|
239
387
|
}
|
|
240
388
|
|
|
389
|
+
private _startBufferOnlyRecording(): void {
|
|
390
|
+
if (!this._configs?.buffering?.enabled) return;
|
|
391
|
+
if (this.sessionState !== SessionState.stopped || this.sessionId) return;
|
|
392
|
+
|
|
393
|
+
const windowMs = Math.max(
|
|
394
|
+
10_000,
|
|
395
|
+
(this._configs.buffering.windowMinutes || 2) * 60 * 1000
|
|
396
|
+
);
|
|
397
|
+
|
|
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
|
+
// Wire buffer into tracer + recorder (only used when sessionId is null).
|
|
406
|
+
this._tracer.setCrashBuffer(this._crashBuffer, windowMs);
|
|
407
|
+
this._recorder.init(this._configs, this._socketService, this._crashBuffer, {
|
|
408
|
+
enabled: true,
|
|
409
|
+
windowMs,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Start capturing events without an active debug session id.
|
|
413
|
+
try {
|
|
414
|
+
this._recorder.stop();
|
|
415
|
+
} catch (_e) {}
|
|
416
|
+
this._recorder.start(null, SessionType.MANUAL);
|
|
417
|
+
}
|
|
418
|
+
|
|
241
419
|
/**
|
|
242
420
|
* Register socket service event listeners
|
|
243
421
|
*/
|
|
@@ -248,18 +426,32 @@ class SessionRecorder
|
|
|
248
426
|
});
|
|
249
427
|
|
|
250
428
|
this._socketService.on(REMOTE_SESSION_RECORDING_START, (payload: any) => {
|
|
251
|
-
logger.info(
|
|
429
|
+
logger.info(
|
|
430
|
+
'SessionRecorder',
|
|
431
|
+
'Remote session recording started',
|
|
432
|
+
payload
|
|
433
|
+
);
|
|
252
434
|
if (this.sessionState === SessionState.stopped) {
|
|
253
435
|
this.start();
|
|
254
436
|
}
|
|
255
437
|
});
|
|
256
438
|
|
|
257
439
|
this._socketService.on(REMOTE_SESSION_RECORDING_STOP, (payload: any) => {
|
|
258
|
-
logger.info(
|
|
440
|
+
logger.info(
|
|
441
|
+
'SessionRecorder',
|
|
442
|
+
'Remote session recording stopped',
|
|
443
|
+
payload
|
|
444
|
+
);
|
|
259
445
|
if (this.sessionState !== SessionState.stopped) {
|
|
260
446
|
this.stop();
|
|
261
447
|
}
|
|
262
448
|
});
|
|
449
|
+
|
|
450
|
+
this._socketService.on(SESSION_SAVE_BUFFER_EVENT, (payload: any) => {
|
|
451
|
+
if (payload?.debugSession?._id) {
|
|
452
|
+
void this._flushBuffer(payload.debugSession._id);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
263
455
|
}
|
|
264
456
|
|
|
265
457
|
/**
|
|
@@ -409,7 +601,7 @@ class SessionRecorder
|
|
|
409
601
|
sessionAttributes: this.sessionAttributes,
|
|
410
602
|
resourceAttributes: getNavigatorInfo(),
|
|
411
603
|
stoppedAt: Date.now(),
|
|
412
|
-
name: this._getSessionName()
|
|
604
|
+
name: this._getSessionName(),
|
|
413
605
|
}
|
|
414
606
|
);
|
|
415
607
|
|
|
@@ -486,7 +678,9 @@ class SessionRecorder
|
|
|
486
678
|
sessionAttributes: this.sessionAttributes,
|
|
487
679
|
resourceAttributes: getNavigatorInfo(),
|
|
488
680
|
name: this._getSessionName(),
|
|
489
|
-
...(this._userAttributes
|
|
681
|
+
...(this._userAttributes
|
|
682
|
+
? { userAttributes: this._userAttributes }
|
|
683
|
+
: {}),
|
|
490
684
|
};
|
|
491
685
|
const request: StartSessionRequest = !this.continuousRecording
|
|
492
686
|
? payload
|
|
@@ -519,6 +713,10 @@ class SessionRecorder
|
|
|
519
713
|
this.sessionType = this.sessionType;
|
|
520
714
|
|
|
521
715
|
if (this.sessionId) {
|
|
716
|
+
// Switch from buffer-only recording to session recording cleanly.
|
|
717
|
+
try {
|
|
718
|
+
this._recorder.stop();
|
|
719
|
+
} catch (_e) {}
|
|
522
720
|
this._tracer.start(this.sessionId, this.sessionType);
|
|
523
721
|
this._recorder.start(this.sessionId, this.sessionType);
|
|
524
722
|
if (this.session) {
|
|
@@ -533,15 +731,16 @@ class SessionRecorder
|
|
|
533
731
|
private _stop(): void {
|
|
534
732
|
this.sessionState = SessionState.stopped;
|
|
535
733
|
this._socketService.unsubscribeFromSession(true);
|
|
536
|
-
this._tracer.
|
|
734
|
+
this._tracer.stop();
|
|
537
735
|
this._recorder.stop();
|
|
736
|
+
this._startBufferOnlyRecording();
|
|
538
737
|
}
|
|
539
738
|
|
|
540
739
|
/**
|
|
541
740
|
* Pause the session tracing and recording
|
|
542
741
|
*/
|
|
543
742
|
private _pause(): void {
|
|
544
|
-
this._tracer.
|
|
743
|
+
this._tracer.stop();
|
|
545
744
|
this._recorder.stop();
|
|
546
745
|
this.sessionState = SessionState.paused;
|
|
547
746
|
}
|
|
@@ -551,7 +750,10 @@ class SessionRecorder
|
|
|
551
750
|
*/
|
|
552
751
|
private _resume(): void {
|
|
553
752
|
if (this.sessionId) {
|
|
554
|
-
this._tracer.
|
|
753
|
+
this._tracer.start(this.sessionId, this.sessionType);
|
|
754
|
+
try {
|
|
755
|
+
this._recorder.stop();
|
|
756
|
+
} catch (_e) {}
|
|
555
757
|
this._recorder.start(this.sessionId, this.sessionType);
|
|
556
758
|
}
|
|
557
759
|
this.sessionState = SessionState.started;
|
|
@@ -729,18 +931,19 @@ class SessionRecorder
|
|
|
729
931
|
}
|
|
730
932
|
}
|
|
731
933
|
|
|
732
|
-
|
|
733
934
|
/**
|
|
734
935
|
* Normalize an error info object to a Record<string, any>
|
|
735
936
|
* @param errorInfo - the error info to normalize
|
|
736
937
|
* @returns the normalized error info
|
|
737
938
|
*/
|
|
738
|
-
private _normalizeErrorInfo(
|
|
739
|
-
|
|
939
|
+
private _normalizeErrorInfo(
|
|
940
|
+
errorInfo?: Record<string, any>
|
|
941
|
+
): Record<string, any> {
|
|
942
|
+
if (!errorInfo) return {};
|
|
740
943
|
try {
|
|
741
|
-
return JSON.parse(JSON.stringify(errorInfo))
|
|
944
|
+
return JSON.parse(JSON.stringify(errorInfo));
|
|
742
945
|
} catch (_e) {
|
|
743
|
-
return { errorInfo: String(errorInfo) }
|
|
946
|
+
return { errorInfo: String(errorInfo) };
|
|
744
947
|
}
|
|
745
948
|
}
|
|
746
949
|
|
|
@@ -749,7 +952,11 @@ class SessionRecorder
|
|
|
749
952
|
* @returns the session name
|
|
750
953
|
*/
|
|
751
954
|
private _getSessionName(date: Date = new Date()): string {
|
|
752
|
-
const userName =
|
|
955
|
+
const userName =
|
|
956
|
+
this.sessionAttributes?.userName ||
|
|
957
|
+
this._userAttributes?.userName ||
|
|
958
|
+
this._userAttributes?.name ||
|
|
959
|
+
'';
|
|
753
960
|
return userName
|
|
754
961
|
? `${userName}'s session on ${getFormattedDate(date, { month: 'short', day: 'numeric' })}`
|
|
755
962
|
: `Session on ${getFormattedDate(date)}`;
|
|
@@ -162,6 +162,18 @@ export interface SessionRecorderOptions {
|
|
|
162
162
|
* @default true
|
|
163
163
|
*/
|
|
164
164
|
useWebsocket?: boolean
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* (Optional) Client-side crash buffer configuration.
|
|
168
|
+
* When enabled, the SDK keeps a rolling window of recent events + traces
|
|
169
|
+
* even if the user did not start a manual/continuous recording.
|
|
170
|
+
*/
|
|
171
|
+
buffering?: {
|
|
172
|
+
/** Enable/disable buffering. @default true */
|
|
173
|
+
enabled?: boolean
|
|
174
|
+
/** Rolling window size (minutes). @default 1 */
|
|
175
|
+
windowMinutes?: number
|
|
176
|
+
}
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
/**
|
|
@@ -349,6 +361,12 @@ export interface ISessionRecorder {
|
|
|
349
361
|
* Capture an exception and send it as an error trace
|
|
350
362
|
*/
|
|
351
363
|
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>;
|
|
352
370
|
}
|
|
353
371
|
|
|
354
372
|
/**
|