@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
package/src/otel/index.ts CHANGED
@@ -4,7 +4,6 @@ import {
4
4
  type ExportResult,
5
5
  } from '@opentelemetry/core';
6
6
  import {
7
- AlwaysOnSampler,
8
7
  BatchSpanProcessor,
9
8
  type ReadableSpan,
10
9
  } from '@opentelemetry/sdk-trace-base';
@@ -17,17 +16,21 @@ import {
17
16
  SessionRecorderBrowserTraceExporter,
18
17
  ATTR_MULTIPLAYER_SESSION_ID,
19
18
  MULTIPLAYER_TRACE_CLIENT_ID_LENGTH,
19
+ SessionRecorderTraceIdRatioBasedSampler,
20
20
  } from '@multiplayer-app/session-recorder-common';
21
21
  import { type TracerReactNativeConfig } from '../types';
22
22
  import { getInstrumentations } from './instrumentations';
23
23
  import { getExporterEndpoint } from './helpers';
24
- import { trace, SpanStatusCode, context, type Span } from '@opentelemetry/api';
25
24
 
26
25
  import { getPlatformAttributes } from '../utils/platform';
27
26
  import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
28
27
  import { CrashBufferService } from '../services/crashBuffer.service';
29
28
  import { CrashBufferSpanProcessor } from './CrashBufferSpanProcessor';
30
29
 
30
+ const clientIdGenerator = SessionRecorderSdk.getIdGenerator(
31
+ MULTIPLAYER_TRACE_CLIENT_ID_LENGTH
32
+ );
33
+
31
34
  export class TracerReactNativeSDK {
32
35
  clientId = '';
33
36
  private tracerProvider?: WebTracerProvider;
@@ -36,7 +39,6 @@ export class TracerReactNativeSDK {
36
39
  private sessionId = '';
37
40
  private idGenerator?: SessionRecorderIdGenerator;
38
41
  private exporter?: SessionRecorderBrowserTraceExporter;
39
- private batchSpanProcessor?: BatchSpanProcessor;
40
42
  private globalErrorHandlerRegistered = false;
41
43
  private crashBuffer?: CrashBufferService;
42
44
 
@@ -47,42 +49,50 @@ export class TracerReactNativeSDK {
47
49
  sessionType: SessionType = SessionType.MANUAL
48
50
  ) {
49
51
  this.sessionId = sessionId;
50
- this.idGenerator?.setSessionId(sessionId, sessionType, this.clientId);
52
+
53
+ if (!this.idGenerator) {
54
+ throw new Error('Id generator not initialized');
55
+ }
56
+
57
+ this.idGenerator.setSessionId(sessionId, sessionType, this.clientId);
51
58
  }
52
59
 
53
60
  init(options: TracerReactNativeConfig): void {
54
61
  this.config = options;
55
- const clientIdGenerator = SessionRecorderSdk.getIdGenerator(
56
- MULTIPLAYER_TRACE_CLIENT_ID_LENGTH
57
- );
58
62
  this.clientId = clientIdGenerator();
59
63
 
60
64
  const { application, version, environment } = this.config;
61
65
 
62
66
  this.idGenerator = new SessionRecorderIdGenerator();
63
67
 
68
+ this._setSessionId('', SessionType.SESSION_CACHE);
69
+
64
70
  this.exporter = new SessionRecorderBrowserTraceExporter({
65
71
  apiKey: options.apiKey,
66
72
  url: getExporterEndpoint(options.exporterEndpoint),
67
73
  });
68
74
 
69
- this.batchSpanProcessor = new BatchSpanProcessor(this.exporter);
75
+ const resourceAttributes = resourceFromAttributes({
76
+ [SemanticAttributes.SEMRESATTRS_SERVICE_NAME]: application,
77
+ [SemanticAttributes.SEMRESATTRS_SERVICE_VERSION]: version,
78
+ [SemanticAttributes.SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: environment,
79
+ ...getPlatformAttributes(),
80
+ });
81
+
82
+ SessionRecorderSdk.setResourceAttributes(resourceAttributes.attributes);
70
83
 
71
84
  this.tracerProvider = new WebTracerProvider({
72
- resource: resourceFromAttributes({
73
- [SemanticAttributes.SEMRESATTRS_SERVICE_NAME]: application,
74
- [SemanticAttributes.SEMRESATTRS_SERVICE_VERSION]: version,
75
- [SemanticAttributes.SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: environment,
76
- ...getPlatformAttributes(),
77
- }),
85
+ resource: resourceAttributes,
78
86
  idGenerator: this.idGenerator,
79
- sampler: new AlwaysOnSampler(),
87
+ sampler: new SessionRecorderTraceIdRatioBasedSampler(
88
+ this.config.sampleTraceRatio
89
+ ),
80
90
  spanProcessors: [
81
91
  this._getSpanSessionIdProcessor(),
92
+ new BatchSpanProcessor(this.exporter),
82
93
  new CrashBufferSpanProcessor(
83
- this.batchSpanProcessor,
84
94
  this.crashBuffer,
85
- this.exporter.serializeSpan.bind(this.exporter)
95
+ this.exporter.serializeSpan
86
96
  ),
87
97
  ],
88
98
  });
@@ -117,27 +127,22 @@ export class TracerReactNativeSDK {
117
127
  async exportTraces(
118
128
  spans: ReadableSpan[]
119
129
  ): Promise<ExportResult | undefined | void> {
120
- if (this.batchSpanProcessor) {
121
- spans.map((span) => {
122
- this.batchSpanProcessor?.onEnd(span);
123
- });
130
+ if (!this.exporter) {
131
+ throw new Error('Trace exporter not initialized');
132
+ }
133
+ if (!spans || spans.length === 0) {
124
134
  return Promise.resolve();
125
135
  }
126
136
 
127
- throw new Error('Buffer span processor not initialized');
128
- }
137
+ const readableSpans = spans.map((s: any) =>
138
+ TracerReactNativeSDK._toReadableSpanLike(s)
139
+ );
129
140
 
130
- private _getSpanSessionIdProcessor() {
131
- return {
132
- onStart: (span: any) => {
133
- if (this.sessionId) {
134
- span.setAttribute(ATTR_MULTIPLAYER_SESSION_ID, this.sessionId);
135
- }
136
- },
137
- onEnd: () => {},
138
- shutdown: () => Promise.resolve(),
139
- forceFlush: () => Promise.resolve(),
140
- };
141
+ return new Promise((resolve) => {
142
+ this.exporter?.exportBuffer(readableSpans, (result) => {
143
+ resolve(result);
144
+ });
145
+ });
141
146
  }
142
147
 
143
148
  start(sessionId: string, sessionType: SessionType): void {
@@ -157,7 +162,7 @@ export class TracerReactNativeSDK {
157
162
  );
158
163
  }
159
164
 
160
- this._setSessionId('');
165
+ this._setSessionId('', SessionType.SESSION_CACHE);
161
166
  }
162
167
 
163
168
  setApiKey(apiKey: string): void {
@@ -167,16 +172,7 @@ export class TracerReactNativeSDK {
167
172
  );
168
173
  }
169
174
 
170
- this.exporter.setApiKey?.(apiKey);
171
- }
172
-
173
- setSessionId(sessionId: string, sessionType: SessionType): void {
174
- this._setSessionId(sessionId, sessionType);
175
- }
176
-
177
- // Shutdown (React Native specific)
178
- shutdown(): Promise<void> {
179
- return Promise.resolve();
175
+ this.exporter.setApiKey(apiKey);
180
176
  }
181
177
 
182
178
  /**
@@ -186,39 +182,20 @@ export class TracerReactNativeSDK {
186
182
  */
187
183
  captureException(error: Error, errorInfo?: Record<string, any>): void {
188
184
  if (!error) return;
189
- // Prefer attaching to the active span to keep correlation intact
190
- try {
191
- const activeSpan = trace.getSpan(context.active());
192
- if (activeSpan) {
193
- this._recordException(activeSpan, error, errorInfo);
194
- return;
195
- }
196
- } catch (_ignored) {}
197
-
198
- // Fallback: create a short-lived span to hold the exception details
199
- try {
200
- const tracer = trace.getTracer('exception');
201
- const span = tracer.startSpan(error.name || 'Error');
202
- this._recordException(span, error, errorInfo);
203
- span.end();
204
- } catch (_ignored) {}
185
+ SessionRecorderSdk.captureException(error, errorInfo);
205
186
  }
206
187
 
207
- private _recordException(
208
- span: Span,
209
- error: Error,
210
- errorInfo?: Record<string, any>
211
- ): void {
212
- span.recordException(error);
213
- span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
214
- span.setAttribute('exception.type', error.name || 'Error');
215
- span.setAttribute('exception.message', error.message);
216
- span.setAttribute('exception.stacktrace', error.stack || '');
217
- if (errorInfo) {
218
- Object.entries(errorInfo).forEach(([key, value]) => {
219
- span.setAttribute(`error_info.${key}`, value);
220
- });
221
- }
188
+ private _getSpanSessionIdProcessor() {
189
+ return {
190
+ onStart: (span: any) => {
191
+ if (this.sessionId) {
192
+ span.setAttribute(ATTR_MULTIPLAYER_SESSION_ID, this.sessionId);
193
+ }
194
+ },
195
+ onEnd: () => {},
196
+ shutdown: () => Promise.resolve(),
197
+ forceFlush: () => Promise.resolve(),
198
+ };
222
199
  }
223
200
 
224
201
  private _registerGlobalErrorHandlers(): void {
@@ -249,4 +226,75 @@ export class TracerReactNativeSDK {
249
226
  this.globalErrorHandlerRegistered = true;
250
227
  }
251
228
  }
229
+
230
+ private static _toReadableSpanLike(span: any): ReadableSpan {
231
+ if (
232
+ span &&
233
+ typeof span.spanContext === 'function' &&
234
+ span.instrumentationScope
235
+ ) {
236
+ return span as ReadableSpan;
237
+ }
238
+
239
+ const spanContext =
240
+ typeof span?.spanContext === 'function'
241
+ ? span.spanContext()
242
+ : span?._spanContext;
243
+ const normalizedCtx =
244
+ spanContext ||
245
+ ({
246
+ traceId: span?.traceId,
247
+ spanId: span?.spanId,
248
+ traceFlags: span?.traceFlags,
249
+ traceState: span?.traceState,
250
+ } as any);
251
+
252
+ const instrumentationScope =
253
+ span?.instrumentationScope ||
254
+ span?.instrumentationLibrary ||
255
+ ({
256
+ name: 'multiplayer-buffer',
257
+ version: undefined,
258
+ schemaUrl: undefined,
259
+ } as any);
260
+
261
+ const normalizedScope = {
262
+ name: instrumentationScope?.name || 'multiplayer-buffer',
263
+ version: instrumentationScope?.version,
264
+ schemaUrl: instrumentationScope?.schemaUrl,
265
+ };
266
+
267
+ const resource = span?.resource || {
268
+ attributes: {},
269
+ asyncAttributesPending: false,
270
+ };
271
+ const parentSpanId = span?.parentSpanId;
272
+
273
+ return {
274
+ name: span?.name || '',
275
+ kind: span?.kind,
276
+ spanContext: () => normalizedCtx,
277
+ parentSpanContext: parentSpanId
278
+ ? ({
279
+ traceId: normalizedCtx?.traceId,
280
+ spanId: parentSpanId,
281
+ traceFlags: normalizedCtx?.traceFlags,
282
+ traceState: normalizedCtx?.traceState,
283
+ } as any)
284
+ : undefined,
285
+ startTime: span?.startTime,
286
+ endTime: span?.endTime ?? span?.startTime,
287
+ duration: span?.duration,
288
+ status: span?.status,
289
+ attributes: span?.attributes || {},
290
+ links: span?.links || [],
291
+ events: span?.events || [],
292
+ ended: typeof span?.ended === 'boolean' ? span.ended : true,
293
+ droppedAttributesCount: span?.droppedAttributesCount || 0,
294
+ droppedEventsCount: span?.droppedEventsCount || 0,
295
+ droppedLinksCount: span?.droppedLinksCount || 0,
296
+ resource,
297
+ instrumentationScope: normalizedScope as any,
298
+ } as any;
299
+ }
252
300
  }
@@ -40,7 +40,7 @@ export class RecorderReactNativeSDK implements EventRecorder {
40
40
  this.bufferingEnabled = Boolean(buffering?.enabled);
41
41
  this.bufferWindowMs = Math.max(
42
42
  10_000,
43
- buffering?.windowMs || 1 * 60 * 1000
43
+ buffering?.windowMs || 0.5 * 60 * 1000
44
44
  );
45
45
  this.screenRecorder.init(config, this);
46
46
  this.navigationTracker.init(config, this.screenRecorder);
@@ -93,7 +93,8 @@ export class ApiService {
93
93
  async updateSessionAttributes(
94
94
  sessionId: string,
95
95
  requestBody: {
96
- name?: string;
96
+ startedAt?: string;
97
+ stoppedAt?: string;
97
98
  userAttributes?: IUserAttributes;
98
99
  sessionAttributes?: ISessionAttributes;
99
100
  resourceAttributes?: IResourceAttributes;
@@ -1,9 +1,14 @@
1
1
  import { Platform } from 'react-native';
2
+ import { EventType } from '@rrweb/types';
2
3
  import type {
3
4
  CrashBufferEventMap,
4
5
  CrashBufferEventName,
5
6
  CrashBufferErrorSpanAppendedEvent,
6
7
  CrashBuffer,
8
+ CrashBufferRrwebEventPayload,
9
+ CrashBufferOtelSpanPayload,
10
+ CrashBufferOtelSpanBatchPayload,
11
+ CrashBufferSnapshot,
7
12
  } from '@multiplayer-app/session-recorder-common';
8
13
  import { SpanStatusCode } from '@opentelemetry/api';
9
14
 
@@ -43,15 +48,6 @@ const RECORD_PREFIX = 'mp_crash_buffer_rec_v1:';
43
48
  const randomId = (): string =>
44
49
  `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
45
50
 
46
- export type CrashBufferSnapshot = {
47
- rrwebEvents: Array<{ ts: number; event: any }>;
48
- otelSpans: Array<{ ts: number; span: any }>;
49
- attrs: any | null;
50
- windowMs: number;
51
- fromTs: number;
52
- toTs: number;
53
- };
54
-
55
51
  export class CrashBufferService implements CrashBuffer {
56
52
  private static instance: CrashBufferService | null = null;
57
53
 
@@ -59,7 +55,9 @@ export class CrashBufferService implements CrashBuffer {
59
55
  private indexLoaded = false;
60
56
  private lastPruneAt = 0;
61
57
  private opChain: Promise<any> = Promise.resolve();
62
- private defaultWindowMs: number = 1 * 60 * 1000;
58
+ private defaultWindowMs: number = 0.5 * 60 * 1000;
59
+ private lastSeenEventTs: number = 0;
60
+
63
61
  private listeners = new Map<
64
62
  CrashBufferEventName,
65
63
  Set<(payload: CrashBufferEventMap[CrashBufferEventName]) => void>
@@ -111,19 +109,34 @@ export class CrashBufferService implements CrashBuffer {
111
109
  }
112
110
 
113
111
  async appendEvent(
114
- payload: { ts: number; event: any },
112
+ payload: CrashBufferRrwebEventPayload,
115
113
  windowMs?: number
116
114
  ): Promise<void> {
115
+ const ts = payload?.ts ?? Date.now();
116
+ this.lastSeenEventTs = Math.max(this.lastSeenEventTs, ts);
117
+
118
+ const rawEventType =
119
+ (payload as any)?.event?.eventType ?? (payload as any)?.event?.type;
120
+ const isFullSnapshot =
121
+ Boolean(payload.isFullSnapshot) ||
122
+ rawEventType === EventType.FullSnapshot;
123
+
124
+ const record: CrashBufferRrwebEventPayload = {
125
+ ...payload,
126
+ ts,
127
+ isFullSnapshot,
128
+ };
129
+
117
130
  return this.appendRecord(
118
131
  'rrweb',
119
- payload.ts,
120
- payload,
132
+ record.ts,
133
+ record,
121
134
  windowMs ?? this.defaultWindowMs
122
135
  );
123
136
  }
124
137
 
125
138
  async appendSpans(
126
- payload: Array<{ ts: number; span: any }>,
139
+ payload: CrashBufferOtelSpanBatchPayload,
127
140
  windowMs?: number
128
141
  ): Promise<void> {
129
142
  if (!payload.length) return;
@@ -161,7 +174,7 @@ export class CrashBufferService implements CrashBuffer {
161
174
  }
162
175
 
163
176
  setDefaultWindowMs(windowMs: number): void {
164
- this.defaultWindowMs = Math.max(10_000, windowMs || 1 * 60 * 1000);
177
+ this.defaultWindowMs = Math.max(10_000, windowMs || 0.5 * 60 * 1000);
165
178
  }
166
179
 
167
180
  on<E extends CrashBufferEventName>(
@@ -281,32 +294,60 @@ export class CrashBufferService implements CrashBuffer {
281
294
  }
282
295
  }
283
296
 
284
- const rrwebEvents: Array<{ ts: number; event: any }> = [];
285
- const otelSpans: Array<{ ts: number; span: any }> = [];
297
+ const allEvents: CrashBufferRrwebEventPayload[] = [];
298
+ const allSpans: CrashBufferOtelSpanPayload[] = [];
286
299
 
287
300
  for (const e of entries.sort((a, b) => a.ts - b.ts)) {
288
301
  const key = `${RECORD_PREFIX}${e.id}`;
289
302
  const payload = byKey.get(key);
290
303
  if (!payload) continue;
291
- if (e.kind === 'rrweb') rrwebEvents.push(payload);
292
- if (e.kind === 'span') otelSpans.push(payload);
304
+ if (e.kind === 'rrweb') allEvents.push(payload);
305
+ if (e.kind === 'span') allSpans.push(payload);
293
306
  }
294
307
 
295
- let attrs: any | null = null;
296
- try {
297
- const raw = AsyncStorage ? await AsyncStorage.getItem(ATTRS_KEY) : null;
298
- attrs = raw ? JSON.parse(raw) : null;
299
- } catch (_e) {
300
- attrs = null;
308
+ // Mirror browser semantics:
309
+ // - Ensure the rrweb stream starts at Meta -> FullSnapshot (or is empty).
310
+ // - Only include spans from the replayable window onward.
311
+ const eventsSorted = allEvents.slice().sort((a, b) => a.ts - b.ts);
312
+ const firstSnapshotIdx = eventsSorted.findIndex((e) => {
313
+ const t = (e as any)?.event?.eventType ?? (e as any)?.event?.type;
314
+ return t === EventType.FullSnapshot;
315
+ });
316
+
317
+ if (firstSnapshotIdx < 0) {
318
+ return {
319
+ events: [],
320
+ spans: [],
321
+ startedAt: fromTs,
322
+ stoppedAt: toTs,
323
+ };
324
+ }
325
+
326
+ let startIdx = firstSnapshotIdx;
327
+ for (let i = firstSnapshotIdx - 1; i >= 0; i--) {
328
+ const t =
329
+ (eventsSorted[i] as any)?.event?.eventType ??
330
+ (eventsSorted[i] as any)?.event?.type;
331
+ if (t === EventType.Meta) {
332
+ startIdx = i;
333
+ break;
334
+ }
301
335
  }
302
336
 
337
+ const rrwebEvents = eventsSorted.slice(startIdx);
338
+ const firstEvent = rrwebEvents[0];
339
+ const replayStartedAt =
340
+ typeof firstEvent?.ts === 'number' ? firstEvent.ts : fromTs;
341
+
342
+ const otelSpans = allSpans
343
+ .filter((s) => typeof s?.ts === 'number' && s.ts >= replayStartedAt)
344
+ .sort((a, b) => a.ts - b.ts);
345
+
303
346
  return {
304
- rrwebEvents,
305
- otelSpans,
306
- attrs,
307
- windowMs: effectiveWindowMs,
308
- fromTs,
309
- toTs,
347
+ spans: otelSpans,
348
+ events: rrwebEvents,
349
+ stoppedAt: toTs,
350
+ startedAt: replayStartedAt,
310
351
  };
311
352
  });
312
353
  }
@@ -317,6 +358,7 @@ export class CrashBufferService implements CrashBuffer {
317
358
  await this.ensureIndexLoaded();
318
359
  const keys = this.index.map((e) => `${RECORD_PREFIX}${e.id}`);
319
360
  this.index = [];
361
+ this.lastSeenEventTs = 0;
320
362
  try {
321
363
  await AsyncStorage.multiRemove([INDEX_KEY, ATTRS_KEY, ...keys]);
322
364
  } catch (_e) {
@@ -216,8 +216,11 @@ export class SocketService extends Observable<SocketServiceEvents> {
216
216
  }
217
217
  }
218
218
 
219
- public setUser(userAttributes: IUserAttributes | null): void {
220
- this.emitSocketEvent(SOCKET_SET_USER_EVENT, userAttributes);
219
+ public setUser(data: {
220
+ userAttributes: IUserAttributes | null;
221
+ clientId?: string;
222
+ }): void {
223
+ this.emitSocketEvent(SOCKET_SET_USER_EVENT, data);
221
224
  }
222
225
 
223
226
  public close(): Promise<void> {