@multiplayer-app/session-recorder-browser
Version:
Multiplayer Fullstack Session Recorder for Browser
190 lines • 6.73 kB
JavaScript
import io from 'socket.io-client';
import { Observable } from 'lib0/observable';
import messagingService from '../services/messaging.service';
import { SESSION_ADD_EVENT, SESSION_AUTO_CREATED, SESSION_STOPPED_EVENT, SESSION_SUBSCRIBE_EVENT, SESSION_UNSUBSCRIBE_EVENT, SOCKET_SET_USER_EVENT, REMOTE_SESSION_RECORDING_START, REMOTE_SESSION_RECORDING_STOP, SESSION_STARTED_EVENT, } from '../config';
const MAX_RECONNECTION_ATTEMPTS = 2;
export class SocketService extends Observable {
constructor() {
super();
this.socket = null;
this.queue = [];
this.isConnecting = false;
this.isConnected = false;
this.attempts = 0;
this.sessionId = null;
this.usePostMessage = false;
this.options = {
apiKey: '',
socketUrl: '',
keepAlive: false,
usePostMessageFallback: false,
};
}
/**
* Initialize the socket service
* @param config - Socket service configuration
*/
init(config) {
this.options = {
...this.options,
...config,
};
if (this.options.keepAlive &&
this.options.socketUrl &&
this.options.apiKey) {
this._initConnection();
}
}
/**
* Update the socket service configuration
* @param config - Partial configuration to update
*/
updateConfigs(config) {
var _a;
// If any config changed, reconnect if connected
const hasChanges = Object.keys(config).some((key) => {
const typedKey = key;
return (config[typedKey] !== undefined &&
config[typedKey] !== this.options[typedKey]);
});
if (hasChanges) {
this.options = { ...this.options, ...config };
if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) {
this.close().then(() => {
if (this.options.keepAlive &&
this.options.socketUrl &&
this.options.apiKey) {
this._initConnection();
}
});
}
}
}
_initConnection() {
if (this.isConnecting || this.isConnected)
return;
this.attempts++;
this.isConnecting = true;
this.usePostMessage = false;
this.socket = io(this.options.socketUrl, {
path: '/v0/radar/ws',
auth: {
'x-api-key': this.options.apiKey,
},
reconnectionAttempts: 2,
transports: ['websocket'],
});
this.socket.on('ready', () => {
this.isConnecting = false;
this.isConnected = true;
this.usePostMessage = false;
this.flushQueue();
});
this.socket.on('disconnect', (err) => {
this.isConnecting = false;
this.isConnected = false;
});
this.socket.on('connect_error', (err) => {
this.isConnecting = false;
this.isConnected = false;
this.checkReconnectionAttempts();
});
this.socket.on(SESSION_STOPPED_EVENT, (data) => {
this.emit(SESSION_STOPPED_EVENT, [data]);
});
this.socket.on(SESSION_AUTO_CREATED, (data) => {
this.emit(SESSION_AUTO_CREATED, [data]);
});
this.socket.on(REMOTE_SESSION_RECORDING_START, (data) => {
this.emit(REMOTE_SESSION_RECORDING_START, [data]);
});
this.socket.on(REMOTE_SESSION_RECORDING_STOP, (data) => {
this.emit(REMOTE_SESSION_RECORDING_STOP, [data]);
});
}
checkReconnectionAttempts() {
if (this.attempts >= MAX_RECONNECTION_ATTEMPTS) {
this.usePostMessage = !!this.options.usePostMessageFallback;
this.flushQueue();
}
}
sendViaPostMessage(name, data) {
const action = name === SESSION_ADD_EVENT ? 'rrweb-event' : 'socket-emit';
messagingService.sendMessage(action, data);
}
emitSocketEvent(name, data) {
if (this.usePostMessage) {
this.sendViaPostMessage(name, data);
}
else if (this.socket && this.isConnected) {
this.socket.emit(name, data);
}
else {
this.queue.push({ data, name });
this._initConnection();
}
}
flushQueue() {
while (this.queue.length > 0 && (this.usePostMessage || this.isConnected)) {
const event = this.queue.shift();
if (!event)
continue;
if (this.usePostMessage) {
this.sendViaPostMessage(event.name, event.data);
}
else if (this.socket && this.isConnected) {
this.socket.emit(event.name, event.data);
}
}
}
send(event) {
this.emitSocketEvent(SESSION_ADD_EVENT, event);
}
subscribeToSession(session) {
this.sessionId = session.shortId || session._id;
const payload = {
projectId: session.project,
workspaceId: session.workspace,
debugSessionId: this.sessionId,
sessionType: session.creationType,
};
this.emitSocketEvent(SESSION_SUBSCRIBE_EVENT, payload);
// use long id instead of short id
this.emitSocketEvent(SESSION_STARTED_EVENT, { debugSessionId: session._id });
}
unsubscribeFromSession(stopSession) {
if (this.sessionId) {
this.emitSocketEvent(SESSION_UNSUBSCRIBE_EVENT, { debugSessionId: this.sessionId });
if (stopSession) {
this.emitSocketEvent(SESSION_STOPPED_EVENT, {});
}
}
}
setUser(userAttributes) {
this.emitSocketEvent(SOCKET_SET_USER_EVENT, userAttributes);
}
close() {
return new Promise((resolve) => {
var _a;
if (this.usePostMessage) {
this.sendViaPostMessage('close', {});
}
if ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) {
setTimeout(() => {
var _a;
this.unsubscribeFromSession();
this.attempts = 0;
this.isConnected = false;
this.isConnecting = false;
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.disconnect();
this.socket = null;
resolve();
}, 500);
}
else {
resolve();
}
});
}
}
//# sourceMappingURL=socket.service.js.map