@multiplayer-app/session-recorder-browser
Version:
Multiplayer Fullstack Session Recorder for Browser
133 lines • 5.03 kB
JavaScript
import { pack } from '@rrweb/packer';
import { record } from 'rrweb';
import { SessionType } from '@multiplayer-app/session-recorder-common';
import { getRecordConsolePlugin } from '@rrweb/rrweb-plugin-console-record';
import { isConsoleEvent } from '../utils';
import { CONTINUOUS_DEBUGGING_TIMEOUT } from '../config';
export class RecorderBrowserSDK {
get startedAt() {
return this._startedAt;
}
set startedAt(v) {
this._startedAt = v;
}
get stoppedAt() {
return this._stoppedAt;
}
set stoppedAt(v) {
this._stoppedAt = v;
}
constructor() {
this.restartInterval = null;
this._startedAt = '';
this._stoppedAt = '';
}
/**
* Initializes the recorder SDK with configuration settings.
* @param config - Configuration settings for the session debugger.
* @param socketService - Optional socket service instance for sending events.
*/
init(config, socketService) {
this.config = config;
this.socketService = socketService;
}
/**
* Starts recording events for a given session ID.
* @param sessionId - The ID of the session to record events for.
*/
start(sessionId, sessionType) {
var _a;
if (!this.config) {
throw new Error('Configuration not initialized. Call init() before start().');
}
const restartInterval = sessionType === SessionType.CONTINUOUS ? CONTINUOUS_DEBUGGING_TIMEOUT : 0;
this.startedAt = new Date().toISOString();
// Build masking configuration
const maskingConfig = this.config.masking || {};
const options = {
maskAllInputs: (_a = maskingConfig.maskAllInputs) !== null && _a !== void 0 ? _a : true,
sampling: { canvas: 5 },
recordCanvas: this.config.recordCanvas,
dataURLOptions: { type: 'image/webp', quality: 0.1 },
plugins: [
getRecordConsolePlugin({ level: ['log', 'error'] }),
],
};
// Add mask input options if provided
if (maskingConfig.maskInputOptions) {
options.maskInputOptions = maskingConfig.maskInputOptions;
}
// Add mask text class if provided
if (maskingConfig.maskTextClass) {
options.maskTextClass = maskingConfig.maskTextClass;
}
// Add mask text selector if provided
if (maskingConfig.maskTextSelector) {
options.maskTextSelector = maskingConfig.maskTextSelector;
}
// Add custom masking functions if provided
if (typeof maskingConfig.maskInput === 'function') {
options.maskInputFn = maskingConfig.maskInput;
}
if (typeof maskingConfig.maskText === 'function') {
options.maskTextFn = maskingConfig.maskText;
}
this.stopFn = record({
...options,
emit: async (event) => {
if (this.socketService) {
if (typeof maskingConfig.maskConsoleEvent === 'function' && isConsoleEvent(event)) {
const { data } = event;
const maskedPayload = maskingConfig.maskConsoleEvent(data.payload);
event.data = { ...data, payload: maskedPayload };
}
const packedEvent = pack(event);
this.stoppedAt = new Date(event.timestamp).toISOString();
this.socketService.send({
event: packedEvent,
eventType: event.type,
timestamp: event.timestamp,
debugSessionId: sessionId,
debugSessionType: sessionType,
});
}
},
});
// It will sent full snapshot again but it will fix missing first snapshot issue
record.takeFullSnapshot();
if (restartInterval > 0) {
this.restartInterval = setInterval(() => {
record.takeFullSnapshot();
}, restartInterval);
}
}
/**
* Restarts the recording of events.
*/
async restart(sessionId, sessionType) {
var _a;
(_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
this.start(sessionId, sessionType);
}
/**
* Clears the restart timeout.
*/
clearRestartInterval() {
if (this.restartInterval) {
clearInterval(this.restartInterval);
this.restartInterval = null;
}
}
/**
* Stops the recording of events.
*/
stop() {
var _a, _b, _c;
(_a = this.stopFn) === null || _a === void 0 ? void 0 : _a.call(this);
if (!((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket)) {
(_c = this.socketService) === null || _c === void 0 ? void 0 : _c.close();
}
this.clearRestartInterval();
}
}
//# sourceMappingURL=index.js.map