@multiplayer-app/session-recorder-browser 2.0.85 → 2.0.88

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.
@@ -1,15 +1,15 @@
1
1
  import { Observable } from './observable';
2
- import { SessionType } from '@multiplayer-app/session-recorder-common';
2
+ import { SessionType, } from '@multiplayer-app/session-recorder-common';
3
3
  import { TracerBrowserSDK } from './otel';
4
4
  import { RecorderBrowserSDK } from './rrweb';
5
5
  import { getStoredItem, setStoredItem, getNavigatorInfo, getFormattedDate, getTimeDifferenceInSeconds, isSessionActive, getOrCreateTabId, } from './utils';
6
- import { SessionState } from './types';
7
- import { BASE_CONFIG, SESSION_PROP_NAME, SESSION_ID_PROP_NAME, SESSION_TYPE_PROP_NAME, SESSION_STATE_PROP_NAME, DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT, SESSION_READY_EVENT, } from './config';
8
- import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize } from './patch';
6
+ import { SessionState, } from './types';
7
+ import { BASE_CONFIG, SESSION_PROP_NAME, SESSION_ID_PROP_NAME, SESSION_TYPE_PROP_NAME, SESSION_STATE_PROP_NAME, DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_SAVE_BUFFER_EVENT, SESSION_READY_EVENT_LEGACY, SESSION_READY_EVENT, } from './config';
8
+ import { setShouldRecordHttpData, setMaxCapturingHttpPayloadSize, } from './patch';
9
9
  import { recorderEventBus } from './eventBus';
10
10
  import { SessionWidget } from './sessionWidget';
11
11
  import messagingService from './services/messaging.service';
12
- import { ApiService } from './services/api.service';
12
+ import { ApiService, } from './services/api.service';
13
13
  import { SocketService } from './services/socket.service';
14
14
  import { IndexedDBService } from './services/indexedDb.service';
15
15
  import { CrashBufferService } from './services/crashBuffer.service';
@@ -51,7 +51,10 @@ export class SessionRecorder extends Observable {
51
51
  messagingService.sendMessage('state-change', this._sessionState);
52
52
  setStoredItem(SESSION_STATE_PROP_NAME, state);
53
53
  // Emit observable event to support React wrapper
54
- this.emit('state-change', [this._sessionState || SessionState.stopped, this.sessionType]);
54
+ this.emit('state-change', [
55
+ this._sessionState || SessionState.stopped,
56
+ this.sessionType,
57
+ ]);
55
58
  }
56
59
  get session() {
57
60
  return this._session;
@@ -122,10 +125,18 @@ export class SessionRecorder extends Observable {
122
125
  this._error = '';
123
126
  // Safety: avoid accessing storage in SSR/non-browser environments
124
127
  const isBrowser = typeof window !== 'undefined';
125
- const sessionLocal = isBrowser ? getStoredItem(SESSION_PROP_NAME, true) : null;
126
- const sessionIdLocal = isBrowser ? getStoredItem(SESSION_ID_PROP_NAME) : null;
127
- const sessionStateLocal = isBrowser ? getStoredItem(SESSION_STATE_PROP_NAME) : null;
128
- const sessionTypeLocal = isBrowser ? getStoredItem(SESSION_TYPE_PROP_NAME) : null;
128
+ const sessionLocal = isBrowser
129
+ ? getStoredItem(SESSION_PROP_NAME, true)
130
+ : null;
131
+ const sessionIdLocal = isBrowser
132
+ ? getStoredItem(SESSION_ID_PROP_NAME)
133
+ : null;
134
+ const sessionStateLocal = isBrowser
135
+ ? getStoredItem(SESSION_STATE_PROP_NAME)
136
+ : null;
137
+ const sessionTypeLocal = isBrowser
138
+ ? getStoredItem(SESSION_TYPE_PROP_NAME)
139
+ : null;
129
140
  if (isSessionActive(sessionLocal, sessionTypeLocal)) {
130
141
  this.session = sessionLocal;
131
142
  this.sessionId = sessionIdLocal;
@@ -157,7 +168,8 @@ export class SessionRecorder extends Observable {
157
168
  // GC: remove orphaned crash buffers from old tabs.
158
169
  // Keep TTL large to avoid any accidental data loss.
159
170
  void this._bufferDb.sweepStaleTabs(10 * 60 * 60 * 1000);
160
- setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize || DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
171
+ setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize ||
172
+ DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE);
161
173
  setShouldRecordHttpData(this._configs.captureBody, this._configs.captureHeaders);
162
174
  this._setupCrashBuffer();
163
175
  this._tracer.init(this._configs);
@@ -179,7 +191,9 @@ export class SessionRecorder extends Observable {
179
191
  if (this._configs.apiKey) {
180
192
  this._recorder.init(this._configs, this._socketService);
181
193
  }
182
- if (this.sessionId && (this.sessionState === SessionState.started || this.sessionState === SessionState.paused)) {
194
+ if (this.sessionId &&
195
+ (this.sessionState === SessionState.started ||
196
+ this.sessionState === SessionState.paused)) {
183
197
  this._start();
184
198
  }
185
199
  else {
@@ -233,7 +247,8 @@ export class SessionRecorder extends Observable {
233
247
  return;
234
248
  let hasFocus = true;
235
249
  try {
236
- hasFocus = typeof document.hasFocus === 'function' ? document.hasFocus() : true;
250
+ hasFocus =
251
+ typeof document.hasFocus === 'function' ? document.hasFocus() : true;
237
252
  }
238
253
  catch (_e) {
239
254
  hasFocus = true;
@@ -255,7 +270,9 @@ export class SessionRecorder extends Observable {
255
270
  // stop, leaving the buffer with no FullSnapshot and silently breaking
256
271
  // exception-triggered flushBuffer. `_recorder.restart(null, ...)` passes
257
272
  // null explicitly, so it's safe regardless of `this.sessionId`.
258
- if (!this._crashBuffer || !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) || this.sessionState !== SessionState.stopped) {
273
+ if (!this._crashBuffer ||
274
+ !((_b = (_a = this._configs) === null || _a === void 0 ? void 0 : _a.buffering) === null || _b === void 0 ? void 0 : _b.enabled) ||
275
+ this.sessionState !== SessionState.stopped) {
259
276
  return;
260
277
  }
261
278
  void this._recorder.restart(null, SessionType.MANUAL);
@@ -306,7 +323,8 @@ export class SessionRecorder extends Observable {
306
323
  start(type = SessionType.MANUAL, session) {
307
324
  this._checkOperation('start');
308
325
  // If continuous recording is disabled, force plain mode
309
- if (type === SessionType.CONTINUOUS && !this._configs.showContinuousRecording) {
326
+ if (type === SessionType.CONTINUOUS &&
327
+ !this._configs.showContinuousRecording) {
310
328
  type = SessionType.MANUAL;
311
329
  }
312
330
  this.sessionType = type;
@@ -337,7 +355,8 @@ export class SessionRecorder extends Observable {
337
355
  stoppedAt: this._recorder.stoppedAt,
338
356
  };
339
357
  const response = await this._apiService.stopSession(sid, request);
340
- recorderEventBus.emit(SESSION_READY_EVENT, response._id);
358
+ recorderEventBus.emit(SESSION_READY_EVENT_LEGACY, response._id);
359
+ recorderEventBus.emit(SESSION_READY_EVENT, response);
341
360
  }
342
361
  }
343
362
  catch (error) {
@@ -433,8 +452,8 @@ export class SessionRecorder extends Observable {
433
452
  this.error = (e === null || e === void 0 ? void 0 : e.message) || 'Failed to capture exception';
434
453
  }
435
454
  }
436
- async _flushBuffer(sessionId, force = false) {
437
- if (!sessionId || !this._crashBuffer || this._isFlushingBuffer) {
455
+ async _flushBuffer(session, force = false) {
456
+ if (!session || !this._crashBuffer || this._isFlushingBuffer) {
438
457
  return null;
439
458
  }
440
459
  this._isFlushingBuffer = true;
@@ -445,8 +464,10 @@ export class SessionRecorder extends Observable {
445
464
  }
446
465
  await Promise.all([
447
466
  this._tracer.exportTraces(spans.map((s) => s.span)),
448
- this._apiService.exportEvents(sessionId, { events: events.map((e) => e.event) }),
449
- this._apiService.updateSessionAttributes(sessionId, {
467
+ this._apiService.exportEvents(session._id, {
468
+ events: events.map((e) => e.event),
469
+ }),
470
+ this._apiService.updateSessionAttributes(session._id, {
450
471
  startedAt: this._toCrashBufferSessionIso(startedAt),
451
472
  stoppedAt: this._toCrashBufferSessionIso(stoppedAt),
452
473
  sessionAttributes: this.sessionAttributes,
@@ -461,7 +482,8 @@ export class SessionRecorder extends Observable {
461
482
  finally {
462
483
  await this._crashBuffer.clear();
463
484
  this._isFlushingBuffer = false;
464
- recorderEventBus.emit(SESSION_READY_EVENT, sessionId);
485
+ recorderEventBus.emit(SESSION_READY_EVENT_LEGACY, session._id);
486
+ recorderEventBus.emit(SESSION_READY_EVENT, session);
465
487
  }
466
488
  }
467
489
  _toCrashBufferSessionIso(ts) {
@@ -550,7 +572,8 @@ export class SessionRecorder extends Observable {
550
572
  * Handle the safe stop event
551
573
  */
552
574
  _handleStop(comment) {
553
- if (this.sessionState === SessionState.started || this.sessionState === SessionState.paused) {
575
+ if (this.sessionState === SessionState.started ||
576
+ this.sessionState === SessionState.paused) {
554
577
  this.stop(comment);
555
578
  }
556
579
  }
@@ -574,7 +597,8 @@ export class SessionRecorder extends Observable {
574
597
  * Handle the safe cancel event
575
598
  */
576
599
  _handleCancel() {
577
- if (this.sessionState === SessionState.started || this.sessionState === SessionState.paused) {
600
+ if (this.sessionState === SessionState.started ||
601
+ this.sessionState === SessionState.paused) {
578
602
  this.cancel();
579
603
  }
580
604
  }
@@ -582,7 +606,8 @@ export class SessionRecorder extends Observable {
582
606
  * Handle the safe save event
583
607
  */
584
608
  _handleSave() {
585
- if (this.sessionState === SessionState.started && this.continuousRecording) {
609
+ if (this.sessionState === SessionState.started &&
610
+ this.continuousRecording) {
586
611
  this.save();
587
612
  }
588
613
  }
@@ -627,12 +652,12 @@ export class SessionRecorder extends Observable {
627
652
  }
628
653
  });
629
654
  this._socketService.on(SESSION_SAVE_BUFFER_EVENT, (payload) => {
630
- var _a, _b, _c;
655
+ var _a, _b;
631
656
  if (this.sessionState !== SessionState.stopped)
632
657
  return;
633
- this._flushBuffer((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a._id, true);
634
- if ((_b = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _b === void 0 ? void 0 : _b.url) {
635
- recorderEventBus.emit(SESSION_AUTO_CREATED, (_c = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _c === void 0 ? void 0 : _c.url);
658
+ this._flushBuffer(payload === null || payload === void 0 ? void 0 : payload.debugSession, true);
659
+ if ((_a = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _a === void 0 ? void 0 : _a.url) {
660
+ recorderEventBus.emit(SESSION_AUTO_CREATED, (_b = payload === null || payload === void 0 ? void 0 : payload.debugSession) === null || _b === void 0 ? void 0 : _b.url);
636
661
  }
637
662
  });
638
663
  }
@@ -640,7 +665,7 @@ export class SessionRecorder extends Observable {
640
665
  try {
641
666
  const session = await this._apiService.createErrorSession({ span });
642
667
  if (session === null || session === void 0 ? void 0 : session._id) {
643
- this._flushBuffer(session._id);
668
+ this._flushBuffer(session);
644
669
  }
645
670
  if (session === null || session === void 0 ? void 0 : session.url) {
646
671
  recorderEventBus.emit(SESSION_AUTO_CREATED, session.url);
@@ -660,14 +685,20 @@ export class SessionRecorder extends Observable {
660
685
  sessionAttributes: this.sessionAttributes,
661
686
  resourceAttributes: getNavigatorInfo(),
662
687
  name: this._getSessionName(),
663
- ...(this._userAttributes ? { userAttributes: this._userAttributes } : {}),
688
+ ...(this._userAttributes
689
+ ? { userAttributes: this._userAttributes }
690
+ : {}),
664
691
  };
665
- const request = !this.continuousRecording ? payload : { debugSessionData: payload };
692
+ const request = !this.continuousRecording
693
+ ? payload
694
+ : { debugSessionData: payload };
666
695
  const session = this.continuousRecording
667
696
  ? await this._apiService.startContinuousDebugSession(request, signal)
668
697
  : await this._apiService.startSession(request, signal);
669
698
  if (session) {
670
- session.sessionType = this.continuousRecording ? SessionType.CONTINUOUS : SessionType.MANUAL;
699
+ session.sessionType = this.continuousRecording
700
+ ? SessionType.CONTINUOUS
701
+ : SessionType.MANUAL;
671
702
  this._setupSessionAndStart(session, false);
672
703
  }
673
704
  }
@@ -690,7 +721,10 @@ export class SessionRecorder extends Observable {
690
721
  this._tracer.start(this.sessionId, this.sessionType);
691
722
  // Ensure we switch from buffer-only recording to session recording cleanly.
692
723
  void this._recorder.restart(this.sessionId, this.sessionType);
693
- this._navigationRecorder.start({ sessionId: this.sessionId, sessionType: this.sessionType });
724
+ this._navigationRecorder.start({
725
+ sessionId: this.sessionId,
726
+ sessionType: this.sessionType,
727
+ });
694
728
  if (this.session) {
695
729
  this._socketService.subscribeToSession(this.session);
696
730
  this._sessionWidget.seconds = getTimeDifferenceInSeconds((_a = this.session) === null || _a === void 0 ? void 0 : _a.startedAt);
@@ -718,8 +752,12 @@ export class SessionRecorder extends Observable {
718
752
  // rrweb assigns new node IDs on each record() call, so the next buffer
719
753
  // segment must not carry events from the previous generation. Await the
720
754
  // clear so its IDB tx can't race past the fresh FullSnapshot.
721
- const cleared = this._crashBuffer ? this._crashBuffer.clear() : Promise.resolve();
722
- void cleared.catch(() => undefined).then(() => this._startBufferOnlyRecording());
755
+ const cleared = this._crashBuffer
756
+ ? this._crashBuffer.clear()
757
+ : Promise.resolve();
758
+ void cleared
759
+ .catch(() => undefined)
760
+ .then(() => this._startBufferOnlyRecording());
723
761
  }
724
762
  /**
725
763
  * Pause the session tracing and recording
@@ -755,7 +793,10 @@ export class SessionRecorder extends Observable {
755
793
  * @param sessionId - the session ID to set or clear
756
794
  */
757
795
  _setSession(session) {
758
- this.session = { ...session, startedAt: session.startedAt || new Date().toISOString() };
796
+ this.session = {
797
+ ...session,
798
+ startedAt: session.startedAt || new Date().toISOString(),
799
+ };
759
800
  this.sessionId = (session === null || session === void 0 ? void 0 : session.shortId) || (session === null || session === void 0 ? void 0 : session._id);
760
801
  }
761
802
  _clearSession() {
@@ -778,7 +819,8 @@ export class SessionRecorder extends Observable {
778
819
  }
779
820
  break;
780
821
  case 'stop':
781
- if (this.sessionState !== SessionState.paused && this.sessionState !== SessionState.started) {
822
+ if (this.sessionState !== SessionState.paused &&
823
+ this.sessionState !== SessionState.started) {
782
824
  throw new Error('Cannot stop. Session is not currently started.');
783
825
  }
784
826
  break;
@@ -856,7 +898,10 @@ export class SessionRecorder extends Observable {
856
898
  */
857
899
  _getSessionName(date = new Date()) {
858
900
  var _a, _b, _c;
859
- const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) || ((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) || ((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) || '';
901
+ const userName = ((_a = this.sessionAttributes) === null || _a === void 0 ? void 0 : _a.userName) ||
902
+ ((_b = this._userAttributes) === null || _b === void 0 ? void 0 : _b.userName) ||
903
+ ((_c = this._userAttributes) === null || _c === void 0 ? void 0 : _c.name) ||
904
+ '';
860
905
  return userName
861
906
  ? `${userName}'s session on ${getFormattedDate(date, { month: 'short', day: 'numeric' })}`
862
907
  : `Session on ${getFormattedDate(date)}`;
@@ -34,8 +34,9 @@ export class UIManager {
34
34
  * tooltip, and inner HTML content (Record icon)
35
35
  */
36
36
  setRecorderButtonProps() {
37
+ var _a;
37
38
  this.recorderButton.className = 'mp-session-debugger-button';
38
- this.recorderButton.dataset.tooltip = 'Record an issue';
39
+ this.recorderButton.dataset.tooltip = (_a = this.widgetTextOverrides.buttonTooltipIdle) !== null && _a !== void 0 ? _a : 'Record an issue';
39
40
  insertTrustedHTML(this.recorderButton, `${RecordIcon}`);
40
41
  }
41
42
  /**
@@ -60,8 +61,7 @@ export class UIManager {
60
61
  * The popover includes a logo, heading, and start recording button.
61
62
  */
62
63
  setInitialPopoverProps() {
63
- this.initialPopover.className =
64
- 'mp-session-debugger-popover mp-initial-popover hidden';
64
+ this.initialPopover.className = 'mp-session-debugger-popover mp-initial-popover hidden';
65
65
  insertTrustedHTML(this.initialPopover, initialPopoverTemplate(this.widgetTextOverrides, this.showContinuousRecording));
66
66
  }
67
67
  /**
@@ -81,12 +81,15 @@ export class UIManager {
81
81
  * @param isLoading - Whether the popover button should show a loading state.
82
82
  */
83
83
  setPopoverLoadingState(isLoading) {
84
+ var _a, _b;
84
85
  const button = this.initialPopover.querySelector('.mp-session-debugger-popover-button');
85
86
  if (!button) {
86
87
  return;
87
88
  }
88
89
  button.classList.toggle('disabled', isLoading);
89
- button.textContent = isLoading ? 'Starting to record...' : 'Start recording';
90
+ button.textContent = isLoading
91
+ ? ((_a = this.widgetTextOverrides.buttonTooltipLoading) !== null && _a !== void 0 ? _a : 'Starting to record...')
92
+ : ((_b = this.widgetTextOverrides.startRecordingButtonText) !== null && _b !== void 0 ? _b : 'Start recording');
90
93
  }
91
94
  setTimerValue(time) {
92
95
  const timerElement = this.recordingOverlay.querySelector('.timer');
@@ -1,3 +1,4 @@
1
+ import { WidgetTextOverridesConfig } from '../types';
1
2
  /**
2
3
  * ButtonState defines the possible states of the recorder button.
3
4
  * It includes IDLE, RECORDING, CANCEL, SENT and LOADING states.
@@ -16,6 +17,17 @@ export declare enum ContinuousRecordingSaveButtonState {
16
17
  SAVED = "SAVED",
17
18
  ERROR = "ERROR"
18
19
  }
20
+ type ButtonStateConfig = {
21
+ icon: string;
22
+ tooltip: string;
23
+ classes: string[];
24
+ excludeClasses: string[];
25
+ };
26
+ type ContinuousRecordingSaveButtonConfig = {
27
+ textContent: string;
28
+ disabled: boolean;
29
+ classes: string[];
30
+ };
19
31
  /**
20
32
  * buttonStates object provides properties for each button state:
21
33
  * IDLE, RECORDING, CANCEL, and SENT.
@@ -80,4 +92,7 @@ export declare const continuousRecordingSaveButtonStates: {
80
92
  classes: never[];
81
93
  };
82
94
  };
95
+ export declare function getButtonStates(overrides: WidgetTextOverridesConfig): Record<ButtonState, ButtonStateConfig>;
96
+ export declare function getContinuousRecordingSaveButtonStates(overrides: WidgetTextOverridesConfig): Record<ContinuousRecordingSaveButtonState, ContinuousRecordingSaveButtonConfig>;
97
+ export {};
83
98
  //# sourceMappingURL=buttonStateConfigs.d.ts.map
@@ -83,4 +83,44 @@ export const continuousRecordingSaveButtonStates = {
83
83
  classes: [],
84
84
  },
85
85
  };
86
+ const buttonTooltipOverrideKeys = {
87
+ [ButtonState.IDLE]: 'buttonTooltipIdle',
88
+ [ButtonState.RECORDING]: 'buttonTooltipRecording',
89
+ [ButtonState.CANCEL]: 'buttonTooltipCancel',
90
+ [ButtonState.SENT]: 'buttonTooltipSent',
91
+ [ButtonState.LOADING]: 'buttonTooltipLoading',
92
+ [ButtonState.CONTINUOUS_DEBUGGING]: 'buttonTooltipContinuousDebugging',
93
+ };
94
+ const continuousSaveTextOverrideKeys = {
95
+ [ContinuousRecordingSaveButtonState.IDLE]: 'saveLastSnapshotButtonText',
96
+ [ContinuousRecordingSaveButtonState.SAVING]: 'saveContinuousRecordingSavingText',
97
+ [ContinuousRecordingSaveButtonState.SAVED]: 'saveContinuousRecordingSavedText',
98
+ [ContinuousRecordingSaveButtonState.ERROR]: 'saveContinuousRecordingErrorText',
99
+ };
100
+ export function getButtonStates(overrides) {
101
+ var _a;
102
+ const resolved = { ...buttonStates };
103
+ for (const state of Object.keys(buttonStates)) {
104
+ const overrideKey = buttonTooltipOverrideKeys[state];
105
+ resolved[state] = {
106
+ ...buttonStates[state],
107
+ tooltip: (_a = overrides[overrideKey]) !== null && _a !== void 0 ? _a : buttonStates[state].tooltip,
108
+ };
109
+ }
110
+ return resolved;
111
+ }
112
+ export function getContinuousRecordingSaveButtonStates(overrides) {
113
+ var _a;
114
+ const resolved = {
115
+ ...continuousRecordingSaveButtonStates,
116
+ };
117
+ for (const state of Object.keys(continuousRecordingSaveButtonStates)) {
118
+ const overrideKey = continuousSaveTextOverrideKeys[state];
119
+ resolved[state] = {
120
+ ...continuousRecordingSaveButtonStates[state],
121
+ textContent: (_a = overrides[overrideKey]) !== null && _a !== void 0 ? _a : continuousRecordingSaveButtonStates[state].textContent,
122
+ };
123
+ }
124
+ return resolved;
125
+ }
86
126
  //# sourceMappingURL=buttonStateConfigs.js.map
@@ -1,12 +1,12 @@
1
1
  import { Observable } from '../observable';
2
2
  import { insertTrustedHTML, injectStylesIntoShadowRoot, formatTimeForSessionTimer } from '../utils';
3
3
  import { SessionState } from '../types';
4
- import { POPOVER_WIDTH, NON_DRAGGABLE_OFFSET, POPOVER_DISTANCE_FROM_BUTTON, } from './constants';
4
+ import { POPOVER_WIDTH, NON_DRAGGABLE_OFFSET, POPOVER_DISTANCE_FROM_BUTTON } from './constants';
5
5
  import { DEFAULT_WIDGET_TEXT_CONFIG } from '../config';
6
6
  import { isBrowser, isBrowserExtension } from '../global';
7
7
  import { UIManager } from './UIManager';
8
8
  import { DragManager } from './dragManager';
9
- import { ButtonState, buttonStates, continuousRecordingSaveButtonStates, } from './buttonStateConfigs';
9
+ import { ButtonState, getButtonStates, getContinuousRecordingSaveButtonStates, } from './buttonStateConfigs';
10
10
  // Import styles as string for shadow DOM injection
11
11
  import widgetStyles from './styles/index.scss?raw';
12
12
  import './styles/button.scss';
@@ -16,7 +16,7 @@ export class SessionWidget extends Observable {
16
16
  this._buttonState = newState;
17
17
  if (!this.isBrowser)
18
18
  return;
19
- const { icon, tooltip, classes, excludeClasses } = buttonStates[newState];
19
+ const { icon, tooltip, classes, excludeClasses } = getButtonStates(this._widgetTextOverrides)[newState];
20
20
  if (newState === ButtonState.CANCEL) {
21
21
  (_a = this.buttonDraggabilityObserver) === null || _a === void 0 ? void 0 : _a.observe(this.recorderButton, {
22
22
  attributes: true,
@@ -54,7 +54,8 @@ export class SessionWidget extends Observable {
54
54
  type: 'error',
55
55
  message: this._error,
56
56
  button: {
57
- text: 'Close', onClick: () => this.hideToast(),
57
+ text: 'Close',
58
+ onClick: () => this.hideToast(),
58
59
  },
59
60
  });
60
61
  }
@@ -201,7 +202,7 @@ export class SessionWidget extends Observable {
201
202
  return;
202
203
  const saveButton = this.initialPopover.querySelector('#mp-save-continuous-debug-session');
203
204
  if (saveButton) {
204
- const { textContent, disabled } = continuousRecordingSaveButtonStates[state];
205
+ const { textContent, disabled } = getContinuousRecordingSaveButtonStates(this._widgetTextOverrides)[state];
205
206
  saveButton.disabled = disabled;
206
207
  saveButton.textContent = textContent;
207
208
  }
@@ -229,14 +230,11 @@ export class SessionWidget extends Observable {
229
230
  return;
230
231
  this.buttonDraggabilityObserver = new MutationObserver((mutationsList) => {
231
232
  for (const mutation of mutationsList) {
232
- if (mutation.type === 'attributes' &&
233
- mutation.attributeName === 'class') {
233
+ if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
234
234
  const oldClassName = mutation.oldValue;
235
235
  const newClassName = mutation.target['className'];
236
- if (((oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')) &&
237
- !newClassName.includes('no-draggable')) ||
238
- ((newClassName === null || newClassName === void 0 ? void 0 : newClassName.includes('no-draggable')) &&
239
- !(oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')))) {
236
+ if (((oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')) && !newClassName.includes('no-draggable')) ||
237
+ ((newClassName === null || newClassName === void 0 ? void 0 : newClassName.includes('no-draggable')) && !(oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')))) {
240
238
  // draggable mode was changed
241
239
  this.initialPopoverVisible = false;
242
240
  this.finalPopoverVisible = false;
@@ -330,7 +328,12 @@ export class SessionWidget extends Observable {
330
328
  popoverBottom = VIEWPORT_HEIGHT - top + POPOVER_DISTANCE_FROM_BUTTON + (isDraggable ? 0 : NON_DRAGGABLE_OFFSET);
331
329
  popoverRight = VIEWPORT_WIDTH - right;
332
330
  if (popoverBottom + POPOVER_HEIGHT > VIEWPORT_HEIGHT) {
333
- popoverBottom = VIEWPORT_HEIGHT - bottom - POPOVER_HEIGHT - POPOVER_DISTANCE_FROM_BUTTON - (isDraggable ? 0 : NON_DRAGGABLE_OFFSET);
331
+ popoverBottom =
332
+ VIEWPORT_HEIGHT -
333
+ bottom -
334
+ POPOVER_HEIGHT -
335
+ POPOVER_DISTANCE_FROM_BUTTON -
336
+ (isDraggable ? 0 : NON_DRAGGABLE_OFFSET);
334
337
  }
335
338
  if (popoverRight + POPOVER_WIDTH > VIEWPORT_WIDTH) {
336
339
  popoverRight = VIEWPORT_WIDTH - left - POPOVER_WIDTH;
@@ -433,9 +436,7 @@ export class SessionWidget extends Observable {
433
436
  this.onCancel();
434
437
  }
435
438
  this.initialPopoverVisible = false;
436
- this.buttonState = this._continuousRecording
437
- ? ButtonState.CONTINUOUS_DEBUGGING
438
- : ButtonState.IDLE;
439
+ this.buttonState = this._continuousRecording ? ButtonState.CONTINUOUS_DEBUGGING : ButtonState.IDLE;
439
440
  if (typeof document !== 'undefined') {
440
441
  document.removeEventListener('click', this.handleClickOutside);
441
442
  }
@@ -502,9 +503,7 @@ export class SessionWidget extends Observable {
502
503
  }
503
504
  }
504
505
  else {
505
- this.buttonState = this._initialPopoverVisible
506
- ? ButtonState.IDLE
507
- : ButtonState.CANCEL;
506
+ this.buttonState = this._initialPopoverVisible ? ButtonState.IDLE : ButtonState.CANCEL;
508
507
  this.initialPopoverVisible = !this._initialPopoverVisible;
509
508
  }
510
509
  if (typeof document !== 'undefined') {
@@ -305,6 +305,24 @@ export interface WidgetTextOverridesConfig {
305
305
  submitDialogSubmitText?: string;
306
306
  /** Text for the cancel button in dialog */
307
307
  submitDialogCancelText?: string;
308
+ /** Tooltip when the recorder button is idle */
309
+ buttonTooltipIdle?: string;
310
+ /** Tooltip while a session is recording */
311
+ buttonTooltipRecording?: string;
312
+ /** Tooltip when cancel is available */
313
+ buttonTooltipCancel?: string;
314
+ /** Tooltip after the recording was sent */
315
+ buttonTooltipSent?: string;
316
+ /** Tooltip while the recorder is starting */
317
+ buttonTooltipLoading?: string;
318
+ /** Tooltip during continuous debugging */
319
+ buttonTooltipContinuousDebugging?: string;
320
+ /** Label while a continuous recording is being saved */
321
+ saveContinuousRecordingSavingText?: string;
322
+ /** Label after a continuous recording was saved */
323
+ saveContinuousRecordingSavedText?: string;
324
+ /** Label when saving a continuous recording failed */
325
+ saveContinuousRecordingErrorText?: string;
308
326
  }
309
327
  /**
310
328
  * Configuration interface for the ApiService class
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplayer-app/session-recorder-browser",
3
- "version": "2.0.85",
3
+ "version": "2.0.88",
4
4
  "description": "Multiplayer Fullstack Session Recorder for Browser",
5
5
  "author": {
6
6
  "name": "Multiplayer Software, Inc.",
@@ -28,7 +28,9 @@
28
28
  "types": "./dist/index.d.ts",
29
29
  "import": "./dist/index.js",
30
30
  "require": "./dist/index.umd.js"
31
- }
31
+ },
32
+ "./browser": "./dist/browser/index.js",
33
+ "./exporters": "./dist/exporters/index.js"
32
34
  },
33
35
  "engines": {
34
36
  "node": ">=18",
@@ -49,7 +51,7 @@
49
51
  "preversion": "npm run lint",
50
52
  "postversion:skip": "git push && git push --tags",
51
53
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && webpack --config-name esm --config-name umd && node scripts/sync-exports.js",
52
- "build:browser": "webpack --config-name browser && node scripts/sync-exports.js",
54
+ "build:browser": "webpack --config-name browser && node scripts/patch-canvas-worker.mjs && node scripts/sync-exports.js",
53
55
  "build:exporters": "webpack --config-name exporters && node scripts/sync-exports.js",
54
56
  "build:extras": "webpack --config-name browser --config-name exporters && node scripts/sync-exports.js",
55
57
  "build:all": "rm -rf dist tsconfig.tsbuildinfo && tsc && webpack && node scripts/sync-exports.js",
@@ -73,7 +75,7 @@
73
75
  "webpack-cli": "5.1.4"
74
76
  },
75
77
  "dependencies": {
76
- "@multiplayer-app/session-recorder-common": "2.0.85",
78
+ "@multiplayer-app/session-recorder-common": "2.0.88",
77
79
  "@opentelemetry/core": "2.0.1",
78
80
  "@opentelemetry/exporter-trace-otlp-http": "0.203.0",
79
81
  "@opentelemetry/instrumentation": "0.203.0",